feat: add rollbar links
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import {
|
||||
Header,
|
||||
HeaderLabel,
|
||||
Page,
|
||||
pageTheme,
|
||||
Content,
|
||||
@@ -36,7 +37,10 @@ export const RollbarLayout = ({ title = 'Dashboard', children }: Props) => {
|
||||
<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>
|
||||
|
||||
@@ -15,7 +15,12 @@
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core';
|
||||
import {
|
||||
ApiProvider,
|
||||
ApiRegistry,
|
||||
ConfigApi,
|
||||
configApiRef,
|
||||
} from '@backstage/core';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { render } from '@testing-library/react';
|
||||
import { RollbarApi, rollbarApiRef } from '../../api/RollbarApi';
|
||||
@@ -30,11 +35,20 @@ describe('RollbarPage component', () => {
|
||||
const rollbarApi: Partial<RollbarApi> = {
|
||||
getAllProjects: () => Promise.resolve(projects),
|
||||
};
|
||||
const config: Partial<ConfigApi> = {
|
||||
getString: () => 'foo',
|
||||
getOptionalString: () => 'foo',
|
||||
};
|
||||
|
||||
const renderWrapped = (children: React.ReactNode) =>
|
||||
render(
|
||||
wrapInTestApp(
|
||||
<ApiProvider apis={ApiRegistry.from([[rollbarApiRef, rollbarApi]])}>
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.from([
|
||||
[rollbarApiRef, rollbarApi],
|
||||
[configApiRef, config],
|
||||
])}
|
||||
>
|
||||
{children}
|
||||
</ApiProvider>,
|
||||
),
|
||||
|
||||
@@ -16,18 +16,27 @@
|
||||
|
||||
import React from 'react';
|
||||
import { useAsync } from 'react-use';
|
||||
import { useApi } from '@backstage/core';
|
||||
import { configApiRef, useApi } from '@backstage/core';
|
||||
import { rollbarApiRef } from '../../api/RollbarApi';
|
||||
import { RollbarLayout } from '../RollbarLayout/RollbarLayout';
|
||||
import { RollbarProjectTable } from './RollbarProjectTable';
|
||||
|
||||
export const RollbarPage = () => {
|
||||
const configApi = useApi(configApiRef);
|
||||
const rollbarApi = useApi(rollbarApiRef);
|
||||
const { value, loading } = useAsync(() => rollbarApi.getAllProjects());
|
||||
const org =
|
||||
configApi.getOptionalString('rollbar.organization') ??
|
||||
configApi.getString('organization.name');
|
||||
const { value, loading, error } = useAsync(() => rollbarApi.getAllProjects());
|
||||
|
||||
return (
|
||||
<RollbarLayout>
|
||||
<RollbarProjectTable loading={loading} projects={value || []} />
|
||||
<RollbarProjectTable
|
||||
projects={value || []}
|
||||
organization={org}
|
||||
loading={loading}
|
||||
error={error}
|
||||
/>
|
||||
</RollbarLayout>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,9 +17,14 @@
|
||||
import React from 'react';
|
||||
import { Link as RouterLink } from 'react-router-dom';
|
||||
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}`;
|
||||
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
title: 'ID',
|
||||
@@ -32,7 +37,6 @@ const columns: TableColumn[] = [
|
||||
title: 'Name',
|
||||
field: 'name',
|
||||
type: 'string',
|
||||
align: 'left',
|
||||
highlight: true,
|
||||
render: (row: Partial<RollbarProject>) => (
|
||||
<Link component={RouterLink} to={`/rollbar/${row.name}`}>
|
||||
@@ -44,29 +48,58 @@ const columns: TableColumn[] = [
|
||||
title: 'Status',
|
||||
field: 'status',
|
||||
type: 'string',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
title: 'Open',
|
||||
width: '10%',
|
||||
render: (row: any) => (
|
||||
<Link
|
||||
href={projectUrl(row.organization, row.id)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<OpenInNewIcon />
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
type Props = {
|
||||
projects: RollbarProject[];
|
||||
loading: boolean;
|
||||
organization: string;
|
||||
error?: any;
|
||||
};
|
||||
|
||||
export const RollbarProjectTable = ({ projects, loading }: Props) => {
|
||||
export const RollbarProjectTable = ({
|
||||
projects,
|
||||
organization,
|
||||
loading,
|
||||
error,
|
||||
}: Props) => {
|
||||
if (error) {
|
||||
return (
|
||||
<div>
|
||||
<Alert severity="error">
|
||||
Error encountered while fetching rollbar projects. {error.toString()}
|
||||
</Alert>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Table
|
||||
isLoading={loading}
|
||||
columns={columns}
|
||||
options={{
|
||||
padding: 'dense',
|
||||
paging: true,
|
||||
search: true,
|
||||
paging: true,
|
||||
pageSize: 10,
|
||||
showEmptyDataSourceMessage: !loading,
|
||||
}}
|
||||
title="Projects"
|
||||
data={projects}
|
||||
data={projects.map(p => ({ organization, ...p }))}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -15,7 +15,12 @@
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import { ApiProvider, ApiRegistry } from '@backstage/core';
|
||||
import {
|
||||
ApiProvider,
|
||||
ApiRegistry,
|
||||
ConfigApi,
|
||||
configApiRef,
|
||||
} from '@backstage/core';
|
||||
import { wrapInTestApp } from '@backstage/test-utils';
|
||||
import { render } from '@testing-library/react';
|
||||
import { RollbarApi, rollbarApiRef } from '../../api/RollbarApi';
|
||||
@@ -43,11 +48,20 @@ describe('RollbarProjectPage component', () => {
|
||||
const rollbarApi: Partial<RollbarApi> = {
|
||||
getTopActiveItems: () => Promise.resolve(items),
|
||||
};
|
||||
const config: Partial<ConfigApi> = {
|
||||
getString: () => 'foo',
|
||||
getOptionalString: () => 'foo',
|
||||
};
|
||||
|
||||
const renderWrapped = (children: React.ReactNode) =>
|
||||
render(
|
||||
wrapInTestApp(
|
||||
<ApiProvider apis={ApiRegistry.from([[rollbarApiRef, rollbarApi]])}>
|
||||
<ApiProvider
|
||||
apis={ApiRegistry.from([
|
||||
[rollbarApiRef, rollbarApi],
|
||||
[configApiRef, config],
|
||||
])}
|
||||
>
|
||||
{children}
|
||||
</ApiProvider>,
|
||||
),
|
||||
|
||||
@@ -17,17 +17,21 @@
|
||||
import React from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useAsync } from 'react-use';
|
||||
import { useApi } from '@backstage/core';
|
||||
import { configApiRef, useApi } from '@backstage/core';
|
||||
import { rollbarApiRef } from '../../api/RollbarApi';
|
||||
import { RollbarLayout } from '../RollbarLayout/RollbarLayout';
|
||||
import { RollbarTopItemsTable } from '../RollbarTopItemsTable/RollbarTopItemsTable';
|
||||
|
||||
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 } = useAsync(() =>
|
||||
const { value, loading, error } = useAsync(() =>
|
||||
rollbarApi
|
||||
.getTopActiveItems(componentId, 168)
|
||||
.then(data =>
|
||||
@@ -37,7 +41,13 @@ export const RollbarProjectPage = () => {
|
||||
|
||||
return (
|
||||
<RollbarLayout>
|
||||
<RollbarTopItemsTable loading={loading} items={value || []} />
|
||||
<RollbarTopItemsTable
|
||||
items={value || []}
|
||||
organization={org}
|
||||
project={componentId}
|
||||
loading={loading}
|
||||
error={error}
|
||||
/>
|
||||
</RollbarLayout>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -41,14 +41,28 @@ const items: RollbarTopActiveItem[] = [
|
||||
describe('RollbarTopItemsTable component', () => {
|
||||
it('should render empty data message when loaded and no data', async () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(<RollbarTopItemsTable items={[]} loading={false} />),
|
||||
wrapInTestApp(
|
||||
<RollbarTopItemsTable
|
||||
items={[]}
|
||||
organization="foo"
|
||||
project="bar"
|
||||
loading={false}
|
||||
/>,
|
||||
),
|
||||
);
|
||||
expect(rendered.getByText(/No records to display/)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should display item attributes when loading has finished', async () => {
|
||||
const rendered = render(
|
||||
wrapInTestApp(<RollbarTopItemsTable items={items} loading={false} />),
|
||||
wrapInTestApp(
|
||||
<RollbarTopItemsTable
|
||||
items={items}
|
||||
organization="foo"
|
||||
project="bar"
|
||||
loading={false}
|
||||
/>,
|
||||
),
|
||||
);
|
||||
expect(rendered.getByText(/1234/)).toBeInTheDocument();
|
||||
expect(rendered.getByText(/Error in foo/)).toBeInTheDocument();
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
import React from 'react';
|
||||
import { Table, TableColumn } from '@backstage/core';
|
||||
import { Link } from '@material-ui/core';
|
||||
import { Alert } from '@material-ui/lab';
|
||||
import {
|
||||
RollbarFrameworkId,
|
||||
RollbarLevel,
|
||||
@@ -23,6 +25,9 @@ import {
|
||||
} from '../../api/types';
|
||||
import { RollbarTrendGraph } from '../RollbarTrendGraph/RollbarTrendGraph';
|
||||
|
||||
const itemUrl = (org: string, project: string, id: number) =>
|
||||
`https://rollbar.com/${org}/${project}/items/${id}`;
|
||||
|
||||
const columns: TableColumn[] = [
|
||||
{
|
||||
title: 'ID',
|
||||
@@ -30,6 +35,15 @@ const columns: TableColumn[] = [
|
||||
type: 'string',
|
||||
align: 'left',
|
||||
width: '70px',
|
||||
render: (data: any) => (
|
||||
<Link
|
||||
href={itemUrl(data.org, data.project, data.item.counter)}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{data.item.counter}
|
||||
</Link>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Title',
|
||||
@@ -40,9 +54,7 @@ const columns: TableColumn[] = [
|
||||
{
|
||||
title: 'Trend',
|
||||
sorting: false,
|
||||
render: data => (
|
||||
<RollbarTrendGraph counts={(data as RollbarTopActiveItem).counts} />
|
||||
),
|
||||
render: (data: any) => <RollbarTrendGraph counts={data.counts} />,
|
||||
},
|
||||
{
|
||||
title: 'Occurrences',
|
||||
@@ -81,22 +93,42 @@ const columns: TableColumn[] = [
|
||||
|
||||
type Props = {
|
||||
items: RollbarTopActiveItem[];
|
||||
organization: string;
|
||||
project: string;
|
||||
loading: boolean;
|
||||
error?: any;
|
||||
};
|
||||
|
||||
export const RollbarTopItemsTable = ({ items, loading }: Props) => {
|
||||
export const RollbarTopItemsTable = ({
|
||||
items,
|
||||
organization,
|
||||
project,
|
||||
loading,
|
||||
error,
|
||||
}: Props) => {
|
||||
if (error) {
|
||||
return (
|
||||
<div>
|
||||
<Alert severity="error">
|
||||
Error encountered while fetching rollbar top items. {error.toString()}
|
||||
</Alert>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Table
|
||||
isLoading={loading}
|
||||
columns={columns}
|
||||
options={{
|
||||
padding: 'dense',
|
||||
paging: false,
|
||||
search: true,
|
||||
paging: true,
|
||||
pageSize: 5,
|
||||
showEmptyDataSourceMessage: !loading,
|
||||
}}
|
||||
title="Top Active Items"
|
||||
data={items}
|
||||
data={items.map(i => ({ org: organization, project, ...i }))}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user