@incident-io/backstage 0.0.2 → 0.0.4

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 CHANGED
@@ -1,8 +1,10 @@
1
1
  # incident.io
2
2
 
3
- Use this plugin to display on-going and historic incidents against Backstage
4
- components, and to provide quick-links to open new incidents for that service
5
- inside of incident.io.
3
+ [incident]: https://incident.io
4
+
5
+ Use this plugin to display ongoing and historic incidents against Backstage
6
+ components, and to provide quick links to open new incidents for that service
7
+ inside of [incident.io][incident].
6
8
 
7
9
  ## How it works
8
10
 
@@ -46,50 +48,18 @@ inside the outermost `Grid` defined there, just before the closing `</Grid>`
46
48
  tag:
47
49
 
48
50
  ```ts
49
- <EntitySwitch>
50
- <EntitySwitch.Case if={isIncidentAvailable}>
51
- <Grid item md={6}>
52
- <EntityIncidentCard />
53
- </Grid>
54
- </EntitySwitch.Case>
55
- </EntitySwitch>
56
- ```
57
-
58
- When you're done, the `overviewContent` definition should look something like
59
- this:
60
-
61
- ```ts
62
- const overviewContent = (
63
- <Grid ...>
64
- ...
65
- <EntitySwitch>
66
- <EntitySwitch.Case if={isIncidentAvailable}>
67
- <Grid item md={6}>
68
- <EntityIncidentCard />
69
- </Grid>
70
- </EntitySwitch.Case>
71
- </EntitySwitch>
72
- </Grid>
73
- );
51
+ <Grid item md={6}>
52
+ <EntityIncidentCard />
53
+ </Grid>
74
54
  ```
75
55
 
76
56
  ## Configure the plugin
77
57
 
78
- [annotate]: https://backstage.io/docs/features/software-catalog/descriptor-format#annotations-optional
79
-
80
- First, [annotate][annotate] the appropriate entity with the incident.io
81
- integration key in its `.yaml` configuration file:
82
-
83
- ```yaml
84
- annotations:
85
- incident.io/api-key: [API_KEY]
86
- ```
87
-
88
58
  [api-keys]: https://app.incident.io/settings/api-keys/
89
59
  [api-docs]: https://api-docs.incident.io/
90
60
 
91
- Next, provide the [API key][api-keys] that the client will use to make requests
92
- to the [incident.io API][api-docs].
61
+ First, provide the [API key][api-keys] that the client will use to make
62
+ requests to the [incident.io API][api-docs].
93
63
 
94
64
  Add the proxy configuration in `app-config.yaml`:
95
65
 
@@ -103,13 +73,14 @@ proxy:
103
73
  ```
104
74
 
105
75
  Finally, for any of the custom fields you've configured in incident that are
106
- powered by Backstage catalog types, fill out the following `app-config.yaml`:
76
+ powered by Backstage catalog types, fill out the following details within
77
+ `app-config.yaml`:
107
78
 
108
79
  ```yaml
109
- integrations:
110
- incident:
111
- api-field: "<id-of-api-custom-field>"
112
- component-field: "<id-of-component-custom-field>"
113
- system-field: "<id-of-system-custom-field>"
114
- domain-field: "<id-of-domain-custom-field>"
80
+ incident:
81
+ fields:
82
+ api: "<id-of-api-custom-field>"
83
+ component: "<id-of-component-custom-field>"
84
+ system: "<id-of-system-custom-field>"
85
+ domain: "<id-of-domain-custom-field>"
115
86
  ```
@@ -9,7 +9,7 @@ import WhatshotIcon from '@material-ui/icons/Whatshot';
9
9
  import { Alert } from '@material-ui/lab';
10
10
  import React, { useState } from 'react';
11
11
  import { useAsync } from 'react-use';
12
- import { I as IncidentApiRef } from './index-91fad684.esm.js';
12
+ import { I as IncidentApiRef } from './index-44e2d69d.esm.js';
13
13
  import { DateTime, Duration } from 'luxon';
14
14
  import OpenInBrowserIcon from '@material-ui/icons/OpenInBrowser';
15
15
 
@@ -131,7 +131,7 @@ const EntityIncidentCard = ({
131
131
  return /* @__PURE__ */ React.createElement(Card, null, /* @__PURE__ */ React.createElement(
132
132
  CardHeader,
133
133
  {
134
- title: "Incidents",
134
+ title: "incident.io",
135
135
  action: /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
136
136
  IconButton,
137
137
  {
@@ -145,7 +145,7 @@ const EntityIncidentCard = ({
145
145
  )),
146
146
  subheader: /* @__PURE__ */ React.createElement(HeaderIconLinkRow, { links: [createIncidentLink, viewIncidentsLink] })
147
147
  }
148
- ), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(CardContent, null, incidentsLoading && /* @__PURE__ */ React.createElement(Progress, null), incidentsError && /* @__PURE__ */ React.createElement(Alert, { severity: "error" }, incidentsError.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) => {
148
+ ), /* @__PURE__ */ React.createElement(Divider, null), /* @__PURE__ */ React.createElement(CardContent, null, incidentsLoading && /* @__PURE__ */ React.createElement(Progress, null), incidentsError && /* @__PURE__ */ React.createElement(Alert, { severity: "error" }, incidentsError.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) => {
149
149
  return /* @__PURE__ */ React.createElement(
150
150
  IncidentListItem,
151
151
  {
@@ -166,17 +166,17 @@ const EntityIncidentCard = ({
166
166
  function getEntityFieldID(config, entity) {
167
167
  switch (entity.kind) {
168
168
  case "API":
169
- return config.get("integrations.incident.fields.api");
169
+ return config.get("incident.fields.api");
170
170
  case "Component":
171
- return config.get("integrations.incident.fields.component");
171
+ return config.get("incident.fields.component");
172
172
  case "Domain":
173
- return config.get("integrations.incident.fields.domain");
173
+ return config.get("incident.fields.domain");
174
174
  case "System":
175
- return config.get("integrations.incident.fields.system");
175
+ return config.get("incident.fields.system");
176
176
  default:
177
177
  throw new Error(`unrecognised entity kind: ${entity.kind}`);
178
178
  }
179
179
  }
180
180
 
181
181
  export { EntityIncidentCard };
182
- //# sourceMappingURL=index-c77be796.esm.js.map
182
+ //# sourceMappingURL=index-0b104295.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-0b104295.esm.js","sources":["../../src/config.ts","../../src/components/IncidentListItem/index.tsx","../../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 { ConfigApi } from '@backstage/core-plugin-api';\n\n// Find the baseUrl of the incident dashboard.\nexport function getBaseUrl(config: ConfigApi) {\n try {\n const baseUrl = config.getString('incident.baseUrl');\n if (baseUrl !== '') {\n return baseUrl;\n }\n } catch (e) {\n // no action\n }\n\n return 'https://app.incident.io';\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 ['live'].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","/*\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 { useAsync } from 'react-use';\nimport { IncidentApiRef } from '../../api/client';\nimport { definitions } from '../../api/types';\nimport { getBaseUrl } from '../../config';\nimport { IncidentListItem } from '../IncidentListItem';\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 baseUrl = getBaseUrl(config);\n const { entity } = useEntity();\n\n const IncidentApi = useApi(IncidentApiRef);\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]`, 'live');\n\n const createIncidentLink: IconLinkVerticalProps = {\n label: 'Create 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 const {\n value: incidentsResponse,\n loading: incidentsLoading,\n error: incidentsError,\n } = useAsync(async () => {\n return await IncidentApi.request<\n definitions['IncidentsV2ListResponseBody']\n >({\n path: `/v2/incidents?${queryLive.toString()}`,\n });\n }, [reload]);\n\n const incidents = incidentsResponse?.incidents;\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 {incidentsLoading && <Progress />}\n {incidentsError && (\n <Alert severity=\"error\">{incidentsError.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.get('incident.fields.api');\n case 'Component':\n return config.get('incident.fields.component');\n case 'Domain':\n return config.get('incident.fields.domain');\n case 'System':\n return config.get('incident.fields.system');\n default:\n throw new Error(`unrecognised entity kind: ${entity.kind}`);\n }\n}\n"],"names":[],"mappings":"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"}
@@ -51,7 +51,7 @@ const EntityIncidentCard = incidentPlugin.provide(
51
51
  createComponentExtension({
52
52
  name: "EntityIncidentCard",
53
53
  component: {
54
- lazy: () => import('./index-c77be796.esm.js').then(
54
+ lazy: () => import('./index-0b104295.esm.js').then(
55
55
  (m) => m.EntityIncidentCard
56
56
  )
57
57
  }
@@ -59,4 +59,4 @@ const EntityIncidentCard = incidentPlugin.provide(
59
59
  );
60
60
 
61
61
  export { EntityIncidentCard as E, IncidentApiRef as I, incidentPlugin as i };
62
- //# sourceMappingURL=index-91fad684.esm.js.map
62
+ //# sourceMappingURL=index-44e2d69d.esm.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index-91fad684.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';\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"],"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;AApD7B,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,IAAO,GAAA,MAAM,KAAM,CAAA,CAAA,EAAG,SAAS,IAAQ,CAAA,CAAA,EAAA;AAAA,MAC3C,MAAA;AAAA,MACA,IAAA;AAAA,MACA,OAAS,EAAA;AAAA,QACP,eAAe,CAAU,OAAA,EAAA,KAAA,CAAA,CAAA;AAAA,OAC3B;AAAA,KACD,CAAA,CAAA;AACD,IAAI,IAAA,CAAC,KAAK,EAAI,EAAA;AACZ,MAAA,MAAM,IAAI,KAAM,CAAA,CAAA,EAAG,IAAK,CAAA,MAAA,CAAA,CAAA,EAAU,KAAK,UAAY,CAAA,CAAA,CAAA,CAAA;AAAA,KACrD;AAEA,IAAO,OAAA,MAAM,KAAK,IAAK,EAAA,CAAA;AAAA,GACzB;AACF;;AC3DO,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,OAAK,CAAE,CAAA,kBAAA;AAAA,OACT;AAAA,KACJ;AAAA,GACD,CAAA;AACH;;;;"}
1
+ {"version":3,"file":"index-44e2d69d.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';\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"],"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;AApD7B,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,IAAO,GAAA,MAAM,KAAM,CAAA,CAAA,EAAG,SAAS,IAAQ,CAAA,CAAA,EAAA;AAAA,MAC3C,MAAA;AAAA,MACA,IAAA;AAAA,MACA,OAAS,EAAA;AAAA,QACP,eAAe,CAAU,OAAA,EAAA,KAAA,CAAA,CAAA;AAAA,OAC3B;AAAA,KACD,CAAA,CAAA;AACD,IAAI,IAAA,CAAC,KAAK,EAAI,EAAA;AACZ,MAAA,MAAM,IAAI,KAAM,CAAA,CAAA,EAAG,IAAK,CAAA,MAAA,CAAA,CAAA,EAAU,KAAK,UAAY,CAAA,CAAA,CAAA,CAAA;AAAA,KACrD;AAEA,IAAO,OAAA,MAAM,KAAK,IAAK,EAAA,CAAA;AAAA,GACzB;AACF;;AC3DO,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,OAAK,CAAE,CAAA,kBAAA;AAAA,OACT;AAAA,KACJ;AAAA,GACD,CAAA;AACH;;;;"}
package/dist/index.esm.js CHANGED
@@ -1,3 +1,3 @@
1
- export { E as EntityIncidentCard, i as incidentPlugin } from './esm/index-91fad684.esm.js';
1
+ export { E as EntityIncidentCard, i as incidentPlugin } from './esm/index-44e2d69d.esm.js';
2
2
  import '@backstage/core-plugin-api';
3
3
  //# 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.2",
3
+ "version": "0.0.4",
4
4
  "main": "dist/index.esm.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "license": "Apache-2.0",
@@ -100,7 +100,7 @@ export const EntityIncidentCard = ({
100
100
  return (
101
101
  <Card>
102
102
  <CardHeader
103
- title="Incidents"
103
+ title="incident.io"
104
104
  action={
105
105
  <>
106
106
  <IconButton
@@ -126,7 +126,7 @@ export const EntityIncidentCard = ({
126
126
  )}
127
127
  {!incidentsLoading && !incidentsError && incidents && (
128
128
  <>
129
- {incidents && incidents.length >= 0 && (
129
+ {incidents && incidents.length > 0 && (
130
130
  <Typography variant="subtitle1">
131
131
  There are <strong>{incidents.length}</strong> ongoing incidents
132
132
  involving <strong>{entity.metadata.name}</strong>.
@@ -169,13 +169,13 @@ export const EntityIncidentCard = ({
169
169
  function getEntityFieldID(config: ConfigApi, entity: Entity) {
170
170
  switch (entity.kind) {
171
171
  case 'API':
172
- return config.get('integrations.incident.fields.api');
172
+ return config.get('incident.fields.api');
173
173
  case 'Component':
174
- return config.get('integrations.incident.fields.component');
174
+ return config.get('incident.fields.component');
175
175
  case 'Domain':
176
- return config.get('integrations.incident.fields.domain');
176
+ return config.get('incident.fields.domain');
177
177
  case 'System':
178
- return config.get('integrations.incident.fields.system');
178
+ return config.get('incident.fields.system');
179
179
  default:
180
180
  throw new Error(`unrecognised entity kind: ${entity.kind}`);
181
181
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"index-c77be796.esm.js","sources":["../../src/config.ts","../../src/components/IncidentListItem/index.tsx","../../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 { ConfigApi } from '@backstage/core-plugin-api';\n\n// Find the baseUrl of the incident dashboard.\nexport function getBaseUrl(config: ConfigApi) {\n try {\n const baseUrl = config.getString('incident.baseUrl');\n if (baseUrl !== '') {\n return baseUrl;\n }\n } catch (e) {\n // no action\n }\n\n return 'https://app.incident.io';\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 ['live'].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","/*\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 { useAsync } from 'react-use';\nimport { IncidentApiRef } from '../../api/client';\nimport { definitions } from '../../api/types';\nimport { getBaseUrl } from '../../config';\nimport { IncidentListItem } from '../IncidentListItem';\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 baseUrl = getBaseUrl(config);\n const { entity } = useEntity();\n\n const IncidentApi = useApi(IncidentApiRef);\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]`, 'live');\n\n const createIncidentLink: IconLinkVerticalProps = {\n label: 'Create 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 const {\n value: incidentsResponse,\n loading: incidentsLoading,\n error: incidentsError,\n } = useAsync(async () => {\n return await IncidentApi.request<\n definitions['IncidentsV2ListResponseBody']\n >({\n path: `/v2/incidents?${queryLive.toString()}`,\n });\n }, [reload]);\n\n const incidents = incidentsResponse?.incidents;\n\n return (\n <Card>\n <CardHeader\n title=\"Incidents\"\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 {incidentsLoading && <Progress />}\n {incidentsError && (\n <Alert severity=\"error\">{incidentsError.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.get('integrations.incident.fields.api');\n case 'Component':\n return config.get('integrations.incident.fields.component');\n case 'Domain':\n return config.get('integrations.incident.fields.domain');\n case 'System':\n return config.get('integrations.incident.fields.system');\n default:\n throw new Error(`unrecognised entity kind: ${entity.kind}`);\n }\n}\n"],"names":[],"mappings":"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"}