@axis-backstage/plugin-readme 0.7.1 → 0.9.0
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/dist/api/ReadmeApi.esm.js +8 -0
- package/dist/api/ReadmeApi.esm.js.map +1 -0
- package/dist/api/ReadmeClient.esm.js +39 -0
- package/dist/api/ReadmeClient.esm.js.map +1 -0
- package/dist/components/FetchComponent/FetchComponent.esm.js +50 -0
- package/dist/components/FetchComponent/FetchComponent.esm.js.map +1 -0
- package/dist/components/ReadmeCard/ReadmeCard.esm.js +41 -0
- package/dist/components/ReadmeCard/ReadmeCard.esm.js.map +1 -0
- package/dist/components/ReadmeCard/index.esm.js +2 -0
- package/dist/components/ReadmeCard/index.esm.js.map +1 -0
- package/dist/components/ReadmeDialog/ReadmeDialog.esm.js +35 -0
- package/dist/components/ReadmeDialog/ReadmeDialog.esm.js.map +1 -0
- package/dist/index.esm.js +1 -3
- package/dist/index.esm.js.map +1 -1
- package/dist/plugin.esm.js +32 -0
- package/dist/plugin.esm.js.map +1 -0
- package/dist/routes.esm.js +8 -0
- package/dist/routes.esm.js.map +1 -0
- package/package.json +15 -10
- package/dist/esm/index-CnQYQb-x.esm.js +0 -113
- package/dist/esm/index-CnQYQb-x.esm.js.map +0 -1
- package/dist/esm/index-DemiDWtG.esm.js +0 -79
- package/dist/esm/index-DemiDWtG.esm.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReadmeApi.esm.js","sources":["../../src/api/ReadmeApi.tsx"],"sourcesContent":["import { createApiRef } from '@backstage/core-plugin-api';\n\nexport const readmeApiRef = createApiRef<ReadmeApi>({\n id: 'plugin.readme',\n});\n\nexport type ReadmeApi = {\n getReadmeContent(entityRef: string): Promise<[string, string]>;\n};\n"],"names":[],"mappings":";;AAEO,MAAM,eAAe,YAAwB,CAAA;AAAA,EAClD,EAAI,EAAA,eAAA;AACN,CAAC;;;;"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { parseEntityRef, DEFAULT_NAMESPACE } from '@backstage/catalog-model';
|
|
2
|
+
|
|
3
|
+
class ReadmeClient {
|
|
4
|
+
discoveryApi;
|
|
5
|
+
fetchApi;
|
|
6
|
+
identityApi;
|
|
7
|
+
constructor(options) {
|
|
8
|
+
this.discoveryApi = options.discoveryApi;
|
|
9
|
+
this.fetchApi = options.fetchApi;
|
|
10
|
+
this.identityApi = options.identityApi;
|
|
11
|
+
}
|
|
12
|
+
async getReadmeContent(entityRef) {
|
|
13
|
+
const { token } = await this.identityApi.getCredentials();
|
|
14
|
+
const baseUrl = await this.discoveryApi.getBaseUrl("readme");
|
|
15
|
+
const { kind, name, namespace } = parseEntityRef(entityRef, {
|
|
16
|
+
defaultNamespace: DEFAULT_NAMESPACE
|
|
17
|
+
});
|
|
18
|
+
const resp = await this.fetchApi.fetch(
|
|
19
|
+
`${baseUrl}/${kind}/${namespace}/${name}`,
|
|
20
|
+
{
|
|
21
|
+
method: "GET",
|
|
22
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
if (resp.ok) {
|
|
26
|
+
return [
|
|
27
|
+
await resp.text(),
|
|
28
|
+
resp.headers.get("Content-Type") || "text/plain"
|
|
29
|
+
];
|
|
30
|
+
}
|
|
31
|
+
if (resp.status === 404) {
|
|
32
|
+
throw new Error("404");
|
|
33
|
+
}
|
|
34
|
+
throw new Error(`${resp.status}: ${resp.statusText}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { ReadmeClient };
|
|
39
|
+
//# sourceMappingURL=ReadmeClient.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReadmeClient.esm.js","sources":["../../src/api/ReadmeClient.tsx"],"sourcesContent":["import {\n DiscoveryApi,\n FetchApi,\n IdentityApi,\n} from '@backstage/core-plugin-api';\nimport { DEFAULT_NAMESPACE, parseEntityRef } from '@backstage/catalog-model';\nimport { ReadmeApi } from './ReadmeApi';\n\nexport class ReadmeClient implements ReadmeApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n private readonly identityApi: IdentityApi;\n\n constructor(options: {\n discoveryApi: DiscoveryApi;\n fetchApi: FetchApi;\n identityApi: IdentityApi;\n }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi;\n this.identityApi = options.identityApi;\n }\n\n async getReadmeContent(entityRef: string): Promise<[string, string]> {\n const { token } = await this.identityApi.getCredentials();\n const baseUrl = await this.discoveryApi.getBaseUrl('readme');\n\n const { kind, name, namespace } = parseEntityRef(entityRef, {\n defaultNamespace: DEFAULT_NAMESPACE,\n });\n\n const resp = await this.fetchApi.fetch(\n `${baseUrl}/${kind}/${namespace}/${name}`,\n {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n },\n );\n if (resp.ok) {\n return [\n await resp.text(),\n resp.headers.get('Content-Type') || 'text/plain',\n ];\n }\n if (resp.status === 404) {\n throw new Error('404');\n }\n throw new Error(`${resp.status}: ${resp.statusText}`);\n }\n}\n"],"names":[],"mappings":";;AAQO,MAAM,YAAkC,CAAA;AAAA,EAC5B,YAAA,CAAA;AAAA,EACA,QAAA,CAAA;AAAA,EACA,WAAA,CAAA;AAAA,EAEjB,YAAY,OAIT,EAAA;AACD,IAAA,IAAA,CAAK,eAAe,OAAQ,CAAA,YAAA,CAAA;AAC5B,IAAA,IAAA,CAAK,WAAW,OAAQ,CAAA,QAAA,CAAA;AACxB,IAAA,IAAA,CAAK,cAAc,OAAQ,CAAA,WAAA,CAAA;AAAA,GAC7B;AAAA,EAEA,MAAM,iBAAiB,SAA8C,EAAA;AACnE,IAAA,MAAM,EAAE,KAAM,EAAA,GAAI,MAAM,IAAA,CAAK,YAAY,cAAe,EAAA,CAAA;AACxD,IAAA,MAAM,OAAU,GAAA,MAAM,IAAK,CAAA,YAAA,CAAa,WAAW,QAAQ,CAAA,CAAA;AAE3D,IAAA,MAAM,EAAE,IAAM,EAAA,IAAA,EAAM,SAAU,EAAA,GAAI,eAAe,SAAW,EAAA;AAAA,MAC1D,gBAAkB,EAAA,iBAAA;AAAA,KACnB,CAAA,CAAA;AAED,IAAM,MAAA,IAAA,GAAO,MAAM,IAAA,CAAK,QAAS,CAAA,KAAA;AAAA,MAC/B,GAAG,OAAO,CAAA,CAAA,EAAI,IAAI,CAAI,CAAA,EAAA,SAAS,IAAI,IAAI,CAAA,CAAA;AAAA,MACvC;AAAA,QACE,MAAQ,EAAA,KAAA;AAAA,QACR,OAAS,EAAA,EAAE,aAAe,EAAA,CAAA,OAAA,EAAU,KAAK,CAAG,CAAA,EAAA;AAAA,OAC9C;AAAA,KACF,CAAA;AACA,IAAA,IAAI,KAAK,EAAI,EAAA;AACX,MAAO,OAAA;AAAA,QACL,MAAM,KAAK,IAAK,EAAA;AAAA,QAChB,IAAK,CAAA,OAAA,CAAQ,GAAI,CAAA,cAAc,CAAK,IAAA,YAAA;AAAA,OACtC,CAAA;AAAA,KACF;AACA,IAAI,IAAA,IAAA,CAAK,WAAW,GAAK,EAAA;AACvB,MAAM,MAAA,IAAI,MAAM,KAAK,CAAA,CAAA;AAAA,KACvB;AACA,IAAM,MAAA,IAAI,MAAM,CAAG,EAAA,IAAA,CAAK,MAAM,CAAK,EAAA,EAAA,IAAA,CAAK,UAAU,CAAE,CAAA,CAAA,CAAA;AAAA,GACtD;AACF;;;;"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { useEntity } from '@backstage/plugin-catalog-react';
|
|
3
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
4
|
+
import { Progress, Link, ErrorPanel, MarkdownContent, CodeSnippet } from '@backstage/core-components';
|
|
5
|
+
import useAsync from 'react-use/lib/useAsync';
|
|
6
|
+
import { readmeApiRef } from '../../api/ReadmeApi.esm.js';
|
|
7
|
+
import { getEntitySourceLocation, stringifyEntityRef } from '@backstage/catalog-model';
|
|
8
|
+
import Box from '@mui/material/Box';
|
|
9
|
+
import Typography from '@mui/material/Typography';
|
|
10
|
+
|
|
11
|
+
const FetchComponent = () => {
|
|
12
|
+
const { entity } = useEntity();
|
|
13
|
+
const readmeApi = useApi(readmeApiRef);
|
|
14
|
+
let location;
|
|
15
|
+
try {
|
|
16
|
+
const { target } = getEntitySourceLocation(entity);
|
|
17
|
+
location = target ?? target;
|
|
18
|
+
} catch (err) {
|
|
19
|
+
location = ": Unknown";
|
|
20
|
+
}
|
|
21
|
+
const {
|
|
22
|
+
value: content,
|
|
23
|
+
loading,
|
|
24
|
+
error
|
|
25
|
+
} = useAsync(
|
|
26
|
+
async () => await readmeApi.getReadmeContent(stringifyEntityRef(entity)),
|
|
27
|
+
[entity]
|
|
28
|
+
);
|
|
29
|
+
if (loading) {
|
|
30
|
+
return /* @__PURE__ */ React.createElement(Progress, null);
|
|
31
|
+
}
|
|
32
|
+
if (error?.message === "404") {
|
|
33
|
+
return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Typography, { pb: 2, variant: "body2" }, "No README.md file found at source location:", " ", location && /* @__PURE__ */ React.createElement("strong", null, location)), /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "Need help? Go to our", " ", /* @__PURE__ */ React.createElement(Link, { to: "https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/readme/README.md" }, "documentation")));
|
|
34
|
+
}
|
|
35
|
+
if (error) {
|
|
36
|
+
return /* @__PURE__ */ React.createElement(ErrorPanel, { error });
|
|
37
|
+
}
|
|
38
|
+
if (!content) {
|
|
39
|
+
return /* @__PURE__ */ React.createElement(ErrorPanel, { error: Error("Unknown error") });
|
|
40
|
+
}
|
|
41
|
+
if (!content[1] || content[1] === "error") {
|
|
42
|
+
return /* @__PURE__ */ React.createElement(ErrorPanel, { error: Error(content[1] ?? "Unknown error") });
|
|
43
|
+
} else if (content[1].startsWith("text/markdown")) {
|
|
44
|
+
return /* @__PURE__ */ React.createElement(MarkdownContent, { content: content[0] });
|
|
45
|
+
}
|
|
46
|
+
return /* @__PURE__ */ React.createElement("div", { "data-testid": "readme-content" }, /* @__PURE__ */ React.createElement(CodeSnippet, { text: content[0], language: "plaintext" }));
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export { FetchComponent };
|
|
50
|
+
//# sourceMappingURL=FetchComponent.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FetchComponent.esm.js","sources":["../../../src/components/FetchComponent/FetchComponent.tsx"],"sourcesContent":["import React from 'react';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n CodeSnippet,\n ErrorPanel,\n Link,\n MarkdownContent,\n Progress,\n} from '@backstage/core-components';\nimport useAsync from 'react-use/lib/useAsync';\nimport { readmeApiRef } from '../../api/ReadmeApi';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { getEntitySourceLocation } from '@backstage/catalog-model';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\n\nexport const FetchComponent = () => {\n const { entity } = useEntity();\n const readmeApi = useApi(readmeApiRef);\n let location;\n\n try {\n const { target } = getEntitySourceLocation(entity);\n location = target ?? target;\n } catch (err) {\n location = ': Unknown';\n }\n\n const {\n value: content,\n loading,\n error,\n } = useAsync(\n async (): Promise<string[]> =>\n await readmeApi.getReadmeContent(stringifyEntityRef(entity)),\n [entity],\n );\n\n if (loading) {\n return <Progress />;\n }\n if (error?.message === '404') {\n return (\n <Box>\n <Typography pb={2} variant=\"body2\">\n No README.md file found at source location:{' '}\n {location && <strong>{location}</strong>}\n </Typography>\n <Typography variant=\"body2\">\n Need help? Go to our{' '}\n <Link to=\"https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/readme/README.md\">\n documentation\n </Link>\n </Typography>\n </Box>\n );\n }\n\n if (error) {\n return <ErrorPanel error={error} />;\n }\n if (!content) {\n return <ErrorPanel error={Error('Unknown error')} />;\n }\n if (!content[1] || content[1] === 'error') {\n return <ErrorPanel error={Error(content[1] ?? 'Unknown error')} />;\n } else if (content[1].startsWith('text/markdown')) {\n return <MarkdownContent content={content[0]} />;\n }\n // probably text/plain\n return (\n <div data-testid=\"readme-content\">\n <CodeSnippet text={content[0]} language=\"plaintext\" />\n </div>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;;AAiBO,MAAM,iBAAiB,MAAM;AAClC,EAAM,MAAA,EAAE,MAAO,EAAA,GAAI,SAAU,EAAA,CAAA;AAC7B,EAAM,MAAA,SAAA,GAAY,OAAO,YAAY,CAAA,CAAA;AACrC,EAAI,IAAA,QAAA,CAAA;AAEJ,EAAI,IAAA;AACF,IAAA,MAAM,EAAE,MAAA,EAAW,GAAA,uBAAA,CAAwB,MAAM,CAAA,CAAA;AACjD,IAAA,QAAA,GAAW,MAAU,IAAA,MAAA,CAAA;AAAA,WACd,GAAK,EAAA;AACZ,IAAW,QAAA,GAAA,WAAA,CAAA;AAAA,GACb;AAEA,EAAM,MAAA;AAAA,IACJ,KAAO,EAAA,OAAA;AAAA,IACP,OAAA;AAAA,IACA,KAAA;AAAA,GACE,GAAA,QAAA;AAAA,IACF,YACE,MAAM,SAAA,CAAU,gBAAiB,CAAA,kBAAA,CAAmB,MAAM,CAAC,CAAA;AAAA,IAC7D,CAAC,MAAM,CAAA;AAAA,GACT,CAAA;AAEA,EAAA,IAAI,OAAS,EAAA;AACX,IAAA,2CAAQ,QAAS,EAAA,IAAA,CAAA,CAAA;AAAA,GACnB;AACA,EAAI,IAAA,KAAA,EAAO,YAAY,KAAO,EAAA;AAC5B,IAAA,uBACG,KAAA,CAAA,aAAA,CAAA,GAAA,EAAA,IAAA,kBACE,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,EAAI,EAAA,CAAA,EAAG,OAAQ,EAAA,OAAA,EAAA,EAAQ,6CACW,EAAA,GAAA,EAC3C,QAAY,oBAAA,KAAA,CAAA,aAAA,CAAC,QAAQ,EAAA,IAAA,EAAA,QAAS,CACjC,CAAA,kBACC,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,OAAQ,EAAA,OAAA,EAAA,EAAQ,sBACL,EAAA,GAAA,kBACpB,KAAA,CAAA,aAAA,CAAA,IAAA,EAAA,EAAK,EAAG,EAAA,4FAAA,EAAA,EAA6F,eAEtG,CACF,CACF,CAAA,CAAA;AAAA,GAEJ;AAEA,EAAA,IAAI,KAAO,EAAA;AACT,IAAO,uBAAA,KAAA,CAAA,aAAA,CAAC,cAAW,KAAc,EAAA,CAAA,CAAA;AAAA,GACnC;AACA,EAAA,IAAI,CAAC,OAAS,EAAA;AACZ,IAAA,uBAAQ,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,KAAO,EAAA,KAAA,CAAM,eAAe,CAAG,EAAA,CAAA,CAAA;AAAA,GACpD;AACA,EAAA,IAAI,CAAC,OAAQ,CAAA,CAAC,KAAK,OAAQ,CAAA,CAAC,MAAM,OAAS,EAAA;AACzC,IAAO,uBAAA,KAAA,CAAA,aAAA,CAAC,cAAW,KAAO,EAAA,KAAA,CAAM,QAAQ,CAAC,CAAA,IAAK,eAAe,CAAG,EAAA,CAAA,CAAA;AAAA,aACvD,OAAQ,CAAA,CAAC,CAAE,CAAA,UAAA,CAAW,eAAe,CAAG,EAAA;AACjD,IAAA,uBAAQ,KAAA,CAAA,aAAA,CAAA,eAAA,EAAA,EAAgB,OAAS,EAAA,OAAA,CAAQ,CAAC,CAAG,EAAA,CAAA,CAAA;AAAA,GAC/C;AAEA,EAAA,uBACG,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAI,aAAY,EAAA,gBAAA,EAAA,kBACd,KAAA,CAAA,aAAA,CAAA,WAAA,EAAA,EAAY,IAAM,EAAA,OAAA,CAAQ,CAAC,CAAA,EAAG,QAAS,EAAA,WAAA,EAAY,CACtD,CAAA,CAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import IconButton from '@mui/material/IconButton';
|
|
3
|
+
import Box from '@mui/material/Box';
|
|
4
|
+
import { InfoCard } from '@backstage/core-components';
|
|
5
|
+
import { FetchComponent } from '../FetchComponent/FetchComponent.esm.js';
|
|
6
|
+
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
|
7
|
+
import { ReadmeDialog } from '../ReadmeDialog/ReadmeDialog.esm.js';
|
|
8
|
+
|
|
9
|
+
const ReadmeCard = (props) => {
|
|
10
|
+
const { variant = "gridItem" } = props;
|
|
11
|
+
const maxHeight = variant === "fullHeight" ? "none" : "235px";
|
|
12
|
+
const [displayDialog, setDisplayDialog] = useState(false);
|
|
13
|
+
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
|
|
14
|
+
InfoCard,
|
|
15
|
+
{
|
|
16
|
+
title: "README",
|
|
17
|
+
variant,
|
|
18
|
+
action: variant !== "fullHeight" ? /* @__PURE__ */ React.createElement(
|
|
19
|
+
IconButton,
|
|
20
|
+
{
|
|
21
|
+
onClick: () => setDisplayDialog(true),
|
|
22
|
+
"aria-label": "open dialog",
|
|
23
|
+
role: "button",
|
|
24
|
+
title: "Open in dialog",
|
|
25
|
+
size: "large"
|
|
26
|
+
},
|
|
27
|
+
/* @__PURE__ */ React.createElement(OpenInNewIcon, null)
|
|
28
|
+
) : void 0
|
|
29
|
+
},
|
|
30
|
+
/* @__PURE__ */ React.createElement("div", { style: { overflow: "auto" } }, /* @__PURE__ */ React.createElement(Box, { maxHeight }, /* @__PURE__ */ React.createElement(FetchComponent, null)))
|
|
31
|
+
), /* @__PURE__ */ React.createElement(
|
|
32
|
+
ReadmeDialog,
|
|
33
|
+
{
|
|
34
|
+
open: displayDialog,
|
|
35
|
+
onClose: () => setDisplayDialog(false)
|
|
36
|
+
}
|
|
37
|
+
));
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export { ReadmeCard };
|
|
41
|
+
//# sourceMappingURL=ReadmeCard.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReadmeCard.esm.js","sources":["../../../src/components/ReadmeCard/ReadmeCard.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport IconButton from '@mui/material/IconButton';\nimport Box from '@mui/material/Box';\nimport { InfoCard, InfoCardVariants } from '@backstage/core-components';\nimport { FetchComponent } from '../FetchComponent';\nimport OpenInNewIcon from '@mui/icons-material/OpenInNew';\nimport { ReadmeDialog } from '../ReadmeDialog/ReadmeDialog';\n\n/**\n * ReadmeCardProps props.\n *\n * @public\n */\n\nexport type ReadmeCardProps = {\n variant?: InfoCardVariants;\n};\n\nexport const ReadmeCard = (props: ReadmeCardProps) => {\n const { variant = 'gridItem' } = props;\n const maxHeight = variant === 'fullHeight' ? 'none' : '235px';\n\n const [displayDialog, setDisplayDialog] = useState(false);\n\n return (\n <>\n <InfoCard\n title=\"README\"\n variant={variant}\n action={\n variant !== 'fullHeight' ? (\n <IconButton\n onClick={() => setDisplayDialog(true)}\n aria-label=\"open dialog\"\n role=\"button\"\n title=\"Open in dialog\"\n size=\"large\"\n >\n <OpenInNewIcon />\n </IconButton>\n ) : undefined\n }\n >\n <div style={{ overflow: 'auto' }}>\n <Box maxHeight={maxHeight}>\n <FetchComponent />\n </Box>\n </div>\n </InfoCard>\n\n <ReadmeDialog\n open={displayDialog}\n onClose={() => setDisplayDialog(false)}\n />\n </>\n );\n};\n"],"names":[],"mappings":";;;;;;;;AAkBa,MAAA,UAAA,GAAa,CAAC,KAA2B,KAAA;AACpD,EAAM,MAAA,EAAE,OAAU,GAAA,UAAA,EAAe,GAAA,KAAA,CAAA;AACjC,EAAM,MAAA,SAAA,GAAY,OAAY,KAAA,YAAA,GAAe,MAAS,GAAA,OAAA,CAAA;AAEtD,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAAS,KAAK,CAAA,CAAA;AAExD,EAAA,uBAEI,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,QAAA;AAAA,IAAA;AAAA,MACC,KAAM,EAAA,QAAA;AAAA,MACN,OAAA;AAAA,MACA,MAAA,EACE,YAAY,YACV,mBAAA,KAAA,CAAA,aAAA;AAAA,QAAC,UAAA;AAAA,QAAA;AAAA,UACC,OAAA,EAAS,MAAM,gBAAA,CAAiB,IAAI,CAAA;AAAA,UACpC,YAAW,EAAA,aAAA;AAAA,UACX,IAAK,EAAA,QAAA;AAAA,UACL,KAAM,EAAA,gBAAA;AAAA,UACN,IAAK,EAAA,OAAA;AAAA,SAAA;AAAA,4CAEJ,aAAc,EAAA,IAAA,CAAA;AAAA,OAEf,GAAA,KAAA,CAAA;AAAA,KAAA;AAAA,oBAGL,KAAA,CAAA,aAAA,CAAA,KAAA,EAAA,EAAI,KAAO,EAAA,EAAE,QAAU,EAAA,MAAA,EACtB,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,GAAI,EAAA,EAAA,SAAA,EAAA,kBACF,KAAA,CAAA,aAAA,CAAA,cAAA,EAAA,IAAe,CAClB,CACF,CAAA;AAAA,GAGF,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,YAAA;AAAA,IAAA;AAAA,MACC,IAAM,EAAA,aAAA;AAAA,MACN,OAAA,EAAS,MAAM,gBAAA,CAAiB,KAAK,CAAA;AAAA,KAAA;AAAA,GAEzC,CAAA,CAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Button from '@mui/material/Button';
|
|
3
|
+
import Dialog from '@mui/material/Dialog';
|
|
4
|
+
import DialogActions from '@mui/material/DialogActions';
|
|
5
|
+
import DialogContent from '@mui/material/DialogContent';
|
|
6
|
+
import DialogTitle from '@mui/material/DialogTitle';
|
|
7
|
+
import Box from '@mui/material/Box';
|
|
8
|
+
import { FetchComponent } from '../FetchComponent/FetchComponent.esm.js';
|
|
9
|
+
|
|
10
|
+
const ReadmeDialog = ({ open, onClose }) => {
|
|
11
|
+
return /* @__PURE__ */ React.createElement(
|
|
12
|
+
Dialog,
|
|
13
|
+
{
|
|
14
|
+
maxWidth: "md",
|
|
15
|
+
open,
|
|
16
|
+
onClose,
|
|
17
|
+
"data-testid": "content-dialog"
|
|
18
|
+
},
|
|
19
|
+
/* @__PURE__ */ React.createElement(DialogTitle, null, "README"),
|
|
20
|
+
/* @__PURE__ */ React.createElement(DialogContent, { dividers: true }, /* @__PURE__ */ React.createElement(Box, { minWidth: 650 }, /* @__PURE__ */ React.createElement(FetchComponent, null))),
|
|
21
|
+
/* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(
|
|
22
|
+
Button,
|
|
23
|
+
{
|
|
24
|
+
variant: "contained",
|
|
25
|
+
color: "primary",
|
|
26
|
+
"aria-label": "close",
|
|
27
|
+
onClick: onClose
|
|
28
|
+
},
|
|
29
|
+
"Close"
|
|
30
|
+
))
|
|
31
|
+
);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export { ReadmeDialog };
|
|
35
|
+
//# sourceMappingURL=ReadmeDialog.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ReadmeDialog.esm.js","sources":["../../../src/components/ReadmeDialog/ReadmeDialog.tsx"],"sourcesContent":["import React from 'react';\nimport Button from '@mui/material/Button';\nimport Dialog from '@mui/material/Dialog';\nimport DialogActions from '@mui/material/DialogActions';\nimport DialogContent from '@mui/material/DialogContent';\nimport DialogTitle from '@mui/material/DialogTitle';\nimport Box from '@mui/material/Box';\nimport { FetchComponent } from '../FetchComponent';\n\ntype Props = {\n open: boolean;\n onClose: () => void;\n};\n\nexport const ReadmeDialog = ({ open, onClose }: Props) => {\n return (\n <Dialog\n maxWidth=\"md\"\n open={open}\n onClose={onClose}\n data-testid=\"content-dialog\"\n >\n <DialogTitle>README</DialogTitle>\n <DialogContent dividers>\n <Box minWidth={650}>\n <FetchComponent />\n </Box>\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"contained\"\n color=\"primary\"\n aria-label=\"close\"\n onClick={onClose}\n >\n Close\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n"],"names":[],"mappings":";;;;;;;;;AAcO,MAAM,YAAe,GAAA,CAAC,EAAE,IAAA,EAAM,SAAqB,KAAA;AACxD,EACE,uBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,MAAA;AAAA,IAAA;AAAA,MACC,QAAS,EAAA,IAAA;AAAA,MACT,IAAA;AAAA,MACA,OAAA;AAAA,MACA,aAAY,EAAA,gBAAA;AAAA,KAAA;AAAA,oBAEZ,KAAA,CAAA,aAAA,CAAC,mBAAY,QAAM,CAAA;AAAA,oBACnB,KAAA,CAAA,aAAA,CAAC,aAAc,EAAA,EAAA,QAAA,EAAQ,IACrB,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,GAAI,EAAA,EAAA,QAAA,EAAU,GACb,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,cAAe,EAAA,IAAA,CAClB,CACF,CAAA;AAAA,wCACC,aACC,EAAA,IAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,OAAQ,EAAA,WAAA;AAAA,QACR,KAAM,EAAA,SAAA;AAAA,QACN,YAAW,EAAA,OAAA;AAAA,QACX,OAAS,EAAA,OAAA;AAAA,OAAA;AAAA,MACV,OAAA;AAAA,KAGH,CAAA;AAAA,GACF,CAAA;AAEJ;;;;"}
|
package/dist/index.esm.js
CHANGED
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createPlugin, createApiFactory, discoveryApiRef, fetchApiRef, identityApiRef, createRoutableExtension } from '@backstage/core-plugin-api';
|
|
2
|
+
import { rootRouteRef } from './routes.esm.js';
|
|
3
|
+
import { readmeApiRef } from './api/ReadmeApi.esm.js';
|
|
4
|
+
import { ReadmeClient } from './api/ReadmeClient.esm.js';
|
|
5
|
+
|
|
6
|
+
const readmePlugin = createPlugin({
|
|
7
|
+
id: "readme",
|
|
8
|
+
apis: [
|
|
9
|
+
createApiFactory({
|
|
10
|
+
api: readmeApiRef,
|
|
11
|
+
deps: {
|
|
12
|
+
discoveryApi: discoveryApiRef,
|
|
13
|
+
fetchApi: fetchApiRef,
|
|
14
|
+
identityApi: identityApiRef
|
|
15
|
+
},
|
|
16
|
+
factory: ({ discoveryApi, fetchApi, identityApi }) => new ReadmeClient({ discoveryApi, fetchApi, identityApi })
|
|
17
|
+
})
|
|
18
|
+
],
|
|
19
|
+
routes: {
|
|
20
|
+
root: rootRouteRef
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
const ReadmeCard = readmePlugin.provide(
|
|
24
|
+
createRoutableExtension({
|
|
25
|
+
name: "ReadmeCard",
|
|
26
|
+
component: () => import('./components/ReadmeCard/index.esm.js').then((m) => m.ReadmeCard),
|
|
27
|
+
mountPoint: rootRouteRef
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
export { ReadmeCard, readmePlugin };
|
|
32
|
+
//# sourceMappingURL=plugin.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.esm.js","sources":["../src/plugin.ts"],"sourcesContent":["import {\n createApiFactory,\n createPlugin,\n createRoutableExtension,\n discoveryApiRef,\n fetchApiRef,\n identityApiRef,\n} from '@backstage/core-plugin-api';\n\nimport { rootRouteRef } from './routes';\nimport { readmeApiRef } from './api/ReadmeApi';\nimport { ReadmeClient } from './api/ReadmeClient';\n\n/**\n * Plugin that provides the Readme api\n * @public */\nexport const readmePlugin = createPlugin({\n id: 'readme',\n apis: [\n createApiFactory({\n api: readmeApiRef,\n deps: {\n discoveryApi: discoveryApiRef,\n fetchApi: fetchApiRef,\n identityApi: identityApiRef,\n },\n factory: ({ discoveryApi, fetchApi, identityApi }) =>\n new ReadmeClient({ discoveryApi, fetchApi, identityApi }),\n }),\n ],\n routes: {\n root: rootRouteRef,\n },\n});\n\n/**\n * Readme card exported from the Readme plugin\n * @public */\nexport const ReadmeCard = readmePlugin.provide(\n createRoutableExtension({\n name: 'ReadmeCard',\n component: () => import('./components/ReadmeCard').then(m => m.ReadmeCard),\n mountPoint: rootRouteRef,\n }),\n);\n"],"names":[],"mappings":";;;;;AAgBO,MAAM,eAAe,YAAa,CAAA;AAAA,EACvC,EAAI,EAAA,QAAA;AAAA,EACJ,IAAM,EAAA;AAAA,IACJ,gBAAiB,CAAA;AAAA,MACf,GAAK,EAAA,YAAA;AAAA,MACL,IAAM,EAAA;AAAA,QACJ,YAAc,EAAA,eAAA;AAAA,QACd,QAAU,EAAA,WAAA;AAAA,QACV,WAAa,EAAA,cAAA;AAAA,OACf;AAAA,MACA,OAAS,EAAA,CAAC,EAAE,YAAA,EAAc,QAAU,EAAA,WAAA,EAClC,KAAA,IAAI,YAAa,CAAA,EAAE,YAAc,EAAA,QAAA,EAAU,aAAa,CAAA;AAAA,KAC3D,CAAA;AAAA,GACH;AAAA,EACA,MAAQ,EAAA;AAAA,IACN,IAAM,EAAA,YAAA;AAAA,GACR;AACF,CAAC,EAAA;AAKM,MAAM,aAAa,YAAa,CAAA,OAAA;AAAA,EACrC,uBAAwB,CAAA;AAAA,IACtB,IAAM,EAAA,YAAA;AAAA,IACN,SAAA,EAAW,MAAM,OAAO,sCAAyB,EAAE,IAAK,CAAA,CAAA,CAAA,KAAK,EAAE,UAAU,CAAA;AAAA,IACzE,UAAY,EAAA,YAAA;AAAA,GACb,CAAA;AACH;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.esm.js","sources":["../src/routes.ts"],"sourcesContent":["import { createRouteRef } from '@backstage/core-plugin-api';\n\nexport const rootRouteRef = createRouteRef({\n id: 'readme',\n});\n"],"names":[],"mappings":";;AAEO,MAAM,eAAe,cAAe,CAAA;AAAA,EACzC,EAAI,EAAA,QAAA;AACN,CAAC;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axis-backstage/plugin-readme",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"main": "dist/index.esm.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -11,7 +11,12 @@
|
|
|
11
11
|
"directory": "_release/package"
|
|
12
12
|
},
|
|
13
13
|
"backstage": {
|
|
14
|
-
"role": "frontend-plugin"
|
|
14
|
+
"role": "frontend-plugin",
|
|
15
|
+
"pluginId": "readme",
|
|
16
|
+
"pluginPackages": [
|
|
17
|
+
"@axis-backstage/plugin-readme",
|
|
18
|
+
"@axis-backstage/plugin-readme-backend"
|
|
19
|
+
]
|
|
15
20
|
},
|
|
16
21
|
"sideEffects": false,
|
|
17
22
|
"scripts": {
|
|
@@ -24,11 +29,11 @@
|
|
|
24
29
|
"postpack": "backstage-cli package postpack"
|
|
25
30
|
},
|
|
26
31
|
"dependencies": {
|
|
27
|
-
"@backstage/catalog-model": "^1.
|
|
28
|
-
"@backstage/core-components": "^0.14.
|
|
32
|
+
"@backstage/catalog-model": "^1.5.0",
|
|
33
|
+
"@backstage/core-components": "^0.14.7",
|
|
29
34
|
"@backstage/core-plugin-api": "^1.9.2",
|
|
30
|
-
"@backstage/plugin-catalog-react": "^1.
|
|
31
|
-
"@backstage/theme": "^0.5.
|
|
35
|
+
"@backstage/plugin-catalog-react": "^1.12.0",
|
|
36
|
+
"@backstage/theme": "^0.5.5",
|
|
32
37
|
"@mui/icons-material": "^5.15.7",
|
|
33
38
|
"@mui/material": "^5.15.7",
|
|
34
39
|
"react-use": "^17.2.4"
|
|
@@ -39,10 +44,10 @@
|
|
|
39
44
|
"react": "^17.0.0 || ^18.0.0"
|
|
40
45
|
},
|
|
41
46
|
"devDependencies": {
|
|
42
|
-
"@backstage/cli": "^0.26.
|
|
43
|
-
"@backstage/core-app-api": "^1.12.
|
|
44
|
-
"@backstage/dev-utils": "^1.0.
|
|
45
|
-
"@backstage/test-utils": "^1.5.
|
|
47
|
+
"@backstage/cli": "^0.26.10",
|
|
48
|
+
"@backstage/core-app-api": "^1.12.5",
|
|
49
|
+
"@backstage/dev-utils": "^1.0.32",
|
|
50
|
+
"@backstage/test-utils": "^1.5.5",
|
|
46
51
|
"@testing-library/jest-dom": "6.0.0",
|
|
47
52
|
"@testing-library/react": "^14.0.0",
|
|
48
53
|
"@testing-library/user-event": "^14.0.0",
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import IconButton from '@mui/material/IconButton';
|
|
3
|
-
import Box from '@mui/material/Box';
|
|
4
|
-
import { Progress, Link, ErrorPanel, MarkdownContent, CodeSnippet, InfoCard } from '@backstage/core-components';
|
|
5
|
-
import { useEntity } from '@backstage/plugin-catalog-react';
|
|
6
|
-
import { useApi } from '@backstage/core-plugin-api';
|
|
7
|
-
import useAsync from 'react-use/lib/useAsync';
|
|
8
|
-
import { r as readmeApiRef } from './index-DemiDWtG.esm.js';
|
|
9
|
-
import { getEntitySourceLocation, stringifyEntityRef } from '@backstage/catalog-model';
|
|
10
|
-
import Typography from '@mui/material/Typography';
|
|
11
|
-
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
|
|
12
|
-
import Button from '@mui/material/Button';
|
|
13
|
-
import Dialog from '@mui/material/Dialog';
|
|
14
|
-
import DialogActions from '@mui/material/DialogActions';
|
|
15
|
-
import DialogContent from '@mui/material/DialogContent';
|
|
16
|
-
import DialogTitle from '@mui/material/DialogTitle';
|
|
17
|
-
|
|
18
|
-
const FetchComponent = () => {
|
|
19
|
-
var _a;
|
|
20
|
-
const { entity } = useEntity();
|
|
21
|
-
const readmeApi = useApi(readmeApiRef);
|
|
22
|
-
let location;
|
|
23
|
-
try {
|
|
24
|
-
const { target } = getEntitySourceLocation(entity);
|
|
25
|
-
location = target != null ? target : target;
|
|
26
|
-
} catch (err) {
|
|
27
|
-
location = ": Unknown";
|
|
28
|
-
}
|
|
29
|
-
const {
|
|
30
|
-
value: content,
|
|
31
|
-
loading,
|
|
32
|
-
error
|
|
33
|
-
} = useAsync(
|
|
34
|
-
async () => await readmeApi.getReadmeContent(stringifyEntityRef(entity)),
|
|
35
|
-
[entity]
|
|
36
|
-
);
|
|
37
|
-
if (loading) {
|
|
38
|
-
return /* @__PURE__ */ React.createElement(Progress, null);
|
|
39
|
-
}
|
|
40
|
-
if ((error == null ? void 0 : error.message) === "404") {
|
|
41
|
-
return /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Typography, { pb: 2, variant: "body2" }, "No README.md file found at source location:", " ", location && /* @__PURE__ */ React.createElement("strong", null, location)), /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, "Need help? Go to our", " ", /* @__PURE__ */ React.createElement(Link, { to: "https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/readme/README.md" }, "documentation")));
|
|
42
|
-
}
|
|
43
|
-
if (error) {
|
|
44
|
-
return /* @__PURE__ */ React.createElement(ErrorPanel, { error });
|
|
45
|
-
}
|
|
46
|
-
if (!content) {
|
|
47
|
-
return /* @__PURE__ */ React.createElement(ErrorPanel, { error: Error("Unknown error") });
|
|
48
|
-
}
|
|
49
|
-
if (!content[1] || content[1] === "error") {
|
|
50
|
-
return /* @__PURE__ */ React.createElement(ErrorPanel, { error: Error((_a = content[1]) != null ? _a : "Unknown error") });
|
|
51
|
-
} else if (content[1].startsWith("text/markdown")) {
|
|
52
|
-
return /* @__PURE__ */ React.createElement(MarkdownContent, { content: content[0] });
|
|
53
|
-
}
|
|
54
|
-
return /* @__PURE__ */ React.createElement("div", { "data-testid": "readme-content" }, /* @__PURE__ */ React.createElement(CodeSnippet, { text: content[0], language: "plaintext" }));
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const ReadmeDialog = ({ open, onClose }) => {
|
|
58
|
-
return /* @__PURE__ */ React.createElement(
|
|
59
|
-
Dialog,
|
|
60
|
-
{
|
|
61
|
-
maxWidth: "md",
|
|
62
|
-
open,
|
|
63
|
-
onClose,
|
|
64
|
-
"data-testid": "content-dialog"
|
|
65
|
-
},
|
|
66
|
-
/* @__PURE__ */ React.createElement(DialogTitle, null, "README"),
|
|
67
|
-
/* @__PURE__ */ React.createElement(DialogContent, { dividers: true }, /* @__PURE__ */ React.createElement(Box, { minWidth: 650 }, /* @__PURE__ */ React.createElement(FetchComponent, null))),
|
|
68
|
-
/* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(
|
|
69
|
-
Button,
|
|
70
|
-
{
|
|
71
|
-
variant: "contained",
|
|
72
|
-
color: "primary",
|
|
73
|
-
"aria-label": "close",
|
|
74
|
-
onClick: onClose
|
|
75
|
-
},
|
|
76
|
-
"Close"
|
|
77
|
-
))
|
|
78
|
-
);
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const ReadmeCard = (props) => {
|
|
82
|
-
const { variant = "gridItem" } = props;
|
|
83
|
-
const maxHeight = variant === "fullHeight" ? "none" : "235px";
|
|
84
|
-
const [displayDialog, setDisplayDialog] = useState(false);
|
|
85
|
-
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
|
|
86
|
-
InfoCard,
|
|
87
|
-
{
|
|
88
|
-
title: "README",
|
|
89
|
-
variant,
|
|
90
|
-
action: variant !== "fullHeight" ? /* @__PURE__ */ React.createElement(
|
|
91
|
-
IconButton,
|
|
92
|
-
{
|
|
93
|
-
onClick: () => setDisplayDialog(true),
|
|
94
|
-
"aria-label": "open dialog",
|
|
95
|
-
role: "button",
|
|
96
|
-
title: "Open in dialog",
|
|
97
|
-
size: "large"
|
|
98
|
-
},
|
|
99
|
-
/* @__PURE__ */ React.createElement(OpenInNewIcon, null)
|
|
100
|
-
) : void 0
|
|
101
|
-
},
|
|
102
|
-
/* @__PURE__ */ React.createElement("div", { style: { overflow: "auto" } }, /* @__PURE__ */ React.createElement(Box, { maxHeight }, /* @__PURE__ */ React.createElement(FetchComponent, null)))
|
|
103
|
-
), /* @__PURE__ */ React.createElement(
|
|
104
|
-
ReadmeDialog,
|
|
105
|
-
{
|
|
106
|
-
open: displayDialog,
|
|
107
|
-
onClose: () => setDisplayDialog(false)
|
|
108
|
-
}
|
|
109
|
-
));
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
export { ReadmeCard };
|
|
113
|
-
//# sourceMappingURL=index-CnQYQb-x.esm.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-CnQYQb-x.esm.js","sources":["../../src/components/FetchComponent/FetchComponent.tsx","../../src/components/ReadmeDialog/ReadmeDialog.tsx","../../src/components/ReadmeCard/ReadmeCard.tsx"],"sourcesContent":["import React from 'react';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { useApi } from '@backstage/core-plugin-api';\nimport {\n CodeSnippet,\n ErrorPanel,\n Link,\n MarkdownContent,\n Progress,\n} from '@backstage/core-components';\nimport useAsync from 'react-use/lib/useAsync';\nimport { readmeApiRef } from '../../api/ReadmeApi';\nimport { stringifyEntityRef } from '@backstage/catalog-model';\nimport { getEntitySourceLocation } from '@backstage/catalog-model';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\n\nexport const FetchComponent = () => {\n const { entity } = useEntity();\n const readmeApi = useApi(readmeApiRef);\n let location;\n\n try {\n const { target } = getEntitySourceLocation(entity);\n location = target ?? target;\n } catch (err) {\n location = ': Unknown';\n }\n\n const {\n value: content,\n loading,\n error,\n } = useAsync(\n async (): Promise<string[]> =>\n await readmeApi.getReadmeContent(stringifyEntityRef(entity)),\n [entity],\n );\n\n if (loading) {\n return <Progress />;\n }\n if (error?.message === '404') {\n return (\n <Box>\n <Typography pb={2} variant=\"body2\">\n No README.md file found at source location:{' '}\n {location && <strong>{location}</strong>}\n </Typography>\n <Typography variant=\"body2\">\n Need help? Go to our{' '}\n <Link to=\"https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/readme/README.md\">\n documentation\n </Link>\n </Typography>\n </Box>\n );\n }\n\n if (error) {\n return <ErrorPanel error={error} />;\n }\n if (!content) {\n return <ErrorPanel error={Error('Unknown error')} />;\n }\n if (!content[1] || content[1] === 'error') {\n return <ErrorPanel error={Error(content[1] ?? 'Unknown error')} />;\n } else if (content[1].startsWith('text/markdown')) {\n return <MarkdownContent content={content[0]} />;\n }\n // probably text/plain\n return (\n <div data-testid=\"readme-content\">\n <CodeSnippet text={content[0]} language=\"plaintext\" />\n </div>\n );\n};\n","import React from 'react';\nimport Button from '@mui/material/Button';\nimport Dialog from '@mui/material/Dialog';\nimport DialogActions from '@mui/material/DialogActions';\nimport DialogContent from '@mui/material/DialogContent';\nimport DialogTitle from '@mui/material/DialogTitle';\nimport Box from '@mui/material/Box';\nimport { FetchComponent } from '../FetchComponent';\n\ntype Props = {\n open: boolean;\n onClose: () => void;\n};\n\nexport const ReadmeDialog = ({ open, onClose }: Props) => {\n return (\n <Dialog\n maxWidth=\"md\"\n open={open}\n onClose={onClose}\n data-testid=\"content-dialog\"\n >\n <DialogTitle>README</DialogTitle>\n <DialogContent dividers>\n <Box minWidth={650}>\n <FetchComponent />\n </Box>\n </DialogContent>\n <DialogActions>\n <Button\n variant=\"contained\"\n color=\"primary\"\n aria-label=\"close\"\n onClick={onClose}\n >\n Close\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import React, { useState } from 'react';\nimport IconButton from '@mui/material/IconButton';\nimport Box from '@mui/material/Box';\nimport { InfoCard, InfoCardVariants } from '@backstage/core-components';\nimport { FetchComponent } from '../FetchComponent';\nimport OpenInNewIcon from '@mui/icons-material/OpenInNew';\nimport { ReadmeDialog } from '../ReadmeDialog/ReadmeDialog';\n\n/**\n * ReadmeCardProps props.\n *\n * @public\n */\n\nexport type ReadmeCardProps = {\n variant?: InfoCardVariants;\n};\n\nexport const ReadmeCard = (props: ReadmeCardProps) => {\n const { variant = 'gridItem' } = props;\n const maxHeight = variant === 'fullHeight' ? 'none' : '235px';\n\n const [displayDialog, setDisplayDialog] = useState(false);\n\n return (\n <>\n <InfoCard\n title=\"README\"\n variant={variant}\n action={\n variant !== 'fullHeight' ? (\n <IconButton\n onClick={() => setDisplayDialog(true)}\n aria-label=\"open dialog\"\n role=\"button\"\n title=\"Open in dialog\"\n size=\"large\"\n >\n <OpenInNewIcon />\n </IconButton>\n ) : undefined\n }\n >\n <div style={{ overflow: 'auto' }}>\n <Box maxHeight={maxHeight}>\n <FetchComponent />\n </Box>\n </div>\n </InfoCard>\n\n <ReadmeDialog\n open={displayDialog}\n onClose={() => setDisplayDialog(false)}\n />\n </>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import { createRouteRef, createApiRef, createPlugin, createApiFactory, discoveryApiRef, fetchApiRef, identityApiRef, createRoutableExtension } from '@backstage/core-plugin-api';
|
|
2
|
-
import { parseEntityRef, DEFAULT_NAMESPACE } from '@backstage/catalog-model';
|
|
3
|
-
|
|
4
|
-
const rootRouteRef = createRouteRef({
|
|
5
|
-
id: "readme"
|
|
6
|
-
});
|
|
7
|
-
|
|
8
|
-
const readmeApiRef = createApiRef({
|
|
9
|
-
id: "plugin.readme"
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
var __defProp = Object.defineProperty;
|
|
13
|
-
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
14
|
-
var __publicField = (obj, key, value) => {
|
|
15
|
-
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
16
|
-
return value;
|
|
17
|
-
};
|
|
18
|
-
class ReadmeClient {
|
|
19
|
-
constructor(options) {
|
|
20
|
-
__publicField(this, "discoveryApi");
|
|
21
|
-
__publicField(this, "fetchApi");
|
|
22
|
-
__publicField(this, "identityApi");
|
|
23
|
-
this.discoveryApi = options.discoveryApi;
|
|
24
|
-
this.fetchApi = options.fetchApi;
|
|
25
|
-
this.identityApi = options.identityApi;
|
|
26
|
-
}
|
|
27
|
-
async getReadmeContent(entityRef) {
|
|
28
|
-
const { token } = await this.identityApi.getCredentials();
|
|
29
|
-
const baseUrl = await this.discoveryApi.getBaseUrl("readme");
|
|
30
|
-
const { kind, name, namespace } = parseEntityRef(entityRef, {
|
|
31
|
-
defaultNamespace: DEFAULT_NAMESPACE
|
|
32
|
-
});
|
|
33
|
-
const resp = await this.fetchApi.fetch(
|
|
34
|
-
`${baseUrl}/${kind}/${namespace}/${name}`,
|
|
35
|
-
{
|
|
36
|
-
method: "GET",
|
|
37
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
38
|
-
}
|
|
39
|
-
);
|
|
40
|
-
if (resp.ok) {
|
|
41
|
-
return [
|
|
42
|
-
await resp.text(),
|
|
43
|
-
resp.headers.get("Content-Type") || "text/plain"
|
|
44
|
-
];
|
|
45
|
-
}
|
|
46
|
-
if (resp.status === 404) {
|
|
47
|
-
throw new Error("404");
|
|
48
|
-
}
|
|
49
|
-
throw new Error(`${resp.status}: ${resp.statusText}`);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const readmePlugin = createPlugin({
|
|
54
|
-
id: "readme",
|
|
55
|
-
apis: [
|
|
56
|
-
createApiFactory({
|
|
57
|
-
api: readmeApiRef,
|
|
58
|
-
deps: {
|
|
59
|
-
discoveryApi: discoveryApiRef,
|
|
60
|
-
fetchApi: fetchApiRef,
|
|
61
|
-
identityApi: identityApiRef
|
|
62
|
-
},
|
|
63
|
-
factory: ({ discoveryApi, fetchApi, identityApi }) => new ReadmeClient({ discoveryApi, fetchApi, identityApi })
|
|
64
|
-
})
|
|
65
|
-
],
|
|
66
|
-
routes: {
|
|
67
|
-
root: rootRouteRef
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
const ReadmeCard = readmePlugin.provide(
|
|
71
|
-
createRoutableExtension({
|
|
72
|
-
name: "ReadmeCard",
|
|
73
|
-
component: () => import('./index-CnQYQb-x.esm.js').then((m) => m.ReadmeCard),
|
|
74
|
-
mountPoint: rootRouteRef
|
|
75
|
-
})
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
export { ReadmeCard as R, readmePlugin as a, readmeApiRef as r };
|
|
79
|
-
//# sourceMappingURL=index-DemiDWtG.esm.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-DemiDWtG.esm.js","sources":["../../src/routes.ts","../../src/api/ReadmeApi.tsx","../../src/api/ReadmeClient.tsx","../../src/plugin.ts"],"sourcesContent":["import { createRouteRef } from '@backstage/core-plugin-api';\n\nexport const rootRouteRef = createRouteRef({\n id: 'readme',\n});\n","import { createApiRef } from '@backstage/core-plugin-api';\n\nexport const readmeApiRef = createApiRef<ReadmeApi>({\n id: 'plugin.readme',\n});\n\nexport type ReadmeApi = {\n getReadmeContent(entityRef: string): Promise<[string, string]>;\n};\n","import {\n DiscoveryApi,\n FetchApi,\n IdentityApi,\n} from '@backstage/core-plugin-api';\nimport { DEFAULT_NAMESPACE, parseEntityRef } from '@backstage/catalog-model';\nimport { ReadmeApi } from './ReadmeApi';\n\nexport class ReadmeClient implements ReadmeApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n private readonly identityApi: IdentityApi;\n\n constructor(options: {\n discoveryApi: DiscoveryApi;\n fetchApi: FetchApi;\n identityApi: IdentityApi;\n }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi;\n this.identityApi = options.identityApi;\n }\n\n async getReadmeContent(entityRef: string): Promise<[string, string]> {\n const { token } = await this.identityApi.getCredentials();\n const baseUrl = await this.discoveryApi.getBaseUrl('readme');\n\n const { kind, name, namespace } = parseEntityRef(entityRef, {\n defaultNamespace: DEFAULT_NAMESPACE,\n });\n\n const resp = await this.fetchApi.fetch(\n `${baseUrl}/${kind}/${namespace}/${name}`,\n {\n method: 'GET',\n headers: { Authorization: `Bearer ${token}` },\n },\n );\n if (resp.ok) {\n return [\n await resp.text(),\n resp.headers.get('Content-Type') || 'text/plain',\n ];\n }\n if (resp.status === 404) {\n throw new Error('404');\n }\n throw new Error(`${resp.status}: ${resp.statusText}`);\n }\n}\n","import {\n createApiFactory,\n createPlugin,\n createRoutableExtension,\n discoveryApiRef,\n fetchApiRef,\n identityApiRef,\n} from '@backstage/core-plugin-api';\n\nimport { rootRouteRef } from './routes';\nimport { readmeApiRef } from './api/ReadmeApi';\nimport { ReadmeClient } from './api/ReadmeClient';\n\n/**\n * Plugin that provides the Readme api\n * @public */\nexport const readmePlugin = createPlugin({\n id: 'readme',\n apis: [\n createApiFactory({\n api: readmeApiRef,\n deps: {\n discoveryApi: discoveryApiRef,\n fetchApi: fetchApiRef,\n identityApi: identityApiRef,\n },\n factory: ({ discoveryApi, fetchApi, identityApi }) =>\n new ReadmeClient({ discoveryApi, fetchApi, identityApi }),\n }),\n ],\n routes: {\n root: rootRouteRef,\n },\n});\n\n/**\n * Readme card exported from the Readme plugin\n * @public */\nexport const ReadmeCard = readmePlugin.provide(\n createRoutableExtension({\n name: 'ReadmeCard',\n component: () => import('./components/ReadmeCard').then(m => m.ReadmeCard),\n mountPoint: rootRouteRef,\n }),\n);\n"],"names":[],"mappings":"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"}
|