refactor(rollbar): update to use routing & entities
This commit is contained in:
@@ -29,6 +29,7 @@ export const rollbarApiRef = createApiRef<RollbarApi>({
|
||||
|
||||
export interface RollbarApi {
|
||||
getAllProjects(): Promise<RollbarProject[]>;
|
||||
getProject(projectName: string): Promise<RollbarProject>;
|
||||
getTopActiveItems(
|
||||
project: string,
|
||||
hours?: number,
|
||||
|
||||
@@ -30,9 +30,11 @@ export class RollbarClient implements RollbarApi {
|
||||
}
|
||||
|
||||
async getAllProjects(): Promise<RollbarProject[]> {
|
||||
const path = `/projects`;
|
||||
return await this.get(`/projects`);
|
||||
}
|
||||
|
||||
return await this.get(path);
|
||||
async getProject(projectName: string): Promise<RollbarProject> {
|
||||
return await this.get(`/projects/${projectName}`);
|
||||
}
|
||||
|
||||
async getTopActiveItems(
|
||||
@@ -40,15 +42,13 @@ export class RollbarClient implements RollbarApi {
|
||||
hours = 24,
|
||||
environment = 'production',
|
||||
): Promise<RollbarTopActiveItem[]> {
|
||||
const path = `/projects/${project}/top_active_items?environment=${environment}&hours=${hours}`;
|
||||
|
||||
return await this.get(path);
|
||||
return await this.get(
|
||||
`/projects/${project}/top_active_items?environment=${environment}&hours=${hours}`,
|
||||
);
|
||||
}
|
||||
|
||||
async getProjectItems(project: string): Promise<RollbarItemsResponse> {
|
||||
const path = `/projects/${project}/items`;
|
||||
|
||||
return await this.get(path);
|
||||
return await this.get(`/projects/${project}/items`);
|
||||
}
|
||||
|
||||
private async get(path: string): Promise<any> {
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
|
||||
import { RollbarApi } from './RollbarApi';
|
||||
import {
|
||||
RollbarItemsResponse,
|
||||
RollbarProject,
|
||||
RollbarTopActiveItem,
|
||||
} from './types';
|
||||
|
||||
export class RollbarMockClient implements RollbarApi {
|
||||
async getAllProjects(): Promise<RollbarProject[]> {
|
||||
return Promise.resolve([
|
||||
{ id: 123, name: 'project-a', accountId: 1, status: 'enabled' },
|
||||
{ id: 356, name: 'project-b', accountId: 1, status: 'enabled' },
|
||||
{ id: 789, name: 'project-c', accountId: 1, status: 'enabled' },
|
||||
]);
|
||||
}
|
||||
|
||||
async getTopActiveItems(
|
||||
_project: string,
|
||||
_hours = 24,
|
||||
_environment = 'production',
|
||||
): Promise<RollbarTopActiveItem[]> {
|
||||
const createItem = (id: number): RollbarTopActiveItem => ({
|
||||
item: {
|
||||
id,
|
||||
counter: id,
|
||||
environment: 'production',
|
||||
framework: 2,
|
||||
lastOccurrenceTimestamp: new Date().getTime() / 1000,
|
||||
level: 50,
|
||||
occurrences: 100,
|
||||
projectId: 12345,
|
||||
title: `Some error occurred in service - ${id}`,
|
||||
uniqueOccurrences: 10,
|
||||
},
|
||||
counts: Array.from({ length: 168 }, () =>
|
||||
Math.floor(Math.random() * 100),
|
||||
),
|
||||
});
|
||||
|
||||
const items = Array.from({ length: 10 }, (_, i) => createItem(i));
|
||||
|
||||
return Promise.resolve(items);
|
||||
}
|
||||
|
||||
async getProjectItems(_project: string): Promise<RollbarItemsResponse> {
|
||||
return Promise.resolve({
|
||||
items: [],
|
||||
page: 0,
|
||||
totalCount: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './RollbarApi';
|
||||
export * from './RollbarClient';
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { RollbarProject } from '../RollbarProject/RollbarProject';
|
||||
|
||||
type Props = {
|
||||
entity: Entity;
|
||||
};
|
||||
|
||||
export const EntityPageRollbar = ({ entity }: Props) => {
|
||||
return <RollbarProject entity={entity} />;
|
||||
};
|
||||
+12
-3
@@ -21,13 +21,14 @@ import {
|
||||
ConfigApi,
|
||||
configApiRef,
|
||||
} from '@backstage/core';
|
||||
import { catalogApiRef, CatalogApi } from '@backstage/plugin-catalog';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { render } from '@testing-library/react';
|
||||
import { RollbarApi, rollbarApiRef } from '../../api/RollbarApi';
|
||||
import { RollbarProject } from '../../api/types';
|
||||
import { RollbarPage } from './RollbarPage';
|
||||
import { RollbarHome } from './RollbarHome';
|
||||
|
||||
describe('RollbarPage component', () => {
|
||||
describe('RollbarHome component', () => {
|
||||
const projects: RollbarProject[] = [
|
||||
{ id: 123, name: 'abc', accountId: 1, status: 'enabled' },
|
||||
{ id: 456, name: 'xyz', accountId: 1, status: 'enabled' },
|
||||
@@ -47,6 +48,14 @@ describe('RollbarPage component', () => {
|
||||
apis={ApiRegistry.from([
|
||||
[rollbarApiRef, rollbarApi],
|
||||
[configApiRef, config],
|
||||
[
|
||||
catalogApiRef,
|
||||
({
|
||||
async getEntities() {
|
||||
return [];
|
||||
},
|
||||
} as Partial<CatalogApi>) as CatalogApi,
|
||||
],
|
||||
])}
|
||||
>
|
||||
{children}
|
||||
@@ -55,7 +64,7 @@ describe('RollbarPage component', () => {
|
||||
);
|
||||
|
||||
it('should render rollbar landing page', async () => {
|
||||
const rendered = renderWrapped(<RollbarPage />);
|
||||
const rendered = renderWrapped(<RollbarHome />);
|
||||
expect(rendered.getByText(/Rollbar/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
+12
-28
@@ -14,42 +14,26 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { ReactNode } from 'react';
|
||||
import {
|
||||
Header,
|
||||
HeaderLabel,
|
||||
Page,
|
||||
pageTheme,
|
||||
Content,
|
||||
ContentHeader,
|
||||
SupportButton,
|
||||
} from '@backstage/core';
|
||||
import { Grid } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
import { Content, Header, Page, pageTheme } from '@backstage/core';
|
||||
import { RollbarProjectTable } from '../RollbarProjectTable/RollbarProjectTable';
|
||||
import { useRollbarEntities } from '../../hooks/useRollbarEntities';
|
||||
|
||||
type Props = {
|
||||
title?: string;
|
||||
children: ReactNode;
|
||||
};
|
||||
export const RollbarHome = () => {
|
||||
const { entities, loading, error } = useRollbarEntities();
|
||||
|
||||
export const RollbarLayout = ({ title = 'Dashboard', children }: Props) => {
|
||||
return (
|
||||
<Page theme={pageTheme.tool}>
|
||||
<Header
|
||||
title="Rollbar"
|
||||
subtitle="Real-time error tracking & debugging tools for developers"
|
||||
>
|
||||
<HeaderLabel label="Owner" value="Spotify" />
|
||||
<HeaderLabel label="Lifecycle" value="Alpha" />
|
||||
</Header>
|
||||
/>
|
||||
<Content>
|
||||
<ContentHeader title={title}>
|
||||
<SupportButton>
|
||||
Rollbar plugin allows you to preview issues and navigate to rollbar.
|
||||
</SupportButton>
|
||||
</ContentHeader>
|
||||
<Grid container spacing={3} direction="column">
|
||||
{children}
|
||||
</Grid>
|
||||
<RollbarProjectTable
|
||||
entities={entities || []}
|
||||
loading={loading}
|
||||
error={error}
|
||||
/>
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { useTopActiveItems } from '../../hooks/useTopActiveItems';
|
||||
import { RollbarTopItemsTable } from '../RollbarTopItemsTable/RollbarTopItemsTable';
|
||||
|
||||
type Props = {
|
||||
entity: Entity;
|
||||
};
|
||||
|
||||
export const RollbarProject = ({ entity }: Props) => {
|
||||
const { items, organization, project, loading, error } = useTopActiveItems(
|
||||
entity,
|
||||
);
|
||||
|
||||
return (
|
||||
<RollbarTopItemsTable
|
||||
organization={organization}
|
||||
project={project}
|
||||
items={items || []}
|
||||
loading={loading}
|
||||
error={error}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -26,6 +26,7 @@ import { render } from '@testing-library/react';
|
||||
import { RollbarApi, rollbarApiRef } from '../../api/RollbarApi';
|
||||
import { RollbarTopActiveItem } from '../../api/types';
|
||||
import { RollbarProjectPage } from './RollbarProjectPage';
|
||||
import { catalogApiRef, CatalogApi } from '@backstage/plugin-catalog';
|
||||
|
||||
describe('RollbarProjectPage component', () => {
|
||||
const items: RollbarTopActiveItem[] = [
|
||||
@@ -60,6 +61,17 @@ describe('RollbarProjectPage component', () => {
|
||||
apis={ApiRegistry.from([
|
||||
[rollbarApiRef, rollbarApi],
|
||||
[configApiRef, config],
|
||||
[
|
||||
catalogApiRef,
|
||||
({
|
||||
async getEntityByName() {
|
||||
return {
|
||||
metadata: { name: 'foo' },
|
||||
spec: { owner: 'bar', lifecycle: 'experimental' },
|
||||
} as any;
|
||||
},
|
||||
} as Partial<CatalogApi>) as CatalogApi,
|
||||
],
|
||||
])}
|
||||
>
|
||||
{children}
|
||||
@@ -69,6 +81,6 @@ describe('RollbarProjectPage component', () => {
|
||||
|
||||
it('should render rollbar project page', async () => {
|
||||
const rendered = renderWrapped(<RollbarProjectPage />);
|
||||
expect(rendered.getByText(/Top Active Items/)).toBeInTheDocument();
|
||||
expect(rendered.getByText(/Rollbar/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,39 +15,22 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useAsync } from 'react-use';
|
||||
import { configApiRef, useApi } from '@backstage/core';
|
||||
import { rollbarApiRef } from '../../api/RollbarApi';
|
||||
import { RollbarLayout } from '../RollbarLayout/RollbarLayout';
|
||||
import { RollbarTopItemsTable } from '../RollbarTopItemsTable/RollbarTopItemsTable';
|
||||
import { Content, Header, HeaderLabel, Page, pageTheme } from '@backstage/core';
|
||||
import { useCatalogEntity } from '../../hooks/useCatalogEntity';
|
||||
import { RollbarProject } from '../RollbarProject/RollbarProject';
|
||||
|
||||
export const RollbarProjectPage = () => {
|
||||
const configApi = useApi(configApiRef);
|
||||
const rollbarApi = useApi(rollbarApiRef);
|
||||
const org =
|
||||
configApi.getOptionalString('rollbar.organization') ??
|
||||
configApi.getString('organization.name');
|
||||
const { componentId } = useParams() as {
|
||||
componentId: string;
|
||||
};
|
||||
const { value, loading, error } = useAsync(() =>
|
||||
rollbarApi
|
||||
.getTopActiveItems(componentId, 168)
|
||||
.then(data =>
|
||||
data.sort((a, b) => b.item.occurrences - a.item.occurrences),
|
||||
),
|
||||
);
|
||||
const { entity } = useCatalogEntity();
|
||||
|
||||
return (
|
||||
<RollbarLayout>
|
||||
<RollbarTopItemsTable
|
||||
items={value || []}
|
||||
organization={org}
|
||||
project={componentId}
|
||||
loading={loading}
|
||||
error={error}
|
||||
/>
|
||||
</RollbarLayout>
|
||||
<Page theme={pageTheme.tool}>
|
||||
<Header title={entity?.metadata?.name} subtitle="Rollbar Project">
|
||||
<HeaderLabel label="Owner" value={entity?.spec?.owner} />
|
||||
<HeaderLabel label="Lifecycle" value={entity?.spec?.lifecycle} />
|
||||
</Header>
|
||||
<Content>
|
||||
{entity ? <RollbarProject entity={entity} /> : 'Loading'}
|
||||
</Content>
|
||||
</Page>
|
||||
);
|
||||
};
|
||||
|
||||
+24
-44
@@ -15,68 +15,49 @@
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
import { Link as RouterLink, generatePath } from 'react-router-dom';
|
||||
import { Table, TableColumn } from '@backstage/core';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { Link } from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import OpenInNewIcon from '@material-ui/icons/OpenInNew';
|
||||
import { Table, TableColumn } from '@backstage/core';
|
||||
import { RollbarProject } from '../../api/types';
|
||||
|
||||
const projectUrl = (org: string, id: number) =>
|
||||
`https://rollbar.com/${org}/all/items/?projects=${id}`;
|
||||
import { entityRouteRef } from '../../routes';
|
||||
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
title: 'ID',
|
||||
field: 'id',
|
||||
type: 'numeric',
|
||||
align: 'left',
|
||||
width: '100px',
|
||||
},
|
||||
{
|
||||
title: 'Name',
|
||||
field: 'name',
|
||||
field: 'metadata.name',
|
||||
type: 'string',
|
||||
highlight: true,
|
||||
render: (row: Partial<RollbarProject>) => (
|
||||
<Link component={RouterLink} to={`/rollbar/${row.name}`}>
|
||||
{row.name}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Status',
|
||||
field: 'status',
|
||||
type: 'string',
|
||||
},
|
||||
{
|
||||
title: 'Open',
|
||||
width: '10%',
|
||||
render: (row: any) => (
|
||||
render: (entity: any) => (
|
||||
<Link
|
||||
href={projectUrl(row.organization, row.id)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
component={RouterLink}
|
||||
to={generatePath(entityRouteRef.path, {
|
||||
optionalNamespaceAndName: [
|
||||
entity.metadata.namespace,
|
||||
entity.metadata.name,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join(':'),
|
||||
kind: entity.kind,
|
||||
})}
|
||||
>
|
||||
<OpenInNewIcon />
|
||||
{entity.metadata.name}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Description',
|
||||
field: 'metadata.description',
|
||||
},
|
||||
];
|
||||
|
||||
type Props = {
|
||||
projects: RollbarProject[];
|
||||
entities: Entity[];
|
||||
loading: boolean;
|
||||
organization: string;
|
||||
error?: any;
|
||||
};
|
||||
|
||||
export const RollbarProjectTable = ({
|
||||
projects,
|
||||
organization,
|
||||
loading,
|
||||
error,
|
||||
}: Props) => {
|
||||
export const RollbarProjectTable = ({ entities, loading, error }: Props) => {
|
||||
if (error) {
|
||||
return (
|
||||
<div>
|
||||
@@ -92,14 +73,13 @@ export const RollbarProjectTable = ({
|
||||
isLoading={loading}
|
||||
columns={columns}
|
||||
options={{
|
||||
padding: 'dense',
|
||||
search: true,
|
||||
paging: true,
|
||||
pageSize: 10,
|
||||
showEmptyDataSourceMessage: !loading,
|
||||
}}
|
||||
title="Projects"
|
||||
data={projects.map(p => ({ organization, ...p }))}
|
||||
data={entities}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -16,17 +16,15 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Table, TableColumn } from '@backstage/core';
|
||||
import { Link } from '@material-ui/core';
|
||||
import { Box, Link, Typography } from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import {
|
||||
RollbarFrameworkId,
|
||||
RollbarLevel,
|
||||
RollbarTopActiveItem,
|
||||
} from '../../api/types';
|
||||
import { RollbarTrendGraph } from '../RollbarTrendGraph/RollbarTrendGraph';
|
||||
|
||||
const itemUrl = (org: string, project: string, id: number) =>
|
||||
`https://rollbar.com/${org}/${project}/items/${id}`;
|
||||
import { buildItemUrl } from '../../utils';
|
||||
import { TrendGraph } from '../TrendGraph/TrendGraph';
|
||||
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
@@ -37,7 +35,7 @@ const columns: TableColumn[] = [
|
||||
width: '70px',
|
||||
render: (data: any) => (
|
||||
<Link
|
||||
href={itemUrl(data.org, data.project, data.item.counter)}
|
||||
href={buildItemUrl(data.org, data.project, data.item.counter)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
@@ -54,7 +52,7 @@ const columns: TableColumn[] = [
|
||||
{
|
||||
title: 'Trend',
|
||||
sorting: false,
|
||||
render: (data: any) => <RollbarTrendGraph counts={data.counts} />,
|
||||
render: (data: any) => <TrendGraph counts={data.counts} />,
|
||||
},
|
||||
{
|
||||
title: 'Occurrences',
|
||||
@@ -127,7 +125,12 @@ export const RollbarTopItemsTable = ({
|
||||
pageSize: 5,
|
||||
showEmptyDataSourceMessage: !loading,
|
||||
}}
|
||||
title="Top Active Items"
|
||||
title={
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box mr={1} />
|
||||
<Typography variant="h6">Top Active Items / {project}</Typography>
|
||||
</Box>
|
||||
}
|
||||
data={items.map(i => ({ org: organization, project, ...i }))}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Routes, Route } from 'react-router';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { WarningPanel } from '@backstage/core';
|
||||
import { catalogRouteRef } from '../routes';
|
||||
import { ROLLBAR_ANNOTATION } from '../constants';
|
||||
import { EntityPageRollbar } from './EntityPageRollbar/EntityPageRollbar';
|
||||
|
||||
export const isPluginApplicableToEntity = (entity: Entity) =>
|
||||
entity.metadata.annotations?.[ROLLBAR_ANNOTATION] !== '';
|
||||
|
||||
type Props = {
|
||||
entity: Entity;
|
||||
};
|
||||
|
||||
export const Router = ({ entity }: Props) =>
|
||||
!isPluginApplicableToEntity(entity) ? (
|
||||
<WarningPanel title="Rollbar plugin:">
|
||||
<pre>
|
||||
entity.metadata.annotations['{ROLLBAR_ANNOTATION}']` key is missing on
|
||||
the entity.
|
||||
</pre>
|
||||
</WarningPanel>
|
||||
) : (
|
||||
<Routes>
|
||||
<Route
|
||||
path={`/${catalogRouteRef.path}`}
|
||||
element={<EntityPageRollbar entity={entity} />}
|
||||
/>
|
||||
</Routes>
|
||||
);
|
||||
+3
-5
@@ -17,15 +17,13 @@
|
||||
import React from 'react';
|
||||
import { render } from '@testing-library/react';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { RollbarTrendGraph } from './RollbarTrendGraph';
|
||||
import { TrendGraph } from './TrendGraph';
|
||||
|
||||
describe('RollbarTrendGraph component', () => {
|
||||
describe('TrendGraph component', () => {
|
||||
it('should render a trend graph sparkline', async () => {
|
||||
const mockCounts = [1, 2, 3, 4];
|
||||
const rendered = render(
|
||||
wrapInTestApp(
|
||||
<RollbarTrendGraph counts={mockCounts} data-testid="graph" />,
|
||||
),
|
||||
wrapInTestApp(<TrendGraph counts={mockCounts} data-testid="graph" />),
|
||||
);
|
||||
expect(rendered).toBeTruthy();
|
||||
});
|
||||
+1
-1
@@ -21,7 +21,7 @@ type Props = {
|
||||
counts: number[];
|
||||
};
|
||||
|
||||
export const RollbarTrendGraph = ({ counts }: Props) => {
|
||||
export const TrendGraph = ({ counts }: Props) => {
|
||||
return (
|
||||
<Sparklines data={counts} svgHeight={48} min={0} margin={4}>
|
||||
<SparklinesBars barWidth={2} />
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const ROLLBAR_ANNOTATION = 'rollbar.com/project-slug';
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useAsync } from 'react-use';
|
||||
import { useApi } from '@backstage/core';
|
||||
import {
|
||||
catalogApiRef,
|
||||
useEntityCompoundName,
|
||||
} from '@backstage/plugin-catalog';
|
||||
|
||||
export function useCatalogEntity() {
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
const { namespace, name } = useEntityCompoundName();
|
||||
|
||||
const { value: entity, error, loading } = useAsync(
|
||||
() => catalogApi.getEntityByName({ kind: 'Component', namespace, name }),
|
||||
[catalogApi, namespace, name],
|
||||
);
|
||||
|
||||
return { entity, error, loading };
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useApi, configApiRef } from '@backstage/core';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { ROLLBAR_ANNOTATION } from '../constants';
|
||||
|
||||
export function useProjectSlugFromEntity(entity: Entity) {
|
||||
const configApi = useApi(configApiRef);
|
||||
|
||||
const [project, organization] = (
|
||||
entity?.metadata?.annotations?.[ROLLBAR_ANNOTATION] ?? ''
|
||||
)
|
||||
.split('/')
|
||||
.reverse();
|
||||
|
||||
return {
|
||||
project,
|
||||
organization:
|
||||
organization ??
|
||||
configApi.getOptionalString('rollbar.organization') ??
|
||||
configApi.getString('organization.name'),
|
||||
};
|
||||
}
|
||||
+16
-20
@@ -14,29 +14,25 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
import { configApiRef, useApi } from '@backstage/core';
|
||||
import { rollbarApiRef } from '../../api/RollbarApi';
|
||||
import { RollbarLayout } from '../RollbarLayout/RollbarLayout';
|
||||
import { RollbarProjectTable } from './RollbarProjectTable';
|
||||
import { useApi, configApiRef } from '@backstage/core';
|
||||
import { catalogApiRef } from '@backstage/plugin-catalog';
|
||||
import { ROLLBAR_ANNOTATION } from '../constants';
|
||||
|
||||
export const RollbarPage = () => {
|
||||
export function useRollbarEntities() {
|
||||
const configApi = useApi(configApiRef);
|
||||
const rollbarApi = useApi(rollbarApiRef);
|
||||
const org =
|
||||
const catalogApi = useApi(catalogApiRef);
|
||||
|
||||
const organization =
|
||||
configApi.getOptionalString('rollbar.organization') ??
|
||||
configApi.getString('organization.name');
|
||||
const { value, loading, error } = useAsync(() => rollbarApi.getAllProjects());
|
||||
|
||||
return (
|
||||
<RollbarLayout>
|
||||
<RollbarProjectTable
|
||||
projects={value || []}
|
||||
organization={org}
|
||||
loading={loading}
|
||||
error={error}
|
||||
/>
|
||||
</RollbarLayout>
|
||||
);
|
||||
};
|
||||
const { value, loading, error } = useAsync(async () => {
|
||||
const entities = await catalogApi.getEntities();
|
||||
return entities.filter(entity => {
|
||||
return !!entity.metadata.annotations?.[ROLLBAR_ANNOTATION];
|
||||
});
|
||||
}, [catalogApi]);
|
||||
|
||||
return { entities: value, organization, loading, error };
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { useAsync } from 'react-use';
|
||||
import { useApi } from '@backstage/core';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { rollbarApiRef } from '../api';
|
||||
import { RollbarTopActiveItem } from '../api/types';
|
||||
import { useProjectSlugFromEntity } from './useProject';
|
||||
|
||||
export function useTopActiveItems(entity: Entity) {
|
||||
const api = useApi(rollbarApiRef);
|
||||
const { organization, project } = useProjectSlugFromEntity(entity);
|
||||
const { value, loading, error } = useAsync(() => {
|
||||
if (!project) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
return api
|
||||
.getTopActiveItems(project, 168)
|
||||
.then(data =>
|
||||
data.sort((a, b) => b.item.occurrences - a.item.occurrences),
|
||||
);
|
||||
}, [api, organization, project, entity]);
|
||||
|
||||
return {
|
||||
items: value as RollbarTopActiveItem[],
|
||||
organization,
|
||||
project,
|
||||
loading,
|
||||
error,
|
||||
};
|
||||
}
|
||||
@@ -15,6 +15,9 @@
|
||||
*/
|
||||
|
||||
export { plugin } from './plugin';
|
||||
export * from './api/RollbarApi';
|
||||
export { RollbarClient } from './api/RollbarClient';
|
||||
export { RollbarMockClient } from './api/RollbarMockClient';
|
||||
export * from './api';
|
||||
export * from './routes';
|
||||
export { Router } from './components/Router';
|
||||
export { RollbarProjectPage } from './components/RollbarProjectPage/RollbarProjectPage';
|
||||
export { EntityPageRollbar } from './components/EntityPageRollbar/EntityPageRollbar';
|
||||
export { ROLLBAR_ANNOTATION } from './constants';
|
||||
|
||||
@@ -15,14 +15,14 @@
|
||||
*/
|
||||
|
||||
import { createPlugin } from '@backstage/core';
|
||||
import { RollbarPage } from './components/RollbarPage/RollbarPage';
|
||||
import { rootRouteRef, entityRouteRef } from './routes';
|
||||
import { RollbarHome } from './components/RollbarHome/RollbarHome';
|
||||
import { RollbarProjectPage } from './components/RollbarProjectPage/RollbarProjectPage';
|
||||
import { rootRoute, rootProjectRoute } from './routes';
|
||||
|
||||
export const plugin = createPlugin({
|
||||
id: 'rollbar',
|
||||
register({ router }) {
|
||||
router.addRoute(rootRoute, RollbarPage);
|
||||
router.addRoute(rootProjectRoute, RollbarProjectPage);
|
||||
router.addRoute(rootRouteRef, RollbarHome);
|
||||
router.addRoute(entityRouteRef, RollbarProjectPage);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -16,12 +16,21 @@
|
||||
|
||||
import { createRouteRef } from '@backstage/core';
|
||||
|
||||
export const rootRoute = createRouteRef({
|
||||
const NoIcon = () => null;
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
icon: NoIcon,
|
||||
path: '/rollbar',
|
||||
title: 'Rollbar Home',
|
||||
});
|
||||
|
||||
export const rootProjectRoute = createRouteRef({
|
||||
path: '/rollbar/:componentId/*',
|
||||
export const entityRouteRef = createRouteRef({
|
||||
path: '/rollbar/:optionalNamespaceAndName',
|
||||
title: 'Rollbar',
|
||||
});
|
||||
|
||||
export const catalogRouteRef = createRouteRef({
|
||||
icon: NoIcon,
|
||||
path: '',
|
||||
title: 'Rollbar',
|
||||
});
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export const buildProjectUrl = (org: string, id: number) =>
|
||||
`https://rollbar.com/${org}/all/items/?projects=${id}`;
|
||||
|
||||
export const buildItemUrl = (org: string, project: string, id: number) =>
|
||||
`https://rollbar.com/${org}/${project}/items/${id}`;
|
||||
Reference in New Issue
Block a user