@nospt/backstage-plugin-apigee 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +98 -0
  2. package/config.d.ts +30 -0
  3. package/dist/components/ApiCard.esm.js +105 -0
  4. package/dist/components/ApiCard.esm.js.map +1 -0
  5. package/dist/components/ApiCard.module.css.esm.js +8 -0
  6. package/dist/components/ApiCard.module.css.esm.js.map +1 -0
  7. package/dist/components/ApiCardGrid.esm.js +14 -0
  8. package/dist/components/ApiCardGrid.esm.js.map +1 -0
  9. package/dist/components/ApiDetailsDrawer.esm.js +709 -0
  10. package/dist/components/ApiDetailsDrawer.esm.js.map +1 -0
  11. package/dist/components/ApiDetailsDrawer.module.css.esm.js +8 -0
  12. package/dist/components/ApiDetailsDrawer.module.css.esm.js.map +1 -0
  13. package/dist/components/ApiTable.esm.js +184 -0
  14. package/dist/components/ApiTable.esm.js.map +1 -0
  15. package/dist/components/ApiTable.module.css.esm.js +8 -0
  16. package/dist/components/ApiTable.module.css.esm.js.map +1 -0
  17. package/dist/components/ApigeeApiCatalogPage.esm.js +245 -0
  18. package/dist/components/ApigeeApiCatalogPage.esm.js.map +1 -0
  19. package/dist/components/ApigeeHomePage.esm.js +190 -0
  20. package/dist/components/ApigeeHomePage.esm.js.map +1 -0
  21. package/dist/components/ApigeeHomePage.module.css.esm.js +8 -0
  22. package/dist/components/ApigeeHomePage.module.css.esm.js.map +1 -0
  23. package/dist/components/ApigeeNavContent.esm.js +28 -0
  24. package/dist/components/ApigeeNavContent.esm.js.map +1 -0
  25. package/dist/components/CountCards.esm.js +120 -0
  26. package/dist/components/CountCards.esm.js.map +1 -0
  27. package/dist/components/CountCards.module.css.esm.js +8 -0
  28. package/dist/components/CountCards.module.css.esm.js.map +1 -0
  29. package/dist/components/FilterToolbar.esm.js +131 -0
  30. package/dist/components/FilterToolbar.esm.js.map +1 -0
  31. package/dist/components/FilterToolbar.module.css.esm.js +8 -0
  32. package/dist/components/FilterToolbar.module.css.esm.js.map +1 -0
  33. package/dist/components/SearchBar.esm.js +20 -0
  34. package/dist/components/SearchBar.esm.js.map +1 -0
  35. package/dist/components/apiCardViewModel.esm.js +57 -0
  36. package/dist/components/apiCardViewModel.esm.js.map +1 -0
  37. package/dist/components/apiDetailViewModel.esm.js +98 -0
  38. package/dist/components/apiDetailViewModel.esm.js.map +1 -0
  39. package/dist/components/apiOperations.esm.js +196 -0
  40. package/dist/components/apiOperations.esm.js.map +1 -0
  41. package/dist/components/apigeeFacetFilters.esm.js +62 -0
  42. package/dist/components/apigeeFacetFilters.esm.js.map +1 -0
  43. package/dist/components/apigeeOrgFilter.esm.js +11 -0
  44. package/dist/components/apigeeOrgFilter.esm.js.map +1 -0
  45. package/dist/components/apigeeSearchParams.esm.js +55 -0
  46. package/dist/components/apigeeSearchParams.esm.js.map +1 -0
  47. package/dist/components/brandIcons.esm.js +36 -0
  48. package/dist/components/brandIcons.esm.js.map +1 -0
  49. package/dist/components/codeHighlight.esm.js +71 -0
  50. package/dist/components/codeHighlight.esm.js.map +1 -0
  51. package/dist/components/codeSnippet.esm.js +128 -0
  52. package/dist/components/codeSnippet.esm.js.map +1 -0
  53. package/dist/components/facetModel.esm.js +86 -0
  54. package/dist/components/facetModel.esm.js.map +1 -0
  55. package/dist/components/grouping.esm.js +6 -0
  56. package/dist/components/grouping.esm.js.map +1 -0
  57. package/dist/components/states.esm.js +65 -0
  58. package/dist/components/states.esm.js.map +1 -0
  59. package/dist/components/useApigeeFilters.esm.js +191 -0
  60. package/dist/components/useApigeeFilters.esm.js.map +1 -0
  61. package/dist/components/useDebouncedValue.esm.js +13 -0
  62. package/dist/components/useDebouncedValue.esm.js.map +1 -0
  63. package/dist/constants.esm.js +9 -0
  64. package/dist/constants.esm.js.map +1 -0
  65. package/dist/index.d.ts +203 -0
  66. package/dist/index.esm.js +2 -0
  67. package/dist/index.esm.js.map +1 -0
  68. package/dist/node_modules_dist/style-inject/dist/style-inject.es.esm.js +29 -0
  69. package/dist/node_modules_dist/style-inject/dist/style-inject.es.esm.js.map +1 -0
  70. package/dist/plugin.esm.js +59 -0
  71. package/dist/plugin.esm.js.map +1 -0
  72. package/dist/routes.esm.js +8 -0
  73. package/dist/routes.esm.js.map +1 -0
  74. package/package.json +95 -0
@@ -0,0 +1,709 @@
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import { Link } from '@backstage/core-components';
3
+ import { useApi } from '@backstage/core-plugin-api';
4
+ import { useRouteRef } from '@backstage/frontend-plugin-api';
5
+ import { parseEntityRef } from '@backstage/catalog-model';
6
+ import { catalogApiRef, entityRouteRef, EntityRefLink } from '@backstage/plugin-catalog-react';
7
+ import { Text, ButtonIcon, Button, Tabs, TabList, Tab, TabPanel } from '@backstage/ui';
8
+ import { RiCheckLine, RiShareLine, RiCloseLine, RiGithubFill, RiDownloadLine, RiFileCopyLine, RiExternalLinkLine, RiArrowDownSLine, RiArrowRightSLine } from '@remixicon/react';
9
+ import { useState, useEffect, useMemo } from 'react';
10
+ import { useLocation, useHref, useNavigate } from 'react-router-dom';
11
+ import { LifecycleBadge } from './ApiCard.esm.js';
12
+ import { BackstageIcon, ApigeeIcon } from './brandIcons.esm.js';
13
+ import { toApiDetailViewModel, specFileName } from './apiDetailViewModel.esm.js';
14
+ import { parseOperations } from './apiOperations.esm.js';
15
+ import { generateSnippet, SNIPPET_LANGUAGES } from './codeSnippet.esm.js';
16
+ import { HighlightedCode } from './codeHighlight.esm.js';
17
+ import styles from './ApiDetailsDrawer.module.css.esm.js';
18
+
19
+ function MetaRow({
20
+ label,
21
+ value,
22
+ children
23
+ }) {
24
+ return /* @__PURE__ */ jsxs("div", { className: styles.metaRow, children: [
25
+ /* @__PURE__ */ jsx("dt", { className: styles.metaLabel, children: label }),
26
+ /* @__PURE__ */ jsx("dd", { className: styles.metaValue, children: children ?? value })
27
+ ] });
28
+ }
29
+ function OwnerValue({ vm }) {
30
+ if (vm.ownerRef) {
31
+ try {
32
+ parseEntityRef(vm.ownerRef, {
33
+ defaultKind: "group",
34
+ defaultNamespace: "default"
35
+ });
36
+ return /* @__PURE__ */ jsx(EntityRefLink, { entityRef: vm.ownerRef, defaultKind: "group" });
37
+ } catch {
38
+ }
39
+ }
40
+ return /* @__PURE__ */ jsx(Fragment, { children: vm.ownerLabel });
41
+ }
42
+ const METHOD_CLASS = {
43
+ GET: styles.methodGet,
44
+ POST: styles.methodPost,
45
+ PUT: styles.methodPut,
46
+ PATCH: styles.methodPatch,
47
+ DELETE: styles.methodDelete
48
+ };
49
+ function MethodBadge({ method }) {
50
+ return /* @__PURE__ */ jsx(
51
+ "span",
52
+ {
53
+ className: `${styles.methodBadge} ${METHOD_CLASS[method] ?? styles.methodOther}`,
54
+ children: method
55
+ }
56
+ );
57
+ }
58
+ function CopyButton({ text, label }) {
59
+ const [copied, setCopied] = useState(false);
60
+ const handleCopy = async () => {
61
+ try {
62
+ await navigator.clipboard.writeText(text);
63
+ setCopied(true);
64
+ setTimeout(() => setCopied(false), 2e3);
65
+ } catch {
66
+ }
67
+ };
68
+ return /* @__PURE__ */ jsx(
69
+ ButtonIcon,
70
+ {
71
+ icon: copied ? /* @__PURE__ */ jsx(RiCheckLine, {}) : /* @__PURE__ */ jsx(RiFileCopyLine, {}),
72
+ variant: "tertiary",
73
+ onPress: handleCopy,
74
+ "aria-label": copied ? "Copied" : label
75
+ }
76
+ );
77
+ }
78
+ function ParamGroup({
79
+ title,
80
+ params
81
+ }) {
82
+ if (params.length === 0) {
83
+ return null;
84
+ }
85
+ return /* @__PURE__ */ jsxs("section", { className: styles.operationSection, children: [
86
+ /* @__PURE__ */ jsx(
87
+ Text,
88
+ {
89
+ variant: "body-small",
90
+ weight: "bold",
91
+ as: "h4",
92
+ className: styles.operationSectionTitle,
93
+ children: title
94
+ }
95
+ ),
96
+ /* @__PURE__ */ jsx("ul", { className: styles.paramList, children: params.map((p) => /* @__PURE__ */ jsxs("li", { className: styles.paramItem, children: [
97
+ /* @__PURE__ */ jsx("span", { className: styles.paramName, children: p.name }),
98
+ p.required ? /* @__PURE__ */ jsx("span", { className: styles.paramRequired, children: "required" }) : null,
99
+ p.description ? /* @__PURE__ */ jsx("span", { className: styles.paramDescription, children: p.description }) : null
100
+ ] }, p.name)) })
101
+ ] });
102
+ }
103
+ function BodyBlock({ body }) {
104
+ const hasSchema = !!body.schema;
105
+ const hasExample = !!body.example;
106
+ const [view, setView] = useState();
107
+ const pick = (next) => setView((prev) => prev === next ? void 0 : next);
108
+ const text = view === "example" ? body.example : body.schema;
109
+ return /* @__PURE__ */ jsxs("div", { className: styles.bodyBlock, children: [
110
+ /* @__PURE__ */ jsxs("div", { className: styles.bodyBlockBar, children: [
111
+ /* @__PURE__ */ jsxs("div", { className: styles.bodyToggleGroup, children: [
112
+ hasExample ? /* @__PURE__ */ jsx(
113
+ "button",
114
+ {
115
+ type: "button",
116
+ className: `${styles.bodyToggle} ${view === "example" ? styles.bodyToggleActive : ""}`,
117
+ "aria-pressed": view === "example",
118
+ onClick: () => pick("example"),
119
+ children: "Example"
120
+ }
121
+ ) : null,
122
+ hasSchema ? /* @__PURE__ */ jsx(
123
+ "button",
124
+ {
125
+ type: "button",
126
+ className: `${styles.bodyToggle} ${view === "schema" ? styles.bodyToggleActive : ""}`,
127
+ "aria-pressed": view === "schema",
128
+ onClick: () => pick("schema"),
129
+ children: "Schema"
130
+ }
131
+ ) : null
132
+ ] }),
133
+ view && text ? /* @__PURE__ */ jsx(
134
+ CopyButton,
135
+ {
136
+ text,
137
+ label: view === "example" ? "Copy example" : "Copy schema"
138
+ }
139
+ ) : null
140
+ ] }),
141
+ view && text ? /* @__PURE__ */ jsx("pre", { className: styles.bodyCode, children: /* @__PURE__ */ jsx("code", { children: text }) }) : null
142
+ ] });
143
+ }
144
+ function CodeSnippetBlock({ op }) {
145
+ const [language, setLanguage] = useState("curl");
146
+ const snippet = useMemo(() => generateSnippet(op, language), [op, language]);
147
+ return /* @__PURE__ */ jsxs("div", { className: styles.snippetBlock, children: [
148
+ /* @__PURE__ */ jsxs("div", { className: styles.snippetHeaderRow, children: [
149
+ /* @__PURE__ */ jsx(
150
+ Text,
151
+ {
152
+ variant: "body-small",
153
+ weight: "bold",
154
+ as: "h4",
155
+ className: styles.operationSectionTitle,
156
+ children: "Code snippet"
157
+ }
158
+ ),
159
+ /* @__PURE__ */ jsxs("div", { className: styles.snippetActions, children: [
160
+ /* @__PURE__ */ jsx(
161
+ "div",
162
+ {
163
+ className: styles.snippetLangGroup,
164
+ role: "group",
165
+ "aria-label": "Snippet language",
166
+ children: SNIPPET_LANGUAGES.map((lang) => /* @__PURE__ */ jsx(
167
+ "button",
168
+ {
169
+ type: "button",
170
+ className: `${styles.bodyToggle} ${language === lang.id ? styles.bodyToggleActive : ""}`,
171
+ "aria-pressed": language === lang.id,
172
+ onClick: () => setLanguage(lang.id),
173
+ children: lang.label
174
+ },
175
+ lang.id
176
+ ))
177
+ }
178
+ ),
179
+ /* @__PURE__ */ jsx(CopyButton, { text: snippet, label: "Copy code snippet" })
180
+ ] })
181
+ ] }),
182
+ /* @__PURE__ */ jsx(HighlightedCode, { code: snippet })
183
+ ] });
184
+ }
185
+ function OperationDetail({ op }) {
186
+ const pathParams = op.parameters.filter((p) => p.in === "path");
187
+ const headerParams = op.parameters.filter((p) => p.in === "header");
188
+ const successResponses = op.responses.filter((r) => r.status.startsWith("2"));
189
+ const errorResponses = op.responses.filter((r) => !r.status.startsWith("2"));
190
+ const responseBody = successResponses.filter(
191
+ (r) => r.contentTypes.length > 0 || r.body
192
+ );
193
+ return /* @__PURE__ */ jsx("div", { className: styles.operationDetail, children: /* @__PURE__ */ jsxs("div", { className: styles.operationDetailColumns, children: [
194
+ /* @__PURE__ */ jsxs("div", { className: styles.operationDetailMain, children: [
195
+ /* @__PURE__ */ jsxs("dl", { className: styles.operationMeta, children: [
196
+ /* @__PURE__ */ jsxs("div", { className: styles.operationRow, children: [
197
+ /* @__PURE__ */ jsx("dt", { className: styles.operationLabel, children: "Operation ID" }),
198
+ /* @__PURE__ */ jsx("dd", { className: styles.operationValue, children: op.operationId ?? "\u2014" })
199
+ ] }),
200
+ /* @__PURE__ */ jsxs("div", { className: styles.operationRow, children: [
201
+ /* @__PURE__ */ jsx("dt", { className: styles.operationLabel, children: "Path" }),
202
+ /* @__PURE__ */ jsx("dd", { className: styles.operationValue, children: /* @__PURE__ */ jsx("code", { className: styles.operationPath, children: op.path }) })
203
+ ] }),
204
+ /* @__PURE__ */ jsxs("div", { className: styles.operationRow, children: [
205
+ /* @__PURE__ */ jsx("dt", { className: styles.operationLabel, children: "Method" }),
206
+ /* @__PURE__ */ jsx("dd", { className: styles.operationValue, children: /* @__PURE__ */ jsx(MethodBadge, { method: op.method }) })
207
+ ] }),
208
+ /* @__PURE__ */ jsxs("div", { className: styles.operationRow, children: [
209
+ /* @__PURE__ */ jsx("dt", { className: styles.operationLabel, children: "Description" }),
210
+ /* @__PURE__ */ jsx("dd", { className: styles.operationValue, children: op.summary ?? "\u2014" })
211
+ ] })
212
+ ] }),
213
+ /* @__PURE__ */ jsx(ParamGroup, { title: "Path parameters", params: pathParams }),
214
+ /* @__PURE__ */ jsx(ParamGroup, { title: "Header parameters", params: headerParams }),
215
+ /* @__PURE__ */ jsxs("section", { className: styles.operationSection, children: [
216
+ /* @__PURE__ */ jsx(
217
+ Text,
218
+ {
219
+ variant: "body-small",
220
+ weight: "bold",
221
+ as: "h4",
222
+ className: styles.operationSectionTitle,
223
+ children: "Request body"
224
+ }
225
+ ),
226
+ (() => {
227
+ if (op.requestBody && op.requestBody.contentTypes.length > 0) {
228
+ return /* @__PURE__ */ jsxs("div", { className: styles.contentTypes, children: [
229
+ op.requestBody.contentTypes.map((ct) => /* @__PURE__ */ jsx("span", { className: styles.contentTypeChip, children: ct }, ct)),
230
+ op.requestBody.required ? /* @__PURE__ */ jsx("span", { className: styles.paramRequired, children: "required" }) : null
231
+ ] });
232
+ }
233
+ if (op.requestBody?.body) {
234
+ return null;
235
+ }
236
+ return /* @__PURE__ */ jsx(Text, { variant: "body-small", color: "secondary", as: "p", children: "No request body." });
237
+ })(),
238
+ op.requestBody?.body ? /* @__PURE__ */ jsx(BodyBlock, { body: op.requestBody.body }) : null
239
+ ] }),
240
+ /* @__PURE__ */ jsxs("section", { className: styles.operationSection, children: [
241
+ /* @__PURE__ */ jsx(
242
+ Text,
243
+ {
244
+ variant: "body-small",
245
+ weight: "bold",
246
+ as: "h4",
247
+ className: styles.operationSectionTitle,
248
+ children: "Response body"
249
+ }
250
+ ),
251
+ responseBody.length > 0 ? /* @__PURE__ */ jsx("ul", { className: styles.responseList, children: responseBody.map((r) => /* @__PURE__ */ jsxs("li", { className: styles.responseRow, children: [
252
+ /* @__PURE__ */ jsx("span", { className: styles.responseStatus, children: r.status }),
253
+ /* @__PURE__ */ jsxs("span", { children: [
254
+ r.contentTypes.length > 0 ? r.contentTypes.join(", ") : null,
255
+ r.body ? /* @__PURE__ */ jsx(BodyBlock, { body: r.body }) : null
256
+ ] })
257
+ ] }, r.status)) }) : /* @__PURE__ */ jsx(Text, { variant: "body-small", color: "secondary", as: "p", children: "No response body declared." })
258
+ ] }),
259
+ /* @__PURE__ */ jsxs("section", { className: styles.operationSection, children: [
260
+ /* @__PURE__ */ jsx(
261
+ Text,
262
+ {
263
+ variant: "body-small",
264
+ weight: "bold",
265
+ as: "h4",
266
+ className: styles.operationSectionTitle,
267
+ children: "Response errors"
268
+ }
269
+ ),
270
+ errorResponses.length > 0 ? /* @__PURE__ */ jsx("ul", { className: styles.responseList, children: errorResponses.map((r) => /* @__PURE__ */ jsxs("li", { className: styles.responseRow, children: [
271
+ /* @__PURE__ */ jsx(
272
+ "span",
273
+ {
274
+ className: `${styles.responseStatus} ${styles.responseStatusError}`,
275
+ children: r.status
276
+ }
277
+ ),
278
+ /* @__PURE__ */ jsx("span", { className: styles.responseMessage, children: r.description ?? "\u2014" })
279
+ ] }, r.status)) }) : /* @__PURE__ */ jsx(Text, { variant: "body-small", color: "secondary", as: "p", children: "No error responses declared." })
280
+ ] })
281
+ ] }),
282
+ /* @__PURE__ */ jsx("aside", { className: styles.operationDetailSide, children: /* @__PURE__ */ jsx(CodeSnippetBlock, { op }) })
283
+ ] }) });
284
+ }
285
+ function OperationsPanel({ operations }) {
286
+ const [expanded, setExpanded] = useState(/* @__PURE__ */ new Set());
287
+ if (operations.length === 0) {
288
+ return /* @__PURE__ */ jsx(Text, { variant: "body-small", color: "secondary", as: "p", children: "No operations found in this specification." });
289
+ }
290
+ const toggle = (index) => {
291
+ setExpanded((prev) => {
292
+ const next = new Set(prev);
293
+ if (next.has(index)) {
294
+ next.delete(index);
295
+ } else {
296
+ next.add(index);
297
+ }
298
+ return next;
299
+ });
300
+ };
301
+ return /* @__PURE__ */ jsx("ul", { className: styles.operationList, children: operations.map((item, index) => {
302
+ const isOpen = expanded.has(index);
303
+ return /* @__PURE__ */ jsxs("li", { children: [
304
+ /* @__PURE__ */ jsxs(
305
+ "button",
306
+ {
307
+ type: "button",
308
+ className: styles.operationItem,
309
+ "aria-expanded": isOpen,
310
+ onClick: () => toggle(index),
311
+ children: [
312
+ /* @__PURE__ */ jsxs("span", { className: styles.operationItemHead, children: [
313
+ /* @__PURE__ */ jsx(MethodBadge, { method: item.method }),
314
+ /* @__PURE__ */ jsx("code", { className: styles.operationPath, children: item.path }),
315
+ /* @__PURE__ */ jsx("span", { className: styles.operationChevron, children: isOpen ? /* @__PURE__ */ jsx(RiArrowDownSLine, { size: 16 }) : /* @__PURE__ */ jsx(RiArrowRightSLine, { size: 16 }) })
316
+ ] }),
317
+ item.summary ? /* @__PURE__ */ jsx("span", { className: styles.operationItemSummary, children: item.summary }) : null
318
+ ]
319
+ }
320
+ ),
321
+ isOpen ? /* @__PURE__ */ jsx(OperationDetail, { op: item }) : null
322
+ ] }, `${item.method} ${item.path} ${index}`);
323
+ }) });
324
+ }
325
+ function SpecSection({
326
+ definition,
327
+ fileBaseName,
328
+ definitionHref,
329
+ hasSpec
330
+ }) {
331
+ const navigate = useNavigate();
332
+ const [copied, setCopied] = useState(false);
333
+ const [tab, setTab] = useState("operations");
334
+ const operations = useMemo(() => parseOperations(definition), [definition]);
335
+ const title = /* @__PURE__ */ jsx(
336
+ Text,
337
+ {
338
+ id: "apigee-spec-title",
339
+ variant: "title-x-small",
340
+ weight: "bold",
341
+ as: "h3",
342
+ className: styles.cardTitle,
343
+ children: "Specification"
344
+ }
345
+ );
346
+ if (!hasSpec || !definition.trim()) {
347
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
348
+ /* @__PURE__ */ jsx("div", { className: styles.cardHeaderRow, children: title }),
349
+ /* @__PURE__ */ jsx(Text, { variant: "body-small", color: "secondary", as: "p", children: "No specification available for preview." })
350
+ ] });
351
+ }
352
+ const handleCopy = async () => {
353
+ await navigator.clipboard.writeText(definition);
354
+ setCopied(true);
355
+ setTimeout(() => setCopied(false), 2e3);
356
+ };
357
+ const handleDownload = () => {
358
+ const blob = new Blob([definition], { type: "text/plain;charset=utf-8" });
359
+ const url = URL.createObjectURL(blob);
360
+ try {
361
+ const anchor = document.createElement("a");
362
+ anchor.href = url;
363
+ anchor.download = specFileName(fileBaseName, definition);
364
+ anchor.click();
365
+ } finally {
366
+ URL.revokeObjectURL(url);
367
+ }
368
+ };
369
+ return /* @__PURE__ */ jsxs("div", { className: styles.spec, children: [
370
+ /* @__PURE__ */ jsxs("div", { className: styles.cardHeaderRow, children: [
371
+ title,
372
+ /* @__PURE__ */ jsxs("div", { className: styles.specActions, children: [
373
+ /* @__PURE__ */ jsx(
374
+ Button,
375
+ {
376
+ size: "small",
377
+ variant: "secondary",
378
+ iconStart: /* @__PURE__ */ jsx(RiDownloadLine, {}),
379
+ onPress: handleDownload,
380
+ children: "Download"
381
+ }
382
+ ),
383
+ /* @__PURE__ */ jsx(
384
+ Button,
385
+ {
386
+ size: "small",
387
+ variant: "secondary",
388
+ iconStart: /* @__PURE__ */ jsx(RiFileCopyLine, {}),
389
+ onPress: handleCopy,
390
+ children: copied ? "Copied!" : "Copy"
391
+ }
392
+ )
393
+ ] })
394
+ ] }),
395
+ /* @__PURE__ */ jsxs(
396
+ Tabs,
397
+ {
398
+ selectedKey: tab,
399
+ onSelectionChange: (key) => {
400
+ if (key === "openapi") {
401
+ if (definitionHref) navigate(definitionHref);
402
+ return;
403
+ }
404
+ setTab(String(key));
405
+ },
406
+ children: [
407
+ /* @__PURE__ */ jsxs(TabList, { "aria-label": "Specification format", className: styles.tabList, children: [
408
+ /* @__PURE__ */ jsx(Tab, { id: "operations", children: "Operations" }),
409
+ /* @__PURE__ */ jsx(Tab, { id: "raw", children: "Raw" }),
410
+ /* @__PURE__ */ jsx(Tab, { id: "openapi", isDisabled: !definitionHref, children: /* @__PURE__ */ jsxs("span", { className: styles.tabLabel, children: [
411
+ "OpenAPI ",
412
+ /* @__PURE__ */ jsx(RiExternalLinkLine, { "aria-hidden": true, size: 12 })
413
+ ] }) })
414
+ ] }),
415
+ /* @__PURE__ */ jsx(TabPanel, { id: "operations", children: /* @__PURE__ */ jsx(OperationsPanel, { operations }) }),
416
+ /* @__PURE__ */ jsx(TabPanel, { id: "raw", children: /* @__PURE__ */ jsx("pre", { className: styles.code, children: /* @__PURE__ */ jsx("code", { children: definition }) }) })
417
+ ]
418
+ }
419
+ )
420
+ ] });
421
+ }
422
+ function ApiDetailsDrawerContent({
423
+ entity,
424
+ orgLabels,
425
+ onClose
426
+ }) {
427
+ const catalogApi = useApi(catalogApiRef);
428
+ const entityRoute = useRouteRef(entityRouteRef);
429
+ const location = useLocation();
430
+ const shareHref = useHref({
431
+ pathname: location.pathname,
432
+ search: location.search,
433
+ hash: location.hash
434
+ });
435
+ const shareUrl = `${window.location.origin}${shareHref}`;
436
+ const [shareCopied, setShareCopied] = useState(false);
437
+ const handleShare = async () => {
438
+ try {
439
+ await navigator.clipboard.writeText(shareUrl);
440
+ setShareCopied(true);
441
+ setTimeout(() => setShareCopied(false), 2e3);
442
+ } catch {
443
+ }
444
+ };
445
+ const [component, setComponent] = useState();
446
+ const namespace = entity.metadata.namespace ?? "default";
447
+ const name = entity.metadata.name;
448
+ useEffect(() => {
449
+ let active = true;
450
+ catalogApi.getEntityByRef({ kind: "Component", namespace, name }).then((found) => {
451
+ if (active) {
452
+ setComponent(found ?? void 0);
453
+ }
454
+ }).catch(() => {
455
+ if (active) {
456
+ setComponent(void 0);
457
+ }
458
+ });
459
+ return () => {
460
+ active = false;
461
+ };
462
+ }, [catalogApi, namespace, name]);
463
+ const vm = useMemo(
464
+ () => toApiDetailViewModel(entity, component, orgLabels),
465
+ [entity, component, orgLabels]
466
+ );
467
+ const catalogHref = entityRoute ? entityRoute({
468
+ kind: vm.targetRef.kind.toLowerCase(),
469
+ namespace: vm.targetRef.namespace,
470
+ name: vm.targetRef.name
471
+ }) : void 0;
472
+ const [open, setOpen] = useState(false);
473
+ useEffect(() => {
474
+ const id = requestAnimationFrame(() => setOpen(true));
475
+ return () => cancelAnimationFrame(id);
476
+ }, []);
477
+ useEffect(() => {
478
+ const onKey = (event) => {
479
+ if (event.key === "Escape") {
480
+ onClose();
481
+ }
482
+ };
483
+ document.addEventListener("keydown", onKey);
484
+ return () => document.removeEventListener("keydown", onKey);
485
+ }, [onClose]);
486
+ const showDeployments = vm.deployments.environments.length > 0;
487
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
488
+ /* @__PURE__ */ jsx(
489
+ "div",
490
+ {
491
+ className: styles.overlay,
492
+ "data-open": open,
493
+ onClick: onClose,
494
+ role: "presentation",
495
+ "aria-hidden": !open
496
+ }
497
+ ),
498
+ /* @__PURE__ */ jsxs(
499
+ "aside",
500
+ {
501
+ className: styles.drawer,
502
+ "data-open": open,
503
+ role: "dialog",
504
+ "aria-modal": "true",
505
+ "aria-label": `${vm.name} API details`,
506
+ children: [
507
+ /* @__PURE__ */ jsxs("header", { className: styles.header, children: [
508
+ /* @__PURE__ */ jsxs("div", { className: styles.headerText, children: [
509
+ /* @__PURE__ */ jsx(
510
+ Text,
511
+ {
512
+ variant: "title-small",
513
+ weight: "bold",
514
+ as: "h2",
515
+ className: styles.title,
516
+ children: vm.name
517
+ }
518
+ ),
519
+ /* @__PURE__ */ jsxs("div", { className: styles.badges, children: [
520
+ vm.version ? /* @__PURE__ */ jsx("span", { className: `${styles.badge} ${styles.badgeVersion}`, children: vm.version }) : null,
521
+ /* @__PURE__ */ jsx(LifecycleBadge, { kind: vm.badge.kind, label: vm.badge.label })
522
+ ] })
523
+ ] }),
524
+ /* @__PURE__ */ jsxs("div", { className: styles.headerActions, children: [
525
+ /* @__PURE__ */ jsx(
526
+ ButtonIcon,
527
+ {
528
+ icon: shareCopied ? /* @__PURE__ */ jsx(RiCheckLine, {}) : /* @__PURE__ */ jsx(RiShareLine, {}),
529
+ variant: "tertiary",
530
+ onPress: handleShare,
531
+ "aria-label": shareCopied ? "Link copied" : "Copy link to this API"
532
+ }
533
+ ),
534
+ /* @__PURE__ */ jsx(
535
+ ButtonIcon,
536
+ {
537
+ icon: /* @__PURE__ */ jsx(RiCloseLine, {}),
538
+ variant: "tertiary",
539
+ onPress: onClose,
540
+ "aria-label": "Close"
541
+ }
542
+ )
543
+ ] })
544
+ ] }),
545
+ /* @__PURE__ */ jsxs("div", { className: styles.body, children: [
546
+ /* @__PURE__ */ jsxs(
547
+ "section",
548
+ {
549
+ className: `${styles.card} ${styles.overviewCard}`,
550
+ "aria-labelledby": "apigee-overview-title",
551
+ children: [
552
+ /* @__PURE__ */ jsx(
553
+ Text,
554
+ {
555
+ id: "apigee-overview-title",
556
+ variant: "title-x-small",
557
+ weight: "bold",
558
+ as: "h3",
559
+ className: styles.cardTitle,
560
+ children: "Overview"
561
+ }
562
+ ),
563
+ /* @__PURE__ */ jsxs("dl", { className: styles.metaList, children: [
564
+ /* @__PURE__ */ jsx(MetaRow, { label: "Title", value: vm.name }),
565
+ vm.description ? /* @__PURE__ */ jsx(MetaRow, { label: "Description", value: vm.description }) : null,
566
+ /* @__PURE__ */ jsx(MetaRow, { label: "Kind", value: vm.kind }),
567
+ vm.type ? /* @__PURE__ */ jsx(MetaRow, { label: "Type", value: vm.type }) : null,
568
+ vm.version ? /* @__PURE__ */ jsx(MetaRow, { label: "Version", value: vm.version }) : null,
569
+ /* @__PURE__ */ jsx(MetaRow, { label: "Lifecycle", value: vm.badge.label }),
570
+ /* @__PURE__ */ jsx(MetaRow, { label: "Owner", children: /* @__PURE__ */ jsx(OwnerValue, { vm }) }),
571
+ vm.department ? /* @__PURE__ */ jsx(MetaRow, { label: "Department", value: vm.department }) : null,
572
+ vm.tags.length > 0 ? /* @__PURE__ */ jsx(MetaRow, { label: "Tags", children: /* @__PURE__ */ jsx("div", { className: styles.tags, children: vm.tags.map((tag) => /* @__PURE__ */ jsx("span", { className: styles.tag, children: tag }, tag)) }) }) : null
573
+ ] })
574
+ ]
575
+ }
576
+ ),
577
+ /* @__PURE__ */ jsxs("div", { className: styles.sideColumn, children: [
578
+ catalogHref || vm.sourceRepo || vm.apiHub ? /* @__PURE__ */ jsxs(
579
+ "section",
580
+ {
581
+ className: `${styles.card} ${styles.linksCard}`,
582
+ "aria-labelledby": "apigee-links-title",
583
+ children: [
584
+ /* @__PURE__ */ jsx(
585
+ Text,
586
+ {
587
+ id: "apigee-links-title",
588
+ variant: "title-x-small",
589
+ weight: "bold",
590
+ as: "h3",
591
+ className: styles.cardTitle,
592
+ children: "Links"
593
+ }
594
+ ),
595
+ catalogHref ? /* @__PURE__ */ jsxs(Link, { to: catalogHref, className: styles.catalogLink, children: [
596
+ /* @__PURE__ */ jsx(BackstageIcon, { size: 14 }),
597
+ " View in catalog"
598
+ ] }) : null,
599
+ vm.sourceRepo ? /* @__PURE__ */ jsxs(Link, { to: vm.sourceRepo.url, className: styles.sourceRepo, children: [
600
+ vm.sourceRepo.icon === "github" ? /* @__PURE__ */ jsx(RiGithubFill, { "aria-hidden": true, size: 14 }) : /* @__PURE__ */ jsx(ApigeeIcon, { size: 14 }),
601
+ " ",
602
+ vm.sourceRepo.title
603
+ ] }) : null,
604
+ vm.apiHub ? /* @__PURE__ */ jsxs(Link, { to: vm.apiHub.url, className: styles.apiHubLink, children: [
605
+ vm.apiHub.icon === "apigee" ? /* @__PURE__ */ jsx(ApigeeIcon, { size: 14 }) : /* @__PURE__ */ jsx(RiGithubFill, { "aria-hidden": true, size: 14 }),
606
+ " ",
607
+ vm.apiHub.title
608
+ ] }) : null
609
+ ]
610
+ }
611
+ ) : null,
612
+ showDeployments ? /* @__PURE__ */ jsxs(
613
+ "section",
614
+ {
615
+ className: `${styles.card} ${styles.deploymentsCard}`,
616
+ "aria-labelledby": "apigee-deployments-title",
617
+ children: [
618
+ /* @__PURE__ */ jsx(
619
+ Text,
620
+ {
621
+ id: "apigee-deployments-title",
622
+ variant: "title-x-small",
623
+ weight: "bold",
624
+ as: "h3",
625
+ className: styles.cardTitle,
626
+ children: "Deployments"
627
+ }
628
+ ),
629
+ /* @__PURE__ */ jsx("div", { className: styles.deploymentsScroll, children: /* @__PURE__ */ jsxs("table", { className: styles.deploymentsTable, children: [
630
+ /* @__PURE__ */ jsxs("thead", { children: [
631
+ /* @__PURE__ */ jsxs("tr", { children: [
632
+ /* @__PURE__ */ jsx(
633
+ "th",
634
+ {
635
+ scope: "col",
636
+ rowSpan: 2,
637
+ className: styles.orgHeader,
638
+ children: "Orgs"
639
+ }
640
+ ),
641
+ /* @__PURE__ */ jsx(
642
+ "th",
643
+ {
644
+ scope: "colgroup",
645
+ colSpan: vm.deployments.environments.length,
646
+ children: "Revisions"
647
+ }
648
+ )
649
+ ] }),
650
+ /* @__PURE__ */ jsx("tr", { children: vm.deployments.environments.map((env) => /* @__PURE__ */ jsx("th", { scope: "col", children: env }, env)) })
651
+ ] }),
652
+ /* @__PURE__ */ jsx("tbody", { children: vm.deployments.rows.map((row) => /* @__PURE__ */ jsxs("tr", { children: [
653
+ /* @__PURE__ */ jsx("th", { scope: "row", className: styles.orgCell, children: /* @__PURE__ */ jsx("span", { className: styles.orgBadge, children: row.label }) }),
654
+ vm.deployments.environments.map((env) => /* @__PURE__ */ jsx("td", { children: row.revisions[env] ? /* @__PURE__ */ jsx("span", { className: styles.revisionBadge, children: row.revisions[env] }) : /* @__PURE__ */ jsx(
655
+ "span",
656
+ {
657
+ className: styles.revisionEmpty,
658
+ "aria-label": "Not deployed",
659
+ children: "\u2014"
660
+ }
661
+ ) }, env))
662
+ ] }, row.value)) })
663
+ ] }) })
664
+ ]
665
+ }
666
+ ) : null
667
+ ] }),
668
+ /* @__PURE__ */ jsx(
669
+ "section",
670
+ {
671
+ className: `${styles.card} ${styles.specCard}`,
672
+ "aria-labelledby": "apigee-spec-title",
673
+ children: /* @__PURE__ */ jsx(
674
+ SpecSection,
675
+ {
676
+ definition: vm.definition,
677
+ fileBaseName: vm.targetRef.name,
678
+ definitionHref: catalogHref ? `${catalogHref}/definition` : void 0,
679
+ hasSpec: vm.hasSpec
680
+ }
681
+ )
682
+ }
683
+ )
684
+ ] })
685
+ ]
686
+ }
687
+ )
688
+ ] });
689
+ }
690
+ function ApiDetailsDrawer({
691
+ entity,
692
+ orgLabels,
693
+ onClose
694
+ }) {
695
+ if (!entity) {
696
+ return null;
697
+ }
698
+ return /* @__PURE__ */ jsx(
699
+ ApiDetailsDrawerContent,
700
+ {
701
+ entity,
702
+ orgLabels,
703
+ onClose
704
+ }
705
+ );
706
+ }
707
+
708
+ export { ApiDetailsDrawer };
709
+ //# sourceMappingURL=ApiDetailsDrawer.esm.js.map