@incident-io/backstage 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/esm/{index-a76fa310.esm.js → index-0db1f4e5.esm.js} +25 -17
- package/dist/esm/index-0db1f4e5.esm.js.map +1 -0
- package/dist/esm/{index-6eb32695.esm.js → index-4d6d45fc.esm.js} +3 -3
- package/dist/esm/index-4d6d45fc.esm.js.map +1 -0
- package/dist/esm/{index-40a020a8.esm.js → index-4e4c47b6.esm.js} +13 -15
- package/dist/esm/index-4e4c47b6.esm.js.map +1 -0
- package/dist/esm/{index-ba2cdf39.esm.js → index-8c1936e7.esm.js} +5 -5
- package/dist/esm/index-8c1936e7.esm.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.esm.js +1 -1
- package/package.json +4 -2
- package/src/api/client.ts +6 -6
- package/src/api/types.ts +1008 -1003
- package/src/components/EntityIncidentCard/index.tsx +51 -40
- package/src/components/HomePageIncidentCard/Content.test.tsx +19 -19
- package/src/components/HomePageIncidentCard/Content.tsx +11 -12
- package/src/components/HomePageIncidentCard/index.ts +1 -1
- package/src/components/IncidentListItem/index.tsx +20 -20
- package/src/hooks/useIncidentRequest.ts +21 -7
- package/src/index.ts +1 -1
- package/src/plugin.ts +13 -13
- package/src/setupTests.ts +2 -2
- package/dist/esm/index-40a020a8.esm.js.map +0 -1
- package/dist/esm/index-6eb32695.esm.js.map +0 -1
- package/dist/esm/index-a76fa310.esm.js.map +0 -1
- package/dist/esm/index-ba2cdf39.esm.js.map +0 -1
- package/src/config.ts +0 -30
package/README.md
CHANGED
|
@@ -18,7 +18,7 @@ We recommend creating a multi-select field called something like "Affected
|
|
|
18
18
|
services" or "Impacted components".
|
|
19
19
|
|
|
20
20
|
Remember the custom field ID (taken from the incident.io dashboard) as you'll
|
|
21
|
-
need it later -- you'll find it after `custom-field` in the URL (e.g.
|
|
21
|
+
need it later -- you'll find it after `custom-field` in the URL (e.g.
|
|
22
22
|
https://app.incident.io/~/settings/custom-fields/01GD0ECMPR9WF330S1PHSRDVB7/edit)
|
|
23
23
|
or in the responses from our API.
|
|
24
24
|
|
|
@@ -64,7 +64,7 @@ You may find you need to make more edits to `EntityPage`, based on your setup.
|
|
|
64
64
|
[api-keys]: https://app.incident.io/settings/api-keys/
|
|
65
65
|
[api-docs]: https://api-docs.incident.io/
|
|
66
66
|
|
|
67
|
-
First, provide the [API key][api-keys] that the client will use to make
|
|
67
|
+
First, provide the [API key][api-keys] that the client will use to make
|
|
68
68
|
requests to the [incident.io API][api-docs].
|
|
69
69
|
|
|
70
70
|
Add the proxy configuration in `app-config.yaml`:
|
|
@@ -94,4 +94,4 @@ incident:
|
|
|
94
94
|
If you don't have a custom field set up for one of these entities, then you
|
|
95
95
|
can omit that field completely. If you try and include the `EntityIncidentCard`
|
|
96
96
|
on the page for an entity which doesn't have the configuration, we'll show you
|
|
97
|
-
an error that directs you to update your config.
|
|
97
|
+
an error that directs you to update your config.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Progress, HeaderIconLinkRow } from '@backstage/core-components';
|
|
2
2
|
import { useApi, configApiRef } from '@backstage/core-plugin-api';
|
|
3
3
|
import { useEntity } from '@backstage/plugin-catalog-react';
|
|
4
4
|
import { Card, CardHeader, IconButton, Divider, CardContent, Typography, List } from '@material-ui/core';
|
|
@@ -8,9 +8,9 @@ import HistoryIcon from '@material-ui/icons/History';
|
|
|
8
8
|
import WhatshotIcon from '@material-ui/icons/Whatshot';
|
|
9
9
|
import { Alert } from '@material-ui/lab';
|
|
10
10
|
import React, { useState } from 'react';
|
|
11
|
-
import {
|
|
11
|
+
import { u as useIdentity, a as useIncidentList, I as IncidentListItem } from './index-4e4c47b6.esm.js';
|
|
12
12
|
import 'react-use';
|
|
13
|
-
import './index-
|
|
13
|
+
import './index-4d6d45fc.esm.js';
|
|
14
14
|
import '@backstage/plugin-home-react';
|
|
15
15
|
import 'luxon';
|
|
16
16
|
import '@material-ui/icons/OpenInBrowser';
|
|
@@ -23,17 +23,34 @@ const EntityIncidentCard = ({
|
|
|
23
23
|
}) => {
|
|
24
24
|
var _a;
|
|
25
25
|
const config = useApi(configApiRef);
|
|
26
|
-
const baseUrl = getBaseUrl(config);
|
|
27
26
|
const { entity } = useEntity();
|
|
27
|
+
const {
|
|
28
|
+
value: identityResponse,
|
|
29
|
+
loading: identityResponseLoading,
|
|
30
|
+
error: identityResponseError
|
|
31
|
+
} = useIdentity();
|
|
28
32
|
const [reload, setReload] = useState(false);
|
|
29
33
|
const entityFieldID = getEntityFieldID(config, entity);
|
|
30
34
|
const entityID = `${entity.metadata.namespace}/${entity.metadata.name}`;
|
|
31
35
|
const query = new URLSearchParams();
|
|
32
36
|
query.set(`custom_field[${entityFieldID}][one_of]`, entityID);
|
|
33
37
|
const queryLive = new URLSearchParams(query);
|
|
34
|
-
queryLive.set(`status_category[one_of]`, "
|
|
38
|
+
queryLive.set(`status_category[one_of]`, "active");
|
|
39
|
+
const {
|
|
40
|
+
value: incidentsResponse,
|
|
41
|
+
loading: incidentsLoading,
|
|
42
|
+
error: incidentsError
|
|
43
|
+
} = useIncidentList(queryLive, [reload]);
|
|
44
|
+
const incidents = incidentsResponse == null ? void 0 : incidentsResponse.incidents;
|
|
45
|
+
if (!entityFieldID) {
|
|
46
|
+
return /* @__PURE__ */ React.createElement(IncorrectConfigCard, null);
|
|
47
|
+
}
|
|
48
|
+
if (incidentsLoading || identityResponseLoading || !identityResponse) {
|
|
49
|
+
return /* @__PURE__ */ React.createElement(Progress, null);
|
|
50
|
+
}
|
|
51
|
+
const baseUrl = identityResponse.identity.dashboard_url;
|
|
35
52
|
const createIncidentLink = {
|
|
36
|
-
label: "
|
|
53
|
+
label: "Declare incident",
|
|
37
54
|
disabled: false,
|
|
38
55
|
icon: /* @__PURE__ */ React.createElement(WhatshotIcon, null),
|
|
39
56
|
href: `${baseUrl}/incidents/create`
|
|
@@ -44,15 +61,6 @@ const EntityIncidentCard = ({
|
|
|
44
61
|
icon: /* @__PURE__ */ React.createElement(HistoryIcon, null),
|
|
45
62
|
href: `${baseUrl}/incidents?${query.toString()}`
|
|
46
63
|
};
|
|
47
|
-
const {
|
|
48
|
-
value: incidentsResponse,
|
|
49
|
-
loading: incidentsLoading,
|
|
50
|
-
error: incidentsError
|
|
51
|
-
} = useIncidentList(queryLive, [reload]);
|
|
52
|
-
const incidents = incidentsResponse == null ? void 0 : incidentsResponse.incidents;
|
|
53
|
-
if (!entityFieldID) {
|
|
54
|
-
return /* @__PURE__ */ React.createElement(IncorrectConfigCard, null);
|
|
55
|
-
}
|
|
56
64
|
return /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(
|
|
57
65
|
CardHeader,
|
|
58
66
|
{
|
|
@@ -70,7 +78,7 @@ const EntityIncidentCard = ({
|
|
|
70
78
|
)),
|
|
71
79
|
subheader: /* @__PURE__ */ React.createElement(HeaderIconLinkRow, { links: [createIncidentLink, viewIncidentsLink] })
|
|
72
80
|
}
|
|
73
|
-
), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(CardContent, null,
|
|
81
|
+
), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(CardContent, null, incidentsError && /* @__PURE__ */ React.createElement(Alert, { severity: "error" }, incidentsError.message), identityResponseError && /* @__PURE__ */ React.createElement(Alert, { severity: "error" }, identityResponseError.message), !incidentsLoading && !incidentsError && incidents && /* @__PURE__ */ React.createElement(React.Fragment, null, incidents && incidents.length > 0 && /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle1" }, "There are ", /* @__PURE__ */ React.createElement("strong", null, incidents.length), " ongoing incidents involving ", /* @__PURE__ */ React.createElement("strong", null, entity.metadata.name), "."), incidents && incidents.length === 0 && /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle1" }, "No ongoing incidents."), /* @__PURE__ */ React.createElement(List, { dense: true }, (_a = incidents == null ? void 0 : incidents.slice(0, maxIncidents)) == null ? void 0 : _a.map((incident) => {
|
|
74
82
|
return /* @__PURE__ */ React.createElement(
|
|
75
83
|
IncidentListItem,
|
|
76
84
|
{
|
|
@@ -104,4 +112,4 @@ function getEntityFieldID(config, entity) {
|
|
|
104
112
|
}
|
|
105
113
|
|
|
106
114
|
export { EntityIncidentCard };
|
|
107
|
-
//# sourceMappingURL=index-
|
|
115
|
+
//# sourceMappingURL=index-0db1f4e5.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-0db1f4e5.esm.js","sources":["../../src/components/EntityIncidentCard/index.tsx"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { Entity } from \"@backstage/catalog-model\";\nimport {\n HeaderIconLinkRow,\n IconLinkVerticalProps,\n Progress,\n} from \"@backstage/core-components\";\nimport { ConfigApi, configApiRef, useApi } from \"@backstage/core-plugin-api\";\nimport { useEntity } from \"@backstage/plugin-catalog-react\";\nimport {\n Card,\n CardContent,\n CardHeader,\n Divider,\n IconButton,\n List,\n Typography,\n} from \"@material-ui/core\";\nimport Link from \"@material-ui/core/Link\";\nimport CachedIcon from \"@material-ui/icons/Cached\";\nimport HistoryIcon from \"@material-ui/icons/History\";\nimport WhatshotIcon from \"@material-ui/icons/Whatshot\";\nimport { Alert } from \"@material-ui/lab\";\nimport React, { useState } from \"react\";\nimport { useIncidentList, useIdentity } from \"../../hooks/useIncidentRequest\";\nimport { IncidentListItem } from \"../IncidentListItem\";\n\nconst IncorrectConfigCard = () => {\n return (\n <Card>\n <CardHeader title=\"incident.io\" />\n <Divider />\n <CardContent>\n <Typography variant=\"subtitle1\">\n No custom field configuration was found. In order to display\n incidents, this entity must be mapped to an incident.io custom field\n ID in Backstage's app-config.yaml.\n </Typography>\n </CardContent>\n </Card>\n );\n};\n\n// The card displayed on the entity page showing a handful of the most recent\n// incidents that are on-going for that component.\nexport const EntityIncidentCard = ({\n maxIncidents = 2,\n}: {\n maxIncidents?: number;\n}) => {\n const config = useApi(configApiRef);\n const { entity } = useEntity();\n const {\n value: identityResponse,\n loading: identityResponseLoading,\n error: identityResponseError,\n } = useIdentity();\n\n const [reload, setReload] = useState(false);\n\n const entityFieldID = getEntityFieldID(config, entity);\n const entityID = `${entity.metadata.namespace}/${entity.metadata.name}`;\n\n // This query filters incidents for those that are associated with this\n // entity.\n const query = new URLSearchParams();\n query.set(`custom_field[${entityFieldID}][one_of]`, entityID);\n\n // This restricts the previous filter to focus only on live incidents.\n const queryLive = new URLSearchParams(query);\n queryLive.set(`status_category[one_of]`, \"active\");\n\n const {\n value: incidentsResponse,\n loading: incidentsLoading,\n error: incidentsError,\n } = useIncidentList(queryLive, [reload]);\n\n const incidents = incidentsResponse?.incidents;\n\n if (!entityFieldID) {\n return <IncorrectConfigCard />;\n }\n\n if (incidentsLoading || identityResponseLoading || !identityResponse) {\n return <Progress />;\n }\n\n const baseUrl = identityResponse.identity.dashboard_url;\n\n const createIncidentLink: IconLinkVerticalProps = {\n label: \"Declare incident\",\n disabled: false,\n icon: <WhatshotIcon />,\n href: `${baseUrl}/incidents/create`,\n };\n\n const viewIncidentsLink: IconLinkVerticalProps = {\n label: \"View past incidents\",\n disabled: false,\n icon: <HistoryIcon />,\n href: `${baseUrl}/incidents?${query.toString()}`,\n };\n\n return (\n <Card>\n <CardHeader\n title=\"incident.io\"\n action={\n <>\n <IconButton\n component={Link}\n aria-label=\"Refresh\"\n disabled={false}\n title=\"Refresh\"\n onClick={() => setReload(!reload)}\n >\n <CachedIcon />\n </IconButton>\n </>\n }\n subheader={\n <HeaderIconLinkRow links={[createIncidentLink, viewIncidentsLink]} />\n }\n />\n <Divider />\n <CardContent>\n {incidentsError && (\n <Alert severity=\"error\">{incidentsError.message}</Alert>\n )}\n {identityResponseError && (\n <Alert severity=\"error\">{identityResponseError.message}</Alert>\n )}\n {!incidentsLoading && !incidentsError && incidents && (\n <>\n {incidents && incidents.length > 0 && (\n <Typography variant=\"subtitle1\">\n There are <strong>{incidents.length}</strong> ongoing incidents\n involving <strong>{entity.metadata.name}</strong>.\n </Typography>\n )}\n {incidents && incidents.length === 0 && (\n <Typography variant=\"subtitle1\">No ongoing incidents.</Typography>\n )}\n <List dense>\n {incidents?.slice(0, maxIncidents)?.map((incident) => {\n return (\n <IncidentListItem\n key={incident.id}\n incident={incident}\n baseUrl={baseUrl}\n />\n );\n })}\n </List>\n <Typography variant=\"subtitle1\">\n Click to{\" \"}\n <Link\n target=\"_blank\"\n href={`${baseUrl}/incidents?${queryLive.toString()}`}\n >\n see more.\n </Link>\n </Typography>\n </>\n )}\n </CardContent>\n </Card>\n );\n};\n\n// Find the ID of the custom field in incident that represents the association\n// to this type of entity.\n//\n// In practice, this will be kind=Component => ID of Affected components field.\nfunction getEntityFieldID(config: ConfigApi, entity: Entity) {\n switch (entity.kind) {\n case \"API\":\n return config.getOptional(\"incident.fields.api\");\n case \"Component\":\n return config.getOptional(\"incident.fields.component\");\n case \"Domain\":\n return config.getOptional(\"incident.fields.domain\");\n case \"System\":\n return config.getOptional(\"incident.fields.system\");\n default:\n throw new Error(`unrecognised entity kind: ${entity.kind}`);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -61,7 +61,7 @@ const EntityIncidentCard = incidentPlugin.provide(
|
|
|
61
61
|
createComponentExtension({
|
|
62
62
|
name: "EntityIncidentCard",
|
|
63
63
|
component: {
|
|
64
|
-
lazy: () => import('./index-
|
|
64
|
+
lazy: () => import('./index-0db1f4e5.esm.js').then(
|
|
65
65
|
(m) => m.EntityIncidentCard
|
|
66
66
|
)
|
|
67
67
|
}
|
|
@@ -71,9 +71,9 @@ const HomePageIncidentCard = incidentPlugin.provide(
|
|
|
71
71
|
createCardExtension({
|
|
72
72
|
name: "HomePageIncidentCard",
|
|
73
73
|
title: "Ongoing Incidents",
|
|
74
|
-
components: () => import('./index-
|
|
74
|
+
components: () => import('./index-8c1936e7.esm.js')
|
|
75
75
|
})
|
|
76
76
|
);
|
|
77
77
|
|
|
78
78
|
export { EntityIncidentCard as E, HomePageIncidentCard as H, IncidentApiRef as I, incidentPlugin as i };
|
|
79
|
-
//# sourceMappingURL=index-
|
|
79
|
+
//# sourceMappingURL=index-4d6d45fc.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-4d6d45fc.esm.js","sources":["../../src/api/client.ts","../../src/plugin.ts"],"sourcesContent":["/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n DiscoveryApi,\n IdentityApi,\n createApiRef,\n} from \"@backstage/core-plugin-api\";\n\nexport const IncidentApiRef = createApiRef<Incident>({\n id: \"plugin.incident.service\",\n});\n\ntype HTTPMethods = \"GET\" | \"PUT\" | \"POST\" | \"PATCH\" | \"DELETE\";\n\nexport interface Incident {\n request<T>({\n method,\n path,\n body,\n }: {\n method?: HTTPMethods;\n path: string;\n body?: string;\n }): Promise<T>;\n}\n\nconst DEFAULT_PROXY_PATH = \"/incident/api\";\n\ntype Options = {\n discoveryApi: DiscoveryApi;\n identityApi: IdentityApi;\n proxyPath?: string;\n};\n\nexport class IncidentApi implements Incident {\n private readonly discoveryApi: DiscoveryApi;\n private readonly identityApi: IdentityApi;\n private readonly proxyPath: string;\n\n constructor(opts: Options) {\n this.discoveryApi = opts.discoveryApi;\n this.identityApi = opts.identityApi;\n this.proxyPath = opts.proxyPath ?? DEFAULT_PROXY_PATH;\n }\n\n async request<T = any>({\n path,\n method = \"GET\",\n body,\n }: {\n path: string;\n method?: HTTPMethods;\n body?: string;\n }): Promise<T> {\n const apiUrl =\n (await this.discoveryApi.getBaseUrl(\"proxy\")) + this.proxyPath;\n const { token } = await this.identityApi.getCredentials();\n\n const resp = await fetch(`${apiUrl}${path}`, {\n method: method,\n body: body,\n headers: {\n Authorization: `Bearer ${token}`,\n },\n });\n if (!resp.ok) {\n throw new Error(`${resp.status} ${resp.statusText}`);\n }\n\n return await resp.json();\n }\n}\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport {\n createApiFactory,\n createComponentExtension,\n createPlugin,\n discoveryApiRef,\n identityApiRef,\n} from \"@backstage/core-plugin-api\";\nimport {CardExtensionProps, createCardExtension} from \"@backstage/plugin-home-react\";\n\nimport { IncidentApi, IncidentApiRef } from \"./api/client\";\n\nexport const incidentPlugin = createPlugin({\n id: \"incident\",\n apis: [\n createApiFactory({\n api: IncidentApiRef,\n deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef },\n factory: ({ discoveryApi, identityApi }) => {\n return new IncidentApi({\n discoveryApi: discoveryApi,\n identityApi: identityApi,\n });\n },\n }),\n ],\n});\n\nexport const EntityIncidentCard = incidentPlugin.provide(\n createComponentExtension({\n name: \"EntityIncidentCard\",\n component: {\n lazy: () =>\n import(\"./components/EntityIncidentCard\").then(\n (m) => m.EntityIncidentCard,\n ),\n },\n }),\n);\n\nexport const HomePageIncidentCard: (props: CardExtensionProps<unknown>) => React.JSX.Element = incidentPlugin.provide(\n createCardExtension({\n name: \"HomePageIncidentCard\",\n title: \"Ongoing Incidents\",\n components: () => import(\"./components/HomePageIncidentCard\"),\n }),\n);\n"],"names":[],"mappings":";;;;;;;;;AAqBO,MAAM,iBAAiB,YAAuB,CAAA;AAAA,EACnD,EAAI,EAAA,yBAAA;AACN,CAAC,EAAA;AAgBD,MAAM,kBAAqB,GAAA,eAAA,CAAA;AAQpB,MAAM,WAAgC,CAAA;AAAA,EAK3C,YAAY,IAAe,EAAA;AAJ3B,IAAiB,aAAA,CAAA,IAAA,EAAA,cAAA,CAAA,CAAA;AACjB,IAAiB,aAAA,CAAA,IAAA,EAAA,aAAA,CAAA,CAAA;AACjB,IAAiB,aAAA,CAAA,IAAA,EAAA,WAAA,CAAA,CAAA;AAlDnB,IAAA,IAAA,EAAA,CAAA;AAqDI,IAAA,IAAA,CAAK,eAAe,IAAK,CAAA,YAAA,CAAA;AACzB,IAAA,IAAA,CAAK,cAAc,IAAK,CAAA,WAAA,CAAA;AACxB,IAAK,IAAA,CAAA,SAAA,GAAA,CAAY,EAAK,GAAA,IAAA,CAAA,SAAA,KAAL,IAAkB,GAAA,EAAA,GAAA,kBAAA,CAAA;AAAA,GACrC;AAAA,EAEA,MAAM,OAAiB,CAAA;AAAA,IACrB,IAAA;AAAA,IACA,MAAS,GAAA,KAAA;AAAA,IACT,IAAA;AAAA,GAKa,EAAA;AACb,IAAA,MAAM,SACH,MAAM,IAAA,CAAK,aAAa,UAAW,CAAA,OAAO,IAAK,IAAK,CAAA,SAAA,CAAA;AACvD,IAAA,MAAM,EAAE,KAAM,EAAA,GAAI,MAAM,IAAA,CAAK,YAAY,cAAe,EAAA,CAAA;AAExD,IAAA,MAAM,OAAO,MAAM,KAAA,CAAM,GAAG,MAAM,CAAA,EAAG,IAAI,CAAI,CAAA,EAAA;AAAA,MAC3C,MAAA;AAAA,MACA,IAAA;AAAA,MACA,OAAS,EAAA;AAAA,QACP,aAAA,EAAe,UAAU,KAAK,CAAA,CAAA;AAAA,OAChC;AAAA,KACD,CAAA,CAAA;AACD,IAAI,IAAA,CAAC,KAAK,EAAI,EAAA;AACZ,MAAM,MAAA,IAAI,MAAM,CAAG,EAAA,IAAA,CAAK,MAAM,CAAI,CAAA,EAAA,IAAA,CAAK,UAAU,CAAE,CAAA,CAAA,CAAA;AAAA,KACrD;AAEA,IAAO,OAAA,MAAM,KAAK,IAAK,EAAA,CAAA;AAAA,GACzB;AACF;;AC1DO,MAAM,iBAAiB,YAAa,CAAA;AAAA,EACzC,EAAI,EAAA,UAAA;AAAA,EACJ,IAAM,EAAA;AAAA,IACJ,gBAAiB,CAAA;AAAA,MACf,GAAK,EAAA,cAAA;AAAA,MACL,IAAM,EAAA,EAAE,YAAc,EAAA,eAAA,EAAiB,aAAa,cAAe,EAAA;AAAA,MACnE,OAAS,EAAA,CAAC,EAAE,YAAA,EAAc,aAAkB,KAAA;AAC1C,QAAA,OAAO,IAAI,WAAY,CAAA;AAAA,UACrB,YAAA;AAAA,UACA,WAAA;AAAA,SACD,CAAA,CAAA;AAAA,OACH;AAAA,KACD,CAAA;AAAA,GACH;AACF,CAAC,EAAA;AAEM,MAAM,qBAAqB,cAAe,CAAA,OAAA;AAAA,EAC/C,wBAAyB,CAAA;AAAA,IACvB,IAAM,EAAA,oBAAA;AAAA,IACN,SAAW,EAAA;AAAA,MACT,IAAM,EAAA,MACJ,OAAO,yBAAiC,CAAE,CAAA,IAAA;AAAA,QACxC,CAAC,MAAM,CAAE,CAAA,kBAAA;AAAA,OACX;AAAA,KACJ;AAAA,GACD,CAAA;AACH,EAAA;AAEO,MAAM,uBAAkF,cAAe,CAAA,OAAA;AAAA,EAC5G,mBAAoB,CAAA;AAAA,IAClB,IAAM,EAAA,sBAAA;AAAA,IACN,KAAO,EAAA,mBAAA;AAAA,IACP,UAAA,EAAY,MAAM,OAAO,yBAAmC,CAAA;AAAA,GAC7D,CAAA;AACH;;;;"}
|
|
@@ -1,22 +1,11 @@
|
|
|
1
1
|
import { useApi } from '@backstage/core-plugin-api';
|
|
2
2
|
import { useAsync } from 'react-use';
|
|
3
|
-
import { I as IncidentApiRef } from './index-
|
|
3
|
+
import { I as IncidentApiRef } from './index-4d6d45fc.esm.js';
|
|
4
4
|
import { DateTime, Duration } from 'luxon';
|
|
5
5
|
import { makeStyles, ListItem, ListItemText, Chip, Typography, ListItemSecondaryAction, Tooltip, IconButton } from '@material-ui/core';
|
|
6
6
|
import OpenInBrowserIcon from '@material-ui/icons/OpenInBrowser';
|
|
7
7
|
import React from 'react';
|
|
8
8
|
|
|
9
|
-
function getBaseUrl(config) {
|
|
10
|
-
try {
|
|
11
|
-
const baseUrl = config.getString("incident.baseUrl");
|
|
12
|
-
if (baseUrl !== "") {
|
|
13
|
-
return baseUrl;
|
|
14
|
-
}
|
|
15
|
-
} catch (e) {
|
|
16
|
-
}
|
|
17
|
-
return "https://app.incident.io";
|
|
18
|
-
}
|
|
19
|
-
|
|
20
9
|
const useIncidentList = (query, deps) => {
|
|
21
10
|
const IncidentApi = useApi(IncidentApiRef);
|
|
22
11
|
const { value, loading, error } = useAsync(async () => {
|
|
@@ -26,6 +15,15 @@ const useIncidentList = (query, deps) => {
|
|
|
26
15
|
}, deps);
|
|
27
16
|
return { loading, error, value };
|
|
28
17
|
};
|
|
18
|
+
const useIdentity = () => {
|
|
19
|
+
const IncidentApi = useApi(IncidentApiRef);
|
|
20
|
+
const { value, loading, error } = useAsync(async () => {
|
|
21
|
+
return await IncidentApi.request({
|
|
22
|
+
path: `/v1/identity`
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
return { value, loading, error };
|
|
26
|
+
};
|
|
29
27
|
|
|
30
28
|
const useStyles = makeStyles((theme) => ({
|
|
31
29
|
listItemPrimary: {
|
|
@@ -73,7 +71,7 @@ const IncidentListItem = ({
|
|
|
73
71
|
label: incident.incident_status.name,
|
|
74
72
|
size: "small",
|
|
75
73
|
variant: "outlined",
|
|
76
|
-
className: ["
|
|
74
|
+
className: ["active"].includes(incident.incident_status.category) ? classes.error : classes.warning
|
|
77
75
|
}
|
|
78
76
|
), incident.reference, " ", incident.name),
|
|
79
77
|
primaryTypographyProps: {
|
|
@@ -94,5 +92,5 @@ const IncidentListItem = ({
|
|
|
94
92
|
))));
|
|
95
93
|
};
|
|
96
94
|
|
|
97
|
-
export { IncidentListItem as I,
|
|
98
|
-
//# sourceMappingURL=index-
|
|
95
|
+
export { IncidentListItem as I, useIncidentList as a, useIdentity as u };
|
|
96
|
+
//# sourceMappingURL=index-4e4c47b6.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-4e4c47b6.esm.js","sources":["../../src/hooks/useIncidentRequest.ts","../../src/components/IncidentListItem/index.tsx"],"sourcesContent":["import { useApi } from \"@backstage/core-plugin-api\";\nimport { useAsync } from \"react-use\";\nimport { IncidentApiRef } from \"../api/client\";\nimport { definitions } from \"../api/types\";\nimport { DependencyList } from \"react\";\n\nexport const useIncidentList = (\n query: URLSearchParams,\n deps?: DependencyList,\n) => {\n const IncidentApi = useApi(IncidentApiRef);\n\n const { value, loading, error } = useAsync(async () => {\n return await IncidentApi.request<\n definitions[\"IncidentsV2ListResponseBody\"]\n >({\n path: `/v2/incidents?${query.toString()}`,\n });\n }, deps);\n\n return { loading, error, value };\n};\n\nexport const useIdentity = () => {\n const IncidentApi = useApi(IncidentApiRef);\n\n const { value, loading, error } = useAsync(async () => {\n return await IncidentApi.request<\n definitions[\"UtilitiesV1IdentityResponseBody\"]\n >({\n path: `/v1/identity`,\n });\n });\n\n return { value, loading, error };\n};\n","/*\n * Copyright 2023 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\nimport { DateTime, Duration } from \"luxon\";\nimport { BackstageTheme } from \"@backstage/theme\";\nimport {\n Chip,\n IconButton,\n ListItem,\n ListItemSecondaryAction,\n ListItemText,\n Tooltip,\n Typography,\n makeStyles,\n} from \"@material-ui/core\";\nimport OpenInBrowserIcon from \"@material-ui/icons/OpenInBrowser\";\nimport React from \"react\";\nimport { definitions } from \"../../api/types\";\n\nconst useStyles = makeStyles<BackstageTheme>((theme) => ({\n listItemPrimary: {\n display: \"flex\", // vertically align with chip\n fontWeight: \"bold\",\n },\n warning: {\n borderColor: theme.palette.status.warning,\n color: theme.palette.status.warning,\n \"& *\": {\n color: theme.palette.status.warning,\n },\n },\n error: {\n borderColor: theme.palette.status.error,\n color: theme.palette.status.error,\n \"& *\": {\n color: theme.palette.status.error,\n },\n },\n}));\n\n// Single item in the list of on-going incidents.\nexport const IncidentListItem = ({\n baseUrl,\n incident,\n}: {\n baseUrl: string;\n incident: definitions[\"IncidentV2ResponseBody\"];\n}) => {\n const classes = useStyles();\n const reportedAt = incident.incident_timestamp_values?.find((ts) =>\n ts.incident_timestamp.name.match(/reported/i),\n );\n\n // If reported isn't here for some reason, use created at.\n const reportedAtDate = reportedAt?.value?.value || incident.created_at;\n\n const sinceReported =\n new Date().getTime() - new Date(reportedAtDate).getTime();\n const sinceReportedLabel = DateTime.local()\n .minus(Duration.fromMillis(sinceReported))\n .toRelative({ locale: \"en\" });\n const lead = incident.incident_role_assignments.find((roleAssignment) => {\n return roleAssignment.role.role_type === \"lead\";\n });\n\n return (\n <ListItem dense key={incident.id}>\n <ListItemText\n primary={\n <>\n <Chip\n data-testid={`chip-${incident.incident_status.id}`}\n label={incident.incident_status.name}\n size=\"small\"\n variant=\"outlined\"\n className={\n [\"active\"].includes(incident.incident_status.category)\n ? classes.error\n : classes.warning\n }\n />\n {incident.reference} {incident.name}\n </>\n }\n primaryTypographyProps={{\n variant: \"body1\",\n className: classes.listItemPrimary,\n }}\n secondary={\n <Typography noWrap variant=\"body2\" color=\"textSecondary\">\n Reported {sinceReportedLabel} and{\" \"}\n {lead?.assignee\n ? `${lead.assignee.name} is lead`\n : \"the lead is unassigned\"}\n .\n </Typography>\n }\n />\n <ListItemSecondaryAction>\n <Tooltip title=\"View in incident.io\" placement=\"top\">\n <IconButton\n href={`${baseUrl}/incidents/${incident.id}`}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n color=\"primary\"\n >\n <OpenInBrowserIcon />\n </IconButton>\n </Tooltip>\n </ListItemSecondaryAction>\n </ListItem>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -2,20 +2,20 @@ import { Progress } from '@backstage/core-components';
|
|
|
2
2
|
import Link from '@material-ui/core/Link';
|
|
3
3
|
import { Alert } from '@material-ui/lab';
|
|
4
4
|
import React from 'react';
|
|
5
|
-
import {
|
|
5
|
+
import { a as useIncidentList, I as IncidentListItem } from './index-4e4c47b6.esm.js';
|
|
6
6
|
import { Typography, List } from '@material-ui/core';
|
|
7
7
|
import { useApi, configApiRef } from '@backstage/core-plugin-api';
|
|
8
8
|
import 'react-use';
|
|
9
|
-
import './index-
|
|
9
|
+
import './index-4d6d45fc.esm.js';
|
|
10
10
|
import '@backstage/plugin-home-react';
|
|
11
11
|
import 'luxon';
|
|
12
12
|
import '@material-ui/icons/OpenInBrowser';
|
|
13
13
|
|
|
14
14
|
const HomePageIncidentCardContent = () => {
|
|
15
15
|
const config = useApi(configApiRef);
|
|
16
|
-
const baseUrl =
|
|
16
|
+
const baseUrl = config.getOptionalString("incident.baseUrl") || "https://app.incident.io";
|
|
17
17
|
const query = new URLSearchParams();
|
|
18
|
-
query.set(`status_category[one_of]`, "
|
|
18
|
+
query.set(`status_category[one_of]`, "active");
|
|
19
19
|
const { loading, error, value } = useIncidentList(query);
|
|
20
20
|
const incidents = value == null ? void 0 : value.incidents;
|
|
21
21
|
if (loading)
|
|
@@ -38,4 +38,4 @@ const Content = () => {
|
|
|
38
38
|
};
|
|
39
39
|
|
|
40
40
|
export { Content };
|
|
41
|
-
//# sourceMappingURL=index-
|
|
41
|
+
//# sourceMappingURL=index-8c1936e7.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-8c1936e7.esm.js","sources":["../../src/components/HomePageIncidentCard/Content.tsx"],"sourcesContent":["import { Progress } from \"@backstage/core-components\";\nimport Link from \"@material-ui/core/Link\";\nimport { Alert } from \"@material-ui/lab\";\nimport React from \"react\";\nimport { useIncidentList } from \"../../hooks/useIncidentRequest\";\nimport { Typography, List } from \"@material-ui/core\";\nimport { IncidentListItem } from \"../IncidentListItem\";\nimport { configApiRef, useApi } from \"@backstage/core-plugin-api\";\n\nexport const HomePageIncidentCardContent = () => {\n const config = useApi(configApiRef);\n const baseUrl = config.getOptionalString('incident.baseUrl') || \"https://app.incident.io\";\n\n const query = new URLSearchParams();\n query.set(`status_category[one_of]`, \"active\");\n const { loading, error, value } = useIncidentList(query);\n const incidents = value?.incidents;\n\n if (loading) return <Progress />;\n if (error) return <Alert severity=\"error\">{error.message}</Alert>;\n\n return (\n <>\n {incidents && incidents.length > 0 && (\n <Typography variant=\"subtitle1\">\n There are <strong>{incidents.length}</strong> ongoing incidents.\n </Typography>\n )}\n {incidents && incidents.length === 0 && (\n <Typography variant=\"subtitle1\">No ongoing incidents.</Typography>\n )}\n <List dense>\n {incidents?.map((incident) => {\n return (\n <IncidentListItem\n key={incident.id}\n incident={incident}\n baseUrl={baseUrl}\n />\n );\n })}\n </List>\n <Typography variant=\"subtitle1\">\n Click to{\" \"}\n <Link target=\"_blank\" href={`${baseUrl}/incidents?${query.toString()}`}>\n see more.\n </Link>\n </Typography>\n </>\n );\n};\n\nexport const Content = () => {\n return <HomePageIncidentCardContent />;\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;AASO,MAAM,8BAA8B,MAAM;AAC/C,EAAM,MAAA,MAAA,GAAS,OAAO,YAAY,CAAA,CAAA;AAChC,EAAA,MAAM,OAAU,GAAA,MAAA,CAAO,iBAAkB,CAAA,kBAAkB,CAAK,IAAA,yBAAA,CAAA;AAElE,EAAM,MAAA,KAAA,GAAQ,IAAI,eAAgB,EAAA,CAAA;AAClC,EAAM,KAAA,CAAA,GAAA,CAAI,2BAA2B,QAAQ,CAAA,CAAA;AAC7C,EAAA,MAAM,EAAE,OAAS,EAAA,KAAA,EAAO,KAAM,EAAA,GAAI,gBAAgB,KAAK,CAAA,CAAA;AACvD,EAAA,MAAM,YAAY,KAAO,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,KAAA,CAAA,SAAA,CAAA;AAEzB,EAAI,IAAA,OAAA;AAAS,IAAA,2CAAQ,QAAS,EAAA,IAAA,CAAA,CAAA;AAC9B,EAAI,IAAA,KAAA;AAAO,IAAA,uBAAQ,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAM,QAAS,EAAA,OAAA,EAAA,EAAS,MAAM,OAAQ,CAAA,CAAA;AAEzD,EAAA,uBAEK,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,EAAA,SAAA,IAAa,SAAU,CAAA,MAAA,GAAS,qBAC9B,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,OAAQ,EAAA,WAAA,EAAA,EAAY,8BACnB,KAAA,CAAA,aAAA,CAAA,QAAA,EAAA,IAAA,EAAQ,SAAU,CAAA,MAAO,GAAS,qBAC/C,CAAA,EAED,SAAa,IAAA,SAAA,CAAU,MAAW,KAAA,CAAA,oBAChC,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,SAAQ,WAAY,EAAA,EAAA,uBAAqB,CAEvD,kBAAA,KAAA,CAAA,aAAA,CAAC,QAAK,KAAK,EAAA,IAAA,EAAA,EACR,SAAW,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,SAAA,CAAA,GAAA,CAAI,CAAC,QAAa,KAAA;AAC5B,IACE,uBAAA,KAAA,CAAA,aAAA;AAAA,MAAC,gBAAA;AAAA,MAAA;AAAA,QACC,KAAK,QAAS,CAAA,EAAA;AAAA,QACd,QAAA;AAAA,QACA,OAAA;AAAA,OAAA;AAAA,KACF,CAAA;AAAA,GAEJ,CACF,mBACC,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,SAAQ,WAAY,EAAA,EAAA,UAAA,EACrB,GACT,kBAAA,KAAA,CAAA,aAAA,CAAC,IAAK,EAAA,EAAA,MAAA,EAAO,UAAS,IAAM,EAAA,CAAA,EAAG,OAAO,CAAc,WAAA,EAAA,KAAA,CAAM,UAAU,CAAA,CAAA,EAAA,EAAI,WAExE,CACF,CACF,CAAA,CAAA;AAEJ,CAAA,CAAA;AAEO,MAAM,UAAU,MAAM;AAC3B,EAAA,2CAAQ,2BAA4B,EAAA,IAAA,CAAA,CAAA;AACtC;;;;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/// <reference types="react" />
|
|
2
|
-
import * as _backstage_plugin_home_react from '@backstage/plugin-home-react';
|
|
3
2
|
import * as react from 'react';
|
|
4
3
|
import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
|
|
4
|
+
import { CardExtensionProps } from '@backstage/plugin-home-react';
|
|
5
5
|
|
|
6
6
|
declare const incidentPlugin: _backstage_core_plugin_api.BackstagePlugin<{}, {}, {}>;
|
|
7
7
|
declare const EntityIncidentCard: ({ maxIncidents, }: {
|
|
8
8
|
maxIncidents?: number | undefined;
|
|
9
9
|
}) => react.JSX.Element;
|
|
10
|
-
declare const HomePageIncidentCard: (props:
|
|
10
|
+
declare const HomePageIncidentCard: (props: CardExtensionProps<unknown>) => React.JSX.Element;
|
|
11
11
|
|
|
12
12
|
export { EntityIncidentCard, HomePageIncidentCard, incidentPlugin };
|
package/dist/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { E as EntityIncidentCard, H as HomePageIncidentCard, i as incidentPlugin } from './esm/index-
|
|
1
|
+
export { E as EntityIncidentCard, H as HomePageIncidentCard, i as incidentPlugin } from './esm/index-4d6d45fc.esm.js';
|
|
2
2
|
import '@backstage/core-plugin-api';
|
|
3
3
|
import '@backstage/plugin-home-react';
|
|
4
4
|
//# sourceMappingURL=index.esm.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@incident-io/backstage",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.12",
|
|
4
4
|
"main": "dist/index.esm.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"test": "backstage-cli package test",
|
|
21
21
|
"clean": "backstage-cli package clean",
|
|
22
22
|
"prepack": "backstage-cli package prepack",
|
|
23
|
-
"postpack": "backstage-cli package postpack"
|
|
23
|
+
"postpack": "backstage-cli package postpack",
|
|
24
|
+
"prettier": "npx prettier . --write"
|
|
24
25
|
},
|
|
25
26
|
"dependencies": {
|
|
26
27
|
"@backstage/backend-tasks": "^0.5.11",
|
|
@@ -57,6 +58,7 @@
|
|
|
57
58
|
"@types/node": "*",
|
|
58
59
|
"cross-fetch": "^3.1.5",
|
|
59
60
|
"msw": "^1.0.0",
|
|
61
|
+
"prettier": "3.2.5",
|
|
60
62
|
"react": "^17.0.0",
|
|
61
63
|
"react-dom": "^17.0.0",
|
|
62
64
|
"react-router-dom": "^6.0.0"
|
package/src/api/client.ts
CHANGED
|
@@ -17,13 +17,13 @@ import {
|
|
|
17
17
|
DiscoveryApi,
|
|
18
18
|
IdentityApi,
|
|
19
19
|
createApiRef,
|
|
20
|
-
} from
|
|
20
|
+
} from "@backstage/core-plugin-api";
|
|
21
21
|
|
|
22
22
|
export const IncidentApiRef = createApiRef<Incident>({
|
|
23
|
-
id:
|
|
23
|
+
id: "plugin.incident.service",
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
type HTTPMethods =
|
|
26
|
+
type HTTPMethods = "GET" | "PUT" | "POST" | "PATCH" | "DELETE";
|
|
27
27
|
|
|
28
28
|
export interface Incident {
|
|
29
29
|
request<T>({
|
|
@@ -37,7 +37,7 @@ export interface Incident {
|
|
|
37
37
|
}): Promise<T>;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const DEFAULT_PROXY_PATH =
|
|
40
|
+
const DEFAULT_PROXY_PATH = "/incident/api";
|
|
41
41
|
|
|
42
42
|
type Options = {
|
|
43
43
|
discoveryApi: DiscoveryApi;
|
|
@@ -58,7 +58,7 @@ export class IncidentApi implements Incident {
|
|
|
58
58
|
|
|
59
59
|
async request<T = any>({
|
|
60
60
|
path,
|
|
61
|
-
method =
|
|
61
|
+
method = "GET",
|
|
62
62
|
body,
|
|
63
63
|
}: {
|
|
64
64
|
path: string;
|
|
@@ -66,7 +66,7 @@ export class IncidentApi implements Incident {
|
|
|
66
66
|
body?: string;
|
|
67
67
|
}): Promise<T> {
|
|
68
68
|
const apiUrl =
|
|
69
|
-
(await this.discoveryApi.getBaseUrl(
|
|
69
|
+
(await this.discoveryApi.getBaseUrl("proxy")) + this.proxyPath;
|
|
70
70
|
const { token } = await this.identityApi.getCredentials();
|
|
71
71
|
|
|
72
72
|
const resp = await fetch(`${apiUrl}${path}`, {
|