refactor(rollbar): update to use routing & entities

This commit is contained in:
Andrew Thauer
2020-09-07 08:38:02 -04:00
parent 2b114fde44
commit 66ddd0cc56
27 changed files with 489 additions and 242 deletions
+46 -14
View File
@@ -26,25 +26,57 @@ export { plugin as Rollbar } from '@backstage/plugin-rollbar';
import { RollbarClient, rollbarApiRef } from '@backstage/plugin-rollbar';
// ...
builder.add(
rollbarApiRef,
new RollbarClient({
apiOrigin: backendUrl,
basePath: '/rollbar',
}),
);
// Alternatively you can use the mock client
// builder.add(rollbarApiRef, new RollbarMockClient());
builder.add(rollbarApiRef, new RollbarClient({ discoveryApi }));
```
5. Run app with `yarn start` and navigate to `/rollbar`
5. Add to the app `EntityPage` component:
```ts
// packages/app/src/components/catalog/EntityPage.tsx
import { Router as RollbarRouter } from '@backstage/plugin-rollbar';
// ...
const ServiceEntityPage = ({ entity }: { entity: Entity }) => (
<EntityPageLayout>
// ...
<EntityPageLayout.Content
path="/rollbar"
title="Errors"
element={<RollbarRouter entity={entity} />}
/>
</EntityPageLayout>
);
```
6. Setup the `app.config.yaml` and account token environment variable
```yaml
# app.config.yaml
rollbar:
organization: spotify
accountToken:
$secret:
env: ROLLBAR_ACCOUNT_TOKEN
```
7. Annotate entities with the rollbar project slug
```yaml
# pump-station-catalog-component.yaml
# ...
metadata:
annotations:
rollbar.com/project-slug: organization-name/project-name
# -- or just ---
rollbar.com/project-slug: project-name
```
8. Run app with `yarn start` and navigate to `/rollbar` or a catalog entity
## Features
- List rollbar projects
- View top active items for each project
- List rollbar entities that are annotated with `rollbar.com/project-slug`
- View top active items for each rollbar annotated entity
## Limitations
+2
View File
@@ -21,7 +21,9 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/catalog-model": "^0.1.1-alpha.21",
"@backstage/core": "^0.1.1-alpha.21",
"@backstage/plugin-catalog": "^0.1.1-alpha.21",
"@backstage/theme": "^0.1.1-alpha.21",
"@material-ui/core": "^4.9.1",
"@material-ui/icons": "^4.9.1",
+1
View File
@@ -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,
+8 -8
View File
@@ -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,
});
}
}
+18
View File
@@ -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} />;
};
@@ -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();
});
});
@@ -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>
);
};
@@ -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 }))}
/>
);
+47
View File
@@ -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>
);
@@ -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();
});
@@ -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} />
+17
View File
@@ -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 };
}
+37
View File
@@ -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'),
};
}
@@ -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,
};
}
+6 -3
View File
@@ -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';
+4 -4
View File
@@ -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);
},
});
+12 -3
View File
@@ -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',
});
+21
View File
@@ -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}`;