diff --git a/docs/features/software-catalog/well-known-annotations.md b/docs/features/software-catalog/well-known-annotations.md
index 159cfb4ca1..18de63d704 100644
--- a/docs/features/software-catalog/well-known-annotations.md
+++ b/docs/features/software-catalog/well-known-annotations.md
@@ -100,6 +100,24 @@ the same for all entities in the catalog.
Specifying this annotation may enable Sentry related features in Backstage for
that entity.
+### rollbar.com/project-slug
+
+```yaml
+# Example:
+metadata:
+ annotations:
+ rollbar.com/project-slug: spotify/pump-station
+```
+
+The value of this annotation is the so-called slug (or alternatively, the ID) of
+a [Rollbar](https://rollbar.com) project within your organization. The value can
+be the format of `[organization]/[project-slug]` or just `[project-slug]`. When
+the organization slug is omitted the `app-config.yaml` will be used as a
+fallback (`rollbar.organization` followed by `organization.name`).
+
+Specifying this annotation may enable Rollbar related features in Backstage for
+that entity.
+
## Deprecated Annotations
The following annotations are deprecated, and only listed here to aid in
diff --git a/plugins/rollbar/README.md b/plugins/rollbar/README.md
index 94ef38b43a..b2d2100dd9 100644
--- a/plugins/rollbar/README.md
+++ b/plugins/rollbar/README.md
@@ -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 }) => (
+
+ // ...
+ }
+ />
+
+);
+```
+
+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
diff --git a/plugins/rollbar/package.json b/plugins/rollbar/package.json
index 9c5343247c..1708dc31ce 100644
--- a/plugins/rollbar/package.json
+++ b/plugins/rollbar/package.json
@@ -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",
diff --git a/plugins/rollbar/src/api/RollbarApi.ts b/plugins/rollbar/src/api/RollbarApi.ts
index c08bc08d48..bde6c5e9f2 100644
--- a/plugins/rollbar/src/api/RollbarApi.ts
+++ b/plugins/rollbar/src/api/RollbarApi.ts
@@ -29,6 +29,7 @@ export const rollbarApiRef = createApiRef({
export interface RollbarApi {
getAllProjects(): Promise;
+ getProject(projectName: string): Promise;
getTopActiveItems(
project: string,
hours?: number,
diff --git a/plugins/rollbar/src/api/RollbarClient.ts b/plugins/rollbar/src/api/RollbarClient.ts
index 1862ad8270..e612890655 100644
--- a/plugins/rollbar/src/api/RollbarClient.ts
+++ b/plugins/rollbar/src/api/RollbarClient.ts
@@ -30,9 +30,11 @@ export class RollbarClient implements RollbarApi {
}
async getAllProjects(): Promise {
- const path = `/projects`;
+ return await this.get(`/projects`);
+ }
- return await this.get(path);
+ async getProject(projectName: string): Promise {
+ return await this.get(`/projects/${projectName}`);
}
async getTopActiveItems(
@@ -40,15 +42,13 @@ export class RollbarClient implements RollbarApi {
hours = 24,
environment = 'production',
): Promise {
- 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 {
- const path = `/projects/${project}/items`;
-
- return await this.get(path);
+ return await this.get(`/projects/${project}/items`);
}
private async get(path: string): Promise {
diff --git a/plugins/rollbar/src/api/RollbarMockClient.ts b/plugins/rollbar/src/api/RollbarMockClient.ts
deleted file mode 100644
index cab9a0d23a..0000000000
--- a/plugins/rollbar/src/api/RollbarMockClient.ts
+++ /dev/null
@@ -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 {
- 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 {
- 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 {
- return Promise.resolve({
- items: [],
- page: 0,
- totalCount: 0,
- });
- }
-}
diff --git a/plugins/rollbar/src/api/index.ts b/plugins/rollbar/src/api/index.ts
new file mode 100644
index 0000000000..f45ff5bf8a
--- /dev/null
+++ b/plugins/rollbar/src/api/index.ts
@@ -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';
diff --git a/plugins/rollbar/src/components/EntityPageRollbar/EntityPageRollbar.tsx b/plugins/rollbar/src/components/EntityPageRollbar/EntityPageRollbar.tsx
new file mode 100644
index 0000000000..888b9aabe0
--- /dev/null
+++ b/plugins/rollbar/src/components/EntityPageRollbar/EntityPageRollbar.tsx
@@ -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 ;
+};
diff --git a/plugins/rollbar/src/components/RollbarPage/RollbarPage.test.tsx b/plugins/rollbar/src/components/RollbarHome/RollbarHome.test.tsx
similarity index 80%
rename from plugins/rollbar/src/components/RollbarPage/RollbarPage.test.tsx
rename to plugins/rollbar/src/components/RollbarHome/RollbarHome.test.tsx
index 11263ed820..c6ee052c0e 100644
--- a/plugins/rollbar/src/components/RollbarPage/RollbarPage.test.tsx
+++ b/plugins/rollbar/src/components/RollbarHome/RollbarHome.test.tsx
@@ -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) as CatalogApi,
+ ],
])}
>
{children}
@@ -55,7 +64,7 @@ describe('RollbarPage component', () => {
);
it('should render rollbar landing page', async () => {
- const rendered = renderWrapped();
+ const rendered = renderWrapped();
expect(rendered.getByText(/Rollbar/)).toBeInTheDocument();
});
});
diff --git a/plugins/rollbar/src/components/RollbarLayout/RollbarLayout.tsx b/plugins/rollbar/src/components/RollbarHome/RollbarHome.tsx
similarity index 51%
rename from plugins/rollbar/src/components/RollbarLayout/RollbarLayout.tsx
rename to plugins/rollbar/src/components/RollbarHome/RollbarHome.tsx
index c0524fabc5..d09c2054b1 100644
--- a/plugins/rollbar/src/components/RollbarLayout/RollbarLayout.tsx
+++ b/plugins/rollbar/src/components/RollbarHome/RollbarHome.tsx
@@ -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 (
-
-
-
+ />
-
-
- Rollbar plugin allows you to preview issues and navigate to rollbar.
-
-
-
- {children}
-
+
);
diff --git a/plugins/rollbar/src/components/RollbarProject/RollbarProject.tsx b/plugins/rollbar/src/components/RollbarProject/RollbarProject.tsx
new file mode 100644
index 0000000000..34881ba560
--- /dev/null
+++ b/plugins/rollbar/src/components/RollbarProject/RollbarProject.tsx
@@ -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 (
+
+ );
+};
diff --git a/plugins/rollbar/src/components/RollbarProjectPage/RollbarProjectPage.test.tsx b/plugins/rollbar/src/components/RollbarProjectPage/RollbarProjectPage.test.tsx
index e4919a68bd..ad3d4a554d 100644
--- a/plugins/rollbar/src/components/RollbarProjectPage/RollbarProjectPage.test.tsx
+++ b/plugins/rollbar/src/components/RollbarProjectPage/RollbarProjectPage.test.tsx
@@ -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) as CatalogApi,
+ ],
])}
>
{children}
@@ -69,6 +81,6 @@ describe('RollbarProjectPage component', () => {
it('should render rollbar project page', async () => {
const rendered = renderWrapped();
- expect(rendered.getByText(/Top Active Items/)).toBeInTheDocument();
+ expect(rendered.getByText(/Rollbar/)).toBeInTheDocument();
});
});
diff --git a/plugins/rollbar/src/components/RollbarProjectPage/RollbarProjectPage.tsx b/plugins/rollbar/src/components/RollbarProjectPage/RollbarProjectPage.tsx
index 6635a72086..b18357e310 100644
--- a/plugins/rollbar/src/components/RollbarProjectPage/RollbarProjectPage.tsx
+++ b/plugins/rollbar/src/components/RollbarProjectPage/RollbarProjectPage.tsx
@@ -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 (
-
-
-
+
+
+
+
+
+
+ {entity ? : 'Loading'}
+
+
);
};
diff --git a/plugins/rollbar/src/components/RollbarPage/RollbarProjectTable.tsx b/plugins/rollbar/src/components/RollbarProjectTable/RollbarProjectTable.tsx
similarity index 57%
rename from plugins/rollbar/src/components/RollbarPage/RollbarProjectTable.tsx
rename to plugins/rollbar/src/components/RollbarProjectTable/RollbarProjectTable.tsx
index fdf90c468e..5b3631ade9 100644
--- a/plugins/rollbar/src/components/RollbarPage/RollbarProjectTable.tsx
+++ b/plugins/rollbar/src/components/RollbarProjectTable/RollbarProjectTable.tsx
@@ -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) => (
-
- {row.name}
-
- ),
- },
- {
- title: 'Status',
- field: 'status',
- type: 'string',
- },
- {
- title: 'Open',
- width: '10%',
- render: (row: any) => (
+ render: (entity: any) => (
-
+ {entity.metadata.name}
),
},
+ {
+ 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 (
@@ -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}
/>
);
};
diff --git a/plugins/rollbar/src/components/RollbarTopItemsTable/RollbarTopItemsTable.tsx b/plugins/rollbar/src/components/RollbarTopItemsTable/RollbarTopItemsTable.tsx
index 913ecca28a..a04155a0c3 100644
--- a/plugins/rollbar/src/components/RollbarTopItemsTable/RollbarTopItemsTable.tsx
+++ b/plugins/rollbar/src/components/RollbarTopItemsTable/RollbarTopItemsTable.tsx
@@ -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) => (
@@ -54,7 +52,7 @@ const columns: TableColumn[] = [
{
title: 'Trend',
sorting: false,
- render: (data: any) => ,
+ render: (data: any) => ,
},
{
title: 'Occurrences',
@@ -127,7 +125,12 @@ export const RollbarTopItemsTable = ({
pageSize: 5,
showEmptyDataSourceMessage: !loading,
}}
- title="Top Active Items"
+ title={
+
+
+ Top Active Items / {project}
+
+ }
data={items.map(i => ({ org: organization, project, ...i }))}
/>
);
diff --git a/plugins/rollbar/src/components/Router.tsx b/plugins/rollbar/src/components/Router.tsx
new file mode 100644
index 0000000000..c765c14f14
--- /dev/null
+++ b/plugins/rollbar/src/components/Router.tsx
@@ -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) ? (
+
+
+ entity.metadata.annotations['{ROLLBAR_ANNOTATION}']` key is missing on
+ the entity.
+
+
+ ) : (
+
+ }
+ />
+
+ );
diff --git a/plugins/rollbar/src/components/RollbarTrendGraph/RollbarTrendGraph.test.tsx b/plugins/rollbar/src/components/TrendGraph/TrendGraph.test.tsx
similarity index 81%
rename from plugins/rollbar/src/components/RollbarTrendGraph/RollbarTrendGraph.test.tsx
rename to plugins/rollbar/src/components/TrendGraph/TrendGraph.test.tsx
index 1f9d317e5b..ac1a328521 100644
--- a/plugins/rollbar/src/components/RollbarTrendGraph/RollbarTrendGraph.test.tsx
+++ b/plugins/rollbar/src/components/TrendGraph/TrendGraph.test.tsx
@@ -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(
- ,
- ),
+ wrapInTestApp(),
);
expect(rendered).toBeTruthy();
});
diff --git a/plugins/rollbar/src/components/RollbarTrendGraph/RollbarTrendGraph.tsx b/plugins/rollbar/src/components/TrendGraph/TrendGraph.tsx
similarity index 93%
rename from plugins/rollbar/src/components/RollbarTrendGraph/RollbarTrendGraph.tsx
rename to plugins/rollbar/src/components/TrendGraph/TrendGraph.tsx
index 4f0b53eb4a..5d518817bc 100644
--- a/plugins/rollbar/src/components/RollbarTrendGraph/RollbarTrendGraph.tsx
+++ b/plugins/rollbar/src/components/TrendGraph/TrendGraph.tsx
@@ -21,7 +21,7 @@ type Props = {
counts: number[];
};
-export const RollbarTrendGraph = ({ counts }: Props) => {
+export const TrendGraph = ({ counts }: Props) => {
return (
diff --git a/plugins/rollbar/src/constants.ts b/plugins/rollbar/src/constants.ts
new file mode 100644
index 0000000000..e3a89d655e
--- /dev/null
+++ b/plugins/rollbar/src/constants.ts
@@ -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';
diff --git a/plugins/rollbar/src/hooks/useCatalogEntity.ts b/plugins/rollbar/src/hooks/useCatalogEntity.ts
new file mode 100644
index 0000000000..172d057738
--- /dev/null
+++ b/plugins/rollbar/src/hooks/useCatalogEntity.ts
@@ -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 };
+}
diff --git a/plugins/rollbar/src/hooks/useProject.ts b/plugins/rollbar/src/hooks/useProject.ts
new file mode 100644
index 0000000000..5d61b63ffb
--- /dev/null
+++ b/plugins/rollbar/src/hooks/useProject.ts
@@ -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'),
+ };
+}
diff --git a/plugins/rollbar/src/components/RollbarPage/RollbarPage.tsx b/plugins/rollbar/src/hooks/useRollbarEntities.ts
similarity index 55%
rename from plugins/rollbar/src/components/RollbarPage/RollbarPage.tsx
rename to plugins/rollbar/src/hooks/useRollbarEntities.ts
index 0460b4a308..3edf34d1f3 100644
--- a/plugins/rollbar/src/components/RollbarPage/RollbarPage.tsx
+++ b/plugins/rollbar/src/hooks/useRollbarEntities.ts
@@ -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 (
-
-
-
- );
-};
+ 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 };
+}
diff --git a/plugins/rollbar/src/hooks/useTopActiveItems.ts b/plugins/rollbar/src/hooks/useTopActiveItems.ts
new file mode 100644
index 0000000000..cb1689355f
--- /dev/null
+++ b/plugins/rollbar/src/hooks/useTopActiveItems.ts
@@ -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,
+ };
+}
diff --git a/plugins/rollbar/src/index.ts b/plugins/rollbar/src/index.ts
index 3a8288a545..2a31c91a13 100644
--- a/plugins/rollbar/src/index.ts
+++ b/plugins/rollbar/src/index.ts
@@ -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';
diff --git a/plugins/rollbar/src/plugin.ts b/plugins/rollbar/src/plugin.ts
index d682750a83..2cf464a8f6 100644
--- a/plugins/rollbar/src/plugin.ts
+++ b/plugins/rollbar/src/plugin.ts
@@ -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);
},
});
diff --git a/plugins/rollbar/src/routes.ts b/plugins/rollbar/src/routes.ts
index ecf20f3167..b514dff3c8 100644
--- a/plugins/rollbar/src/routes.ts
+++ b/plugins/rollbar/src/routes.ts
@@ -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',
});
diff --git a/plugins/rollbar/src/utils/index.ts b/plugins/rollbar/src/utils/index.ts
new file mode 100644
index 0000000000..1c8f8ff39e
--- /dev/null
+++ b/plugins/rollbar/src/utils/index.ts
@@ -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}`;