@incident-io/backstage 0.0.14 → 0.0.15

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.
Files changed (32) hide show
  1. package/README.md +1 -1
  2. package/config.d.ts +3 -3
  3. package/dist/{components/IncidentListItem/index.esm.js → esm/index-3220e1e0.esm.js} +28 -5
  4. package/dist/esm/index-3220e1e0.esm.js.map +1 -0
  5. package/dist/esm/index-5cb52808.esm.js +73 -0
  6. package/dist/esm/index-5cb52808.esm.js.map +1 -0
  7. package/dist/{components/EntityIncidentCard/index.esm.js → esm/index-632bbd58.esm.js} +10 -5
  8. package/dist/esm/index-632bbd58.esm.js.map +1 -0
  9. package/dist/esm/index-8f5c4c5f.esm.js +101 -0
  10. package/dist/esm/index-8f5c4c5f.esm.js.map +1 -0
  11. package/dist/index.d.ts +2 -1
  12. package/dist/index.esm.js +3 -1
  13. package/dist/index.esm.js.map +1 -1
  14. package/package.json +3 -10
  15. package/src/api/client.ts +1 -1
  16. package/src/components/HomePageIncidentCard/Content.test.tsx +4 -1
  17. package/src/components/HomePageIncidentCard/Content.tsx +11 -4
  18. package/src/components/HomePageIncidentCard/Context.tsx +46 -0
  19. package/src/components/HomePageIncidentCard/index.ts +1 -0
  20. package/src/plugin.ts +33 -4
  21. package/dist/api/client.esm.js +0 -34
  22. package/dist/api/client.esm.js.map +0 -1
  23. package/dist/components/EntityIncidentCard/index.esm.js.map +0 -1
  24. package/dist/components/HomePageIncidentCard/Content.esm.js +0 -35
  25. package/dist/components/HomePageIncidentCard/Content.esm.js.map +0 -1
  26. package/dist/components/HomePageIncidentCard/index.esm.js +0 -2
  27. package/dist/components/HomePageIncidentCard/index.esm.js.map +0 -1
  28. package/dist/components/IncidentListItem/index.esm.js.map +0 -1
  29. package/dist/hooks/useIncidentRequest.esm.js +0 -25
  30. package/dist/hooks/useIncidentRequest.esm.js.map +0 -1
  31. package/dist/plugin.esm.js +0 -42
  32. package/dist/plugin.esm.js.map +0 -1
package/README.md CHANGED
@@ -33,7 +33,7 @@ First, install the incident plugin via a CLI:
33
33
 
34
34
  ```bash
35
35
  # From your Backstage app root directory
36
- yarn add --cwd packages/app @incident-io/backstage
36
+ yarn --cwd packages/app add @incident-io/backstage
37
37
  ```
38
38
 
39
39
  Next, add the plugin to `EntityPage.tsx` in
package/config.d.ts CHANGED
@@ -60,9 +60,9 @@ export interface Config {
60
60
  */
61
61
  system?: string;
62
62
  /**
63
- * The custom field ID that associated Group entities to incidents.
64
- * @visibility frontend
65
- */
63
+ * The custom field ID that associated Group entities to incidents.
64
+ * @visibility frontend
65
+ */
66
66
  group?: string;
67
67
  };
68
68
  };
@@ -1,8 +1,30 @@
1
+ import { useApi } from '@backstage/core-plugin-api';
2
+ import { useAsync } from 'react-use';
3
+ import { I as IncidentApiRef } from './index-8f5c4c5f.esm.js';
1
4
  import { DateTime, Duration } from 'luxon';
2
5
  import { makeStyles, ListItem, ListItemText, Chip, Typography, ListItemSecondaryAction, Tooltip, IconButton } from '@material-ui/core';
3
6
  import OpenInBrowserIcon from '@material-ui/icons/OpenInBrowser';
4
7
  import React from 'react';
5
8
 
9
+ const useIncidentList = (query, deps) => {
10
+ const IncidentApi = useApi(IncidentApiRef);
11
+ const { value, loading, error } = useAsync(async () => {
12
+ return await IncidentApi.request({
13
+ path: `/v2/incidents?${query.toString()}`
14
+ });
15
+ }, deps);
16
+ return { loading, error, value };
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
+ };
27
+
6
28
  const useStyles = makeStyles((theme) => ({
7
29
  listItemPrimary: {
8
30
  display: "flex",
@@ -28,11 +50,12 @@ const IncidentListItem = ({
28
50
  baseUrl,
29
51
  incident
30
52
  }) => {
53
+ var _a, _b;
31
54
  const classes = useStyles();
32
- const reportedAt = incident.incident_timestamp_values?.find(
55
+ const reportedAt = (_a = incident.incident_timestamp_values) == null ? void 0 : _a.find(
33
56
  (ts) => ts.incident_timestamp.name.match(/reported/i)
34
57
  );
35
- const reportedAtDate = reportedAt?.value?.value || incident.created_at;
58
+ const reportedAtDate = ((_b = reportedAt == null ? void 0 : reportedAt.value) == null ? void 0 : _b.value) || incident.created_at;
36
59
  const sinceReported = (/* @__PURE__ */ new Date()).getTime() - new Date(reportedAtDate).getTime();
37
60
  const sinceReportedLabel = DateTime.local().minus(Duration.fromMillis(sinceReported)).toRelative({ locale: "en" });
38
61
  const lead = incident.incident_role_assignments.find((roleAssignment) => {
@@ -55,7 +78,7 @@ const IncidentListItem = ({
55
78
  variant: "body1",
56
79
  className: classes.listItemPrimary
57
80
  },
58
- secondary: /* @__PURE__ */ React.createElement(Typography, { noWrap: true, variant: "body2", color: "textSecondary" }, "Reported ", sinceReportedLabel, " and", " ", lead?.assignee ? `${lead.assignee.name} is lead` : "the lead is unassigned", ".")
81
+ secondary: /* @__PURE__ */ React.createElement(Typography, { noWrap: true, variant: "body2", color: "textSecondary" }, "Reported ", sinceReportedLabel, " and", " ", (lead == null ? void 0 : lead.assignee) ? `${lead.assignee.name} is lead` : "the lead is unassigned", ".")
59
82
  }
60
83
  ), /* @__PURE__ */ React.createElement(ListItemSecondaryAction, null, /* @__PURE__ */ React.createElement(Tooltip, { title: "View in incident.io", placement: "top" }, /* @__PURE__ */ React.createElement(
61
84
  IconButton,
@@ -69,5 +92,5 @@ const IncidentListItem = ({
69
92
  ))));
70
93
  };
71
94
 
72
- export { IncidentListItem };
73
- //# sourceMappingURL=index.esm.js.map
95
+ export { IncidentListItem as I, useIncidentList as a, useIdentity as u };
96
+ //# sourceMappingURL=index-3220e1e0.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-3220e1e0.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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"}
@@ -0,0 +1,73 @@
1
+ import { Progress } from '@backstage/core-components';
2
+ import Link from '@material-ui/core/Link';
3
+ import { Alert } from '@material-ui/lab';
4
+ import React, { createContext, useMemo, useContext } from 'react';
5
+ import { a as useIncidentList, I as IncidentListItem } from './index-3220e1e0.esm.js';
6
+ import { Typography, List } from '@material-ui/core';
7
+ import { useApi, configApiRef } from '@backstage/core-plugin-api';
8
+ import 'react-use';
9
+ import './index-8f5c4c5f.esm.js';
10
+ import '@backstage/plugin-home-react';
11
+ import 'luxon';
12
+ import '@material-ui/icons/OpenInBrowser';
13
+
14
+ const Context = createContext(
15
+ void 0
16
+ );
17
+ const ContextProvider = (props) => {
18
+ const {
19
+ children,
20
+ filterType: defaultFilterType,
21
+ filter: defaultFilter
22
+ } = props;
23
+ const value = useMemo(
24
+ () => ({
25
+ filterType: defaultFilterType || "status_category",
26
+ filter: defaultFilter || "active"
27
+ }),
28
+ [defaultFilter, defaultFilterType]
29
+ );
30
+ return /* @__PURE__ */ React.createElement(Context.Provider, { value }, children);
31
+ };
32
+ const useHomePageIncidentCard = () => {
33
+ const value = useContext(Context);
34
+ if (value === void 0) {
35
+ throw new Error(
36
+ "useHomePageIncidentCard must be used within a HomePageIncidentCardContextProvider"
37
+ );
38
+ }
39
+ return value;
40
+ };
41
+
42
+ const HomePageIncidentCardContent = () => {
43
+ const { filterType, filter } = useHomePageIncidentCard();
44
+ const config = useApi(configApiRef);
45
+ const baseUrl = config.getOptionalString("incident.baseUrl") || "https://app.incident.io";
46
+ const query = React.useMemo(() => {
47
+ const params = new URLSearchParams();
48
+ params.set(`${filterType}[one_of]`, filter);
49
+ return params;
50
+ }, [filterType, filter]);
51
+ const { loading, error, value } = useIncidentList(query, [query]);
52
+ const incidents = value == null ? void 0 : value.incidents;
53
+ if (loading)
54
+ return /* @__PURE__ */ React.createElement(Progress, null);
55
+ if (error)
56
+ return /* @__PURE__ */ React.createElement(Alert, { severity: "error" }, error.message);
57
+ return /* @__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."), incidents && incidents.length === 0 && /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle1" }, "No ongoing incidents."), /* @__PURE__ */ React.createElement(List, { dense: true }, incidents == null ? void 0 : incidents.map((incident) => {
58
+ return /* @__PURE__ */ React.createElement(
59
+ IncidentListItem,
60
+ {
61
+ key: incident.id,
62
+ incident,
63
+ baseUrl
64
+ }
65
+ );
66
+ })), /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle1" }, "Click to", " ", /* @__PURE__ */ React.createElement(Link, { target: "_blank", href: `${baseUrl}/incidents?${query.toString()}` }, "see more.")));
67
+ };
68
+ const Content = () => {
69
+ return /* @__PURE__ */ React.createElement(HomePageIncidentCardContent, null);
70
+ };
71
+
72
+ export { Content, ContextProvider };
73
+ //# sourceMappingURL=index-5cb52808.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-5cb52808.esm.js","sources":["../../src/components/HomePageIncidentCard/Context.tsx","../../src/components/HomePageIncidentCard/Content.tsx"],"sourcesContent":["import React, { createContext, useContext, useMemo } from \"react\";\n\ntype HomePageIncidentCardContextValue = {\n filterType: \"status_category\" | \"status\";\n filter: string;\n};\n\nconst Context = createContext<HomePageIncidentCardContextValue | undefined>(\n undefined,\n);\n\nexport const ContextProvider = (props: {\n children: React.JSX.Element;\n filterType?: \"status_category\" | \"status\";\n filter?: string;\n}) => {\n const {\n children,\n filterType: defaultFilterType,\n filter: defaultFilter,\n } = props;\n\n const value = useMemo(\n () => ({\n filterType: defaultFilterType || \"status_category\",\n filter: defaultFilter || \"active\",\n }),\n [defaultFilter, defaultFilterType],\n );\n\n return <Context.Provider value={value}>{children}</Context.Provider>;\n};\n\nexport const useHomePageIncidentCard = () => {\n const value = useContext(Context);\n\n if (value === undefined) {\n throw new Error(\n \"useHomePageIncidentCard must be used within a HomePageIncidentCardContextProvider\",\n );\n }\n\n return value;\n};\n\nexport default Context;\n","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\";\nimport { useHomePageIncidentCard } from \"./Context\";\n\nexport const HomePageIncidentCardContent = () => {\n const { filterType, filter } = useHomePageIncidentCard();\n const config = useApi(configApiRef);\n const baseUrl =\n config.getOptionalString(\"incident.baseUrl\") || \"https://app.incident.io\";\n\n const query = React.useMemo(() => {\n const params = new URLSearchParams();\n params.set(`${filterType}[one_of]`, filter);\n return params;\n }, [filterType, filter]);\n\n const { loading, error, value } = useIncidentList(query, [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":";;;;;;;;;;;;;AAOA,MAAM,OAAU,GAAA,aAAA;AAAA,EACd,KAAA,CAAA;AACF,CAAA,CAAA;AAEa,MAAA,eAAA,GAAkB,CAAC,KAI1B,KAAA;AACJ,EAAM,MAAA;AAAA,IACJ,QAAA;AAAA,IACA,UAAY,EAAA,iBAAA;AAAA,IACZ,MAAQ,EAAA,aAAA;AAAA,GACN,GAAA,KAAA,CAAA;AAEJ,EAAA,MAAM,KAAQ,GAAA,OAAA;AAAA,IACZ,OAAO;AAAA,MACL,YAAY,iBAAqB,IAAA,iBAAA;AAAA,MACjC,QAAQ,aAAiB,IAAA,QAAA;AAAA,KAC3B,CAAA;AAAA,IACA,CAAC,eAAe,iBAAiB,CAAA;AAAA,GACnC,CAAA;AAEA,EAAA,uBAAQ,KAAA,CAAA,aAAA,CAAA,OAAA,CAAQ,QAAR,EAAA,EAAiB,SAAe,QAAS,CAAA,CAAA;AACnD,EAAA;AAEO,MAAM,0BAA0B,MAAM;AAC3C,EAAM,MAAA,KAAA,GAAQ,WAAW,OAAO,CAAA,CAAA;AAEhC,EAAA,IAAI,UAAU,KAAW,CAAA,EAAA;AACvB,IAAA,MAAM,IAAI,KAAA;AAAA,MACR,mFAAA;AAAA,KACF,CAAA;AAAA,GACF;AAEA,EAAO,OAAA,KAAA,CAAA;AACT,CAAA;;ACjCO,MAAM,8BAA8B,MAAM;AAC/C,EAAA,MAAM,EAAE,UAAA,EAAY,MAAO,EAAA,GAAI,uBAAwB,EAAA,CAAA;AACvD,EAAM,MAAA,MAAA,GAAS,OAAO,YAAY,CAAA,CAAA;AAClC,EAAA,MAAM,OACJ,GAAA,MAAA,CAAO,iBAAkB,CAAA,kBAAkB,CAAK,IAAA,yBAAA,CAAA;AAElD,EAAM,MAAA,KAAA,GAAQ,KAAM,CAAA,OAAA,CAAQ,MAAM;AAChC,IAAM,MAAA,MAAA,GAAS,IAAI,eAAgB,EAAA,CAAA;AACnC,IAAA,MAAA,CAAO,GAAI,CAAA,CAAA,EAAG,UAAU,CAAA,QAAA,CAAA,EAAY,MAAM,CAAA,CAAA;AAC1C,IAAO,OAAA,MAAA,CAAA;AAAA,GACN,EAAA,CAAC,UAAY,EAAA,MAAM,CAAC,CAAA,CAAA;AAEvB,EAAM,MAAA,EAAE,SAAS,KAAO,EAAA,KAAA,KAAU,eAAgB,CAAA,KAAA,EAAO,CAAC,KAAK,CAAC,CAAA,CAAA;AAChE,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;;;;"}
@@ -8,8 +8,12 @@ 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 { useIdentity, useIncidentList } from '../../hooks/useIncidentRequest.esm.js';
12
- import { IncidentListItem } from '../IncidentListItem/index.esm.js';
11
+ import { u as useIdentity, a as useIncidentList, I as IncidentListItem } from './index-3220e1e0.esm.js';
12
+ import 'react-use';
13
+ import './index-8f5c4c5f.esm.js';
14
+ import '@backstage/plugin-home-react';
15
+ import 'luxon';
16
+ import '@material-ui/icons/OpenInBrowser';
13
17
 
14
18
  const IncorrectConfigCard = () => {
15
19
  return /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(CardHeader, { title: "incident.io" }), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(CardContent, null, /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle1" }, "No custom field configuration was found. In order to display incidents, this entity must be mapped to an incident.io custom field ID in Backstage's app-config.yaml.")));
@@ -17,6 +21,7 @@ const IncorrectConfigCard = () => {
17
21
  const EntityIncidentCard = ({
18
22
  maxIncidents = 2
19
23
  }) => {
24
+ var _a;
20
25
  const config = useApi(configApiRef);
21
26
  const { entity } = useEntity();
22
27
  const {
@@ -36,7 +41,7 @@ const EntityIncidentCard = ({
36
41
  loading: incidentsLoading,
37
42
  error: incidentsError
38
43
  } = useIncidentList(queryLive, [reload]);
39
- const incidents = incidentsResponse?.incidents;
44
+ const incidents = incidentsResponse == null ? void 0 : incidentsResponse.incidents;
40
45
  if (!entityFieldID) {
41
46
  return /* @__PURE__ */ React.createElement(IncorrectConfigCard, null);
42
47
  }
@@ -73,7 +78,7 @@ const EntityIncidentCard = ({
73
78
  )),
74
79
  subheader: /* @__PURE__ */ React.createElement(HeaderIconLinkRow, { links: [createIncidentLink, viewIncidentsLink] })
75
80
  }
76
- ), /* @__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 }, incidents?.slice(0, maxIncidents)?.map((incident) => {
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) => {
77
82
  return /* @__PURE__ */ React.createElement(
78
83
  IncidentListItem,
79
84
  {
@@ -109,4 +114,4 @@ function getEntityFieldID(config, entity) {
109
114
  }
110
115
 
111
116
  export { EntityIncidentCard };
112
- //# sourceMappingURL=index.esm.js.map
117
+ //# sourceMappingURL=index-632bbd58.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-632bbd58.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 case \"Group\":\n return config.getOptional(\"incident.fields.group\");\n default:\n throw new Error(`unrecognised entity kind: ${entity.kind}`);\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ import { createApiRef, createPlugin, createApiFactory, discoveryApiRef, fetchApiRef, createComponentExtension } from '@backstage/core-plugin-api';
2
+ import { createCardExtension } from '@backstage/plugin-home-react';
3
+
4
+ var __defProp = Object.defineProperty;
5
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
6
+ var __publicField = (obj, key, value) => {
7
+ __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
8
+ return value;
9
+ };
10
+ const IncidentApiRef = createApiRef({
11
+ id: "plugin.incident.service"
12
+ });
13
+ const DEFAULT_PROXY_PATH = "/incident/api";
14
+ class IncidentApi {
15
+ constructor(opts) {
16
+ __publicField(this, "discoveryApi");
17
+ __publicField(this, "fetchApi");
18
+ __publicField(this, "proxyPath");
19
+ var _a;
20
+ this.discoveryApi = opts.discoveryApi;
21
+ this.fetchApi = opts.fetchApi;
22
+ this.proxyPath = (_a = opts.proxyPath) != null ? _a : DEFAULT_PROXY_PATH;
23
+ }
24
+ async request({
25
+ path,
26
+ method = "GET",
27
+ body
28
+ }) {
29
+ const apiUrl = await this.discoveryApi.getBaseUrl("proxy") + this.proxyPath;
30
+ const resp = await this.fetchApi.fetch(`${apiUrl}${path}`, {
31
+ method,
32
+ body
33
+ });
34
+ if (!resp.ok) {
35
+ throw new Error(`${resp.status} ${resp.statusText}`);
36
+ }
37
+ return await resp.json();
38
+ }
39
+ }
40
+
41
+ const incidentPlugin = createPlugin({
42
+ id: "incident",
43
+ apis: [
44
+ createApiFactory({
45
+ api: IncidentApiRef,
46
+ deps: {
47
+ discoveryApi: discoveryApiRef,
48
+ fetchApi: fetchApiRef
49
+ },
50
+ factory: ({ discoveryApi, fetchApi }) => {
51
+ return new IncidentApi({
52
+ discoveryApi,
53
+ fetchApi
54
+ });
55
+ }
56
+ })
57
+ ]
58
+ });
59
+ const EntityIncidentCard = incidentPlugin.provide(
60
+ createComponentExtension({
61
+ name: "EntityIncidentCard",
62
+ component: {
63
+ lazy: () => import('./index-632bbd58.esm.js').then(
64
+ (m) => m.EntityIncidentCard
65
+ )
66
+ }
67
+ })
68
+ );
69
+ const HomePageIncidentCard = incidentPlugin.provide(
70
+ createCardExtension({
71
+ name: "HomePageIncidentCard",
72
+ title: "Ongoing Incidents",
73
+ components: () => import('./index-5cb52808.esm.js'),
74
+ settings: {
75
+ schema: {
76
+ type: "object",
77
+ properties: {
78
+ filterType: {
79
+ type: "string",
80
+ title: "Filter Type",
81
+ description: "Whether to filter on status category or status",
82
+ oneOf: [
83
+ { enum: ["status_category"], title: "Status Category" },
84
+ { enum: ["status"], title: "Status" }
85
+ ],
86
+ default: "status_category"
87
+ },
88
+ filter: {
89
+ type: "string",
90
+ title: "Filter",
91
+ description: "The filter to use. This is a string that will be passed to the API.",
92
+ default: "active"
93
+ }
94
+ }
95
+ }
96
+ }
97
+ })
98
+ );
99
+
100
+ export { EntityIncidentCard as E, HomePageIncidentCard as H, IncidentApiRef as I, incidentPlugin as i };
101
+ //# sourceMappingURL=index-8f5c4c5f.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-8f5c4c5f.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 FetchApi,\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 fetchApi: FetchApi;\n proxyPath?: string;\n};\n\nexport class IncidentApi implements Incident {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n private readonly proxyPath: string;\n\n constructor(opts: Options) {\n this.discoveryApi = opts.discoveryApi;\n this.fetchApi = opts.fetchApi;\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\n const resp = await this.fetchApi.fetch(`${apiUrl}${path}`, {\n method: method,\n body: body,\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 fetchApiRef,\n} from \"@backstage/core-plugin-api\";\nimport {\n CardExtensionProps,\n createCardExtension,\n} 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: {\n discoveryApi: discoveryApiRef,\n fetchApi: fetchApiRef,\n },\n factory: ({ discoveryApi, fetchApi }) => {\n return new IncidentApi({\n discoveryApi: discoveryApi,\n fetchApi: fetchApi,\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: (\n props: CardExtensionProps<unknown>,\n) => React.JSX.Element = incidentPlugin.provide(\n createCardExtension({\n name: \"HomePageIncidentCard\",\n title: \"Ongoing Incidents\",\n components: () => import(\"./components/HomePageIncidentCard\"),\n settings: {\n schema: {\n type: \"object\",\n properties: {\n filterType: {\n type: \"string\",\n title: \"Filter Type\",\n description: \"Whether to filter on status category or status\",\n oneOf: [\n { enum: [\"status_category\"], title: \"Status Category\" },\n { enum: [\"status\"], title: \"Status\" },\n ],\n default: \"status_category\",\n },\n filter: {\n type: \"string\",\n title: \"Filter\",\n description:\n \"The filter to use. This is a string that will be passed to the API.\",\n default: \"active\",\n },\n },\n },\n },\n }),\n);\n"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  /// <reference types="react" />
2
2
  import * as react from 'react';
3
3
  import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
4
+ import { CardExtensionProps } from '@backstage/plugin-home-react';
4
5
 
5
6
  declare const incidentPlugin: _backstage_core_plugin_api.BackstagePlugin<{}, {}, {}>;
6
7
  declare const EntityIncidentCard: ({ maxIncidents, }: {
7
8
  maxIncidents?: number | undefined;
8
9
  }) => react.JSX.Element;
9
- declare const HomePageIncidentCard: any;
10
+ declare const HomePageIncidentCard: (props: CardExtensionProps<unknown>) => React.JSX.Element;
10
11
 
11
12
  export { EntityIncidentCard, HomePageIncidentCard, incidentPlugin };
package/dist/index.esm.js CHANGED
@@ -1,2 +1,4 @@
1
- export { EntityIncidentCard, HomePageIncidentCard, incidentPlugin } from './plugin.esm.js';
1
+ export { E as EntityIncidentCard, H as HomePageIncidentCard, i as incidentPlugin } from './esm/index-8f5c4c5f.esm.js';
2
+ import '@backstage/core-plugin-api';
3
+ import '@backstage/plugin-home-react';
2
4
  //# sourceMappingURL=index.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@incident-io/backstage",
3
- "version": "0.0.14",
3
+ "version": "0.0.15",
4
4
  "main": "dist/index.esm.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "license": "Apache-2.0",
@@ -20,7 +20,7 @@
20
20
  "sideEffects": false,
21
21
  "scripts": {
22
22
  "start": "backstage-cli package start",
23
- "build": "backstage-cli package build",
23
+ "build": "npx tsc && backstage-cli package build",
24
24
  "lint": "backstage-cli package lint",
25
25
  "test": "backstage-cli package test",
26
26
  "clean": "backstage-cli package clean",
@@ -43,7 +43,7 @@
43
43
  "@material-ui/lab": "^4.0.0-alpha.60",
44
44
  "luxon": "^3.3.0",
45
45
  "moment": "^2.29.4",
46
- "qs": "^6.11.2",
46
+ "qs": "^6.14.1",
47
47
  "react-use": "^17.2.4"
48
48
  },
49
49
  "peerDependencies": {
@@ -78,12 +78,5 @@
78
78
  "url": "https://github.com/incident-io/backstage-plugins.git",
79
79
  "directory": "incident"
80
80
  },
81
- "typesVersions": {
82
- "*": {
83
- "index": [
84
- "dist/index.d.ts"
85
- ]
86
- }
87
- },
88
81
  "module": "./dist/index.esm.js"
89
82
  }
package/src/api/client.ts CHANGED
@@ -41,7 +41,7 @@ const DEFAULT_PROXY_PATH = "/incident/api";
41
41
 
42
42
  type Options = {
43
43
  discoveryApi: DiscoveryApi;
44
- fetchApi: FetchApi,
44
+ fetchApi: FetchApi;
45
45
  proxyPath?: string;
46
46
  };
47
47
 
@@ -2,6 +2,7 @@ import { TestApiProvider, renderInTestApp } from "@backstage/test-utils";
2
2
  import React from "react";
3
3
  import { IncidentApi, IncidentApiRef } from "../../api/client";
4
4
  import { HomePageIncidentCardContent } from "./Content";
5
+ import { ContextProvider } from "./Context";
5
6
 
6
7
  const mockIncidentApi: jest.Mocked<Partial<IncidentApi>> = {
7
8
  request: jest.fn().mockResolvedValue({
@@ -46,7 +47,9 @@ describe("HomePageIncidentCardContent", () => {
46
47
  it("should render a list of live incidents", async () => {
47
48
  const { getByTestId } = await renderInTestApp(
48
49
  <TestApiProvider apis={[[IncidentApiRef, mockIncidentApi]]}>
49
- <HomePageIncidentCardContent />
50
+ <ContextProvider filterType="status_category" filter="active">
51
+ <HomePageIncidentCardContent />
52
+ </ContextProvider>
50
53
  </TestApiProvider>,
51
54
  );
52
55
 
@@ -6,14 +6,21 @@ import { useIncidentList } from "../../hooks/useIncidentRequest";
6
6
  import { Typography, List } from "@material-ui/core";
7
7
  import { IncidentListItem } from "../IncidentListItem";
8
8
  import { configApiRef, useApi } from "@backstage/core-plugin-api";
9
+ import { useHomePageIncidentCard } from "./Context";
9
10
 
10
11
  export const HomePageIncidentCardContent = () => {
12
+ const { filterType, filter } = useHomePageIncidentCard();
11
13
  const config = useApi(configApiRef);
12
- const baseUrl = config.getOptionalString('incident.baseUrl') || "https://app.incident.io";
14
+ const baseUrl =
15
+ config.getOptionalString("incident.baseUrl") || "https://app.incident.io";
13
16
 
14
- const query = new URLSearchParams();
15
- query.set(`status_category[one_of]`, "active");
16
- const { loading, error, value } = useIncidentList(query);
17
+ const query = React.useMemo(() => {
18
+ const params = new URLSearchParams();
19
+ params.set(`${filterType}[one_of]`, filter);
20
+ return params;
21
+ }, [filterType, filter]);
22
+
23
+ const { loading, error, value } = useIncidentList(query, [query]);
17
24
  const incidents = value?.incidents;
18
25
 
19
26
  if (loading) return <Progress />;
@@ -0,0 +1,46 @@
1
+ import React, { createContext, useContext, useMemo } from "react";
2
+
3
+ type HomePageIncidentCardContextValue = {
4
+ filterType: "status_category" | "status";
5
+ filter: string;
6
+ };
7
+
8
+ const Context = createContext<HomePageIncidentCardContextValue | undefined>(
9
+ undefined,
10
+ );
11
+
12
+ export const ContextProvider = (props: {
13
+ children: React.JSX.Element;
14
+ filterType?: "status_category" | "status";
15
+ filter?: string;
16
+ }) => {
17
+ const {
18
+ children,
19
+ filterType: defaultFilterType,
20
+ filter: defaultFilter,
21
+ } = props;
22
+
23
+ const value = useMemo(
24
+ () => ({
25
+ filterType: defaultFilterType || "status_category",
26
+ filter: defaultFilter || "active",
27
+ }),
28
+ [defaultFilter, defaultFilterType],
29
+ );
30
+
31
+ return <Context.Provider value={value}>{children}</Context.Provider>;
32
+ };
33
+
34
+ export const useHomePageIncidentCard = () => {
35
+ const value = useContext(Context);
36
+
37
+ if (value === undefined) {
38
+ throw new Error(
39
+ "useHomePageIncidentCard must be used within a HomePageIncidentCardContextProvider",
40
+ );
41
+ }
42
+
43
+ return value;
44
+ };
45
+
46
+ export default Context;
@@ -1 +1,2 @@
1
1
  export { Content } from "./Content";
2
+ export { ContextProvider } from "./Context";
package/src/plugin.ts CHANGED
@@ -20,7 +20,10 @@ import {
20
20
  discoveryApiRef,
21
21
  fetchApiRef,
22
22
  } from "@backstage/core-plugin-api";
23
- import {CardExtensionProps, createCardExtension} from "@backstage/plugin-home-react";
23
+ import {
24
+ CardExtensionProps,
25
+ createCardExtension,
26
+ } from "@backstage/plugin-home-react";
24
27
 
25
28
  import { IncidentApi, IncidentApiRef } from "./api/client";
26
29
 
@@ -29,8 +32,8 @@ export const incidentPlugin = createPlugin({
29
32
  apis: [
30
33
  createApiFactory({
31
34
  api: IncidentApiRef,
32
- deps: {
33
- discoveryApi: discoveryApiRef,
35
+ deps: {
36
+ discoveryApi: discoveryApiRef,
34
37
  fetchApi: fetchApiRef,
35
38
  },
36
39
  factory: ({ discoveryApi, fetchApi }) => {
@@ -55,10 +58,36 @@ export const EntityIncidentCard = incidentPlugin.provide(
55
58
  }),
56
59
  );
57
60
 
58
- export const HomePageIncidentCard: (props: CardExtensionProps<unknown>) => React.JSX.Element = incidentPlugin.provide(
61
+ export const HomePageIncidentCard: (
62
+ props: CardExtensionProps<unknown>,
63
+ ) => React.JSX.Element = incidentPlugin.provide(
59
64
  createCardExtension({
60
65
  name: "HomePageIncidentCard",
61
66
  title: "Ongoing Incidents",
62
67
  components: () => import("./components/HomePageIncidentCard"),
68
+ settings: {
69
+ schema: {
70
+ type: "object",
71
+ properties: {
72
+ filterType: {
73
+ type: "string",
74
+ title: "Filter Type",
75
+ description: "Whether to filter on status category or status",
76
+ oneOf: [
77
+ { enum: ["status_category"], title: "Status Category" },
78
+ { enum: ["status"], title: "Status" },
79
+ ],
80
+ default: "status_category",
81
+ },
82
+ filter: {
83
+ type: "string",
84
+ title: "Filter",
85
+ description:
86
+ "The filter to use. This is a string that will be passed to the API.",
87
+ default: "active",
88
+ },
89
+ },
90
+ },
91
+ },
63
92
  }),
64
93
  );
@@ -1,34 +0,0 @@
1
- import { createApiRef } from '@backstage/core-plugin-api';
2
-
3
- const IncidentApiRef = createApiRef({
4
- id: "plugin.incident.service"
5
- });
6
- const DEFAULT_PROXY_PATH = "/incident/api";
7
- class IncidentApi {
8
- discoveryApi;
9
- fetchApi;
10
- proxyPath;
11
- constructor(opts) {
12
- this.discoveryApi = opts.discoveryApi;
13
- this.fetchApi = opts.fetchApi;
14
- this.proxyPath = opts.proxyPath ?? DEFAULT_PROXY_PATH;
15
- }
16
- async request({
17
- path,
18
- method = "GET",
19
- body
20
- }) {
21
- const apiUrl = await this.discoveryApi.getBaseUrl("proxy") + this.proxyPath;
22
- const resp = await this.fetchApi.fetch(`${apiUrl}${path}`, {
23
- method,
24
- body
25
- });
26
- if (!resp.ok) {
27
- throw new Error(`${resp.status} ${resp.statusText}`);
28
- }
29
- return await resp.json();
30
- }
31
- }
32
-
33
- export { IncidentApi, IncidentApiRef };
34
- //# sourceMappingURL=client.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"client.esm.js","sources":["../../src/api/client.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 FetchApi,\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 fetchApi: FetchApi,\n proxyPath?: string;\n};\n\nexport class IncidentApi implements Incident {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n private readonly proxyPath: string;\n\n constructor(opts: Options) {\n this.discoveryApi = opts.discoveryApi;\n this.fetchApi = opts.fetchApi;\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\n const resp = await this.fetchApi.fetch(`${apiUrl}${path}`, {\n method: method,\n body: body,\n });\n if (!resp.ok) {\n throw new Error(`${resp.status} ${resp.statusText}`);\n }\n\n return await resp.json();\n }\n}\n"],"names":[],"mappings":";;AAqBO,MAAM,iBAAiB,YAAuB,CAAA;AAAA,EACnD,EAAI,EAAA;AACN,CAAC;AAgBD,MAAM,kBAAqB,GAAA,eAAA;AAQpB,MAAM,WAAgC,CAAA;AAAA,EAC1B,YAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EAEjB,YAAY,IAAe,EAAA;AACzB,IAAA,IAAA,CAAK,eAAe,IAAK,CAAA,YAAA;AACzB,IAAA,IAAA,CAAK,WAAW,IAAK,CAAA,QAAA;AACrB,IAAK,IAAA,CAAA,SAAA,GAAY,KAAK,SAAa,IAAA,kBAAA;AAAA;AACrC,EAEA,MAAM,OAAiB,CAAA;AAAA,IACrB,IAAA;AAAA,IACA,MAAS,GAAA,KAAA;AAAA,IACT;AAAA,GAKa,EAAA;AACb,IAAA,MAAM,SACH,MAAM,IAAA,CAAK,aAAa,UAAW,CAAA,OAAO,IAAK,IAAK,CAAA,SAAA;AAEvD,IAAM,MAAA,IAAA,GAAO,MAAM,IAAK,CAAA,QAAA,CAAS,MAAM,CAAG,EAAA,MAAM,CAAG,EAAA,IAAI,CAAI,CAAA,EAAA;AAAA,MACzD,MAAA;AAAA,MACA;AAAA,KACD,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;AAAA;AAGrD,IAAO,OAAA,MAAM,KAAK,IAAK,EAAA;AAAA;AAE3B;;;;"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.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 case \"Group\":\n return config.getOptional(\"incident.fields.group\");\n default:\n throw new Error(`unrecognised entity kind: ${entity.kind}`);\n }\n}\n"],"names":[],"mappings":"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"}
@@ -1,35 +0,0 @@
1
- import { Progress } from '@backstage/core-components';
2
- import Link from '@material-ui/core/Link';
3
- import { Alert } from '@material-ui/lab';
4
- import React from 'react';
5
- import { useIncidentList } from '../../hooks/useIncidentRequest.esm.js';
6
- import { Typography, List } from '@material-ui/core';
7
- import { IncidentListItem } from '../IncidentListItem/index.esm.js';
8
- import { useApi, configApiRef } from '@backstage/core-plugin-api';
9
-
10
- const HomePageIncidentCardContent = () => {
11
- const config = useApi(configApiRef);
12
- const baseUrl = config.getOptionalString("incident.baseUrl") || "https://app.incident.io";
13
- const query = new URLSearchParams();
14
- query.set(`status_category[one_of]`, "active");
15
- const { loading, error, value } = useIncidentList(query);
16
- const incidents = value?.incidents;
17
- if (loading) return /* @__PURE__ */ React.createElement(Progress, null);
18
- if (error) return /* @__PURE__ */ React.createElement(Alert, { severity: "error" }, error.message);
19
- return /* @__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."), incidents && incidents.length === 0 && /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle1" }, "No ongoing incidents."), /* @__PURE__ */ React.createElement(List, { dense: true }, incidents?.map((incident) => {
20
- return /* @__PURE__ */ React.createElement(
21
- IncidentListItem,
22
- {
23
- key: incident.id,
24
- incident,
25
- baseUrl
26
- }
27
- );
28
- })), /* @__PURE__ */ React.createElement(Typography, { variant: "subtitle1" }, "Click to", " ", /* @__PURE__ */ React.createElement(Link, { target: "_blank", href: `${baseUrl}/incidents?${query.toString()}` }, "see more.")));
29
- };
30
- const Content = () => {
31
- return /* @__PURE__ */ React.createElement(HomePageIncidentCardContent, null);
32
- };
33
-
34
- export { Content, HomePageIncidentCardContent };
35
- //# sourceMappingURL=Content.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Content.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;AAChC,EAAA,MAAM,OAAU,GAAA,MAAA,CAAO,iBAAkB,CAAA,kBAAkB,CAAK,IAAA,yBAAA;AAElE,EAAM,MAAA,KAAA,GAAQ,IAAI,eAAgB,EAAA;AAClC,EAAM,KAAA,CAAA,GAAA,CAAI,2BAA2B,QAAQ,CAAA;AAC7C,EAAA,MAAM,EAAE,OAAS,EAAA,KAAA,EAAO,KAAM,EAAA,GAAI,gBAAgB,KAAK,CAAA;AACvD,EAAA,MAAM,YAAY,KAAO,EAAA,SAAA;AAEzB,EAAI,IAAA,OAAA,EAAgB,uBAAA,KAAA,CAAA,aAAA,CAAC,QAAS,EAAA,IAAA,CAAA;AAC9B,EAAA,IAAI,OAAc,uBAAA,KAAA,CAAA,aAAA,CAAC,SAAM,QAAS,EAAA,OAAA,EAAA,EAAS,MAAM,OAAQ,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,EAAA,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;AAAA;AAAA,KACF;AAAA,GAEH,CACH,CACA,kBAAA,KAAA,CAAA,aAAA,CAAC,cAAW,OAAQ,EAAA,WAAA,EAAA,EAAY,UACrB,EAAA,GAAA,kBACR,KAAA,CAAA,aAAA,CAAA,IAAA,EAAA,EAAK,QAAO,QAAS,EAAA,IAAA,EAAM,CAAG,EAAA,OAAO,CAAc,WAAA,EAAA,KAAA,CAAM,UAAU,CAAA,CAAA,EAAA,EAAI,WAExE,CACF,CACF,CAAA;AAEJ;AAEO,MAAM,UAAU,MAAM;AAC3B,EAAA,2CAAQ,2BAA4B,EAAA,IAAA,CAAA;AACtC;;;;"}
@@ -1,2 +0,0 @@
1
- export { Content } from './Content.esm.js';
2
- //# sourceMappingURL=index.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.esm.js","sources":["../../../src/components/IncidentListItem/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 { 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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"}
@@ -1,25 +0,0 @@
1
- import { useApi } from '@backstage/core-plugin-api';
2
- import { useAsync } from 'react-use';
3
- import { IncidentApiRef } from '../api/client.esm.js';
4
-
5
- const useIncidentList = (query, deps) => {
6
- const IncidentApi = useApi(IncidentApiRef);
7
- const { value, loading, error } = useAsync(async () => {
8
- return await IncidentApi.request({
9
- path: `/v2/incidents?${query.toString()}`
10
- });
11
- }, deps);
12
- return { loading, error, value };
13
- };
14
- const useIdentity = () => {
15
- const IncidentApi = useApi(IncidentApiRef);
16
- const { value, loading, error } = useAsync(async () => {
17
- return await IncidentApi.request({
18
- path: `/v1/identity`
19
- });
20
- });
21
- return { value, loading, error };
22
- };
23
-
24
- export { useIdentity, useIncidentList };
25
- //# sourceMappingURL=useIncidentRequest.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useIncidentRequest.esm.js","sources":["../../src/hooks/useIncidentRequest.ts"],"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"],"names":[],"mappings":";;;;AAMa,MAAA,eAAA,GAAkB,CAC7B,KAAA,EACA,IACG,KAAA;AACH,EAAM,MAAA,WAAA,GAAc,OAAO,cAAc,CAAA;AAEzC,EAAA,MAAM,EAAE,KAAO,EAAA,OAAA,EAAS,KAAM,EAAA,GAAI,SAAS,YAAY;AACrD,IAAO,OAAA,MAAM,YAAY,OAEvB,CAAA;AAAA,MACA,IAAM,EAAA,CAAA,cAAA,EAAiB,KAAM,CAAA,QAAA,EAAU,CAAA;AAAA,KACxC,CAAA;AAAA,KACA,IAAI,CAAA;AAEP,EAAO,OAAA,EAAE,OAAS,EAAA,KAAA,EAAO,KAAM,EAAA;AACjC;AAEO,MAAM,cAAc,MAAM;AAC/B,EAAM,MAAA,WAAA,GAAc,OAAO,cAAc,CAAA;AAEzC,EAAA,MAAM,EAAE,KAAO,EAAA,OAAA,EAAS,KAAM,EAAA,GAAI,SAAS,YAAY;AACrD,IAAO,OAAA,MAAM,YAAY,OAEvB,CAAA;AAAA,MACA,IAAM,EAAA,CAAA,YAAA;AAAA,KACP,CAAA;AAAA,GACF,CAAA;AAED,EAAO,OAAA,EAAE,KAAO,EAAA,OAAA,EAAS,KAAM,EAAA;AACjC;;;;"}
@@ -1,42 +0,0 @@
1
- import { createPlugin, createApiFactory, discoveryApiRef, fetchApiRef, createComponentExtension } from '@backstage/core-plugin-api';
2
- import { createCardExtension } from '@backstage/plugin-home-react';
3
- import { IncidentApiRef, IncidentApi } from './api/client.esm.js';
4
-
5
- const incidentPlugin = createPlugin({
6
- id: "incident",
7
- apis: [
8
- createApiFactory({
9
- api: IncidentApiRef,
10
- deps: {
11
- discoveryApi: discoveryApiRef,
12
- fetchApi: fetchApiRef
13
- },
14
- factory: ({ discoveryApi, fetchApi }) => {
15
- return new IncidentApi({
16
- discoveryApi,
17
- fetchApi
18
- });
19
- }
20
- })
21
- ]
22
- });
23
- const EntityIncidentCard = incidentPlugin.provide(
24
- createComponentExtension({
25
- name: "EntityIncidentCard",
26
- component: {
27
- lazy: () => import('./components/EntityIncidentCard/index.esm.js').then(
28
- (m) => m.EntityIncidentCard
29
- )
30
- }
31
- })
32
- );
33
- const HomePageIncidentCard = incidentPlugin.provide(
34
- createCardExtension({
35
- name: "HomePageIncidentCard",
36
- title: "Ongoing Incidents",
37
- components: () => import('./components/HomePageIncidentCard/index.esm.js')
38
- })
39
- );
40
-
41
- export { EntityIncidentCard, HomePageIncidentCard, incidentPlugin };
42
- //# sourceMappingURL=plugin.esm.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugin.esm.js","sources":["../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 createApiFactory,\n createComponentExtension,\n createPlugin,\n discoveryApiRef,\n fetchApiRef,\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: { \n discoveryApi: discoveryApiRef, \n fetchApi: fetchApiRef,\n },\n factory: ({ discoveryApi, fetchApi }) => {\n return new IncidentApi({\n discoveryApi: discoveryApi,\n fetchApi: fetchApi,\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":";;;;AA0BO,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;AAAA,QACJ,YAAc,EAAA,eAAA;AAAA,QACd,QAAU,EAAA;AAAA,OACZ;AAAA,MACA,OAAS,EAAA,CAAC,EAAE,YAAA,EAAc,UAAe,KAAA;AACvC,QAAA,OAAO,IAAI,WAAY,CAAA;AAAA,UACrB,YAAA;AAAA,UACA;AAAA,SACD,CAAA;AAAA;AACH,KACD;AAAA;AAEL,CAAC;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,8CAAiC,CAAE,CAAA,IAAA;AAAA,QACxC,CAAC,MAAM,CAAE,CAAA;AAAA;AACX;AACJ,GACD;AACH;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,gDAAmC;AAAA,GAC7D;AACH;;;;"}