useEntity and more tests

Signed-off-by: Andrew Johnson <ajohnson@gocardless.com>
This commit is contained in:
Andrew Johnson
2021-03-26 11:30:14 +00:00
parent 29e68d271b
commit 1da2c481d2
8 changed files with 166 additions and 65 deletions
+5 -5
View File
@@ -1,12 +1,12 @@
# Github Deployments Plugin
# GitHub Deployments Plugin
The Github Deployments Plugin displays recent deployments from Github.
The GitHub Deployments Plugin displays recent deployments from GitHub.
![github-deployments-card](./docs/github-deployments-card.png)
## Getting Started
1. Install the Github Deployments Plugin
1. Install the GitHub Deployments Plugin
```bash
# packages/app
@@ -14,7 +14,7 @@ The Github Deployments Plugin displays recent deployments from Github.
yarn add @backstage/plugin-github-deployments
```
2. Add proxy and auth token for Github
2. Add proxy and auth token for GitHub
```yaml
# app-config.yaml
@@ -57,7 +57,7 @@ const OverviewContent = ({ entity }: { entity: Entity }) => (
);
```
5. Add the github.com/project-slug annotation to your catalog-info.yaml file:
5. Add the `github.com/project-slug` annotation to your `catalog-info.yaml` file:
```yaml
apiVersion: backstage.io/v1alpha1
+1 -1
View File
@@ -22,13 +22,13 @@
"dependencies": {
"@backstage/catalog-model": "^0.7.4",
"@backstage/core": "^0.7.2",
"@backstage/plugin-catalog-react": "^0.1.3",
"@backstage/theme": "^0.2.4",
"@material-ui/core": "^4.11.0",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.45",
"@octokit/graphql": "^4.6.1",
"moment": "^2.29.1",
"nock": "^13.0.11",
"react": "^16.13.1",
"react-dom": "^16.13.1",
"react-use": "^15.3.3"
+1 -1
View File
@@ -36,7 +36,7 @@ export interface GithubDeploymentsApi {
export const githubDeploymentsApiRef = createApiRef<GithubDeploymentsApi>({
id: 'plugin.github-deployments.service',
description: 'Used by the Github Deployments plugin to make requests',
description: 'Used by the GitHub Deployments plugin to make requests',
});
export type Options = {
@@ -30,7 +30,14 @@ import { GithubDeploymentsApiClient, githubDeploymentsApiRef } from '../api';
import { githubDeploymentsPlugin } from '../plugin';
import { GithubDeploymentsCard } from './GithubDeploymentsCard';
import { entityStub, responseStub } from '../mocks/mocks';
import { entityStub, noDataResponseStub, responseStub } from '../mocks/mocks';
import { wrapInTestApp } from '@backstage/test-utils';
jest.mock('@backstage/plugin-catalog-react', () => ({
useEntity: () => {
return entityStub;
},
}));
const discoveryApi = UrlPatternDiscovery.compile('http://exampleapi.com');
const errorApiMock = { post: jest.fn(), error$: jest.fn() };
@@ -62,9 +69,11 @@ describe('github-deployments', () => {
worker.use(rest.post('*', (_, res, ctx) => res(ctx.json(responseStub))));
const rendered = render(
<ApiProvider apis={apis}>
<GithubDeploymentsCard entity={entityStub} />
</ApiProvider>,
wrapInTestApp(
<ApiProvider apis={apis}>
<GithubDeploymentsCard />
</ApiProvider>,
),
);
expect(await rendered.findByText('active')).toBeInTheDocument();
@@ -81,5 +90,23 @@ describe('github-deployments', () => {
'https://exampleapi.com/543212345',
);
});
it('should display empty state when no data', async () => {
worker.use(
rest.post('*', (_, res, ctx) => res(ctx.json(noDataResponseStub))),
);
const rendered = render(
wrapInTestApp(
<ApiProvider apis={apis}>
<GithubDeploymentsCard />
</ApiProvider>,
),
);
expect(
await rendered.findByText('No GitHub deployments available.'),
).toBeInTheDocument();
});
});
});
@@ -16,10 +16,16 @@
import React from 'react';
import { LinearProgress } from '@material-ui/core';
import { Entity } from '@backstage/catalog-model';
import { InfoCard, MissingAnnotationEmptyState, useApi } from '@backstage/core';
import {
EmptyState,
InfoCard,
MissingAnnotationEmptyState,
useApi,
} from '@backstage/core';
import { useAsync } from 'react-use';
import { githubDeploymentsApiRef } from '../api';
import GithubDeploymentsTable from './GithubDeploymentsTable/GithubDeploymentsTable';
import { useEntity } from '@backstage/plugin-catalog-react';
export const GITHUB_PROJECT_SLUG_ANNOTATION = 'github.com/project-slug';
@@ -27,16 +33,14 @@ export const isGithubDeploymentsAvailable = (entity: Entity) =>
Boolean(entity?.metadata.annotations?.[GITHUB_PROJECT_SLUG_ANNOTATION]);
const GithubDeploymentsComponent = ({
entity,
projectSlug,
last,
}: {
entity: Entity;
projectSlug: string;
last: number;
}) => {
const api = useApi(githubDeploymentsApiRef);
const annotation =
entity.metadata.annotations?.[GITHUB_PROJECT_SLUG_ANNOTATION] ?? '';
const [owner, repo] = annotation.split('/');
const [owner, repo] = projectSlug.split('/');
const { loading, value, error } = useAsync(
async () => await api.listDeployments({ owner, repo, last }),
@@ -44,32 +48,38 @@ const GithubDeploymentsComponent = ({
if (loading) {
return (
<InfoCard title="Github Deployments">
<InfoCard title="GitHub Deployments">
<LinearProgress />
</InfoCard>
);
}
if (error) {
return (
<InfoCard title="Github Deployments">
Error occurred while fetching data.
<InfoCard title="GitHub Deployments">
Error occured while fetching Data
</InfoCard>
);
}
if (!value || value.length === 0) {
return (
<EmptyState title="No GitHub deployments available." missing="info" />
);
}
return <GithubDeploymentsTable deployments={value || []} />;
};
export const GithubDeploymentsCard = ({
entity,
last,
}: {
entity: Entity;
last?: number;
}) => {
export const GithubDeploymentsCard = ({ last }: { last?: number }) => {
const { entity } = useEntity();
return !isGithubDeploymentsAvailable(entity) ? (
<MissingAnnotationEmptyState annotation={GITHUB_PROJECT_SLUG_ANNOTATION} />
) : (
<GithubDeploymentsComponent entity={entity} last={last || 10} />
<GithubDeploymentsComponent
projectSlug={
entity?.metadata.annotations?.[GITHUB_PROJECT_SLUG_ANNOTATION] || ''
}
last={last || 10}
/>
);
};
@@ -53,7 +53,6 @@ const columns: TableColumn[] = [
},
{
title: 'Status',
field: 'environment',
render: (row: any): React.ReactNode => <State value={row.state} />,
},
{
@@ -81,7 +80,7 @@ const GithubDeploymentsTable = ({
<Table
columns={columns}
options={{ padding: 'dense', paging: true, search: false, pageSize: 5 }}
title="Github Deployments"
title="GitHub Deployments"
data={deployments}
/>
);
+17 -15
View File
@@ -14,23 +14,25 @@
* limitations under the License.
*/
export const entityStub = {
metadata: {
namespace: 'default',
annotations: {
'github.com/project-slug': 'org/repo',
entity: {
metadata: {
namespace: 'default',
annotations: {
'github.com/project-slug': 'org/repo',
},
name: 'sample-service',
description: 'Sample service',
uid: 'g0h33dd9-56h7-835b-b63v-7x5da3j64851',
generation: 1,
},
name: 'sample-service',
description: 'Sample service',
uid: 'g0h33dd9-56h7-835b-b63v-7x5da3j64851',
generation: 1,
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
spec: {
type: 'service',
lifecycle: 'experimental',
},
relations: [],
},
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
spec: {
type: 'service',
lifecycle: 'experimental',
},
relations: [],
};
export const responseStub = {
+83 -20
View File
@@ -1846,6 +1846,23 @@
uuid "^8.0.0"
yup "^0.29.3"
"@backstage/core-api@^0.2.15":
version "0.2.15"
resolved "https://registry.npmjs.org/@backstage/core-api/-/core-api-0.2.15.tgz#24c3e4b1fe0bdee36de26c50b8757f41bb266109"
integrity sha512-OQTmvCGM3hkaLV0+JPF85dZk/X1SXoIFkXZi9pO/DzBPZ1Psuptia9xNXrhnEeYJaCazlCnOCTXS/kdoT1zBVg==
dependencies:
"@backstage/config" "^0.1.4"
"@backstage/theme" "^0.2.5"
"@material-ui/core" "^4.11.0"
"@material-ui/icons" "^4.9.1"
"@types/prop-types" "^15.7.3"
"@types/react" "^16.9"
prop-types "^15.7.2"
react "^16.12.0"
react-router-dom "6.0.0-beta.0"
react-use "^15.3.3"
zen-observable "^0.8.15"
"@backstage/core@^0.3.0":
version "0.7.3"
dependencies:
@@ -1972,6 +1989,65 @@
remark-gfm "^1.0.0"
zen-observable "^0.8.15"
"@backstage/core@^0.7.3":
version "0.7.3"
resolved "https://registry.npmjs.org/@backstage/core/-/core-0.7.3.tgz#a55f094f0aceab4102ea08a75832c2ec5a70e226"
integrity sha512-uKTCYYAYVtmdOiJrNKe00N0AKeEhxZ+y3qQVY+H96WN9QDql/56XTmoiyPjDU7wK4nDj+yGV4j+xM5uxSXaOCg==
dependencies:
"@backstage/config" "^0.1.4"
"@backstage/core-api" "^0.2.15"
"@backstage/errors" "^0.1.1"
"@backstage/theme" "^0.2.5"
"@material-ui/core" "^4.11.0"
"@material-ui/icons" "^4.9.1"
"@material-ui/lab" "4.0.0-alpha.45"
"@testing-library/react-hooks" "^3.4.2"
"@types/dagre" "^0.7.44"
"@types/prop-types" "^15.7.3"
"@types/react" "^16.9"
"@types/react-sparklines" "^1.7.0"
"@types/react-text-truncate" "^0.14.0"
classnames "^2.2.6"
clsx "^1.1.0"
d3-selection "^2.0.0"
d3-shape "^2.0.0"
d3-zoom "^2.0.0"
dagre "^0.8.5"
immer "^8.0.1"
lodash "^4.17.15"
material-table "^1.69.1"
prop-types "^15.7.2"
qs "^6.9.4"
rc-progress "^3.0.0"
react "^16.12.0"
react-dom "^16.12.0"
react-helmet "6.1.0"
react-hook-form "^6.6.0"
react-markdown "^5.0.2"
react-router "6.0.0-beta.0"
react-router-dom "6.0.0-beta.0"
react-sparklines "^1.7.0"
react-syntax-highlighter "^15.4.3"
react-text-truncate "^0.16.0"
react-use "^15.3.3"
remark-gfm "^1.0.0"
zen-observable "^0.8.15"
"@backstage/plugin-catalog-react@^0.1.3":
version "0.1.3"
resolved "https://registry.npmjs.org/@backstage/plugin-catalog-react/-/plugin-catalog-react-0.1.3.tgz#e4cc6c40616976737893cc91d7e373b9397618d1"
integrity sha512-nNIz+tc72UtwB66L++xA9drv5VV0SxMPh0BNLOFAHAllcR2RMQyvWpDq4uBupUXx/lr6bzihngkW9Jqr4rfz/Q==
dependencies:
"@backstage/catalog-client" "^0.3.8"
"@backstage/catalog-model" "^0.7.3"
"@backstage/core" "^0.7.3"
"@material-ui/core" "^4.11.0"
"@types/react" "^16.9"
react "^16.13.1"
react-router "6.0.0-beta.0"
react-router-dom "6.0.0-beta.0"
react-use "^15.3.3"
"@backstage/plugin-catalog@^0.2.1":
version "0.5.1"
dependencies:
@@ -2020,6 +2096,13 @@
react-use "^15.3.3"
swr "^0.3.0"
"@backstage/theme@^0.2.5":
version "0.2.5"
resolved "https://registry.npmjs.org/@backstage/theme/-/theme-0.2.5.tgz#657e2ceaaede1b4dbe8abeafa6e168671a27b6da"
integrity sha512-GT7Ok9QSiZWXkxka+vcpweMrOg1lgDkRRjh7aSU97Mcoiq77szdO45o0hZtQChz8qay60vCRlu57ADvUn4QxZw==
dependencies:
"@material-ui/core" "^4.11.0"
"@bcoe/v8-coverage@^0.2.3":
version "0.2.3"
resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@@ -17949,11 +18032,6 @@ lodash.once@^4.0.0, lodash.once@^4.1.1:
resolved "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=
lodash.set@^4.3.2:
version "4.3.2"
resolved "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz#d8757b1da807dde24816b0d6a84bea1a76230b23"
integrity sha1-2HV7HagH3eJIFrDWqEvqGnYjCyM=
lodash.sortby@^4.7.0:
version "4.7.0"
resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
@@ -19292,16 +19370,6 @@ no-case@^3.0.4:
lower-case "^2.0.2"
tslib "^2.0.3"
nock@^13.0.11:
version "13.0.11"
resolved "https://registry.npmjs.org/nock/-/nock-13.0.11.tgz#ba733252e720897ca50033205c39db0c7470f331"
integrity sha512-sKZltNkkWblkqqPAsjYW0bm3s9DcHRPiMOyKO/PkfJ+ANHZ2+LA2PLe22r4lLrKgXaiSaDQwW3qGsJFtIpQIeQ==
dependencies:
debug "^4.1.0"
json-stringify-safe "^5.0.1"
lodash.set "^4.3.2"
propagate "^2.0.0"
node-abi@^2.7.0:
version "2.19.3"
resolved "https://registry.npmjs.org/node-abi/-/node-abi-2.19.3.tgz#252f5dcab12dad1b5503b2d27eddd4733930282d"
@@ -21672,11 +21740,6 @@ prop-types@^15.5.10, prop-types@^15.5.7, prop-types@^15.5.8, prop-types@^15.6.0,
object-assign "^4.1.1"
react-is "^16.8.1"
propagate@^2.0.0:
version "2.0.1"
resolved "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45"
integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==
property-expr@^2.0.2:
version "2.0.2"
resolved "https://registry.npmjs.org/property-expr/-/property-expr-2.0.2.tgz#fff2a43919135553a3bc2fdd94bdb841965b2330"