@backstage-community/plugin-tech-insights 0.3.27

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 ADDED
@@ -0,0 +1,152 @@
1
+ # Tech Insights
2
+
3
+ This plugin provides the UI for the `@backstage/tech-insights-backend` plugin, in order to display results of the checks running following the rules and the logic defined in the `@backstage/tech-insights-backend` plugin itself.
4
+
5
+ Main areas covered by this plugin currently are:
6
+
7
+ - Providing an overview for default boolean checks in a form of Scorecards.
8
+
9
+ - Providing an option to render different custom components based on type of the checks running in the backend.
10
+
11
+ ## Installation
12
+
13
+ ### Install the plugin
14
+
15
+ ```bash
16
+ # From your Backstage root directory
17
+ yarn --cwd packages/app add @backstage-community/plugin-tech-insights
18
+ ```
19
+
20
+ ### Add boolean checks overview (Scorecards) page to the EntityPage:
21
+
22
+ ```tsx
23
+ // packages/app/src/components/catalog/EntityPage.tsx
24
+
25
+ import { EntityTechInsightsScorecardContent } from '@backstage-community/plugin-tech-insights';
26
+
27
+ const serviceEntityPage = (
28
+ <EntityLayoutWrapper>
29
+ <EntityLayout.Route path="/" title="Overview">
30
+ {overviewContent}
31
+ </EntityLayout.Route>
32
+ <EntityLayout.Route path="/ci-cd" title="CI/CD">
33
+ {cicdContent}
34
+ </EntityLayout.Route>
35
+ ...
36
+ <EntityLayout.Route path="/tech-insights" title="Scorecards">
37
+ <EntityTechInsightsScorecardContent
38
+ title="Customized title for the scorecard"
39
+ description="Small description about scorecards"
40
+ />
41
+ </EntityLayout.Route>
42
+ ...
43
+ </EntityLayoutWrapper>
44
+ );
45
+ ```
46
+
47
+ It is obligatory to pass `title` prop to `EntityTechInsightsScorecardContent`, `description` prop is optional.
48
+
49
+ If you like to display multiple cards in a `EntityLayout.Route` use `EntityTechInsightsScorecardCard`.
50
+
51
+ You can pass an array `checksId` as a prop with the [Fact Retrievers ids](../tech-insights-backend#creating-fact-retrievers) to limit which checks you want to show in this card. If you don't pass, the default value is show all checks.
52
+
53
+ ```tsx
54
+ <EntityTechInsightsScorecardContent
55
+ title="Show only simpleTestCheck in this card"
56
+ checksId={['simpleTestCheck']}
57
+ />
58
+ ```
59
+
60
+ If you want to show checks in the overview of an entity use `EntityTechInsightsScorecardCard`.
61
+
62
+ ```tsx
63
+ // packages/app/src/components/catalog/EntityPage.tsx
64
+
65
+ import { EntityTechInsightsScorecardCard } from '@backstage-community/plugin-tech-insights';
66
+
67
+ const overviewContent = (
68
+ <Grid container spacing={3} alignItems="stretch">
69
+ {entityWarningContent}
70
+ <Grid item md={6} xs={12}>
71
+ <EntityAboutCard variant="gridItem" />
72
+ </Grid>
73
+ <Grid item md={6} xs={12}>
74
+ <EntityCatalogGraphCard variant="gridItem" height={400} />
75
+ </Grid>
76
+ ...
77
+ <Grid item md={8} xs={12}>
78
+ <EntityTechInsightsScorecardCard
79
+ title="Customized title for the scorecard"
80
+ description="Small description about scorecards"
81
+ checksId={['simpleTestCheck']}
82
+ />
83
+ </Grid>
84
+ </Grid>
85
+ );
86
+ ```
87
+
88
+ ## Boolean Scorecard Example
89
+
90
+ If you follow the [Backend Example](https://github.com/backstage/backstage/tree/master/plugins/tech-insights-backend#backend-example), once the needed facts have been generated the default boolean scorecard will look like this:
91
+
92
+ ![Boolean Scorecard Example](./docs/boolean-scorecard-example.png)
93
+
94
+ ## Adding custom rendering components
95
+
96
+ Default scorecard implementation displays only `json-rules-engine` check results. If you would like to support different types, you need to inject custom rendering components to the `TechInsightsClient` constructor.
97
+
98
+ ```ts
99
+ // packages/app/src/apis.ts
100
+
101
+ export const apis: AnyApiFactory[] = [
102
+ ...
103
+ createApiFactory({
104
+ api: techInsightsApiRef,
105
+ deps: { discoveryApi: discoveryApiRef, identityApi: identityApiRef },
106
+ factory: ({ discoveryApi, identityApi }) =>
107
+ new TechInsightsClient({
108
+ discoveryApi,
109
+ identityApi,
110
+ renderers: [
111
+ jsonRulesEngineCheckResultRenderer, // default json-rules-engine renderer
112
+ myCustomBooleanRenderer, // custom renderer
113
+ ],
114
+ }),
115
+ }),
116
+ ...
117
+ ];
118
+ ```
119
+
120
+ ```tsx
121
+ // packages/app/src/components/myCustomBooleanRenderer.tsx
122
+
123
+ export const myCustomBooleanRenderer: CheckResultRenderer = {
124
+ type: 'boolean',
125
+ component: (checkResult: CheckResult) => (
126
+ <BooleanCheck checkResult={checkResult} />
127
+ ),
128
+ };
129
+ ```
130
+
131
+ It's also possible to customize the description. Both strings and React components are accepted. As an example, you would like
132
+ to display another information if the check has failed. In such cases, you could do something like the following:
133
+
134
+ ```tsx
135
+ // packages/app/src/components/myCustomBooleanRenderer.tsx
136
+
137
+ export const myCustomBooleanRenderer: CheckResultRenderer = {
138
+ type: 'boolean',
139
+ component: (checkResult: CheckResult) => (
140
+ <BooleanCheck checkResult={checkResult} />
141
+ ),
142
+ description: (checkResult: CheckResult) => (
143
+ <>
144
+ {
145
+ checkResult.result
146
+ ? checkResult.check.description // In case of success, return the same description
147
+ : `The check has failed! ${checkResult.check.description}` // Add a prefix text if the check failed
148
+ }
149
+ </>
150
+ ),
151
+ };
152
+ ```
@@ -0,0 +1,44 @@
1
+ import React from 'react';
2
+ import Grid from '@material-ui/core/Grid';
3
+ import Typography from '@material-ui/core/Typography';
4
+ import { makeStyles } from '@material-ui/core/styles';
5
+ import { InfoCard } from '@backstage/core-components';
6
+ import Alert from '@material-ui/lab/Alert';
7
+ import { S as ScorecardsList } from './ScorecardsList-mU3LQTf9.esm.js';
8
+
9
+ const useStyles = makeStyles((theme) => ({
10
+ subheader: {
11
+ fontWeight: "bold",
12
+ paddingLeft: theme.spacing(0.5)
13
+ }
14
+ }));
15
+ const infoCard = (title, description, className, element) => /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(InfoCard, { title }, description && /* @__PURE__ */ React.createElement(Typography, { className, variant: "body1", gutterBottom: true }, description), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, element)));
16
+ const ScorecardInfo = (props) => {
17
+ const { checkResults, title, description, noWarning } = props;
18
+ const classes = useStyles();
19
+ if (!checkResults.length) {
20
+ if (noWarning) {
21
+ return infoCard(
22
+ title,
23
+ description,
24
+ classes.subheader,
25
+ /* @__PURE__ */ React.createElement(Alert, { severity: "info" }, "All checks passed, or no checks have been performed yet")
26
+ );
27
+ }
28
+ return infoCard(
29
+ title,
30
+ description,
31
+ classes.subheader,
32
+ /* @__PURE__ */ React.createElement(Alert, { severity: "warning" }, "No checks have any data yet.")
33
+ );
34
+ }
35
+ return infoCard(
36
+ title,
37
+ description,
38
+ classes.subheader,
39
+ /* @__PURE__ */ React.createElement(ScorecardsList, { checkResults })
40
+ );
41
+ };
42
+
43
+ export { ScorecardInfo as S };
44
+ //# sourceMappingURL=ScorecardInfo-DRy_gP7g.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScorecardInfo-DRy_gP7g.esm.js","sources":["../../src/components/ScorecardsInfo/ScorecardInfo.tsx"],"sourcesContent":["/*\n * Copyright 2021 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 */\n\nimport React from 'react';\nimport Grid from '@material-ui/core/Grid';\nimport Typography from '@material-ui/core/Typography';\nimport { makeStyles } from '@material-ui/core/styles';\nimport { InfoCard } from '@backstage/core-components';\nimport { CheckResult } from '@backstage-community/plugin-tech-insights-common';\nimport Alert from '@material-ui/lab/Alert';\nimport { ScorecardsList } from '../ScorecardsList';\n\nconst useStyles = makeStyles(theme => ({\n subheader: {\n fontWeight: 'bold',\n paddingLeft: theme.spacing(0.5),\n },\n}));\n\nconst infoCard = (\n title: string,\n description: string | undefined,\n className: string,\n element: JSX.Element,\n) => (\n <Grid item xs={12}>\n <InfoCard title={title}>\n {description && (\n <Typography className={className} variant=\"body1\" gutterBottom>\n {description}\n </Typography>\n )}\n <Grid item xs={12}>\n {element}\n </Grid>\n </InfoCard>\n </Grid>\n);\n\nexport const ScorecardInfo = (props: {\n checkResults: CheckResult[];\n title: string;\n description?: string;\n noWarning?: boolean;\n}) => {\n const { checkResults, title, description, noWarning } = props;\n const classes = useStyles();\n\n if (!checkResults.length) {\n if (noWarning) {\n return infoCard(\n title,\n description,\n classes.subheader,\n <Alert severity=\"info\">\n All checks passed, or no checks have been performed yet\n </Alert>,\n );\n }\n return infoCard(\n title,\n description,\n classes.subheader,\n <Alert severity=\"warning\">No checks have any data yet.</Alert>,\n );\n }\n\n return infoCard(\n title,\n description,\n classes.subheader,\n <ScorecardsList checkResults={checkResults} />,\n );\n};\n"],"names":[],"mappings":";;;;;;;;AAyBA,MAAM,SAAA,GAAY,WAAW,CAAU,KAAA,MAAA;AAAA,EACrC,SAAW,EAAA;AAAA,IACT,UAAY,EAAA,MAAA;AAAA,IACZ,WAAA,EAAa,KAAM,CAAA,OAAA,CAAQ,GAAG,CAAA;AAAA,GAChC;AACF,CAAE,CAAA,CAAA,CAAA;AAEF,MAAM,QAAW,GAAA,CACf,KACA,EAAA,WAAA,EACA,WACA,OAEA,qBAAA,KAAA,CAAA,aAAA,CAAC,IAAK,EAAA,EAAA,IAAA,EAAI,IAAC,EAAA,EAAA,EAAI,EACb,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,YAAS,KACP,EAAA,EAAA,WAAA,oBACE,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,SAAsB,EAAA,OAAA,EAAQ,OAAQ,EAAA,YAAA,EAAY,QAC3D,WACH,CAAA,kBAED,KAAA,CAAA,aAAA,CAAA,IAAA,EAAA,EAAK,MAAI,IAAC,EAAA,EAAA,EAAI,EACZ,EAAA,EAAA,OACH,CACF,CACF,CAAA,CAAA;AAGW,MAAA,aAAA,GAAgB,CAAC,KAKxB,KAAA;AACJ,EAAA,MAAM,EAAE,YAAA,EAAc,KAAO,EAAA,WAAA,EAAa,WAAc,GAAA,KAAA,CAAA;AACxD,EAAA,MAAM,UAAU,SAAU,EAAA,CAAA;AAE1B,EAAI,IAAA,CAAC,aAAa,MAAQ,EAAA;AACxB,IAAA,IAAI,SAAW,EAAA;AACb,MAAO,OAAA,QAAA;AAAA,QACL,KAAA;AAAA,QACA,WAAA;AAAA,QACA,OAAQ,CAAA,SAAA;AAAA,wBACP,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAM,QAAS,EAAA,MAAA,EAAA,EAAO,yDAEvB,CAAA;AAAA,OACF,CAAA;AAAA,KACF;AACA,IAAO,OAAA,QAAA;AAAA,MACL,KAAA;AAAA,MACA,WAAA;AAAA,MACA,OAAQ,CAAA,SAAA;AAAA,sBACP,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAM,QAAS,EAAA,SAAA,EAAA,EAAU,8BAA4B,CAAA;AAAA,KACxD,CAAA;AAAA,GACF;AAEA,EAAO,OAAA,QAAA;AAAA,IACL,KAAA;AAAA,IACA,WAAA;AAAA,IACA,OAAQ,CAAA,SAAA;AAAA,oBACR,KAAA,CAAA,aAAA,CAAC,kBAAe,YAA4B,EAAA,CAAA;AAAA,GAC9C,CAAA;AACF;;;;"}
@@ -0,0 +1,45 @@
1
+ import React from 'react';
2
+ import { useApi } from '@backstage/core-plugin-api';
3
+ import List from '@material-ui/core/List';
4
+ import ListItem from '@material-ui/core/ListItem';
5
+ import ListItemText from '@material-ui/core/ListItemText';
6
+ import { makeStyles } from '@material-ui/core/styles';
7
+ import { techInsightsApiRef } from '../index.esm.js';
8
+ import '@backstage/errors';
9
+ import '@backstage/catalog-model';
10
+ import '@material-ui/icons/CheckCircleOutline';
11
+ import '@material-ui/icons/ErrorOutline';
12
+ import 'qs';
13
+ import Alert from '@material-ui/lab/Alert';
14
+ import { MarkdownContent } from '@backstage/core-components';
15
+
16
+ const useStyles = makeStyles((theme) => ({
17
+ listItemText: {
18
+ paddingRight: theme.spacing(0.5)
19
+ }
20
+ }));
21
+ const ScorecardsList = (props) => {
22
+ const { checkResults } = props;
23
+ const classes = useStyles();
24
+ const api = useApi(techInsightsApiRef);
25
+ const types = [...new Set(checkResults.map(({ check }) => check.type))];
26
+ const checkResultRenderers = api.getCheckResultRenderers(types);
27
+ return /* @__PURE__ */ React.createElement(List, null, checkResults.map((result, index) => {
28
+ var _a, _b, _c;
29
+ const description = (_a = checkResultRenderers.find(
30
+ (renderer) => renderer.type === result.check.type
31
+ )) == null ? void 0 : _a.description;
32
+ return /* @__PURE__ */ React.createElement(ListItem, { key: result.check.id }, /* @__PURE__ */ React.createElement(
33
+ ListItemText,
34
+ {
35
+ key: index,
36
+ primary: result.check.name,
37
+ secondary: description ? description(result) : /* @__PURE__ */ React.createElement(MarkdownContent, { content: result.check.description }),
38
+ className: classes.listItemText
39
+ }
40
+ ), (_c = (_b = checkResultRenderers.find(({ type }) => type === result.check.type)) == null ? void 0 : _b.component(result)) != null ? _c : /* @__PURE__ */ React.createElement(Alert, { severity: "error" }, "Unknown type."));
41
+ }));
42
+ };
43
+
44
+ export { ScorecardsList as S };
45
+ //# sourceMappingURL=ScorecardsList-mU3LQTf9.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScorecardsList-mU3LQTf9.esm.js","sources":["../../src/components/ScorecardsList/ScorecardsList.tsx"],"sourcesContent":["/*\n * Copyright 2021 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 */\n\nimport React from 'react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport List from '@material-ui/core/List';\nimport ListItem from '@material-ui/core/ListItem';\nimport ListItemText from '@material-ui/core/ListItemText';\nimport { makeStyles } from '@material-ui/core/styles';\nimport { techInsightsApiRef } from '../../api';\nimport { CheckResult } from '@backstage-community/plugin-tech-insights-common';\nimport Alert from '@material-ui/lab/Alert';\nimport { MarkdownContent } from '@backstage/core-components';\n\nconst useStyles = makeStyles(theme => ({\n listItemText: {\n paddingRight: theme.spacing(0.5),\n },\n}));\n\nexport const ScorecardsList = (props: { checkResults: CheckResult[] }) => {\n const { checkResults } = props;\n const classes = useStyles();\n const api = useApi(techInsightsApiRef);\n\n const types = [...new Set(checkResults.map(({ check }) => check.type))];\n const checkResultRenderers = api.getCheckResultRenderers(types);\n\n return (\n <List>\n {checkResults.map((result, index) => {\n const description = checkResultRenderers.find(\n renderer => renderer.type === result.check.type,\n )?.description;\n\n return (\n <ListItem key={result.check.id}>\n <ListItemText\n key={index}\n primary={result.check.name}\n secondary={\n description ? (\n description(result)\n ) : (\n <MarkdownContent content={result.check.description} />\n )\n }\n className={classes.listItemText}\n />\n {checkResultRenderers\n .find(({ type }) => type === result.check.type)\n ?.component(result) ?? (\n <Alert severity=\"error\">Unknown type.</Alert>\n )}\n </ListItem>\n );\n })}\n </List>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;AA2BA,MAAM,SAAA,GAAY,WAAW,CAAU,KAAA,MAAA;AAAA,EACrC,YAAc,EAAA;AAAA,IACZ,YAAA,EAAc,KAAM,CAAA,OAAA,CAAQ,GAAG,CAAA;AAAA,GACjC;AACF,CAAE,CAAA,CAAA,CAAA;AAEW,MAAA,cAAA,GAAiB,CAAC,KAA2C,KAAA;AACxE,EAAM,MAAA,EAAE,cAAiB,GAAA,KAAA,CAAA;AACzB,EAAA,MAAM,UAAU,SAAU,EAAA,CAAA;AAC1B,EAAM,MAAA,GAAA,GAAM,OAAO,kBAAkB,CAAA,CAAA;AAErC,EAAA,MAAM,KAAQ,GAAA,CAAC,GAAG,IAAI,IAAI,YAAa,CAAA,GAAA,CAAI,CAAC,EAAE,KAAM,EAAA,KAAM,KAAM,CAAA,IAAI,CAAC,CAAC,CAAA,CAAA;AACtE,EAAM,MAAA,oBAAA,GAAuB,GAAI,CAAA,uBAAA,CAAwB,KAAK,CAAA,CAAA;AAE9D,EAAA,2CACG,IACE,EAAA,IAAA,EAAA,YAAA,CAAa,GAAI,CAAA,CAAC,QAAQ,KAAU,KAAA;AA3C3C,IAAA,IAAA,EAAA,EAAA,EAAA,EAAA,EAAA,CAAA;AA4CQ,IAAA,MAAM,eAAc,EAAqB,GAAA,oBAAA,CAAA,IAAA;AAAA,MACvC,CAAY,QAAA,KAAA,QAAA,CAAS,IAAS,KAAA,MAAA,CAAO,KAAM,CAAA,IAAA;AAAA,UADzB,IAEjB,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,WAAA,CAAA;AAEH,IAAA,uBACG,KAAA,CAAA,aAAA,CAAA,QAAA,EAAA,EAAS,GAAK,EAAA,MAAA,CAAO,MAAM,EAC1B,EAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,MAAC,YAAA;AAAA,MAAA;AAAA,QACC,GAAK,EAAA,KAAA;AAAA,QACL,OAAA,EAAS,OAAO,KAAM,CAAA,IAAA;AAAA,QACtB,SAAA,EACE,WACE,GAAA,WAAA,CAAY,MAAM,CAAA,uCAEjB,eAAgB,EAAA,EAAA,OAAA,EAAS,MAAO,CAAA,KAAA,CAAM,WAAa,EAAA,CAAA;AAAA,QAGxD,WAAW,OAAQ,CAAA,YAAA;AAAA,OAAA;AAAA,KACrB,EAAA,CACC,gCACE,IAAK,CAAA,CAAC,EAAE,IAAK,EAAA,KAAM,SAAS,MAAO,CAAA,KAAA,CAAM,IAAI,CAD/C,KAAA,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAEG,UAAU,MAFb,CAAA,KAAA,IAAA,GAAA,EAAA,uCAGE,KAAM,EAAA,EAAA,QAAA,EAAS,OAAQ,EAAA,EAAA,eAAa,CAEzC,CAAA,CAAA;AAAA,GAEH,CACH,CAAA,CAAA;AAEJ;;;;"}
@@ -0,0 +1,19 @@
1
+ export { S as ScorecardInfo } from './ScorecardInfo-DRy_gP7g.esm.js';
2
+ import 'react';
3
+ import '@material-ui/core/Grid';
4
+ import '@material-ui/core/Typography';
5
+ import '@material-ui/core/styles';
6
+ import '@backstage/core-components';
7
+ import '@material-ui/lab/Alert';
8
+ import './ScorecardsList-mU3LQTf9.esm.js';
9
+ import '@backstage/core-plugin-api';
10
+ import '@material-ui/core/List';
11
+ import '@material-ui/core/ListItem';
12
+ import '@material-ui/core/ListItemText';
13
+ import '../index.esm.js';
14
+ import '@backstage/errors';
15
+ import '@backstage/catalog-model';
16
+ import '@material-ui/icons/CheckCircleOutline';
17
+ import '@material-ui/icons/ErrorOutline';
18
+ import 'qs';
19
+ //# sourceMappingURL=index-B0n7xKVB.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-B0n7xKVB.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;"}
@@ -0,0 +1,16 @@
1
+ export { S as ScorecardsList } from './ScorecardsList-mU3LQTf9.esm.js';
2
+ import 'react';
3
+ import '@backstage/core-plugin-api';
4
+ import '@material-ui/core/List';
5
+ import '@material-ui/core/ListItem';
6
+ import '@material-ui/core/ListItemText';
7
+ import '@material-ui/core/styles';
8
+ import '../index.esm.js';
9
+ import '@backstage/errors';
10
+ import '@backstage/catalog-model';
11
+ import '@material-ui/icons/CheckCircleOutline';
12
+ import '@material-ui/icons/ErrorOutline';
13
+ import 'qs';
14
+ import '@material-ui/lab/Alert';
15
+ import '@backstage/core-components';
16
+ //# sourceMappingURL=index-B5eokOBn.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-B5eokOBn.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;"}
@@ -0,0 +1,52 @@
1
+ import React from 'react';
2
+ import useAsync from 'react-use/esm/useAsync';
3
+ import { Progress, Page, Content } from '@backstage/core-components';
4
+ import { useApi } from '@backstage/core-plugin-api';
5
+ import { S as ScorecardInfo } from './ScorecardInfo-DRy_gP7g.esm.js';
6
+ import Alert from '@material-ui/lab/Alert';
7
+ import { techInsightsApiRef } from '../index.esm.js';
8
+ import { makeStyles } from '@material-ui/core/styles';
9
+ import { useEntity } from '@backstage/plugin-catalog-react';
10
+ import { getCompoundEntityRef } from '@backstage/catalog-model';
11
+ import '@material-ui/core/Grid';
12
+ import '@material-ui/core/Typography';
13
+ import './ScorecardsList-mU3LQTf9.esm.js';
14
+ import '@material-ui/core/List';
15
+ import '@material-ui/core/ListItem';
16
+ import '@material-ui/core/ListItemText';
17
+ import '@backstage/errors';
18
+ import '@material-ui/icons/CheckCircleOutline';
19
+ import '@material-ui/icons/ErrorOutline';
20
+ import 'qs';
21
+
22
+ const useStyles = makeStyles(() => ({
23
+ contentScorecards: {
24
+ paddingLeft: 0,
25
+ paddingRight: 0
26
+ }
27
+ }));
28
+ const ScorecardsContent = (props) => {
29
+ const { title, description, checksId } = props;
30
+ const classes = useStyles();
31
+ const api = useApi(techInsightsApiRef);
32
+ const { namespace, kind, name } = getCompoundEntityRef(useEntity().entity);
33
+ const { value, loading, error } = useAsync(
34
+ async () => await api.runChecks({ namespace, kind, name }, checksId)
35
+ );
36
+ if (loading) {
37
+ return /* @__PURE__ */ React.createElement(Progress, null);
38
+ } else if (error) {
39
+ return /* @__PURE__ */ React.createElement(Alert, { severity: "error" }, error.message);
40
+ }
41
+ return /* @__PURE__ */ React.createElement(Page, { themeId: "home" }, /* @__PURE__ */ React.createElement(Content, { className: classes.contentScorecards }, /* @__PURE__ */ React.createElement(
42
+ ScorecardInfo,
43
+ {
44
+ title,
45
+ description,
46
+ checkResults: value || []
47
+ }
48
+ )));
49
+ };
50
+
51
+ export { ScorecardsContent };
52
+ //# sourceMappingURL=index-DYpZtEuv.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-DYpZtEuv.esm.js","sources":["../../src/components/ScorecardsContent/ScorecardContent.tsx"],"sourcesContent":["/*\n * Copyright 2021 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 */\n\nimport React from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport { Content, Page, Progress } from '@backstage/core-components';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { ScorecardInfo } from '../ScorecardsInfo';\nimport Alert from '@material-ui/lab/Alert';\nimport { techInsightsApiRef } from '../../api/TechInsightsApi';\nimport { makeStyles } from '@material-ui/core/styles';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { getCompoundEntityRef } from '@backstage/catalog-model';\n\nconst useStyles = makeStyles(() => ({\n contentScorecards: {\n paddingLeft: 0,\n paddingRight: 0,\n },\n}));\n\nexport const ScorecardsContent = (props: {\n title: string;\n description?: string;\n checksId?: string[];\n}) => {\n const { title, description, checksId } = props;\n const classes = useStyles();\n const api = useApi(techInsightsApiRef);\n const { namespace, kind, name } = getCompoundEntityRef(useEntity().entity);\n const { value, loading, error } = useAsync(\n async () => await api.runChecks({ namespace, kind, name }, checksId),\n );\n\n if (loading) {\n return <Progress />;\n } else if (error) {\n return <Alert severity=\"error\">{error.message}</Alert>;\n }\n\n return (\n <Page themeId=\"home\">\n <Content className={classes.contentScorecards}>\n <ScorecardInfo\n title={title}\n description={description}\n checkResults={value || []}\n />\n </Content>\n </Page>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AA2BA,MAAM,SAAA,GAAY,WAAW,OAAO;AAAA,EAClC,iBAAmB,EAAA;AAAA,IACjB,WAAa,EAAA,CAAA;AAAA,IACb,YAAc,EAAA,CAAA;AAAA,GAChB;AACF,CAAE,CAAA,CAAA,CAAA;AAEW,MAAA,iBAAA,GAAoB,CAAC,KAI5B,KAAA;AACJ,EAAA,MAAM,EAAE,KAAA,EAAO,WAAa,EAAA,QAAA,EAAa,GAAA,KAAA,CAAA;AACzC,EAAA,MAAM,UAAU,SAAU,EAAA,CAAA;AAC1B,EAAM,MAAA,GAAA,GAAM,OAAO,kBAAkB,CAAA,CAAA;AACrC,EAAM,MAAA,EAAE,WAAW,IAAM,EAAA,IAAA,KAAS,oBAAqB,CAAA,SAAA,GAAY,MAAM,CAAA,CAAA;AACzE,EAAA,MAAM,EAAE,KAAA,EAAO,OAAS,EAAA,KAAA,EAAU,GAAA,QAAA;AAAA,IAChC,YAAY,MAAM,GAAI,CAAA,SAAA,CAAU,EAAE,SAAW,EAAA,IAAA,EAAM,IAAK,EAAA,EAAG,QAAQ,CAAA;AAAA,GACrE,CAAA;AAEA,EAAA,IAAI,OAAS,EAAA;AACX,IAAA,2CAAQ,QAAS,EAAA,IAAA,CAAA,CAAA;AAAA,aACR,KAAO,EAAA;AAChB,IAAA,uBAAQ,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAM,QAAS,EAAA,OAAA,EAAA,EAAS,MAAM,OAAQ,CAAA,CAAA;AAAA,GAChD;AAEA,EACE,uBAAA,KAAA,CAAA,aAAA,CAAC,QAAK,OAAQ,EAAA,MAAA,EAAA,sCACX,OAAQ,EAAA,EAAA,SAAA,EAAW,QAAQ,iBAC1B,EAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,aAAA;AAAA,IAAA;AAAA,MACC,KAAA;AAAA,MACA,WAAA;AAAA,MACA,YAAA,EAAc,SAAS,EAAC;AAAA,KAAA;AAAA,GAE5B,CACF,CAAA,CAAA;AAEJ;;;;"}
@@ -0,0 +1,62 @@
1
+ import React, { useMemo } from 'react';
2
+ import useAsync from 'react-use/esm/useAsync';
3
+ import { Progress, ErrorPanel } from '@backstage/core-components';
4
+ import { useApi } from '@backstage/core-plugin-api';
5
+ import { S as ScorecardInfo } from './ScorecardInfo-DRy_gP7g.esm.js';
6
+ import { techInsightsApiRef } from '../index.esm.js';
7
+ import { useEntity } from '@backstage/plugin-catalog-react';
8
+ import { getCompoundEntityRef } from '@backstage/catalog-model';
9
+ import '@material-ui/core/Grid';
10
+ import '@material-ui/core/Typography';
11
+ import '@material-ui/core/styles';
12
+ import '@material-ui/lab/Alert';
13
+ import './ScorecardsList-mU3LQTf9.esm.js';
14
+ import '@material-ui/core/List';
15
+ import '@material-ui/core/ListItem';
16
+ import '@material-ui/core/ListItemText';
17
+ import '@backstage/errors';
18
+ import '@material-ui/icons/CheckCircleOutline';
19
+ import '@material-ui/icons/ErrorOutline';
20
+ import 'qs';
21
+
22
+ const ScorecardsCard = (props) => {
23
+ const { title, description, checksId, onlyFailed } = props;
24
+ const api = useApi(techInsightsApiRef);
25
+ const { entity } = useEntity();
26
+ const { value, loading, error } = useAsync(
27
+ async () => await api.runChecks(getCompoundEntityRef(entity), checksId),
28
+ [api, entity, JSON.stringify(checksId)]
29
+ );
30
+ const checkResultRenderers = useMemo(() => {
31
+ if (!onlyFailed || !value)
32
+ return {};
33
+ const types = [...new Set(value.map(({ check }) => check.type))];
34
+ const renderers = api.getCheckResultRenderers(types);
35
+ return Object.fromEntries(
36
+ renderers.map((renderer) => [renderer.type, renderer])
37
+ );
38
+ }, [api, value, onlyFailed]);
39
+ if (loading) {
40
+ return /* @__PURE__ */ React.createElement(Progress, null);
41
+ } else if (error) {
42
+ return /* @__PURE__ */ React.createElement(ErrorPanel, { error });
43
+ }
44
+ const filteredValue = !onlyFailed ? value || [] : (value || []).filter(
45
+ (val) => {
46
+ var _a, _b;
47
+ return (_b = (_a = checkResultRenderers[val.check.type]) == null ? void 0 : _a.isFailed) == null ? void 0 : _b.call(_a, val);
48
+ }
49
+ );
50
+ return /* @__PURE__ */ React.createElement(
51
+ ScorecardInfo,
52
+ {
53
+ title,
54
+ description,
55
+ checkResults: filteredValue,
56
+ noWarning: onlyFailed
57
+ }
58
+ );
59
+ };
60
+
61
+ export { ScorecardsCard };
62
+ //# sourceMappingURL=index-Dk00aaon.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-Dk00aaon.esm.js","sources":["../../src/components/ScorecardsCard/ScorecardsCard.tsx"],"sourcesContent":["/*\n * Copyright 2021 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 */\n\nimport React, { useMemo } from 'react';\nimport useAsync from 'react-use/esm/useAsync';\nimport { ErrorPanel, Progress } from '@backstage/core-components';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { ScorecardInfo } from '../ScorecardsInfo';\nimport { techInsightsApiRef } from '../../api/TechInsightsApi';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { getCompoundEntityRef } from '@backstage/catalog-model';\n\nexport const ScorecardsCard = (props: {\n title: string;\n description?: string;\n checksId?: string[];\n onlyFailed?: boolean;\n}) => {\n const { title, description, checksId, onlyFailed } = props;\n const api = useApi(techInsightsApiRef);\n const { entity } = useEntity();\n const { value, loading, error } = useAsync(\n async () => await api.runChecks(getCompoundEntityRef(entity), checksId),\n [api, entity, JSON.stringify(checksId)],\n );\n\n const checkResultRenderers = useMemo(() => {\n if (!onlyFailed || !value) return {};\n\n const types = [...new Set(value.map(({ check }) => check.type))];\n const renderers = api.getCheckResultRenderers(types);\n return Object.fromEntries(\n renderers.map(renderer => [renderer.type, renderer]),\n );\n }, [api, value, onlyFailed]);\n\n if (loading) {\n return <Progress />;\n } else if (error) {\n return <ErrorPanel error={error} />;\n }\n\n const filteredValue = !onlyFailed\n ? value || []\n : (value || []).filter(val =>\n checkResultRenderers[val.check.type]?.isFailed?.(val),\n );\n\n return (\n <ScorecardInfo\n title={title}\n description={description}\n checkResults={filteredValue}\n noWarning={onlyFailed}\n />\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AAyBa,MAAA,cAAA,GAAiB,CAAC,KAKzB,KAAA;AACJ,EAAA,MAAM,EAAE,KAAA,EAAO,WAAa,EAAA,QAAA,EAAU,YAAe,GAAA,KAAA,CAAA;AACrD,EAAM,MAAA,GAAA,GAAM,OAAO,kBAAkB,CAAA,CAAA;AACrC,EAAM,MAAA,EAAE,MAAO,EAAA,GAAI,SAAU,EAAA,CAAA;AAC7B,EAAA,MAAM,EAAE,KAAA,EAAO,OAAS,EAAA,KAAA,EAAU,GAAA,QAAA;AAAA,IAChC,YAAY,MAAM,GAAA,CAAI,UAAU,oBAAqB,CAAA,MAAM,GAAG,QAAQ,CAAA;AAAA,IACtE,CAAC,GAAK,EAAA,MAAA,EAAQ,IAAK,CAAA,SAAA,CAAU,QAAQ,CAAC,CAAA;AAAA,GACxC,CAAA;AAEA,EAAM,MAAA,oBAAA,GAAuB,QAAQ,MAAM;AACzC,IAAI,IAAA,CAAC,cAAc,CAAC,KAAA;AAAO,MAAA,OAAO,EAAC,CAAA;AAEnC,IAAA,MAAM,KAAQ,GAAA,CAAC,GAAG,IAAI,IAAI,KAAM,CAAA,GAAA,CAAI,CAAC,EAAE,KAAM,EAAA,KAAM,KAAM,CAAA,IAAI,CAAC,CAAC,CAAA,CAAA;AAC/D,IAAM,MAAA,SAAA,GAAY,GAAI,CAAA,uBAAA,CAAwB,KAAK,CAAA,CAAA;AACnD,IAAA,OAAO,MAAO,CAAA,WAAA;AAAA,MACZ,UAAU,GAAI,CAAA,CAAA,QAAA,KAAY,CAAC,QAAS,CAAA,IAAA,EAAM,QAAQ,CAAC,CAAA;AAAA,KACrD,CAAA;AAAA,GACC,EAAA,CAAC,GAAK,EAAA,KAAA,EAAO,UAAU,CAAC,CAAA,CAAA;AAE3B,EAAA,IAAI,OAAS,EAAA;AACX,IAAA,2CAAQ,QAAS,EAAA,IAAA,CAAA,CAAA;AAAA,aACR,KAAO,EAAA;AAChB,IAAO,uBAAA,KAAA,CAAA,aAAA,CAAC,cAAW,KAAc,EAAA,CAAA,CAAA;AAAA,GACnC;AAEA,EAAM,MAAA,aAAA,GAAgB,CAAC,UACnB,GAAA,KAAA,IAAS,EACR,GAAA,CAAA,KAAA,IAAS,EAAI,EAAA,MAAA;AAAA,IAAO,CAAI,GAAA,KAAA;AAzD/B,MAAA,IAAA,EAAA,EAAA,EAAA,CAAA;AA0DQ,MAAA,OAAA,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,oBAAA,CAAqB,IAAI,KAAM,CAAA,IAAI,CAAnC,KAAA,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAsC,aAAtC,IAAiD,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,IAAA,CAAA,EAAA,EAAA,GAAA,CAAA,CAAA;AAAA,KAAA;AAAA,GACnD,CAAA;AAEJ,EACE,uBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,aAAA;AAAA,IAAA;AAAA,MACC,KAAA;AAAA,MACA,WAAA;AAAA,MACA,YAAc,EAAA,aAAA;AAAA,MACd,SAAW,EAAA,UAAA;AAAA,KAAA;AAAA,GACb,CAAA;AAEJ;;;;"}
@@ -0,0 +1,170 @@
1
+ /// <reference types="react" />
2
+ import * as react from 'react';
3
+ import react__default from 'react';
4
+ import * as _backstage_community_plugin_tech_insights_common from '@backstage-community/plugin-tech-insights-common';
5
+ import { CheckResult, BulkCheckResponse, FactSchema } from '@backstage-community/plugin-tech-insights-common';
6
+ import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
7
+ import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api';
8
+ import { JsonValue } from '@backstage/types';
9
+ import { CompoundEntityRef } from '@backstage/catalog-model';
10
+
11
+ /**
12
+ * @public
13
+ */
14
+ declare const techInsightsPlugin: _backstage_core_plugin_api.BackstagePlugin<{
15
+ root: _backstage_core_plugin_api.RouteRef<undefined>;
16
+ }, {}, {}>;
17
+ /**
18
+ * @public
19
+ */
20
+ declare const ScorecardInfo: (props: {
21
+ checkResults: _backstage_community_plugin_tech_insights_common.CheckResult[];
22
+ title: string;
23
+ description?: string | undefined;
24
+ noWarning?: boolean | undefined;
25
+ }) => react.JSX.Element;
26
+ /**
27
+ * @public
28
+ */
29
+ declare const ScorecardsList: (props: {
30
+ checkResults: _backstage_community_plugin_tech_insights_common.CheckResult[];
31
+ }) => react.JSX.Element;
32
+ /**
33
+ * @public
34
+ */
35
+ declare const EntityTechInsightsScorecardContent: (props: {
36
+ title: string;
37
+ description?: string | undefined;
38
+ checksId?: string[] | undefined;
39
+ }) => react.JSX.Element;
40
+ /**
41
+ * @public
42
+ */
43
+ declare const EntityTechInsightsScorecardCard: (props: {
44
+ title: string;
45
+ description?: string | undefined;
46
+ checksId?: string[] | undefined;
47
+ onlyFailed?: boolean | undefined;
48
+ }) => react.JSX.Element;
49
+
50
+ /**
51
+ * Represents a single check defined on the TechInsights backend.
52
+ *
53
+ * @public
54
+ */
55
+ type Check = {
56
+ /**
57
+ * Unique identifier of the check
58
+ *
59
+ * Used to identify which checks to use when running checks.
60
+ */
61
+ id: string;
62
+ /**
63
+ * Type identifier for the check.
64
+ * Can be used to determine storage options, logical routing to correct FactChecker implementation
65
+ * or to help frontend render correct component types based on this
66
+ */
67
+ type: string;
68
+ /**
69
+ * Human readable name of the check, may be displayed in the UI
70
+ */
71
+ name: string;
72
+ /**
73
+ * Human readable description of the check, may be displayed in the UI
74
+ */
75
+ description: string;
76
+ /**
77
+ * A collection of string referencing fact rows that a check will be run against.
78
+ *
79
+ * References the fact container, aka fact retriever itself which may or may not contain multiple individual facts and values
80
+ */
81
+ factIds: string[];
82
+ /**
83
+ * Metadata to be returned in case a check has been successfully evaluated
84
+ * Can contain links, description texts or other actionable items
85
+ */
86
+ successMetadata?: Record<string, unknown>;
87
+ /**
88
+ * Metadata to be returned in case a check evaluation has ended in failure
89
+ * Can contain links, description texts or other actionable items
90
+ */
91
+ failureMetadata?: Record<string, unknown>;
92
+ };
93
+ /**
94
+ * Represents a Fact defined on the TechInsights backend.
95
+ *
96
+ * @public
97
+ */
98
+ interface InsightFacts {
99
+ [factId: string]: {
100
+ timestamp: string;
101
+ version: string;
102
+ facts: Record<string, JsonValue>;
103
+ };
104
+ }
105
+
106
+ /**
107
+ * Defines a react component that is responsible for rendering a result of a given type.
108
+ *
109
+ * @public
110
+ */
111
+ type CheckResultRenderer = {
112
+ type: string;
113
+ component: (check: CheckResult) => react__default.ReactElement;
114
+ description?: (check: CheckResult) => string | react__default.ReactElement;
115
+ isFailed?: (check: CheckResult) => boolean;
116
+ };
117
+ /**
118
+ * Default renderer for json-rules-engine check results.
119
+ *
120
+ * @public
121
+ */
122
+ declare const jsonRulesEngineCheckResultRenderer: CheckResultRenderer;
123
+
124
+ /**
125
+ * {@link @backstage/core-plugin-api#ApiRef} for the {@link TechInsightsApi}
126
+ *
127
+ * @public
128
+ */
129
+ declare const techInsightsApiRef: _backstage_core_plugin_api.ApiRef<TechInsightsApi>;
130
+ /**
131
+ * API client interface for the Tech Insights plugin
132
+ *
133
+ * @public
134
+ */
135
+ interface TechInsightsApi {
136
+ getCheckResultRenderers: (types: string[]) => CheckResultRenderer[];
137
+ getAllChecks(): Promise<Check[]>;
138
+ runChecks(entityParams: CompoundEntityRef, checks?: string[]): Promise<CheckResult[]>;
139
+ runBulkChecks(entities: CompoundEntityRef[], checks?: Check[]): Promise<BulkCheckResponse>;
140
+ getFacts(entity: CompoundEntityRef, facts: string[]): Promise<InsightFacts>;
141
+ getFactSchemas(): Promise<FactSchema[]>;
142
+ }
143
+
144
+ /** @public */
145
+ declare class TechInsightsClient implements TechInsightsApi {
146
+ private readonly discoveryApi;
147
+ private readonly identityApi;
148
+ private readonly renderers?;
149
+ constructor(options: {
150
+ discoveryApi: DiscoveryApi;
151
+ identityApi: IdentityApi;
152
+ renderers?: CheckResultRenderer[];
153
+ });
154
+ getFacts(entity: CompoundEntityRef, facts: string[]): Promise<InsightFacts>;
155
+ getCheckResultRenderers(types: string[]): CheckResultRenderer[];
156
+ getAllChecks(): Promise<Check[]>;
157
+ getFactSchemas(): Promise<FactSchema[]>;
158
+ runChecks(entityParams: CompoundEntityRef, checks?: string[]): Promise<CheckResult[]>;
159
+ runBulkChecks(entities: CompoundEntityRef[], checks?: Check[]): Promise<BulkCheckResponse>;
160
+ private api;
161
+ }
162
+
163
+ /**
164
+ * @public
165
+ */
166
+ declare const BooleanCheck: (props: {
167
+ checkResult: CheckResult;
168
+ }) => react__default.JSX.Element;
169
+
170
+ export { BooleanCheck, type Check, type CheckResultRenderer, EntityTechInsightsScorecardCard, EntityTechInsightsScorecardContent, type InsightFacts, ScorecardInfo, ScorecardsList, type TechInsightsApi, TechInsightsClient, jsonRulesEngineCheckResultRenderer, techInsightsApiRef, techInsightsPlugin };