@backstage/plugin-techdocs 0.12.8 → 0.12.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/config.d.ts +11 -0
- package/dist/index.esm.js +135 -105
- package/dist/index.esm.js.map +1 -1
- package/package.json +19 -18
package/dist/index.esm.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { createApiRef, createRouteRef, useRouteRef, useApi, configApiRef, createPlugin, createApiFactory, discoveryApiRef, identityApiRef, createRoutableExtension, createComponentExtension } from '@backstage/core-plugin-api';
|
|
2
2
|
import { ResponseError, NotFoundError } from '@backstage/errors';
|
|
3
3
|
import { EventSourcePolyfill } from 'event-source-polyfill';
|
|
4
|
-
import React, { useEffect, useState,
|
|
4
|
+
import React, { useEffect, useState, useReducer, useRef, useMemo, createContext, useContext, useCallback } from 'react';
|
|
5
5
|
import { makeStyles, ListItemText, ListItem, Divider, Card, CardMedia, CardContent, CardActions, Grid, TextField, InputAdornment, IconButton, CircularProgress, createStyles, Button as Button$1, Drawer, Typography, useTheme } from '@material-ui/core';
|
|
6
|
-
import { Link, SubvalueCell, Table, EmptyState, Button, WarningPanel, CodeSnippet, PageWithHeader, Content, ContentHeader, SupportButton, ItemCardGrid, ItemCardHeader, Progress, ErrorPage, HeaderLabel, Header, Page, HeaderTabs, MissingAnnotationEmptyState } from '@backstage/core-components';
|
|
6
|
+
import { Link, SubvalueCell, Table, EmptyState, Button, WarningPanel, CodeSnippet, PageWithHeader, Content, ContentHeader, SupportButton, ItemCardGrid, ItemCardHeader, Progress, LogViewer, ErrorPage, HeaderLabel, Header, Page, HeaderTabs, MissingAnnotationEmptyState } from '@backstage/core-components';
|
|
7
7
|
import TextTruncate from 'react-text-truncate';
|
|
8
8
|
import { FilteredEntityLayout, FilterContainer, EntityListContainer } from '@backstage/plugin-catalog';
|
|
9
9
|
import { favoriteEntityIcon, favoriteEntityTooltip, EntityRefLinks, getEntityRelations, formatEntityRefTitle, useEntityListProvider, useStarredEntities, CATALOG_FILTER_EXISTS, EntityListProvider, UserListPicker, EntityOwnerPicker, EntityTagPicker, EntityRefLink, catalogApiRef, useOwnUser, isOwnerOf, useEntity } from '@backstage/plugin-catalog-react';
|
|
@@ -27,12 +27,10 @@ import Close from '@material-ui/icons/Close';
|
|
|
27
27
|
import CodeIcon from '@material-ui/icons/Code';
|
|
28
28
|
|
|
29
29
|
const techdocsStorageApiRef = createApiRef({
|
|
30
|
-
id: "plugin.techdocs.storageservice"
|
|
31
|
-
description: "Used to make requests towards the techdocs storage"
|
|
30
|
+
id: "plugin.techdocs.storageservice"
|
|
32
31
|
});
|
|
33
32
|
const techdocsApiRef = createApiRef({
|
|
34
|
-
id: "plugin.techdocs.service"
|
|
35
|
-
description: "Used to make requests towards techdocs API"
|
|
33
|
+
id: "plugin.techdocs.service"
|
|
36
34
|
});
|
|
37
35
|
|
|
38
36
|
class TechDocsClient {
|
|
@@ -50,12 +48,12 @@ class TechDocsClient {
|
|
|
50
48
|
return (_a = this.configApi.getOptionalString("techdocs.requestUrl")) != null ? _a : await this.discoveryApi.getBaseUrl("techdocs");
|
|
51
49
|
}
|
|
52
50
|
async getTechDocsMetadata(entityId) {
|
|
53
|
-
const {kind, namespace, name} = entityId;
|
|
51
|
+
const { kind, namespace, name } = entityId;
|
|
54
52
|
const apiOrigin = await this.getApiOrigin();
|
|
55
53
|
const requestUrl = `${apiOrigin}/metadata/techdocs/${namespace}/${kind}/${name}`;
|
|
56
54
|
const token = await this.identityApi.getIdToken();
|
|
57
55
|
const request = await fetch(`${requestUrl}`, {
|
|
58
|
-
headers: token ? {Authorization: `Bearer ${token}`} : {}
|
|
56
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
59
57
|
});
|
|
60
58
|
if (!request.ok) {
|
|
61
59
|
throw await ResponseError.fromResponse(request);
|
|
@@ -63,12 +61,12 @@ class TechDocsClient {
|
|
|
63
61
|
return await request.json();
|
|
64
62
|
}
|
|
65
63
|
async getEntityMetadata(entityId) {
|
|
66
|
-
const {kind, namespace, name} = entityId;
|
|
64
|
+
const { kind, namespace, name } = entityId;
|
|
67
65
|
const apiOrigin = await this.getApiOrigin();
|
|
68
66
|
const requestUrl = `${apiOrigin}/metadata/entity/${namespace}/${kind}/${name}`;
|
|
69
67
|
const token = await this.identityApi.getIdToken();
|
|
70
68
|
const request = await fetch(`${requestUrl}`, {
|
|
71
|
-
headers: token ? {Authorization: `Bearer ${token}`} : {}
|
|
69
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
72
70
|
});
|
|
73
71
|
if (!request.ok) {
|
|
74
72
|
throw await ResponseError.fromResponse(request);
|
|
@@ -98,12 +96,12 @@ class TechDocsStorageClient {
|
|
|
98
96
|
return this.configApi.getString("techdocs.builder");
|
|
99
97
|
}
|
|
100
98
|
async getEntityDocs(entityId, path) {
|
|
101
|
-
const {kind, namespace, name} = entityId;
|
|
99
|
+
const { kind, namespace, name } = entityId;
|
|
102
100
|
const storageUrl = await this.getStorageUrl();
|
|
103
101
|
const url = `${storageUrl}/${namespace}/${kind}/${name}/${path}`;
|
|
104
102
|
const token = await this.identityApi.getIdToken();
|
|
105
103
|
const request = await fetch(`${url.endsWith("/") ? url : `${url}/`}index.html`, {
|
|
106
|
-
headers: token ? {Authorization: `Bearer ${token}`} : {}
|
|
104
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
107
105
|
});
|
|
108
106
|
let errorMessage = "";
|
|
109
107
|
switch (request.status) {
|
|
@@ -121,14 +119,14 @@ class TechDocsStorageClient {
|
|
|
121
119
|
}
|
|
122
120
|
async syncEntityDocs(entityId, logHandler = () => {
|
|
123
121
|
}) {
|
|
124
|
-
const {kind, namespace, name} = entityId;
|
|
122
|
+
const { kind, namespace, name } = entityId;
|
|
125
123
|
const apiOrigin = await this.getApiOrigin();
|
|
126
124
|
const url = `${apiOrigin}/sync/${namespace}/${kind}/${name}`;
|
|
127
125
|
const token = await this.identityApi.getIdToken();
|
|
128
126
|
return new Promise((resolve, reject) => {
|
|
129
127
|
const source = new EventSourcePolyfill(url, {
|
|
130
128
|
withCredentials: true,
|
|
131
|
-
headers: token ? {Authorization: `Bearer ${token}`} : {}
|
|
129
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
132
130
|
});
|
|
133
131
|
source.addEventListener("log", (e) => {
|
|
134
132
|
if (e.data) {
|
|
@@ -138,7 +136,7 @@ class TechDocsStorageClient {
|
|
|
138
136
|
source.addEventListener("finish", (e) => {
|
|
139
137
|
let updated = false;
|
|
140
138
|
if (e.data) {
|
|
141
|
-
({updated} = JSON.parse(e.data));
|
|
139
|
+
({ updated } = JSON.parse(e.data));
|
|
142
140
|
}
|
|
143
141
|
resolve(updated ? "updated" : "cached");
|
|
144
142
|
});
|
|
@@ -156,9 +154,10 @@ class TechDocsStorageClient {
|
|
|
156
154
|
});
|
|
157
155
|
}
|
|
158
156
|
async getBaseUrl(oldBaseUrl, entityId, path) {
|
|
159
|
-
const {kind, namespace, name} = entityId;
|
|
157
|
+
const { kind, namespace, name } = entityId;
|
|
160
158
|
const apiOrigin = await this.getApiOrigin();
|
|
161
|
-
|
|
159
|
+
const newBaseUrl = `${apiOrigin}/static/docs/${namespace}/${kind}/${name}/${path}`;
|
|
160
|
+
return new URL(oldBaseUrl, newBaseUrl.endsWith("/") ? newBaseUrl : `${newBaseUrl}/`).toString();
|
|
162
161
|
}
|
|
163
162
|
}
|
|
164
163
|
|
|
@@ -183,7 +182,7 @@ const DocsResultListItem = ({
|
|
|
183
182
|
var _a;
|
|
184
183
|
return /* @__PURE__ */ React.createElement(ListItemText, {
|
|
185
184
|
className: classes.itemText,
|
|
186
|
-
primaryTypographyProps: {variant: "h6"},
|
|
185
|
+
primaryTypographyProps: { variant: "h6" },
|
|
187
186
|
primary: title ? title : `${result.title} | ${(_a = result.entityTitle) != null ? _a : result.name} docs`,
|
|
188
187
|
secondary: /* @__PURE__ */ React.createElement(TextTruncate, {
|
|
189
188
|
line: lineClamp,
|
|
@@ -193,10 +192,10 @@ const DocsResultListItem = ({
|
|
|
193
192
|
})
|
|
194
193
|
});
|
|
195
194
|
};
|
|
196
|
-
const LinkWrapper = ({children}) => asLink ? /* @__PURE__ */ React.createElement(Link, {
|
|
195
|
+
const LinkWrapper = ({ children }) => asLink ? /* @__PURE__ */ React.createElement(Link, {
|
|
197
196
|
to: result.location
|
|
198
197
|
}, children) : /* @__PURE__ */ React.createElement(React.Fragment, null, children);
|
|
199
|
-
const ListItemWrapper = ({children}) => asListItem ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ListItem, {
|
|
198
|
+
const ListItemWrapper = ({ children }) => asListItem ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(ListItem, {
|
|
200
199
|
alignItems: "flex-start",
|
|
201
200
|
className: classes.flexContainer
|
|
202
201
|
}, children), /* @__PURE__ */ React.createElement(Divider, {
|
|
@@ -228,10 +227,10 @@ function createCopyDocsUrlAction(copyToClipboard) {
|
|
|
228
227
|
};
|
|
229
228
|
}
|
|
230
229
|
function createStarEntityAction(isStarredEntity, toggleStarredEntity) {
|
|
231
|
-
return ({entity}) => {
|
|
230
|
+
return ({ entity }) => {
|
|
232
231
|
const isStarred = isStarredEntity(entity);
|
|
233
232
|
return {
|
|
234
|
-
cellStyle: {paddingLeft: "1em"},
|
|
233
|
+
cellStyle: { paddingLeft: "1em" },
|
|
235
234
|
icon: () => favoriteEntityIcon(isStarred),
|
|
236
235
|
tooltip: favoriteEntityTooltip(isStarred),
|
|
237
236
|
onClick: () => toggleStarredEntity(entity)
|
|
@@ -265,7 +264,7 @@ function createOwnerColumn() {
|
|
|
265
264
|
return {
|
|
266
265
|
title: "Owner",
|
|
267
266
|
field: "resolved.ownedByRelationsTitle",
|
|
268
|
-
render: ({resolved}) => /* @__PURE__ */ React.createElement(EntityRefLinks, {
|
|
267
|
+
render: ({ resolved }) => /* @__PURE__ */ React.createElement(EntityRefLinks, {
|
|
269
268
|
entityRefs: resolved.ownedByRelations,
|
|
270
269
|
defaultKind: "group"
|
|
271
270
|
})
|
|
@@ -285,6 +284,10 @@ var columnFactories = /*#__PURE__*/Object.freeze({
|
|
|
285
284
|
createTypeColumn: createTypeColumn
|
|
286
285
|
});
|
|
287
286
|
|
|
287
|
+
function toLowerMaybe(str, config) {
|
|
288
|
+
return config.getOptionalBoolean("techdocs.legacyUseCaseSensitiveTripletPaths") ? str : str.toLocaleLowerCase("en-US");
|
|
289
|
+
}
|
|
290
|
+
|
|
288
291
|
const DocsTable$1 = ({
|
|
289
292
|
entities,
|
|
290
293
|
title,
|
|
@@ -294,7 +297,7 @@ const DocsTable$1 = ({
|
|
|
294
297
|
}) => {
|
|
295
298
|
const [, copyToClipboard] = useCopyToClipboard();
|
|
296
299
|
const getRouteToReaderPageFor = useRouteRef(rootDocsRouteRef);
|
|
297
|
-
const
|
|
300
|
+
const config = useApi(configApiRef);
|
|
298
301
|
if (!entities)
|
|
299
302
|
return null;
|
|
300
303
|
const documents = entities.map((entity) => {
|
|
@@ -304,12 +307,12 @@ const DocsTable$1 = ({
|
|
|
304
307
|
entity,
|
|
305
308
|
resolved: {
|
|
306
309
|
docsUrl: getRouteToReaderPageFor({
|
|
307
|
-
namespace: toLowerMaybe((_a = entity.metadata.namespace) != null ? _a : "default"),
|
|
308
|
-
kind: toLowerMaybe(entity.kind),
|
|
309
|
-
name: toLowerMaybe(entity.metadata.name)
|
|
310
|
+
namespace: toLowerMaybe((_a = entity.metadata.namespace) != null ? _a : "default", config),
|
|
311
|
+
kind: toLowerMaybe(entity.kind, config),
|
|
312
|
+
name: toLowerMaybe(entity.metadata.name, config)
|
|
310
313
|
}),
|
|
311
314
|
ownedByRelations,
|
|
312
|
-
ownedByRelationsTitle: ownedByRelations.map((r) => formatEntityRefTitle(r, {defaultKind: "group"})).join(", ")
|
|
315
|
+
ownedByRelationsTitle: ownedByRelations.map((r) => formatEntityRefTitle(r, { defaultKind: "group" })).join(", ")
|
|
313
316
|
}
|
|
314
317
|
};
|
|
315
318
|
});
|
|
@@ -355,8 +358,8 @@ const EntityListDocsTable = ({
|
|
|
355
358
|
actions
|
|
356
359
|
}) => {
|
|
357
360
|
var _a, _b;
|
|
358
|
-
const {loading, error, entities, filters} = useEntityListProvider();
|
|
359
|
-
const {isStarredEntity, toggleStarredEntity} = useStarredEntities();
|
|
361
|
+
const { loading, error, entities, filters } = useEntityListProvider();
|
|
362
|
+
const { isStarredEntity, toggleStarredEntity } = useStarredEntities();
|
|
360
363
|
const [, copyToClipboard] = useCopyToClipboard();
|
|
361
364
|
const title = capitalize((_b = (_a = filters.user) == null ? void 0 : _a.value) != null ? _b : "all");
|
|
362
365
|
const defaultActions = [
|
|
@@ -383,7 +386,7 @@ const EntityListDocsTable = ({
|
|
|
383
386
|
EntityListDocsTable.columns = columnFactories;
|
|
384
387
|
EntityListDocsTable.actions = actionFactories;
|
|
385
388
|
|
|
386
|
-
const TechDocsPageWrapper = ({children}) => {
|
|
389
|
+
const TechDocsPageWrapper = ({ children }) => {
|
|
387
390
|
var _a;
|
|
388
391
|
const configApi = useApi(configApiRef);
|
|
389
392
|
const generatedSubtitle = `Documentation available in ${(_a = configApi.getOptionalString("organization.name")) != null ? _a : "Backstage"}`;
|
|
@@ -402,7 +405,7 @@ class TechDocsFilter {
|
|
|
402
405
|
}
|
|
403
406
|
}
|
|
404
407
|
const TechDocsPicker = () => {
|
|
405
|
-
const {updateFilters} = useEntityListProvider();
|
|
408
|
+
const { updateFilters } = useEntityListProvider();
|
|
406
409
|
useEffect(() => {
|
|
407
410
|
updateFilters({
|
|
408
411
|
techdocs: new TechDocsFilter()
|
|
@@ -430,7 +433,7 @@ const DocsCardGrid$1 = ({
|
|
|
430
433
|
entities
|
|
431
434
|
}) => {
|
|
432
435
|
const getRouteToReaderPageFor = useRouteRef(rootDocsRouteRef);
|
|
433
|
-
const
|
|
436
|
+
const config = useApi(configApiRef);
|
|
434
437
|
if (!entities)
|
|
435
438
|
return null;
|
|
436
439
|
return /* @__PURE__ */ React.createElement(ItemCardGrid, {
|
|
@@ -443,9 +446,9 @@ const DocsCardGrid$1 = ({
|
|
|
443
446
|
title: (_a = entity.metadata.title) != null ? _a : entity.metadata.name
|
|
444
447
|
})), /* @__PURE__ */ React.createElement(CardContent, null, entity.metadata.description), /* @__PURE__ */ React.createElement(CardActions, null, /* @__PURE__ */ React.createElement(Button, {
|
|
445
448
|
to: getRouteToReaderPageFor({
|
|
446
|
-
namespace: toLowerMaybe((_b = entity.metadata.namespace) != null ? _b : "default"),
|
|
447
|
-
kind: toLowerMaybe(entity.kind),
|
|
448
|
-
name: toLowerMaybe(entity.metadata.name)
|
|
449
|
+
namespace: toLowerMaybe((_b = entity.metadata.namespace) != null ? _b : "default", config),
|
|
450
|
+
kind: toLowerMaybe(entity.kind, config),
|
|
451
|
+
name: toLowerMaybe(entity.metadata.name, config)
|
|
449
452
|
}),
|
|
450
453
|
color: "primary",
|
|
451
454
|
"data-testid": "read_docs"
|
|
@@ -459,7 +462,7 @@ var DocsCardGrid$2 = /*#__PURE__*/Object.freeze({
|
|
|
459
462
|
});
|
|
460
463
|
|
|
461
464
|
const EntityListDocsGrid = () => {
|
|
462
|
-
const {loading, error, entities} = useEntityListProvider();
|
|
465
|
+
const { loading, error, entities } = useEntityListProvider();
|
|
463
466
|
if (error) {
|
|
464
467
|
return /* @__PURE__ */ React.createElement(WarningPanel, {
|
|
465
468
|
severity: "error",
|
|
@@ -491,7 +494,7 @@ const techdocsPlugin = createPlugin({
|
|
|
491
494
|
discoveryApi: discoveryApiRef,
|
|
492
495
|
identityApi: identityApiRef
|
|
493
496
|
},
|
|
494
|
-
factory: ({configApi, discoveryApi, identityApi}) => new TechDocsStorageClient({
|
|
497
|
+
factory: ({ configApi, discoveryApi, identityApi }) => new TechDocsStorageClient({
|
|
495
498
|
configApi,
|
|
496
499
|
discoveryApi,
|
|
497
500
|
identityApi
|
|
@@ -504,7 +507,7 @@ const techdocsPlugin = createPlugin({
|
|
|
504
507
|
discoveryApi: discoveryApiRef,
|
|
505
508
|
identityApi: identityApiRef
|
|
506
509
|
},
|
|
507
|
-
factory: ({configApi, discoveryApi, identityApi}) => new TechDocsClient({
|
|
510
|
+
factory: ({ configApi, discoveryApi, identityApi }) => new TechDocsClient({
|
|
508
511
|
configApi,
|
|
509
512
|
discoveryApi,
|
|
510
513
|
identityApi
|
|
@@ -577,7 +580,7 @@ const addBaseUrl = ({
|
|
|
577
580
|
const newValue = await techdocsStorageApi.getBaseUrl(elemAttribute, entityId, path);
|
|
578
581
|
if (isSvgNeedingInlining(attributeName, elemAttribute, apiOrigin)) {
|
|
579
582
|
try {
|
|
580
|
-
const svg = await fetch(newValue, {credentials: "include"});
|
|
583
|
+
const svg = await fetch(newValue, { credentials: "include" });
|
|
581
584
|
const svgContent = await svg.text();
|
|
582
585
|
elem.setAttribute(attributeName, `data:image/svg+xml;base64,${btoa(svgContent)}`);
|
|
583
586
|
} catch (e) {
|
|
@@ -748,11 +751,37 @@ const safeLinksHook = (node) => {
|
|
|
748
751
|
}
|
|
749
752
|
return node;
|
|
750
753
|
};
|
|
751
|
-
const
|
|
754
|
+
const filterIframeHook = (allowedIframeHosts) => (node) => {
|
|
755
|
+
if (node.nodeName === "IFRAME") {
|
|
756
|
+
const src = node.getAttribute("src");
|
|
757
|
+
if (!src) {
|
|
758
|
+
node.remove();
|
|
759
|
+
return node;
|
|
760
|
+
}
|
|
761
|
+
try {
|
|
762
|
+
const srcUrl = new URL(src);
|
|
763
|
+
const isMatch = allowedIframeHosts.some((host) => srcUrl.host === host);
|
|
764
|
+
if (!isMatch) {
|
|
765
|
+
node.remove();
|
|
766
|
+
}
|
|
767
|
+
} catch (error) {
|
|
768
|
+
console.warn(`Invalid iframe src, ${error}`);
|
|
769
|
+
node.remove();
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
return node;
|
|
773
|
+
};
|
|
774
|
+
const sanitizeDOM = (config) => {
|
|
775
|
+
const allowedIframeHosts = (config == null ? void 0 : config.getOptionalStringArray("allowedIframeHosts")) || [];
|
|
752
776
|
return (dom) => {
|
|
753
777
|
DOMPurify.addHook("afterSanitizeAttributes", safeLinksHook);
|
|
778
|
+
const addTags = ["link"];
|
|
779
|
+
if (allowedIframeHosts.length > 0) {
|
|
780
|
+
DOMPurify.addHook("beforeSanitizeElements", filterIframeHook(allowedIframeHosts));
|
|
781
|
+
addTags.push("iframe");
|
|
782
|
+
}
|
|
754
783
|
return DOMPurify.sanitize(dom.innerHTML, {
|
|
755
|
-
ADD_TAGS:
|
|
784
|
+
ADD_TAGS: addTags,
|
|
756
785
|
FORBID_TAGS: ["style"],
|
|
757
786
|
WHOLE_DOCUMENT: true,
|
|
758
787
|
RETURN_DOM: true
|
|
@@ -760,7 +789,7 @@ const sanitizeDOM = () => {
|
|
|
760
789
|
};
|
|
761
790
|
};
|
|
762
791
|
|
|
763
|
-
const injectCss = ({css}) => {
|
|
792
|
+
const injectCss = ({ css }) => {
|
|
764
793
|
return (dom) => {
|
|
765
794
|
dom.getElementsByTagName("head")[0].insertAdjacentHTML("beforeend", `<style>${css}</style>`);
|
|
766
795
|
return dom;
|
|
@@ -804,7 +833,7 @@ const TechDocsSearchBar = ({
|
|
|
804
833
|
const {
|
|
805
834
|
term,
|
|
806
835
|
setTerm,
|
|
807
|
-
result: {loading, value: searchVal}
|
|
836
|
+
result: { loading, value: searchVal }
|
|
808
837
|
} = useSearch();
|
|
809
838
|
const [options, setOptions] = useState([]);
|
|
810
839
|
useEffect(() => {
|
|
@@ -827,7 +856,7 @@ const TechDocsSearchBar = ({
|
|
|
827
856
|
};
|
|
828
857
|
const handleSelection = (_, selection) => {
|
|
829
858
|
if (selection == null ? void 0 : selection.document) {
|
|
830
|
-
const {location} = selection.document;
|
|
859
|
+
const { location } = selection.document;
|
|
831
860
|
navigate(location);
|
|
832
861
|
}
|
|
833
862
|
};
|
|
@@ -853,7 +882,7 @@ const TechDocsSearchBar = ({
|
|
|
853
882
|
noOptionsText: "No results found",
|
|
854
883
|
value: null,
|
|
855
884
|
options,
|
|
856
|
-
renderOption: ({document}) => /* @__PURE__ */ React.createElement(DocsResultListItem, {
|
|
885
|
+
renderOption: ({ document }) => /* @__PURE__ */ React.createElement(DocsResultListItem, {
|
|
857
886
|
result: document,
|
|
858
887
|
lineClamp: 3,
|
|
859
888
|
asListItem: false,
|
|
@@ -899,7 +928,6 @@ const TechDocsSearch = (props) => {
|
|
|
899
928
|
}));
|
|
900
929
|
};
|
|
901
930
|
|
|
902
|
-
const LazyLog = React.lazy(() => import('react-lazylog/build/LazyLog'));
|
|
903
931
|
const useDrawerStyles = makeStyles((theme) => createStyles({
|
|
904
932
|
paper: {
|
|
905
933
|
width: "100%",
|
|
@@ -914,6 +942,9 @@ const useDrawerStyles = makeStyles((theme) => createStyles({
|
|
|
914
942
|
root: {
|
|
915
943
|
height: "100%",
|
|
916
944
|
overflow: "hidden"
|
|
945
|
+
},
|
|
946
|
+
logs: {
|
|
947
|
+
background: theme.palette.background.default
|
|
917
948
|
}
|
|
918
949
|
}));
|
|
919
950
|
const TechDocsBuildLogsDrawerContent = ({
|
|
@@ -921,6 +952,7 @@ const TechDocsBuildLogsDrawerContent = ({
|
|
|
921
952
|
onClose
|
|
922
953
|
}) => {
|
|
923
954
|
const classes = useDrawerStyles();
|
|
955
|
+
const logText = buildLog.length === 0 ? "Waiting for logs..." : buildLog.join("\n");
|
|
924
956
|
return /* @__PURE__ */ React.createElement(Grid, {
|
|
925
957
|
container: true,
|
|
926
958
|
direction: "column",
|
|
@@ -941,24 +973,19 @@ const TechDocsBuildLogsDrawerContent = ({
|
|
|
941
973
|
title: "Close the drawer",
|
|
942
974
|
onClick: onClose,
|
|
943
975
|
color: "inherit"
|
|
944
|
-
}, /* @__PURE__ */ React.createElement(Close, null))), /* @__PURE__ */ React.createElement(
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
extraLines: 1,
|
|
949
|
-
follow: true,
|
|
950
|
-
selectableLines: true,
|
|
951
|
-
enableSearch: true
|
|
952
|
-
})));
|
|
976
|
+
}, /* @__PURE__ */ React.createElement(Close, null))), /* @__PURE__ */ React.createElement(LogViewer, {
|
|
977
|
+
text: logText,
|
|
978
|
+
classes: { root: classes.logs }
|
|
979
|
+
}));
|
|
953
980
|
};
|
|
954
|
-
const TechDocsBuildLogs = ({buildLog}) => {
|
|
981
|
+
const TechDocsBuildLogs = ({ buildLog }) => {
|
|
955
982
|
const classes = useDrawerStyles();
|
|
956
983
|
const [open, setOpen] = useState(false);
|
|
957
984
|
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Button$1, {
|
|
958
985
|
color: "inherit",
|
|
959
986
|
onClick: () => setOpen(true)
|
|
960
987
|
}, "Show Build Logs"), /* @__PURE__ */ React.createElement(Drawer, {
|
|
961
|
-
classes: {paper: classes.paper},
|
|
988
|
+
classes: { paper: classes.paper },
|
|
962
989
|
anchor: "right",
|
|
963
990
|
open,
|
|
964
991
|
onClose: () => setOpen(false)
|
|
@@ -968,7 +995,7 @@ const TechDocsBuildLogs = ({buildLog}) => {
|
|
|
968
995
|
})));
|
|
969
996
|
};
|
|
970
997
|
|
|
971
|
-
const TechDocsNotFound = ({errorMessage}) => {
|
|
998
|
+
const TechDocsNotFound = ({ errorMessage }) => {
|
|
972
999
|
const techdocsBuilder = useApi(configApiRef).getOptionalString("techdocs.builder");
|
|
973
1000
|
let additionalInfo = "";
|
|
974
1001
|
if (techdocsBuilder !== "local") {
|
|
@@ -1039,7 +1066,7 @@ const TechDocsStateIndicator = () => {
|
|
|
1039
1066
|
action: /* @__PURE__ */ React.createElement(TechDocsBuildLogs, {
|
|
1040
1067
|
buildLog
|
|
1041
1068
|
}),
|
|
1042
|
-
classes: {message: classes.message}
|
|
1069
|
+
classes: { message: classes.message }
|
|
1043
1070
|
}, "Building a newer version of this documentation failed.", " ", syncErrorMessage);
|
|
1044
1071
|
}
|
|
1045
1072
|
if (state === "CONTENT_NOT_FOUND") {
|
|
@@ -1049,7 +1076,7 @@ const TechDocsStateIndicator = () => {
|
|
|
1049
1076
|
action: /* @__PURE__ */ React.createElement(TechDocsBuildLogs, {
|
|
1050
1077
|
buildLog
|
|
1051
1078
|
}),
|
|
1052
|
-
classes: {message: classes.message}
|
|
1079
|
+
classes: { message: classes.message }
|
|
1053
1080
|
}, "Building a newer version of this documentation failed.", " ", syncErrorMessage), /* @__PURE__ */ React.createElement(TechDocsNotFound, {
|
|
1054
1081
|
errorMessage: contentErrorMessage
|
|
1055
1082
|
}));
|
|
@@ -1089,7 +1116,7 @@ function calculateDisplayState({
|
|
|
1089
1116
|
return "CONTENT_FRESH";
|
|
1090
1117
|
}
|
|
1091
1118
|
function reducer(oldState, action) {
|
|
1092
|
-
const newState = {...oldState};
|
|
1119
|
+
const newState = { ...oldState };
|
|
1093
1120
|
switch (action.type) {
|
|
1094
1121
|
case "sync":
|
|
1095
1122
|
if (action.state === "CHECKING") {
|
|
@@ -1131,14 +1158,14 @@ function useReaderState(kind, namespace, name, path) {
|
|
|
1131
1158
|
buildLog: []
|
|
1132
1159
|
});
|
|
1133
1160
|
const techdocsStorageApi = useApi(techdocsStorageApiRef);
|
|
1134
|
-
const {retry: contentReload} = useAsyncRetry(async () => {
|
|
1135
|
-
dispatch({type: "contentLoading"});
|
|
1161
|
+
const { retry: contentReload } = useAsyncRetry(async () => {
|
|
1162
|
+
dispatch({ type: "contentLoading" });
|
|
1136
1163
|
try {
|
|
1137
|
-
const entityDocs = await techdocsStorageApi.getEntityDocs({kind, namespace, name}, path);
|
|
1138
|
-
dispatch({type: "content", content: entityDocs, path});
|
|
1164
|
+
const entityDocs = await techdocsStorageApi.getEntityDocs({ kind, namespace, name }, path);
|
|
1165
|
+
dispatch({ type: "content", content: entityDocs, path });
|
|
1139
1166
|
return entityDocs;
|
|
1140
1167
|
} catch (e) {
|
|
1141
|
-
dispatch({type: "content", contentError: e, path});
|
|
1168
|
+
dispatch({ type: "content", contentError: e, path });
|
|
1142
1169
|
}
|
|
1143
1170
|
return void 0;
|
|
1144
1171
|
}, [techdocsStorageApi, kind, namespace, name, path]);
|
|
@@ -1147,11 +1174,11 @@ function useReaderState(kind, namespace, name, path) {
|
|
|
1147
1174
|
reload: () => {
|
|
1148
1175
|
}
|
|
1149
1176
|
});
|
|
1150
|
-
contentRef.current = {content: state.content, reload: contentReload};
|
|
1177
|
+
contentRef.current = { content: state.content, reload: contentReload };
|
|
1151
1178
|
useAsync(async () => {
|
|
1152
|
-
dispatch({type: "sync", state: "CHECKING"});
|
|
1179
|
+
dispatch({ type: "sync", state: "CHECKING" });
|
|
1153
1180
|
const buildingTimeout = setTimeout(() => {
|
|
1154
|
-
dispatch({type: "sync", state: "BUILDING"});
|
|
1181
|
+
dispatch({ type: "sync", state: "BUILDING" });
|
|
1155
1182
|
}, 1e3);
|
|
1156
1183
|
try {
|
|
1157
1184
|
const result = await techdocsStorageApi.syncEntityDocs({
|
|
@@ -1159,19 +1186,19 @@ function useReaderState(kind, namespace, name, path) {
|
|
|
1159
1186
|
namespace,
|
|
1160
1187
|
name
|
|
1161
1188
|
}, (log) => {
|
|
1162
|
-
dispatch({type: "buildLog", log});
|
|
1189
|
+
dispatch({ type: "buildLog", log });
|
|
1163
1190
|
});
|
|
1164
1191
|
switch (result) {
|
|
1165
1192
|
case "updated":
|
|
1166
1193
|
if (!contentRef.current.content) {
|
|
1167
1194
|
contentRef.current.reload();
|
|
1168
|
-
dispatch({type: "sync", state: "BUILD_READY_RELOAD"});
|
|
1195
|
+
dispatch({ type: "sync", state: "BUILD_READY_RELOAD" });
|
|
1169
1196
|
} else {
|
|
1170
|
-
dispatch({type: "sync", state: "BUILD_READY"});
|
|
1197
|
+
dispatch({ type: "sync", state: "BUILD_READY" });
|
|
1171
1198
|
}
|
|
1172
1199
|
break;
|
|
1173
1200
|
case "cached":
|
|
1174
|
-
dispatch({type: "sync", state: "UP_TO_DATE"});
|
|
1201
|
+
dispatch({ type: "sync", state: "UP_TO_DATE" });
|
|
1175
1202
|
break;
|
|
1176
1203
|
default:
|
|
1177
1204
|
dispatch({
|
|
@@ -1182,7 +1209,7 @@ function useReaderState(kind, namespace, name, path) {
|
|
|
1182
1209
|
break;
|
|
1183
1210
|
}
|
|
1184
1211
|
} catch (e) {
|
|
1185
|
-
dispatch({type: "sync", state: "ERROR", syncError: e});
|
|
1212
|
+
dispatch({ type: "sync", state: "ERROR", syncError: e });
|
|
1186
1213
|
} finally {
|
|
1187
1214
|
clearTimeout(buildingTimeout);
|
|
1188
1215
|
}
|
|
@@ -1219,8 +1246,8 @@ const TechDocsReaderProvider = ({
|
|
|
1219
1246
|
children,
|
|
1220
1247
|
entityRef
|
|
1221
1248
|
}) => {
|
|
1222
|
-
const {"*": path} = useParams();
|
|
1223
|
-
const {kind, namespace, name} = entityRef;
|
|
1249
|
+
const { "*": path } = useParams();
|
|
1250
|
+
const { kind, namespace, name } = entityRef;
|
|
1224
1251
|
const value = useReaderState(kind, namespace, name, path);
|
|
1225
1252
|
return /* @__PURE__ */ React.createElement(TechDocsReaderContext.Provider, {
|
|
1226
1253
|
value
|
|
@@ -1237,8 +1264,9 @@ const useTechDocsReaderDom = (entityRef) => {
|
|
|
1237
1264
|
const theme = useTheme();
|
|
1238
1265
|
const techdocsStorageApi = useApi(techdocsStorageApiRef);
|
|
1239
1266
|
const scmIntegrationsApi = useApi(scmIntegrationsApiRef);
|
|
1240
|
-
const
|
|
1241
|
-
const {
|
|
1267
|
+
const techdocsSanitizer = useApi(configApiRef);
|
|
1268
|
+
const { namespace = "", kind = "", name = "" } = entityRef;
|
|
1269
|
+
const { state, path, content: rawPage } = useTechDocsReader();
|
|
1242
1270
|
const [sidebars, setSidebars] = useState();
|
|
1243
1271
|
const [dom, setDom] = useState(null);
|
|
1244
1272
|
const updateSidebarPosition = useCallback(() => {
|
|
@@ -1260,7 +1288,7 @@ const useTechDocsReaderDom = (entityRef) => {
|
|
|
1260
1288
|
};
|
|
1261
1289
|
}, [updateSidebarPosition, state]);
|
|
1262
1290
|
const preRender = useCallback((rawContent, contentPath) => transform(rawContent, [
|
|
1263
|
-
sanitizeDOM(),
|
|
1291
|
+
sanitizeDOM(techdocsSanitizer.getOptionalConfig("techdocs.sanitizer")),
|
|
1264
1292
|
addBaseUrl({
|
|
1265
1293
|
techdocsStorageApi,
|
|
1266
1294
|
entityId: {
|
|
@@ -1395,6 +1423,7 @@ const useTechDocsReaderDom = (entityRef) => {
|
|
|
1395
1423
|
namespace,
|
|
1396
1424
|
scmIntegrationsApi,
|
|
1397
1425
|
techdocsStorageApi,
|
|
1426
|
+
techdocsSanitizer,
|
|
1398
1427
|
theme.palette.action.disabledBackground,
|
|
1399
1428
|
theme.palette.background.default,
|
|
1400
1429
|
theme.palette.background.paper,
|
|
@@ -1444,7 +1473,7 @@ const useTechDocsReaderDom = (entityRef) => {
|
|
|
1444
1473
|
if (!shouldReplaceContent) {
|
|
1445
1474
|
return;
|
|
1446
1475
|
}
|
|
1447
|
-
window.scroll({top: 0});
|
|
1476
|
+
window.scroll({ top: 0 });
|
|
1448
1477
|
const postTransformedDomElement = await postRender(preTransformedDomElement);
|
|
1449
1478
|
setDom(postTransformedDomElement);
|
|
1450
1479
|
});
|
|
@@ -1472,7 +1501,7 @@ const TheReader = ({
|
|
|
1472
1501
|
if (!dom || !shadowDomRef.current)
|
|
1473
1502
|
return;
|
|
1474
1503
|
const shadowDiv = shadowDomRef.current;
|
|
1475
|
-
const shadowRoot = shadowDiv.shadowRoot || shadowDiv.attachShadow({mode: "open"});
|
|
1504
|
+
const shadowRoot = shadowDiv.shadowRoot || shadowDiv.attachShadow({ mode: "open" });
|
|
1476
1505
|
Array.from(shadowRoot.children).forEach((child) => shadowRoot.removeChild(child));
|
|
1477
1506
|
shadowRoot.appendChild(dom);
|
|
1478
1507
|
onReadyRef.current();
|
|
@@ -1505,9 +1534,9 @@ const TechDocsPageHeader = ({
|
|
|
1505
1534
|
entityMetadata,
|
|
1506
1535
|
techDocsMetadata
|
|
1507
1536
|
}) => {
|
|
1508
|
-
const {name} = entityRef;
|
|
1509
|
-
const {site_name: siteName, site_description: siteDescription} = techDocsMetadata || {};
|
|
1510
|
-
const {locationMetadata, spec} = entityMetadata || {};
|
|
1537
|
+
const { name } = entityRef;
|
|
1538
|
+
const { site_name: siteName, site_description: siteDescription } = techDocsMetadata || {};
|
|
1539
|
+
const { locationMetadata, spec } = entityMetadata || {};
|
|
1511
1540
|
const lifecycle = spec == null ? void 0 : spec.lifecycle;
|
|
1512
1541
|
const ownedByRelations = entityMetadata ? getEntityRelations(entityMetadata, RELATION_OWNED_BY) : [];
|
|
1513
1542
|
const docsRootLink = useRouteRef(rootRouteRef)();
|
|
@@ -1535,7 +1564,7 @@ const TechDocsPageHeader = ({
|
|
|
1535
1564
|
target: "_blank",
|
|
1536
1565
|
rel: "noopener noreferrer"
|
|
1537
1566
|
}, /* @__PURE__ */ React.createElement(CodeIcon, {
|
|
1538
|
-
style: {marginTop: "-25px", fill: "#fff"}
|
|
1567
|
+
style: { marginTop: "-25px", fill: "#fff" }
|
|
1539
1568
|
}))
|
|
1540
1569
|
}) : null);
|
|
1541
1570
|
return /* @__PURE__ */ React.createElement(Header, {
|
|
@@ -1549,16 +1578,16 @@ const TechDocsPageHeader = ({
|
|
|
1549
1578
|
|
|
1550
1579
|
const LegacyTechDocsPage = () => {
|
|
1551
1580
|
const [documentReady, setDocumentReady] = useState(false);
|
|
1552
|
-
const {namespace, kind, name} = useParams();
|
|
1581
|
+
const { namespace, kind, name } = useParams();
|
|
1553
1582
|
const techdocsApi = useApi(techdocsApiRef);
|
|
1554
|
-
const {value: techdocsMetadataValue} = useAsync(() => {
|
|
1583
|
+
const { value: techdocsMetadataValue } = useAsync(() => {
|
|
1555
1584
|
if (documentReady) {
|
|
1556
|
-
return techdocsApi.getTechDocsMetadata({kind, namespace, name});
|
|
1585
|
+
return techdocsApi.getTechDocsMetadata({ kind, namespace, name });
|
|
1557
1586
|
}
|
|
1558
1587
|
return Promise.resolve(void 0);
|
|
1559
1588
|
}, [kind, namespace, name, techdocsApi, documentReady]);
|
|
1560
|
-
const {value: entityMetadataValue, error: entityMetadataError} = useAsync(() => {
|
|
1561
|
-
return techdocsApi.getEntityMetadata({kind, namespace, name});
|
|
1589
|
+
const { value: entityMetadataValue, error: entityMetadataError } = useAsync(() => {
|
|
1590
|
+
return techdocsApi.getEntityMetadata({ kind, namespace, name });
|
|
1562
1591
|
}, [kind, namespace, name, techdocsApi]);
|
|
1563
1592
|
const onReady = useCallback(() => {
|
|
1564
1593
|
setDocumentReady(true);
|
|
@@ -1590,19 +1619,19 @@ const LegacyTechDocsPage = () => {
|
|
|
1590
1619
|
})));
|
|
1591
1620
|
};
|
|
1592
1621
|
|
|
1593
|
-
const TechDocsPage = ({children}) => {
|
|
1622
|
+
const TechDocsPage = ({ children }) => {
|
|
1594
1623
|
const outlet = useOutlet();
|
|
1595
1624
|
const [documentReady, setDocumentReady] = useState(false);
|
|
1596
|
-
const {namespace, kind, name} = useParams();
|
|
1625
|
+
const { namespace, kind, name } = useParams();
|
|
1597
1626
|
const techdocsApi = useApi(techdocsApiRef);
|
|
1598
|
-
const {value: techdocsMetadataValue} = useAsync(() => {
|
|
1627
|
+
const { value: techdocsMetadataValue } = useAsync(() => {
|
|
1599
1628
|
if (documentReady) {
|
|
1600
|
-
return techdocsApi.getTechDocsMetadata({kind, namespace, name});
|
|
1629
|
+
return techdocsApi.getTechDocsMetadata({ kind, namespace, name });
|
|
1601
1630
|
}
|
|
1602
1631
|
return Promise.resolve(void 0);
|
|
1603
1632
|
}, [kind, namespace, name, techdocsApi, documentReady]);
|
|
1604
|
-
const {value: entityMetadataValue, error: entityMetadataError} = useAsync(() => {
|
|
1605
|
-
return techdocsApi.getEntityMetadata({kind, namespace, name});
|
|
1633
|
+
const { value: entityMetadataValue, error: entityMetadataError } = useAsync(() => {
|
|
1634
|
+
return techdocsApi.getEntityMetadata({ kind, namespace, name });
|
|
1606
1635
|
}, [kind, namespace, name, techdocsApi]);
|
|
1607
1636
|
const onReady = useCallback(() => {
|
|
1608
1637
|
setDocumentReady(true);
|
|
@@ -1619,7 +1648,7 @@ const TechDocsPage = ({children}) => {
|
|
|
1619
1648
|
}, children instanceof Function ? children({
|
|
1620
1649
|
techdocsMetadataValue,
|
|
1621
1650
|
entityMetadataValue,
|
|
1622
|
-
entityRef: {kind, namespace, name},
|
|
1651
|
+
entityRef: { kind, namespace, name },
|
|
1623
1652
|
onReady
|
|
1624
1653
|
}) : children);
|
|
1625
1654
|
};
|
|
@@ -1645,7 +1674,7 @@ const CustomPanel = ({
|
|
|
1645
1674
|
}
|
|
1646
1675
|
});
|
|
1647
1676
|
const classes = useStyles();
|
|
1648
|
-
const {value: user} = useOwnUser();
|
|
1677
|
+
const { value: user } = useOwnUser();
|
|
1649
1678
|
const Panel = panels[config.panelType];
|
|
1650
1679
|
const shownEntities = entities.filter((entity) => {
|
|
1651
1680
|
if (config.filterPredicate === "ownedByUser") {
|
|
@@ -1710,7 +1739,7 @@ const TechDocsCustomHome = ({
|
|
|
1710
1739
|
return /* @__PURE__ */ React.createElement(TechDocsPageWrapper, null, /* @__PURE__ */ React.createElement(HeaderTabs, {
|
|
1711
1740
|
selectedIndex: selectedTab,
|
|
1712
1741
|
onChange: (index) => setSelectedTab(index),
|
|
1713
|
-
tabs: tabsConfig.map(({label}, index) => ({
|
|
1742
|
+
tabs: tabsConfig.map(({ label }, index) => ({
|
|
1714
1743
|
id: index.toString(),
|
|
1715
1744
|
label
|
|
1716
1745
|
}))
|
|
@@ -1769,14 +1798,15 @@ var TechDocsIndexPage$1 = /*#__PURE__*/Object.freeze({
|
|
|
1769
1798
|
TechDocsIndexPage: TechDocsIndexPage
|
|
1770
1799
|
});
|
|
1771
1800
|
|
|
1772
|
-
const EntityPageDocs = ({entity}) => {
|
|
1801
|
+
const EntityPageDocs = ({ entity }) => {
|
|
1773
1802
|
var _a;
|
|
1803
|
+
const config = useApi(configApiRef);
|
|
1774
1804
|
return /* @__PURE__ */ React.createElement(Reader, {
|
|
1775
1805
|
withSearch: false,
|
|
1776
1806
|
entityRef: {
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
name: entity.metadata.name
|
|
1807
|
+
namespace: toLowerMaybe((_a = entity.metadata.namespace) != null ? _a : "default", config),
|
|
1808
|
+
kind: toLowerMaybe(entity.kind, config),
|
|
1809
|
+
name: toLowerMaybe(entity.metadata.name, config)
|
|
1780
1810
|
}
|
|
1781
1811
|
});
|
|
1782
1812
|
};
|
|
@@ -1797,7 +1827,7 @@ const Router = () => {
|
|
|
1797
1827
|
};
|
|
1798
1828
|
const EmbeddedDocsRouter = (_props) => {
|
|
1799
1829
|
var _a;
|
|
1800
|
-
const {entity} = useEntity();
|
|
1830
|
+
const { entity } = useEntity();
|
|
1801
1831
|
const projectId = (_a = entity.metadata.annotations) == null ? void 0 : _a[TECHDOCS_ANNOTATION];
|
|
1802
1832
|
if (!projectId) {
|
|
1803
1833
|
return /* @__PURE__ */ React.createElement(MissingAnnotationEmptyState, {
|