Skip to content

Commit 8ec4312

Browse files
feat: add release card (#16)
* feat: add release card
1 parent 52b010a commit 8ec4312

File tree

25 files changed

+820
-11
lines changed

25 files changed

+820
-11
lines changed

.releaserc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@
4646
"pkgRoot": "packages/backstage-plugin-autogov-releases-backend",
4747
"registry": "https://registry.npmjs.org/"
4848
}
49+
],
50+
[
51+
"@semantic-release/npm",
52+
{
53+
"npmPublish": true,
54+
"pkgRoot": "packages/backstage-plugin-autogov-releases-card",
55+
"registry": "https://registry.npmjs.org/"
56+
}
4957
]
5058
]
5159
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require("@backstage/cli/config/eslint-factory")(__dirname);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# autogov-releases-card
2+
3+
Welcome to the autogov-releases-card plugin!
4+
5+
_This plugin was created through the Backstage CLI_
6+
7+
## Getting started
8+
9+
Your plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/autogov-releases-card](http://localhost:3000/autogov-releases-card).
10+
11+
You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
12+
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
13+
It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { createDevApp } from "@backstage/dev-utils";
2+
import { autogovReleasesCardPlugin } from "../src/plugin";
3+
4+
createDevApp().registerPlugin(autogovReleasesCardPlugin).render();
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
{
2+
"name": "@liatrio/backstage-plugin-autogov-releases-card",
3+
"version": "0.1.0",
4+
"main": "dist/index.js",
5+
"types": "../../dist-types/packages/backstage-plugin-autogov-releases-card/index.d.ts",
6+
"license": "Apache-2.0",
7+
"repository": {
8+
"type": "git",
9+
"url": "git+https://github.com/liatrio/backstage-plugin-autogov.git",
10+
"directory": "packages/backstage-plugin-autogov-releases-card"
11+
},
12+
"backstage": {
13+
"role": "frontend-plugin",
14+
"pluginId": "autogov-releases-card",
15+
"pluginPackages": []
16+
},
17+
"sideEffects": false,
18+
"scripts": {
19+
"start": "backstage-cli package start",
20+
"build": "backstage-cli package build",
21+
"lint": "backstage-cli package lint",
22+
"lint:all": "yarn lint && yarn prettier:check",
23+
"test": "backstage-cli package test",
24+
"clean": "backstage-cli package clean",
25+
"prepack": "backstage-cli package prepack",
26+
"postpack": "backstage-cli package postpack",
27+
"prettier:check": "npx --yes prettier --check .",
28+
"prettier:fix": "npx --yes prettier --write .",
29+
"tsc:full": "tsc --skipLibCheck true --incremental false",
30+
"prepare": "husky"
31+
},
32+
"dependencies": {
33+
"@backstage/catalog-model": "^1.7.0",
34+
"@backstage/core-components": "^0.15.0",
35+
"@backstage/core-plugin-api": "^1.9.4",
36+
"@backstage/errors": "^1.2.4",
37+
"@backstage/plugin-catalog-react": "^1.14.0",
38+
"@backstage/theme": "^0.5.7",
39+
"@material-ui/core": "^4.9.13",
40+
"@material-ui/icons": "^4.9.1",
41+
"@material-ui/lab": "^4.0.0-alpha.61",
42+
"react-use": "^17.2.4",
43+
"styled-components": "^6.1.13"
44+
},
45+
"peerDependencies": {
46+
"react": "^16.13.1 || ^17.0.0 || ^18.0.0"
47+
},
48+
"devDependencies": {
49+
"@backstage/cli": "^0.27.1",
50+
"@backstage/core-app-api": "^1.15.0",
51+
"@backstage/dev-utils": "^1.1.0",
52+
"@backstage/test-utils": "^1.6.0",
53+
"@testing-library/jest-dom": "^6.0.0",
54+
"@testing-library/react": "^14.0.0",
55+
"@testing-library/user-event": "^14.0.0",
56+
"msw": "^1.0.0",
57+
"react": "^16.13.1 || ^17.0.0 || ^18.0.0"
58+
},
59+
"files": [
60+
"dist"
61+
]
62+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { createApiRef } from "@backstage/core-plugin-api";
2+
import { ReleaseData } from "./types";
3+
import { CompoundEntityRef } from "@backstage/catalog-model";
4+
5+
export const autogovReleasesApiRef = createApiRef<AutogovReleasesApi>({
6+
id: "plugin.autogov-releases.service",
7+
});
8+
9+
export interface AutogovReleasesApi {
10+
getReleases(entityRef: CompoundEntityRef): Promise<ReleaseData[]>;
11+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { DiscoveryApi, FetchApi } from "@backstage/core-plugin-api";
2+
import { ResponseError } from "@backstage/errors";
3+
import { ReleaseData } from "./types";
4+
5+
import { AutogovReleasesApi } from "./AutogovReleasesApi";
6+
import { CompoundEntityRef } from "@backstage/catalog-model";
7+
8+
export class AutogovReleasesClient implements AutogovReleasesApi {
9+
private readonly discoveryApi: DiscoveryApi;
10+
private readonly fetchApi: FetchApi;
11+
12+
constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }) {
13+
this.discoveryApi = options.discoveryApi;
14+
this.fetchApi = options.fetchApi;
15+
}
16+
17+
async getReleases(entityRef: CompoundEntityRef): Promise<ReleaseData[]> {
18+
const baseUrl = await this.discoveryApi.getBaseUrl("autogov-releases");
19+
const { kind, namespace, name } = entityRef;
20+
const response = await this.fetchApi.fetch(
21+
`${baseUrl}/releases/${kind}/${namespace}/${name}/`,
22+
{
23+
method: "GET",
24+
},
25+
);
26+
27+
if (!response.ok) {
28+
throw await ResponseError.fromResponse(response);
29+
}
30+
31+
return response.json();
32+
}
33+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export * from "./AutogovReleasesClient";
2+
export * from "./AutogovReleasesApi";
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
export interface ReleaseData {
2+
name: string;
3+
tag: string;
4+
url: string;
5+
publishedAt: string;
6+
autogovAssetUrl?: string;
7+
autogovStatus?: string;
8+
autogovFailedPolicies?: any[];
9+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import React from "react";
2+
import {
3+
Table,
4+
ResponseErrorPanel,
5+
InfoCard,
6+
} from "@backstage/core-components";
7+
import { parseEntityRef } from "@backstage/catalog-model";
8+
import { useApi } from "@backstage/core-plugin-api";
9+
import useAsync from "react-use/esm/useAsync";
10+
import { TableWithoutViolations } from "./TableWithoutViolations";
11+
import { TableWithViolations } from "./TableWithViolations";
12+
13+
import { autogovReleasesApiRef } from "../../api";
14+
15+
type AutogovReleasesTableCardProps = {
16+
ownerRef?: string;
17+
title?: string;
18+
};
19+
20+
export const AutogovReleasesTableCard = (
21+
props: AutogovReleasesTableCardProps,
22+
) => {
23+
const { ownerRef, title } = props;
24+
const autogovReleasesApi = useApi(autogovReleasesApiRef);
25+
if (!ownerRef) {
26+
throw new Error("ownerRef must be provided");
27+
}
28+
29+
const {
30+
error: returnedError,
31+
loading,
32+
value: releasesData,
33+
} = useAsync(async () => {
34+
return autogovReleasesApi.getReleases(parseEntityRef(ownerRef));
35+
}, [autogovReleasesApi, ownerRef]);
36+
37+
if (loading) {
38+
return (
39+
<InfoCard title={title || "Releases and Autogov Status"}>
40+
<Table
41+
options={{
42+
paging: false,
43+
padding: "dense",
44+
search: false,
45+
}}
46+
data={[]}
47+
columns={[]}
48+
isLoading
49+
/>
50+
</InfoCard>
51+
);
52+
} else if (returnedError) {
53+
// return null;
54+
return (
55+
<InfoCard title={title || "Releases and Autogov Status"}>
56+
<ResponseErrorPanel error={returnedError} />
57+
</InfoCard>
58+
);
59+
}
60+
61+
if (!releasesData) {
62+
return (
63+
<InfoCard title={title || "Releases and Autogov Status"}>
64+
<ResponseErrorPanel error={Error("No autogov data found")} />
65+
</InfoCard>
66+
);
67+
}
68+
69+
if (
70+
!releasesData.some(
71+
(release: any) => release.autogovFailedPolicies?.length > 0,
72+
)
73+
) {
74+
return (
75+
<InfoCard title={title || "Releases and Autogov Status"}>
76+
<TableWithoutViolations releasesData={releasesData} />
77+
</InfoCard>
78+
);
79+
}
80+
return (
81+
<InfoCard title={title || "Releases and Autogov Status"}>
82+
<TableWithViolations releasesData={releasesData} />
83+
</InfoCard>
84+
);
85+
};

0 commit comments

Comments
 (0)