@anyshift/backstage-plugin-anyshift 0.1.0-beta.5 → 0.1.0-beta.7
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 +11 -0
- package/README.md +7 -1
- package/dist/api/AnyshiftApi.esm.js +8 -0
- package/dist/api/AnyshiftApi.esm.js.map +1 -1
- package/dist/components/cloud/CloudPage.esm.js +104 -0
- package/dist/components/cloud/CloudPage.esm.js.map +1 -0
- package/dist/components/cloud/CloudResourceContent.esm.js +350 -0
- package/dist/components/cloud/CloudResourceContent.esm.js.map +1 -0
- package/dist/index.d.ts +93 -3
- package/dist/index.esm.js +1 -1
- package/dist/package.json.esm.js +1 -1
- package/dist/plugin.esm.js +36 -5
- package/dist/plugin.esm.js.map +1 -1
- package/dist/routes.esm.js +2 -1
- package/dist/routes.esm.js.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.0-beta.7
|
|
4
|
+
|
|
5
|
+
- Load normalized cloud changes, Terraform provenance, and drift evidence on cloud Resource pages.
|
|
6
|
+
- Resolve stable Anyshift resource identity before provider-native ARN fallback.
|
|
7
|
+
- Preserve successful evidence sections across partial upstream failures and render unknown drift neutrally.
|
|
8
|
+
|
|
9
|
+
## 0.1.0-beta.6
|
|
10
|
+
|
|
11
|
+
- Add a discoverable global Cloud inventory page.
|
|
12
|
+
- Add generic cloud Resource content with identity, ownership, relations, ARN, and console links.
|
|
13
|
+
|
|
3
14
|
## 0.1.0-beta.5
|
|
4
15
|
|
|
5
16
|
- Hide entity content for catalog resources without a supported Anyshift brief kind.
|
package/README.md
CHANGED
|
@@ -2,7 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
New-frontend-system plugin for the Anyshift infrastructure graph. Installing the package in an app
|
|
4
4
|
created with `@backstage/frontend-defaults` and configured with `app.packages: all` discovers its
|
|
5
|
-
dashboard, navigation
|
|
5
|
+
dashboard, Cloud inventory page, navigation items, API factory, and entity content automatically.
|
|
6
|
+
|
|
7
|
+
The Cloud page lists provider-managed infrastructure Resources with searchable type, account,
|
|
8
|
+
region, and ownership metadata. Cloud Resource entities receive read-only normalized change,
|
|
9
|
+
Terraform provenance, and drift evidence alongside their stable identity, provider console link,
|
|
10
|
+
and catalog relationships. Evidence sections degrade independently, and an unknown drift verdict
|
|
11
|
+
is not presented as drift.
|
|
6
12
|
|
|
7
13
|
The package has no legacy frontend entry point. Add the corresponding
|
|
8
14
|
`@anyshift/backstage-plugin-anyshift-backend` package to the Backstage backend and configure one
|
|
@@ -33,6 +33,14 @@ class AnyshiftClient {
|
|
|
33
33
|
async datadogSignals(options = {}) {
|
|
34
34
|
return this.get(`/signals/datadog${options.refresh ? "?refresh=1" : ""}`);
|
|
35
35
|
}
|
|
36
|
+
async cloudResourceBrief(resource, options = {}) {
|
|
37
|
+
const params = new URLSearchParams({ resource });
|
|
38
|
+
if (options.since) params.set("since", options.since);
|
|
39
|
+
if (options.cursor) params.set("cursor", options.cursor);
|
|
40
|
+
if (options.diff !== void 0) params.set("diff", String(options.diff));
|
|
41
|
+
if (options.refresh) params.set("refresh", "1");
|
|
42
|
+
return this.get(`/cloud/resources/brief?${params}`);
|
|
43
|
+
}
|
|
36
44
|
async inventory(type, options = {}) {
|
|
37
45
|
const params = new URLSearchParams({ type });
|
|
38
46
|
if (options.limit !== void 0) params.set("limit", String(options.limit));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnyshiftApi.esm.js","sources":["../../src/api/AnyshiftApi.ts"],"sourcesContent":["import {\n createApiRef,\n type DiscoveryApi,\n type FetchApi,\n} from '@backstage/frontend-plugin-api';\nimport type {\n BriefSectionName,\n BriefSectionResponse,\n AnyshiftCapabilities,\n CatalogSummary,\n CatalogSyncReport,\n DatadogSignals,\n EstateInsights,\n QueryResult,\n ServiceBrief,\n} from './types';\n\nexport interface AnyshiftApi {\n capabilities(): Promise<AnyshiftCapabilities>;\n brief(\n kind: string,\n target: string,\n options?: BriefRequestOptions,\n ): Promise<ServiceBrief>;\n briefSection(\n kind: string,\n target: string,\n section: BriefSectionName,\n options?: BriefRequestOptions,\n ): Promise<BriefSectionResponse>;\n insights(): Promise<EstateInsights>;\n datadogSignals(options?: { refresh?: boolean }): Promise<DatadogSignals>;\n inventory(\n type: string,\n options?: { limit?: number; offset?: number },\n ): Promise<CatalogSummary>;\n query(sql: string): Promise<QueryResult>;\n reconcileCatalog(): Promise<CatalogSyncAccepted>;\n}\n\nexport interface BriefRequestOptions {\n namespace?: string;\n refresh?: boolean;\n}\n\nexport interface CatalogSyncAccepted {\n accepted: true;\n acceptedAt: string;\n previous?: CatalogSyncReport;\n}\n\nexport const anyshiftApiRef = createApiRef<AnyshiftApi>({\n id: 'plugin.anyshift.service',\n});\n\nexport class AnyshiftClient implements AnyshiftApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi;\n }\n\n async capabilities(): Promise<AnyshiftCapabilities> {\n return this.get('/capabilities');\n }\n\n async brief(\n kind: string,\n target: string,\n options: BriefRequestOptions = {},\n ): Promise<ServiceBrief> {\n return this.get(\n `/brief/${encodeURIComponent(kind)}/${encodeURIComponent(\n target,\n )}${briefQuery(options)}`,\n );\n }\n\n async briefSection(\n kind: string,\n target: string,\n section: BriefSectionName,\n options: BriefRequestOptions = {},\n ): Promise<BriefSectionResponse> {\n return this.get(\n `/brief/${encodeURIComponent(kind)}/${encodeURIComponent(\n target,\n )}/sections/${encodeURIComponent(section)}${briefQuery(options)}`,\n );\n }\n\n async insights(): Promise<EstateInsights> {\n return this.get('/insights');\n }\n\n async datadogSignals(\n options: { refresh?: boolean } = {},\n ): Promise<DatadogSignals> {\n return this.get(`/signals/datadog${options.refresh ? '?refresh=1' : ''}`);\n }\n\n async inventory(\n type: string,\n options: { limit?: number; offset?: number } = {},\n ): Promise<CatalogSummary> {\n const params = new URLSearchParams({ type });\n if (options.limit !== undefined) params.set('limit', String(options.limit));\n if (options.offset !== undefined)\n params.set('offset', String(options.offset));\n return this.get(`/inventory?${params}`);\n }\n\n async query(sql: string): Promise<QueryResult> {\n return this.request('/query', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ sql }),\n });\n }\n\n async reconcileCatalog(): Promise<CatalogSyncAccepted> {\n return this.request('/anyshift/reconcile', { method: 'POST' }, 'catalog');\n }\n\n private async get<T>(path: string): Promise<T> {\n return this.request(path, { method: 'GET' });\n }\n\n private async request<T>(\n path: string,\n init: RequestInit,\n pluginId = 'anyshift',\n ): Promise<T> {\n const baseUrl = await this.discoveryApi.getBaseUrl(pluginId);\n const response = await this.fetchApi.fetch(`${baseUrl}${path}`, init);\n const text = await response.text();\n let payload: any;\n try {\n payload = text ? JSON.parse(text) : {};\n } catch {\n payload = {};\n }\n if (!response.ok) {\n throw new Error(\n payload?.error?.message ||\n `Anyshift request failed (${response.status})`,\n );\n }\n return payload as T;\n }\n}\n\nfunction briefQuery(options: BriefRequestOptions): string {\n const params = new URLSearchParams();\n if (options.namespace) params.set('namespace', options.namespace);\n if (options.refresh) params.set('refresh', '1');\n const query = params.toString();\n return query ? `?${query}` : '';\n}\n"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"AnyshiftApi.esm.js","sources":["../../src/api/AnyshiftApi.ts"],"sourcesContent":["import {\n createApiRef,\n type DiscoveryApi,\n type FetchApi,\n} from '@backstage/frontend-plugin-api';\nimport type {\n BriefSectionName,\n BriefSectionResponse,\n AnyshiftCapabilities,\n CatalogSummary,\n CatalogSyncReport,\n CloudResourceBrief,\n DatadogSignals,\n EstateInsights,\n QueryResult,\n ServiceBrief,\n} from './types';\n\nexport interface AnyshiftApi {\n capabilities(): Promise<AnyshiftCapabilities>;\n brief(\n kind: string,\n target: string,\n options?: BriefRequestOptions,\n ): Promise<ServiceBrief>;\n briefSection(\n kind: string,\n target: string,\n section: BriefSectionName,\n options?: BriefRequestOptions,\n ): Promise<BriefSectionResponse>;\n insights(): Promise<EstateInsights>;\n datadogSignals(options?: { refresh?: boolean }): Promise<DatadogSignals>;\n cloudResourceBrief(\n resource: string,\n options?: CloudResourceBriefRequestOptions,\n ): Promise<CloudResourceBrief>;\n inventory(\n type: string,\n options?: { limit?: number; offset?: number },\n ): Promise<CatalogSummary>;\n query(sql: string): Promise<QueryResult>;\n reconcileCatalog(): Promise<CatalogSyncAccepted>;\n}\n\nexport interface BriefRequestOptions {\n namespace?: string;\n refresh?: boolean;\n}\n\nexport interface CloudResourceBriefRequestOptions {\n since?: string;\n cursor?: string;\n diff?: boolean;\n refresh?: boolean;\n}\n\nexport interface CatalogSyncAccepted {\n accepted: true;\n acceptedAt: string;\n previous?: CatalogSyncReport;\n}\n\nexport const anyshiftApiRef = createApiRef<AnyshiftApi>({\n id: 'plugin.anyshift.service',\n});\n\nexport class AnyshiftClient implements AnyshiftApi {\n private readonly discoveryApi: DiscoveryApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: { discoveryApi: DiscoveryApi; fetchApi: FetchApi }) {\n this.discoveryApi = options.discoveryApi;\n this.fetchApi = options.fetchApi;\n }\n\n async capabilities(): Promise<AnyshiftCapabilities> {\n return this.get('/capabilities');\n }\n\n async brief(\n kind: string,\n target: string,\n options: BriefRequestOptions = {},\n ): Promise<ServiceBrief> {\n return this.get(\n `/brief/${encodeURIComponent(kind)}/${encodeURIComponent(\n target,\n )}${briefQuery(options)}`,\n );\n }\n\n async briefSection(\n kind: string,\n target: string,\n section: BriefSectionName,\n options: BriefRequestOptions = {},\n ): Promise<BriefSectionResponse> {\n return this.get(\n `/brief/${encodeURIComponent(kind)}/${encodeURIComponent(\n target,\n )}/sections/${encodeURIComponent(section)}${briefQuery(options)}`,\n );\n }\n\n async insights(): Promise<EstateInsights> {\n return this.get('/insights');\n }\n\n async datadogSignals(\n options: { refresh?: boolean } = {},\n ): Promise<DatadogSignals> {\n return this.get(`/signals/datadog${options.refresh ? '?refresh=1' : ''}`);\n }\n\n async cloudResourceBrief(\n resource: string,\n options: CloudResourceBriefRequestOptions = {},\n ): Promise<CloudResourceBrief> {\n const params = new URLSearchParams({ resource });\n if (options.since) params.set('since', options.since);\n if (options.cursor) params.set('cursor', options.cursor);\n if (options.diff !== undefined) params.set('diff', String(options.diff));\n if (options.refresh) params.set('refresh', '1');\n return this.get(`/cloud/resources/brief?${params}`);\n }\n\n async inventory(\n type: string,\n options: { limit?: number; offset?: number } = {},\n ): Promise<CatalogSummary> {\n const params = new URLSearchParams({ type });\n if (options.limit !== undefined) params.set('limit', String(options.limit));\n if (options.offset !== undefined)\n params.set('offset', String(options.offset));\n return this.get(`/inventory?${params}`);\n }\n\n async query(sql: string): Promise<QueryResult> {\n return this.request('/query', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ sql }),\n });\n }\n\n async reconcileCatalog(): Promise<CatalogSyncAccepted> {\n return this.request('/anyshift/reconcile', { method: 'POST' }, 'catalog');\n }\n\n private async get<T>(path: string): Promise<T> {\n return this.request(path, { method: 'GET' });\n }\n\n private async request<T>(\n path: string,\n init: RequestInit,\n pluginId = 'anyshift',\n ): Promise<T> {\n const baseUrl = await this.discoveryApi.getBaseUrl(pluginId);\n const response = await this.fetchApi.fetch(`${baseUrl}${path}`, init);\n const text = await response.text();\n let payload: any;\n try {\n payload = text ? JSON.parse(text) : {};\n } catch {\n payload = {};\n }\n if (!response.ok) {\n throw new Error(\n payload?.error?.message ||\n `Anyshift request failed (${response.status})`,\n );\n }\n return payload as T;\n }\n}\n\nfunction briefQuery(options: BriefRequestOptions): string {\n const params = new URLSearchParams();\n if (options.namespace) params.set('namespace', options.namespace);\n if (options.refresh) params.set('refresh', '1');\n const query = params.toString();\n return query ? `?${query}` : '';\n}\n"],"names":[],"mappings":";;AA+DO,MAAM,iBAAiB,YAAA,CAA0B;AAAA,EACtD,EAAA,EAAI;AACN,CAAC;AAEM,MAAM,cAAA,CAAsC;AAAA,EAChC,YAAA;AAAA,EACA,QAAA;AAAA,EAEjB,YAAY,OAAA,EAA6D;AACvE,IAAA,IAAA,CAAK,eAAe,OAAA,CAAQ,YAAA;AAC5B,IAAA,IAAA,CAAK,WAAW,OAAA,CAAQ,QAAA;AAAA,EAC1B;AAAA,EAEA,MAAM,YAAA,GAA8C;AAClD,IAAA,OAAO,IAAA,CAAK,IAAI,eAAe,CAAA;AAAA,EACjC;AAAA,EAEA,MAAM,KAAA,CACJ,IAAA,EACA,MAAA,EACA,OAAA,GAA+B,EAAC,EACT;AACvB,IAAA,OAAO,IAAA,CAAK,GAAA;AAAA,MACV,CAAA,OAAA,EAAU,kBAAA,CAAmB,IAAI,CAAC,CAAA,CAAA,EAAI,kBAAA;AAAA,QACpC;AAAA,OACD,CAAA,EAAG,UAAA,CAAW,OAAO,CAAC,CAAA;AAAA,KACzB;AAAA,EACF;AAAA,EAEA,MAAM,YAAA,CACJ,IAAA,EACA,QACA,OAAA,EACA,OAAA,GAA+B,EAAC,EACD;AAC/B,IAAA,OAAO,IAAA,CAAK,GAAA;AAAA,MACV,CAAA,OAAA,EAAU,kBAAA,CAAmB,IAAI,CAAC,CAAA,CAAA,EAAI,kBAAA;AAAA,QACpC;AAAA,OACD,aAAa,kBAAA,CAAmB,OAAO,CAAC,CAAA,EAAG,UAAA,CAAW,OAAO,CAAC,CAAA;AAAA,KACjE;AAAA,EACF;AAAA,EAEA,MAAM,QAAA,GAAoC;AACxC,IAAA,OAAO,IAAA,CAAK,IAAI,WAAW,CAAA;AAAA,EAC7B;AAAA,EAEA,MAAM,cAAA,CACJ,OAAA,GAAiC,EAAC,EACT;AACzB,IAAA,OAAO,KAAK,GAAA,CAAI,CAAA,gBAAA,EAAmB,QAAQ,OAAA,GAAU,YAAA,GAAe,EAAE,CAAA,CAAE,CAAA;AAAA,EAC1E;AAAA,EAEA,MAAM,kBAAA,CACJ,QAAA,EACA,OAAA,GAA4C,EAAC,EAChB;AAC7B,IAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB,EAAE,UAAU,CAAA;AAC/C,IAAA,IAAI,QAAQ,KAAA,EAAO,MAAA,CAAO,GAAA,CAAI,OAAA,EAAS,QAAQ,KAAK,CAAA;AACpD,IAAA,IAAI,QAAQ,MAAA,EAAQ,MAAA,CAAO,GAAA,CAAI,QAAA,EAAU,QAAQ,MAAM,CAAA;AACvD,IAAA,IAAI,OAAA,CAAQ,SAAS,MAAA,EAAW,MAAA,CAAO,IAAI,MAAA,EAAQ,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAC,CAAA;AACvE,IAAA,IAAI,OAAA,CAAQ,OAAA,EAAS,MAAA,CAAO,GAAA,CAAI,WAAW,GAAG,CAAA;AAC9C,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAA,uBAAA,EAA0B,MAAM,CAAA,CAAE,CAAA;AAAA,EACpD;AAAA,EAEA,MAAM,SAAA,CACJ,IAAA,EACA,OAAA,GAA+C,EAAC,EACvB;AACzB,IAAA,MAAM,MAAA,GAAS,IAAI,eAAA,CAAgB,EAAE,MAAM,CAAA;AAC3C,IAAA,IAAI,OAAA,CAAQ,UAAU,MAAA,EAAW,MAAA,CAAO,IAAI,OAAA,EAAS,MAAA,CAAO,OAAA,CAAQ,KAAK,CAAC,CAAA;AAC1E,IAAA,IAAI,QAAQ,MAAA,KAAW,MAAA;AACrB,MAAA,MAAA,CAAO,GAAA,CAAI,QAAA,EAAU,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAC,CAAA;AAC7C,IAAA,OAAO,IAAA,CAAK,GAAA,CAAI,CAAA,WAAA,EAAc,MAAM,CAAA,CAAE,CAAA;AAAA,EACxC;AAAA,EAEA,MAAM,MAAM,GAAA,EAAmC;AAC7C,IAAA,OAAO,IAAA,CAAK,QAAQ,QAAA,EAAU;AAAA,MAC5B,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA,EAAS,EAAE,cAAA,EAAgB,kBAAA,EAAmB;AAAA,MAC9C,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,EAAE,KAAK;AAAA,KAC7B,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,gBAAA,GAAiD;AACrD,IAAA,OAAO,KAAK,OAAA,CAAQ,qBAAA,EAAuB,EAAE,MAAA,EAAQ,MAAA,IAAU,SAAS,CAAA;AAAA,EAC1E;AAAA,EAEA,MAAc,IAAO,IAAA,EAA0B;AAC7C,IAAA,OAAO,KAAK,OAAA,CAAQ,IAAA,EAAM,EAAE,MAAA,EAAQ,OAAO,CAAA;AAAA,EAC7C;AAAA,EAEA,MAAc,OAAA,CACZ,IAAA,EACA,IAAA,EACA,WAAW,UAAA,EACC;AACZ,IAAA,MAAM,OAAA,GAAU,MAAM,IAAA,CAAK,YAAA,CAAa,WAAW,QAAQ,CAAA;AAC3D,IAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,QAAA,CAAS,KAAA,CAAM,GAAG,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,IAAI,CAAA;AACpE,IAAA,MAAM,IAAA,GAAO,MAAM,QAAA,CAAS,IAAA,EAAK;AACjC,IAAA,IAAI,OAAA;AACJ,IAAA,IAAI;AACF,MAAA,OAAA,GAAU,IAAA,GAAO,IAAA,CAAK,KAAA,CAAM,IAAI,IAAI,EAAC;AAAA,IACvC,CAAA,CAAA,MAAQ;AACN,MAAA,OAAA,GAAU,EAAC;AAAA,IACb;AACA,IAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,MAAA,MAAM,IAAI,KAAA;AAAA,QACR,OAAA,EAAS,KAAA,EAAO,OAAA,IACd,CAAA,yBAAA,EAA4B,SAAS,MAAM,CAAA,CAAA;AAAA,OAC/C;AAAA,IACF;AACA,IAAA,OAAO,OAAA;AAAA,EACT;AACF;AAEA,SAAS,WAAW,OAAA,EAAsC;AACxD,EAAA,MAAM,MAAA,GAAS,IAAI,eAAA,EAAgB;AACnC,EAAA,IAAI,QAAQ,SAAA,EAAW,MAAA,CAAO,GAAA,CAAI,WAAA,EAAa,QAAQ,SAAS,CAAA;AAChE,EAAA,IAAI,OAAA,CAAQ,OAAA,EAAS,MAAA,CAAO,GAAA,CAAI,WAAW,GAAG,CAAA;AAC9C,EAAA,MAAM,KAAA,GAAQ,OAAO,QAAA,EAAS;AAC9B,EAAA,OAAO,KAAA,GAAQ,CAAA,CAAA,EAAI,KAAK,CAAA,CAAA,GAAK,EAAA;AAC/B;;;;"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useEffect, useMemo } from 'react';
|
|
3
|
+
import { Typography, Grid } from '@material-ui/core';
|
|
4
|
+
import { Page, Header, Content, ContentHeader, Progress, InfoCard, Table } from '@backstage/core-components';
|
|
5
|
+
import { ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION, ANYSHIFT_CLOUD_REGION_ANNOTATION, ANYSHIFT_RESOURCE_TYPE_ANNOTATION, ANYSHIFT_CLOUD_PROVIDER_ANNOTATION } from '@anyshift/backstage-plugin-anyshift-common';
|
|
6
|
+
import { catalogApiRef, EntityRefLink } from '@backstage/plugin-catalog-react';
|
|
7
|
+
import { useApi } from '@backstage/frontend-plugin-api';
|
|
8
|
+
|
|
9
|
+
function isCloudResource(entity) {
|
|
10
|
+
return entity.kind.toLocaleLowerCase("en-US") === "resource" && Boolean(entity.metadata.annotations?.[ANYSHIFT_CLOUD_PROVIDER_ANNOTATION]);
|
|
11
|
+
}
|
|
12
|
+
function cloudResourceRows(entities) {
|
|
13
|
+
return entities.filter(isCloudResource).map((entity) => {
|
|
14
|
+
const annotations = entity.metadata.annotations ?? {};
|
|
15
|
+
return {
|
|
16
|
+
entity,
|
|
17
|
+
name: entity.metadata.title || entity.metadata.name,
|
|
18
|
+
provider: annotations[ANYSHIFT_CLOUD_PROVIDER_ANNOTATION]?.toUpperCase() || "Unknown",
|
|
19
|
+
resourceType: annotations[ANYSHIFT_RESOURCE_TYPE_ANNOTATION] || String(entity.spec?.type || "resource"),
|
|
20
|
+
region: annotations[ANYSHIFT_CLOUD_REGION_ANNOTATION] || "Global",
|
|
21
|
+
account: annotations[ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION] || "Not available",
|
|
22
|
+
owner: typeof entity.spec?.owner === "string" ? entity.spec.owner : "Not assigned"
|
|
23
|
+
};
|
|
24
|
+
}).sort((a, b) => a.name.localeCompare(b.name));
|
|
25
|
+
}
|
|
26
|
+
const columns = [
|
|
27
|
+
{
|
|
28
|
+
title: "Resource",
|
|
29
|
+
field: "name",
|
|
30
|
+
render: (row) => /* @__PURE__ */ jsx(EntityRefLink, { entityRef: row.entity, title: row.name })
|
|
31
|
+
},
|
|
32
|
+
{ title: "Type", field: "resourceType" },
|
|
33
|
+
{ title: "Provider", field: "provider" },
|
|
34
|
+
{ title: "Region", field: "region" },
|
|
35
|
+
{ title: "Account", field: "account" },
|
|
36
|
+
{ title: "Owner", field: "owner" }
|
|
37
|
+
];
|
|
38
|
+
const CloudPage = () => {
|
|
39
|
+
const catalogApi = useApi(catalogApiRef);
|
|
40
|
+
const [entities, setEntities] = useState();
|
|
41
|
+
const [error, setError] = useState();
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
catalogApi.getEntities({ filter: { kind: "Resource" } }).then((response) => setEntities(response.items)).catch(
|
|
44
|
+
(cause) => setError(cause instanceof Error ? cause.message : String(cause))
|
|
45
|
+
);
|
|
46
|
+
}, [catalogApi]);
|
|
47
|
+
const rows = useMemo(() => cloudResourceRows(entities ?? []), [entities]);
|
|
48
|
+
const summary = useMemo(
|
|
49
|
+
() => ({
|
|
50
|
+
resources: rows.length,
|
|
51
|
+
types: new Set(rows.map((row) => row.resourceType)).size,
|
|
52
|
+
accounts: new Set(
|
|
53
|
+
rows.map((row) => row.account).filter((account) => account !== "Not available")
|
|
54
|
+
).size,
|
|
55
|
+
regions: new Set(
|
|
56
|
+
rows.map((row) => row.region).filter((region) => region !== "Global")
|
|
57
|
+
).size
|
|
58
|
+
}),
|
|
59
|
+
[rows]
|
|
60
|
+
);
|
|
61
|
+
return /* @__PURE__ */ jsxs(Page, { themeId: "tool", children: [
|
|
62
|
+
/* @__PURE__ */ jsx(
|
|
63
|
+
Header,
|
|
64
|
+
{
|
|
65
|
+
title: "Cloud",
|
|
66
|
+
subtitle: "Read-only cloud inventory synchronized through Anyshift"
|
|
67
|
+
}
|
|
68
|
+
),
|
|
69
|
+
/* @__PURE__ */ jsxs(Content, { children: [
|
|
70
|
+
/* @__PURE__ */ jsx(ContentHeader, { title: "Cloud inventory" }),
|
|
71
|
+
error ? /* @__PURE__ */ jsx(Typography, { color: "error", children: error }) : !entities ? /* @__PURE__ */ jsx(Progress, {}) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
72
|
+
/* @__PURE__ */ jsx(InfoCard, { children: /* @__PURE__ */ jsxs(Grid, { container: true, spacing: 2, children: [
|
|
73
|
+
/* @__PURE__ */ jsx(Summary, { label: "Resources", value: summary.resources }),
|
|
74
|
+
/* @__PURE__ */ jsx(Summary, { label: "Types", value: summary.types }),
|
|
75
|
+
/* @__PURE__ */ jsx(Summary, { label: "Accounts", value: summary.accounts }),
|
|
76
|
+
/* @__PURE__ */ jsx(Summary, { label: "Regions", value: summary.regions })
|
|
77
|
+
] }) }),
|
|
78
|
+
/* @__PURE__ */ jsx(
|
|
79
|
+
Table,
|
|
80
|
+
{
|
|
81
|
+
title: "Cloud resources",
|
|
82
|
+
columns,
|
|
83
|
+
data: rows,
|
|
84
|
+
options: {
|
|
85
|
+
filtering: true,
|
|
86
|
+
paging: true,
|
|
87
|
+
pageSize: 20,
|
|
88
|
+
pageSizeOptions: [20, 50, 100],
|
|
89
|
+
search: true
|
|
90
|
+
},
|
|
91
|
+
emptyContent: /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: "No cloud resources have been synchronized yet." })
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
] })
|
|
95
|
+
] })
|
|
96
|
+
] });
|
|
97
|
+
};
|
|
98
|
+
const Summary = ({ label, value }) => /* @__PURE__ */ jsxs(Grid, { item: true, xs: 6, md: 3, children: [
|
|
99
|
+
/* @__PURE__ */ jsx(Typography, { variant: "h5", children: value }),
|
|
100
|
+
/* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: label })
|
|
101
|
+
] });
|
|
102
|
+
|
|
103
|
+
export { CloudPage, cloudResourceRows, isCloudResource };
|
|
104
|
+
//# sourceMappingURL=CloudPage.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CloudPage.esm.js","sources":["../../../src/components/cloud/CloudPage.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from 'react';\nimport { Grid, Typography } from '@material-ui/core';\nimport {\n Content,\n ContentHeader,\n Header,\n InfoCard,\n Page,\n Progress,\n Table,\n type TableColumn,\n} from '@backstage/core-components';\nimport type { Entity } from '@backstage/catalog-model';\nimport {\n ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION,\n ANYSHIFT_CLOUD_PROVIDER_ANNOTATION,\n ANYSHIFT_CLOUD_REGION_ANNOTATION,\n ANYSHIFT_RESOURCE_TYPE_ANNOTATION,\n} from '@anyshift/backstage-plugin-anyshift-common';\nimport { catalogApiRef, EntityRefLink } from '@backstage/plugin-catalog-react';\nimport { useApi } from '@backstage/frontend-plugin-api';\n\nexport interface CloudResourceRow {\n entity: Entity;\n name: string;\n provider: string;\n resourceType: string;\n region: string;\n account: string;\n owner: string;\n}\n\nexport function isCloudResource(entity: Entity): boolean {\n return (\n entity.kind.toLocaleLowerCase('en-US') === 'resource' &&\n Boolean(entity.metadata.annotations?.[ANYSHIFT_CLOUD_PROVIDER_ANNOTATION])\n );\n}\n\nexport function cloudResourceRows(entities: Entity[]): CloudResourceRow[] {\n return entities\n .filter(isCloudResource)\n .map(entity => {\n const annotations = entity.metadata.annotations ?? {};\n return {\n entity,\n name: entity.metadata.title || entity.metadata.name,\n provider:\n annotations[ANYSHIFT_CLOUD_PROVIDER_ANNOTATION]?.toUpperCase() ||\n 'Unknown',\n resourceType:\n annotations[ANYSHIFT_RESOURCE_TYPE_ANNOTATION] ||\n String(entity.spec?.type || 'resource'),\n region: annotations[ANYSHIFT_CLOUD_REGION_ANNOTATION] || 'Global',\n account:\n annotations[ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION] || 'Not available',\n owner:\n typeof entity.spec?.owner === 'string'\n ? entity.spec.owner\n : 'Not assigned',\n };\n })\n .sort((a, b) => a.name.localeCompare(b.name));\n}\n\nconst columns: TableColumn<CloudResourceRow>[] = [\n {\n title: 'Resource',\n field: 'name',\n render: row => <EntityRefLink entityRef={row.entity} title={row.name} />,\n },\n { title: 'Type', field: 'resourceType' },\n { title: 'Provider', field: 'provider' },\n { title: 'Region', field: 'region' },\n { title: 'Account', field: 'account' },\n { title: 'Owner', field: 'owner' },\n];\n\nexport const CloudPage = () => {\n const catalogApi = useApi(catalogApiRef);\n const [entities, setEntities] = useState<Entity[]>();\n const [error, setError] = useState<string>();\n\n useEffect(() => {\n catalogApi\n .getEntities({ filter: { kind: 'Resource' } })\n .then(response => setEntities(response.items))\n .catch(cause =>\n setError(cause instanceof Error ? cause.message : String(cause)),\n );\n }, [catalogApi]);\n\n const rows = useMemo(() => cloudResourceRows(entities ?? []), [entities]);\n const summary = useMemo(\n () => ({\n resources: rows.length,\n types: new Set(rows.map(row => row.resourceType)).size,\n accounts: new Set(\n rows\n .map(row => row.account)\n .filter(account => account !== 'Not available'),\n ).size,\n regions: new Set(\n rows.map(row => row.region).filter(region => region !== 'Global'),\n ).size,\n }),\n [rows],\n );\n\n return (\n <Page themeId=\"tool\">\n <Header\n title=\"Cloud\"\n subtitle=\"Read-only cloud inventory synchronized through Anyshift\"\n />\n <Content>\n <ContentHeader title=\"Cloud inventory\" />\n {error ? (\n <Typography color=\"error\">{error}</Typography>\n ) : !entities ? (\n <Progress />\n ) : (\n <>\n <InfoCard>\n <Grid container spacing={2}>\n <Summary label=\"Resources\" value={summary.resources} />\n <Summary label=\"Types\" value={summary.types} />\n <Summary label=\"Accounts\" value={summary.accounts} />\n <Summary label=\"Regions\" value={summary.regions} />\n </Grid>\n </InfoCard>\n <Table\n title=\"Cloud resources\"\n columns={columns}\n data={rows}\n options={{\n filtering: true,\n paging: true,\n pageSize: 20,\n pageSizeOptions: [20, 50, 100],\n search: true,\n }}\n emptyContent={\n <Typography color=\"textSecondary\">\n No cloud resources have been synchronized yet.\n </Typography>\n }\n />\n </>\n )}\n </Content>\n </Page>\n );\n};\n\nconst Summary = ({ label, value }: { label: string; value: number }) => (\n <Grid item xs={6} md={3}>\n <Typography variant=\"h5\">{value}</Typography>\n <Typography color=\"textSecondary\">{label}</Typography>\n </Grid>\n);\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
+
import { useState, useCallback, useEffect } from 'react';
|
|
3
|
+
import { Button, Grid, Typography, CircularProgress, Chip } from '@material-ui/core';
|
|
4
|
+
import { Alert } from '@material-ui/lab';
|
|
5
|
+
import { Progress, InfoCard, Link } from '@backstage/core-components';
|
|
6
|
+
import { useApi } from '@backstage/frontend-plugin-api';
|
|
7
|
+
import { ANYSHIFT_RESOURCE_ID_ANNOTATION, ANYSHIFT_CLOUD_RESOURCE_ARN_ANNOTATION, ANYSHIFT_CLOUD_PROVIDER_ANNOTATION, ANYSHIFT_RESOURCE_TYPE_ANNOTATION, ANYSHIFT_CLOUD_REGION_ANNOTATION, ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION } from '@anyshift/backstage-plugin-anyshift-common';
|
|
8
|
+
import { useEntity, EntityRefLink } from '@backstage/plugin-catalog-react';
|
|
9
|
+
import { anyshiftApiRef } from '../../api/AnyshiftApi.esm.js';
|
|
10
|
+
|
|
11
|
+
function cloudResourceIdentity(entity) {
|
|
12
|
+
const annotations = entity.metadata.annotations ?? {};
|
|
13
|
+
return annotations[ANYSHIFT_RESOURCE_ID_ANNOTATION] || annotations[ANYSHIFT_CLOUD_RESOURCE_ARN_ANNOTATION];
|
|
14
|
+
}
|
|
15
|
+
const CloudResourceContent = () => {
|
|
16
|
+
const { entity } = useEntity();
|
|
17
|
+
const api = useApi(anyshiftApiRef);
|
|
18
|
+
const resourceId = cloudResourceIdentity(entity);
|
|
19
|
+
const [brief, setBrief] = useState();
|
|
20
|
+
const [error, setError] = useState();
|
|
21
|
+
const [refreshing, setRefreshing] = useState(false);
|
|
22
|
+
const [loadingMore, setLoadingMore] = useState(false);
|
|
23
|
+
const load = useCallback(
|
|
24
|
+
async (refresh = false) => {
|
|
25
|
+
if (!resourceId) return;
|
|
26
|
+
setRefreshing(refresh);
|
|
27
|
+
setError(void 0);
|
|
28
|
+
try {
|
|
29
|
+
setBrief(
|
|
30
|
+
await api.cloudResourceBrief(resourceId, {
|
|
31
|
+
since: "24h",
|
|
32
|
+
diff: true,
|
|
33
|
+
refresh
|
|
34
|
+
})
|
|
35
|
+
);
|
|
36
|
+
} catch (loadError) {
|
|
37
|
+
setError(
|
|
38
|
+
loadError instanceof Error ? loadError.message : "Unable to load cloud evidence"
|
|
39
|
+
);
|
|
40
|
+
} finally {
|
|
41
|
+
setRefreshing(false);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
[api, resourceId]
|
|
45
|
+
);
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
void load();
|
|
48
|
+
}, [load]);
|
|
49
|
+
const loadMore = useCallback(async () => {
|
|
50
|
+
const cursor = brief?.changes.data?.page.nextCursor;
|
|
51
|
+
if (!resourceId || !cursor) return;
|
|
52
|
+
setLoadingMore(true);
|
|
53
|
+
try {
|
|
54
|
+
const next = await api.cloudResourceBrief(resourceId, {
|
|
55
|
+
since: brief.since,
|
|
56
|
+
diff: true,
|
|
57
|
+
cursor
|
|
58
|
+
});
|
|
59
|
+
setBrief((current) => {
|
|
60
|
+
if (!current?.changes.data || next.changes.state !== "data" || !next.changes.data) {
|
|
61
|
+
return current;
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
...current,
|
|
65
|
+
refreshedAt: next.refreshedAt,
|
|
66
|
+
changes: {
|
|
67
|
+
state: "data",
|
|
68
|
+
data: {
|
|
69
|
+
...next.changes.data,
|
|
70
|
+
items: [
|
|
71
|
+
...current.changes.data.items,
|
|
72
|
+
...next.changes.data.items
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
} catch (loadError) {
|
|
79
|
+
setError(
|
|
80
|
+
loadError instanceof Error ? loadError.message : "Unable to load more cloud changes"
|
|
81
|
+
);
|
|
82
|
+
} finally {
|
|
83
|
+
setLoadingMore(false);
|
|
84
|
+
}
|
|
85
|
+
}, [api, brief, resourceId]);
|
|
86
|
+
if (!resourceId) {
|
|
87
|
+
return /* @__PURE__ */ jsx(Alert, { severity: "info", children: "This catalog entity has no stable Anyshift resource ID or cloud ARN." });
|
|
88
|
+
}
|
|
89
|
+
if (error && !brief) {
|
|
90
|
+
return /* @__PURE__ */ jsxs(Alert, { severity: "error", children: [
|
|
91
|
+
error,
|
|
92
|
+
" ",
|
|
93
|
+
/* @__PURE__ */ jsx(Button, { onClick: () => void load(true), children: "Retry" })
|
|
94
|
+
] });
|
|
95
|
+
}
|
|
96
|
+
if (!brief) return /* @__PURE__ */ jsx(Progress, {});
|
|
97
|
+
return /* @__PURE__ */ jsx(
|
|
98
|
+
CloudResourceBriefView,
|
|
99
|
+
{
|
|
100
|
+
entity,
|
|
101
|
+
brief,
|
|
102
|
+
refreshing,
|
|
103
|
+
onRefresh: () => void load(true),
|
|
104
|
+
loadingMore,
|
|
105
|
+
onLoadMore: () => void loadMore(),
|
|
106
|
+
error
|
|
107
|
+
}
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
const CloudResourceBriefView = ({
|
|
111
|
+
entity,
|
|
112
|
+
brief,
|
|
113
|
+
refreshing,
|
|
114
|
+
onRefresh,
|
|
115
|
+
loadingMore,
|
|
116
|
+
onLoadMore,
|
|
117
|
+
error
|
|
118
|
+
}) => {
|
|
119
|
+
const annotations = entity.metadata.annotations ?? {};
|
|
120
|
+
const relationships = (entity.relations ?? []).filter(
|
|
121
|
+
(relation) => ["dependencyOf", "dependsOn"].includes(relation.type)
|
|
122
|
+
);
|
|
123
|
+
const consoleLink = entity.metadata.links?.find(
|
|
124
|
+
(link) => link.url.startsWith("https://console.aws.amazon.com/")
|
|
125
|
+
);
|
|
126
|
+
return /* @__PURE__ */ jsxs(Grid, { container: true, spacing: 3, children: [
|
|
127
|
+
error && /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(Alert, { severity: "error", children: error }) }),
|
|
128
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(
|
|
129
|
+
InfoCard,
|
|
130
|
+
{
|
|
131
|
+
title: "Cloud evidence",
|
|
132
|
+
action: /* @__PURE__ */ jsx(
|
|
133
|
+
Button,
|
|
134
|
+
{
|
|
135
|
+
size: "small",
|
|
136
|
+
onClick: onRefresh,
|
|
137
|
+
disabled: refreshing,
|
|
138
|
+
startIcon: refreshing ? /* @__PURE__ */ jsx(CircularProgress, { size: 14 }) : void 0,
|
|
139
|
+
children: "Refresh"
|
|
140
|
+
}
|
|
141
|
+
),
|
|
142
|
+
children: /* @__PURE__ */ jsxs(Typography, { color: "textSecondary", children: [
|
|
143
|
+
"Read-only Anyshift evidence \xB7 last refreshed",
|
|
144
|
+
" ",
|
|
145
|
+
new Date(brief.refreshedAt).toLocaleString(),
|
|
146
|
+
" \xB7 window",
|
|
147
|
+
" ",
|
|
148
|
+
brief.since
|
|
149
|
+
] })
|
|
150
|
+
}
|
|
151
|
+
) }),
|
|
152
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(
|
|
153
|
+
ChangesSection,
|
|
154
|
+
{
|
|
155
|
+
section: brief.changes,
|
|
156
|
+
loadingMore,
|
|
157
|
+
onLoadMore
|
|
158
|
+
}
|
|
159
|
+
) }),
|
|
160
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */ jsx(ProvenanceSection, { section: brief.provenance }) }),
|
|
161
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */ jsx(DriftSection, { section: brief.drift }) }),
|
|
162
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */ jsxs(InfoCard, { title: "Cloud identity", children: [
|
|
163
|
+
/* @__PURE__ */ jsx(
|
|
164
|
+
Definition,
|
|
165
|
+
{
|
|
166
|
+
label: "Provider",
|
|
167
|
+
value: annotations[ANYSHIFT_CLOUD_PROVIDER_ANNOTATION]
|
|
168
|
+
}
|
|
169
|
+
),
|
|
170
|
+
/* @__PURE__ */ jsx(
|
|
171
|
+
Definition,
|
|
172
|
+
{
|
|
173
|
+
label: "Resource type",
|
|
174
|
+
value: annotations[ANYSHIFT_RESOURCE_TYPE_ANNOTATION] || String(entity.spec?.type || "")
|
|
175
|
+
}
|
|
176
|
+
),
|
|
177
|
+
/* @__PURE__ */ jsx(
|
|
178
|
+
Definition,
|
|
179
|
+
{
|
|
180
|
+
label: "Region",
|
|
181
|
+
value: annotations[ANYSHIFT_CLOUD_REGION_ANNOTATION] || "Global"
|
|
182
|
+
}
|
|
183
|
+
),
|
|
184
|
+
/* @__PURE__ */ jsx(
|
|
185
|
+
Definition,
|
|
186
|
+
{
|
|
187
|
+
label: "Account",
|
|
188
|
+
value: annotations[ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION]
|
|
189
|
+
}
|
|
190
|
+
),
|
|
191
|
+
consoleLink && /* @__PURE__ */ jsx(Link, { to: consoleLink.url, children: "Open in AWS Console" })
|
|
192
|
+
] }) }),
|
|
193
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */ jsxs(InfoCard, { title: "Catalog context", children: [
|
|
194
|
+
/* @__PURE__ */ jsx(
|
|
195
|
+
Definition,
|
|
196
|
+
{
|
|
197
|
+
label: "Owner",
|
|
198
|
+
value: typeof entity.spec?.owner === "string" ? entity.spec.owner : void 0
|
|
199
|
+
}
|
|
200
|
+
),
|
|
201
|
+
/* @__PURE__ */ jsx(
|
|
202
|
+
Definition,
|
|
203
|
+
{
|
|
204
|
+
label: "Namespace",
|
|
205
|
+
value: entity.metadata.namespace || "default"
|
|
206
|
+
}
|
|
207
|
+
),
|
|
208
|
+
/* @__PURE__ */ jsx(Typography, { variant: "subtitle2", children: "Relationships" }),
|
|
209
|
+
relationships.length === 0 ? /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: "No component or resource relationships are available yet." }) : /* @__PURE__ */ jsx("ul", { children: relationships.map((relation) => /* @__PURE__ */ jsxs("li", { children: [
|
|
210
|
+
/* @__PURE__ */ jsx(
|
|
211
|
+
Chip,
|
|
212
|
+
{
|
|
213
|
+
size: "small",
|
|
214
|
+
label: relation.type,
|
|
215
|
+
style: { marginRight: 8 }
|
|
216
|
+
}
|
|
217
|
+
),
|
|
218
|
+
/* @__PURE__ */ jsx(EntityRefLink, { entityRef: relation.targetRef })
|
|
219
|
+
] }, `${relation.type}:${relation.targetRef}`)) })
|
|
220
|
+
] }) }),
|
|
221
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(InfoCard, { title: "Stable resource identity", children: /* @__PURE__ */ jsx(
|
|
222
|
+
Typography,
|
|
223
|
+
{
|
|
224
|
+
component: "code",
|
|
225
|
+
style: { overflowWrap: "anywhere", whiteSpace: "pre-wrap" },
|
|
226
|
+
children: brief.resourceId
|
|
227
|
+
}
|
|
228
|
+
) }) })
|
|
229
|
+
] });
|
|
230
|
+
};
|
|
231
|
+
const ChangesSection = ({
|
|
232
|
+
section,
|
|
233
|
+
loadingMore,
|
|
234
|
+
onLoadMore
|
|
235
|
+
}) => /* @__PURE__ */ jsx(InfoCard, { title: "Recent cloud changes", children: /* @__PURE__ */ jsxs(SectionState, { section, children: [
|
|
236
|
+
section.data?.warnings?.map((warning) => /* @__PURE__ */ jsx(Alert, { severity: "warning", style: { marginBottom: 8 }, children: humanize(warning) }, warning)),
|
|
237
|
+
section.data?.items?.length ? section.data.items.map((event) => /* @__PURE__ */ jsxs("div", { style: { marginBottom: 20 }, children: [
|
|
238
|
+
/* @__PURE__ */ jsx(Typography, { variant: "subtitle1", children: event.summary || humanize(event.type) }),
|
|
239
|
+
/* @__PURE__ */ jsxs(Typography, { color: "textSecondary", children: [
|
|
240
|
+
new Date(event.timestamp).toLocaleString(),
|
|
241
|
+
" \xB7 ",
|
|
242
|
+
event.category,
|
|
243
|
+
" \xB7",
|
|
244
|
+
" ",
|
|
245
|
+
event.evidence.source,
|
|
246
|
+
" \xB7 ",
|
|
247
|
+
event.evidence.status,
|
|
248
|
+
event.actor?.name || event.actor?.id ? ` \xB7 ${event.actor.name || event.actor.id}` : ""
|
|
249
|
+
] }),
|
|
250
|
+
event.change.fields.length > 0 && /* @__PURE__ */ jsxs("details", { children: [
|
|
251
|
+
/* @__PURE__ */ jsx("summary", { children: "View sanitized field changes" }),
|
|
252
|
+
/* @__PURE__ */ jsx("ul", { children: event.change.fields.map((field) => {
|
|
253
|
+
const detail = event.change.details.find(
|
|
254
|
+
(candidate) => candidate.field === field
|
|
255
|
+
);
|
|
256
|
+
return /* @__PURE__ */ jsxs("li", { children: [
|
|
257
|
+
/* @__PURE__ */ jsx("code", { children: field }),
|
|
258
|
+
detail ? `: ${detail.before ?? "\u2205"} \u2192 ${detail.after ?? "\u2205"}` : ""
|
|
259
|
+
] }, field);
|
|
260
|
+
}) })
|
|
261
|
+
] })
|
|
262
|
+
] }, event.id)) : /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: "No cloud changes were found in this window." }),
|
|
263
|
+
section.data?.page.hasMore && onLoadMore && /* @__PURE__ */ jsx(
|
|
264
|
+
Button,
|
|
265
|
+
{
|
|
266
|
+
size: "small",
|
|
267
|
+
onClick: onLoadMore,
|
|
268
|
+
disabled: loadingMore,
|
|
269
|
+
startIcon: loadingMore ? /* @__PURE__ */ jsx(CircularProgress, { size: 14 }) : void 0,
|
|
270
|
+
children: "Load more changes"
|
|
271
|
+
}
|
|
272
|
+
)
|
|
273
|
+
] }) });
|
|
274
|
+
const ProvenanceSection = ({
|
|
275
|
+
section
|
|
276
|
+
}) => /* @__PURE__ */ jsx(InfoCard, { title: "IaC provenance", children: /* @__PURE__ */ jsx(SectionState, { section, children: section.data?.resources?.length ? section.data.resources.map((resource) => /* @__PURE__ */ jsxs("div", { style: { marginBottom: 16 }, children: [
|
|
277
|
+
/* @__PURE__ */ jsx(Typography, { variant: "subtitle1", children: resource.terraform.address }),
|
|
278
|
+
/* @__PURE__ */ jsx(Chip, { size: "small", label: humanize(resource.status) }),
|
|
279
|
+
/* @__PURE__ */ jsx(Definition, { label: "File", value: resource.terraform.fileRef ?? "" }),
|
|
280
|
+
/* @__PURE__ */ jsx(
|
|
281
|
+
Definition,
|
|
282
|
+
{
|
|
283
|
+
label: "State links",
|
|
284
|
+
value: String(resource.states.length)
|
|
285
|
+
}
|
|
286
|
+
),
|
|
287
|
+
/* @__PURE__ */ jsx(
|
|
288
|
+
Definition,
|
|
289
|
+
{
|
|
290
|
+
label: "Cloud links",
|
|
291
|
+
value: String(resource.clouds.length)
|
|
292
|
+
}
|
|
293
|
+
),
|
|
294
|
+
resource.terraform.githubUrl && /* @__PURE__ */ jsx(Link, { to: resource.terraform.githubUrl, children: "Open source" })
|
|
295
|
+
] }, resource.terraform.hashedID)) : /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: "No Terraform provenance is linked to this resource." }) }) });
|
|
296
|
+
const DriftSection = ({
|
|
297
|
+
section
|
|
298
|
+
}) => /* @__PURE__ */ jsx(InfoCard, { title: "IaC drift evidence", children: /* @__PURE__ */ jsx(SectionState, { section, children: section.data?.items?.length ? section.data.items.map((item) => /* @__PURE__ */ jsxs("div", { style: { marginBottom: 16 }, children: [
|
|
299
|
+
/* @__PURE__ */ jsx(Typography, { variant: "subtitle1", children: item.terraform.address }),
|
|
300
|
+
/* @__PURE__ */ jsx(
|
|
301
|
+
Chip,
|
|
302
|
+
{
|
|
303
|
+
size: "small",
|
|
304
|
+
color: item.status === "drifted" ? "secondary" : "default",
|
|
305
|
+
label: humanize(item.status)
|
|
306
|
+
}
|
|
307
|
+
),
|
|
308
|
+
/* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: item.status === "unknown" ? `Unknown is not drift: ${humanize(item.reason)}.` : `${item.comparedFields} supported fields compared.` }),
|
|
309
|
+
item.differences.length > 0 && /* @__PURE__ */ jsxs("details", { children: [
|
|
310
|
+
/* @__PURE__ */ jsx("summary", { children: "View sanitized differences" }),
|
|
311
|
+
/* @__PURE__ */ jsx("ul", { children: item.differences.map((difference) => /* @__PURE__ */ jsxs("li", { children: [
|
|
312
|
+
/* @__PURE__ */ jsx("code", { children: difference.field }),
|
|
313
|
+
":",
|
|
314
|
+
" ",
|
|
315
|
+
difference.lastApplied ?? "\u2205",
|
|
316
|
+
" \u2192",
|
|
317
|
+
" ",
|
|
318
|
+
difference.observed ?? "\u2205"
|
|
319
|
+
] }, difference.field)) })
|
|
320
|
+
] })
|
|
321
|
+
] }, item.terraform.hashedID)) : /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: "No comparable Terraform evidence is available." }) }) });
|
|
322
|
+
const SectionState = ({
|
|
323
|
+
section,
|
|
324
|
+
children
|
|
325
|
+
}) => {
|
|
326
|
+
if (section.state === "error") {
|
|
327
|
+
return /* @__PURE__ */ jsx(Alert, { severity: "error", children: section.message });
|
|
328
|
+
}
|
|
329
|
+
if (section.state === "unsupported") {
|
|
330
|
+
return /* @__PURE__ */ jsx(Alert, { severity: "info", children: section.message });
|
|
331
|
+
}
|
|
332
|
+
if (section.state === "empty") {
|
|
333
|
+
return /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: section.message });
|
|
334
|
+
}
|
|
335
|
+
return /* @__PURE__ */ jsx(Fragment, { children });
|
|
336
|
+
};
|
|
337
|
+
const Definition = ({ label, value }) => /* @__PURE__ */ jsxs(Typography, { paragraph: true, children: [
|
|
338
|
+
/* @__PURE__ */ jsxs("strong", { children: [
|
|
339
|
+
label,
|
|
340
|
+
":"
|
|
341
|
+
] }),
|
|
342
|
+
" ",
|
|
343
|
+
value || "Not available"
|
|
344
|
+
] });
|
|
345
|
+
function humanize(value) {
|
|
346
|
+
return value.replaceAll("_", " ");
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export { CloudResourceBriefView, CloudResourceContent, cloudResourceIdentity };
|
|
350
|
+
//# sourceMappingURL=CloudResourceContent.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CloudResourceContent.esm.js","sources":["../../../src/components/cloud/CloudResourceContent.tsx"],"sourcesContent":["import { useCallback, useEffect, useState, type ReactNode } from 'react';\nimport {\n Button,\n Chip,\n CircularProgress,\n Grid,\n Typography,\n} from '@material-ui/core';\nimport { Alert } from '@material-ui/lab';\nimport { InfoCard, Link, Progress } from '@backstage/core-components';\nimport { useApi } from '@backstage/frontend-plugin-api';\nimport type { Entity } from '@backstage/catalog-model';\nimport {\n ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION,\n ANYSHIFT_CLOUD_PROVIDER_ANNOTATION,\n ANYSHIFT_CLOUD_REGION_ANNOTATION,\n ANYSHIFT_CLOUD_RESOURCE_ARN_ANNOTATION,\n ANYSHIFT_RESOURCE_ID_ANNOTATION,\n ANYSHIFT_RESOURCE_TYPE_ANNOTATION,\n type BriefSection,\n type CloudEventsEvidence,\n type CloudResourceBrief,\n type IacDriftEvidence,\n type IacEvidence,\n} from '@anyshift/backstage-plugin-anyshift-common';\nimport { EntityRefLink, useEntity } from '@backstage/plugin-catalog-react';\nimport { anyshiftApiRef } from '../../api/AnyshiftApi';\n\nexport function cloudResourceIdentity(entity: Entity): string | undefined {\n const annotations = entity.metadata.annotations ?? {};\n return (\n annotations[ANYSHIFT_RESOURCE_ID_ANNOTATION] ||\n annotations[ANYSHIFT_CLOUD_RESOURCE_ARN_ANNOTATION]\n );\n}\n\nexport const CloudResourceContent = () => {\n const { entity } = useEntity();\n const api = useApi(anyshiftApiRef);\n const resourceId = cloudResourceIdentity(entity);\n const [brief, setBrief] = useState<CloudResourceBrief>();\n const [error, setError] = useState<string>();\n const [refreshing, setRefreshing] = useState(false);\n const [loadingMore, setLoadingMore] = useState(false);\n\n const load = useCallback(\n async (refresh = false) => {\n if (!resourceId) return;\n setRefreshing(refresh);\n setError(undefined);\n try {\n setBrief(\n await api.cloudResourceBrief(resourceId, {\n since: '24h',\n diff: true,\n refresh,\n }),\n );\n } catch (loadError) {\n setError(\n loadError instanceof Error\n ? loadError.message\n : 'Unable to load cloud evidence',\n );\n } finally {\n setRefreshing(false);\n }\n },\n [api, resourceId],\n );\n\n useEffect(() => {\n void load();\n }, [load]);\n\n const loadMore = useCallback(async () => {\n const cursor = brief?.changes.data?.page.nextCursor;\n if (!resourceId || !cursor) return;\n setLoadingMore(true);\n try {\n const next = await api.cloudResourceBrief(resourceId, {\n since: brief.since,\n diff: true,\n cursor,\n });\n setBrief(current => {\n if (\n !current?.changes.data ||\n next.changes.state !== 'data' ||\n !next.changes.data\n ) {\n return current;\n }\n return {\n ...current,\n refreshedAt: next.refreshedAt,\n changes: {\n state: 'data',\n data: {\n ...next.changes.data,\n items: [\n ...current.changes.data.items,\n ...next.changes.data.items,\n ],\n },\n },\n };\n });\n } catch (loadError) {\n setError(\n loadError instanceof Error\n ? loadError.message\n : 'Unable to load more cloud changes',\n );\n } finally {\n setLoadingMore(false);\n }\n }, [api, brief, resourceId]);\n\n if (!resourceId) {\n return (\n <Alert severity=\"info\">\n This catalog entity has no stable Anyshift resource ID or cloud ARN.\n </Alert>\n );\n }\n if (error && !brief) {\n return (\n <Alert severity=\"error\">\n {error} <Button onClick={() => void load(true)}>Retry</Button>\n </Alert>\n );\n }\n if (!brief) return <Progress />;\n\n return (\n <CloudResourceBriefView\n entity={entity}\n brief={brief}\n refreshing={refreshing}\n onRefresh={() => void load(true)}\n loadingMore={loadingMore}\n onLoadMore={() => void loadMore()}\n error={error}\n />\n );\n};\n\nexport const CloudResourceBriefView = ({\n entity,\n brief,\n refreshing,\n onRefresh,\n loadingMore,\n onLoadMore,\n error,\n}: {\n entity: Entity;\n brief: CloudResourceBrief;\n refreshing?: boolean;\n onRefresh: () => void;\n loadingMore?: boolean;\n onLoadMore?: () => void;\n error?: string;\n}) => {\n const annotations = entity.metadata.annotations ?? {};\n const relationships = (entity.relations ?? []).filter(relation =>\n ['dependencyOf', 'dependsOn'].includes(relation.type),\n );\n const consoleLink = entity.metadata.links?.find(link =>\n link.url.startsWith('https://console.aws.amazon.com/'),\n );\n\n return (\n <Grid container spacing={3}>\n {error && (\n <Grid item xs={12}>\n <Alert severity=\"error\">{error}</Alert>\n </Grid>\n )}\n <Grid item xs={12}>\n <InfoCard\n title=\"Cloud evidence\"\n action={\n <Button\n size=\"small\"\n onClick={onRefresh}\n disabled={refreshing}\n startIcon={\n refreshing ? <CircularProgress size={14} /> : undefined\n }\n >\n Refresh\n </Button>\n }\n >\n <Typography color=\"textSecondary\">\n Read-only Anyshift evidence · last refreshed{' '}\n {new Date(brief.refreshedAt).toLocaleString()} · window{' '}\n {brief.since}\n </Typography>\n </InfoCard>\n </Grid>\n\n <Grid item xs={12}>\n <ChangesSection\n section={brief.changes}\n loadingMore={loadingMore}\n onLoadMore={onLoadMore}\n />\n </Grid>\n <Grid item xs={12} md={6}>\n <ProvenanceSection section={brief.provenance} />\n </Grid>\n <Grid item xs={12} md={6}>\n <DriftSection section={brief.drift} />\n </Grid>\n\n <Grid item xs={12} md={6}>\n <InfoCard title=\"Cloud identity\">\n <Definition\n label=\"Provider\"\n value={annotations[ANYSHIFT_CLOUD_PROVIDER_ANNOTATION]}\n />\n <Definition\n label=\"Resource type\"\n value={\n annotations[ANYSHIFT_RESOURCE_TYPE_ANNOTATION] ||\n String(entity.spec?.type || '')\n }\n />\n <Definition\n label=\"Region\"\n value={annotations[ANYSHIFT_CLOUD_REGION_ANNOTATION] || 'Global'}\n />\n <Definition\n label=\"Account\"\n value={annotations[ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION]}\n />\n {consoleLink && <Link to={consoleLink.url}>Open in AWS Console</Link>}\n </InfoCard>\n </Grid>\n <Grid item xs={12} md={6}>\n <InfoCard title=\"Catalog context\">\n <Definition\n label=\"Owner\"\n value={\n typeof entity.spec?.owner === 'string'\n ? entity.spec.owner\n : undefined\n }\n />\n <Definition\n label=\"Namespace\"\n value={entity.metadata.namespace || 'default'}\n />\n <Typography variant=\"subtitle2\">Relationships</Typography>\n {relationships.length === 0 ? (\n <Typography color=\"textSecondary\">\n No component or resource relationships are available yet.\n </Typography>\n ) : (\n <ul>\n {relationships.map(relation => (\n <li key={`${relation.type}:${relation.targetRef}`}>\n <Chip\n size=\"small\"\n label={relation.type}\n style={{ marginRight: 8 }}\n />\n <EntityRefLink entityRef={relation.targetRef} />\n </li>\n ))}\n </ul>\n )}\n </InfoCard>\n </Grid>\n <Grid item xs={12}>\n <InfoCard title=\"Stable resource identity\">\n <Typography\n component=\"code\"\n style={{ overflowWrap: 'anywhere', whiteSpace: 'pre-wrap' }}\n >\n {brief.resourceId}\n </Typography>\n </InfoCard>\n </Grid>\n </Grid>\n );\n};\n\nconst ChangesSection = ({\n section,\n loadingMore,\n onLoadMore,\n}: {\n section: BriefSection<CloudEventsEvidence>;\n loadingMore?: boolean;\n onLoadMore?: () => void;\n}) => (\n <InfoCard title=\"Recent cloud changes\">\n <SectionState section={section}>\n {section.data?.warnings?.map(warning => (\n <Alert severity=\"warning\" key={warning} style={{ marginBottom: 8 }}>\n {humanize(warning)}\n </Alert>\n ))}\n {section.data?.items?.length ? (\n section.data.items.map(event => (\n <div key={event.id} style={{ marginBottom: 20 }}>\n <Typography variant=\"subtitle1\">\n {event.summary || humanize(event.type)}\n </Typography>\n <Typography color=\"textSecondary\">\n {new Date(event.timestamp).toLocaleString()} · {event.category} ·{' '}\n {event.evidence.source} · {event.evidence.status}\n {event.actor?.name || event.actor?.id\n ? ` · ${event.actor.name || event.actor.id}`\n : ''}\n </Typography>\n {event.change.fields.length > 0 && (\n <details>\n <summary>View sanitized field changes</summary>\n <ul>\n {event.change.fields.map(field => {\n const detail = event.change.details.find(\n candidate => candidate.field === field,\n );\n return (\n <li key={field}>\n <code>{field}</code>\n {detail\n ? `: ${detail.before ?? '∅'} → ${detail.after ?? '∅'}`\n : ''}\n </li>\n );\n })}\n </ul>\n </details>\n )}\n </div>\n ))\n ) : (\n <Typography color=\"textSecondary\">\n No cloud changes were found in this window.\n </Typography>\n )}\n {section.data?.page.hasMore && onLoadMore && (\n <Button\n size=\"small\"\n onClick={onLoadMore}\n disabled={loadingMore}\n startIcon={loadingMore ? <CircularProgress size={14} /> : undefined}\n >\n Load more changes\n </Button>\n )}\n </SectionState>\n </InfoCard>\n);\n\nconst ProvenanceSection = ({\n section,\n}: {\n section: BriefSection<IacEvidence>;\n}) => (\n <InfoCard title=\"IaC provenance\">\n <SectionState section={section}>\n {section.data?.resources?.length ? (\n section.data.resources.map(resource => (\n <div key={resource.terraform.hashedID} style={{ marginBottom: 16 }}>\n <Typography variant=\"subtitle1\">\n {resource.terraform.address}\n </Typography>\n <Chip size=\"small\" label={humanize(resource.status)} />\n <Definition label=\"File\" value={resource.terraform.fileRef ?? ''} />\n <Definition\n label=\"State links\"\n value={String(resource.states.length)}\n />\n <Definition\n label=\"Cloud links\"\n value={String(resource.clouds.length)}\n />\n {resource.terraform.githubUrl && (\n <Link to={resource.terraform.githubUrl}>Open source</Link>\n )}\n </div>\n ))\n ) : (\n <Typography color=\"textSecondary\">\n No Terraform provenance is linked to this resource.\n </Typography>\n )}\n </SectionState>\n </InfoCard>\n);\n\nconst DriftSection = ({\n section,\n}: {\n section: BriefSection<IacDriftEvidence>;\n}) => (\n <InfoCard title=\"IaC drift evidence\">\n <SectionState section={section}>\n {section.data?.items?.length ? (\n section.data.items.map(item => (\n <div key={item.terraform.hashedID} style={{ marginBottom: 16 }}>\n <Typography variant=\"subtitle1\">\n {item.terraform.address}\n </Typography>\n <Chip\n size=\"small\"\n color={item.status === 'drifted' ? 'secondary' : 'default'}\n label={humanize(item.status)}\n />\n <Typography color=\"textSecondary\">\n {item.status === 'unknown'\n ? `Unknown is not drift: ${humanize(item.reason)}.`\n : `${item.comparedFields} supported fields compared.`}\n </Typography>\n {item.differences.length > 0 && (\n <details>\n <summary>View sanitized differences</summary>\n <ul>\n {item.differences.map(difference => (\n <li key={difference.field}>\n <code>{difference.field}</code>:{' '}\n {difference.lastApplied ?? '∅'} →{' '}\n {difference.observed ?? '∅'}\n </li>\n ))}\n </ul>\n </details>\n )}\n </div>\n ))\n ) : (\n <Typography color=\"textSecondary\">\n No comparable Terraform evidence is available.\n </Typography>\n )}\n </SectionState>\n </InfoCard>\n);\n\nconst SectionState = ({\n section,\n children,\n}: {\n section: BriefSection;\n children: ReactNode;\n}) => {\n if (section.state === 'error') {\n return <Alert severity=\"error\">{section.message}</Alert>;\n }\n if (section.state === 'unsupported') {\n return <Alert severity=\"info\">{section.message}</Alert>;\n }\n if (section.state === 'empty') {\n return <Typography color=\"textSecondary\">{section.message}</Typography>;\n }\n return <>{children}</>;\n};\n\nconst Definition = ({ label, value }: { label: string; value?: string }) => (\n <Typography paragraph>\n <strong>{label}:</strong> {value || 'Not available'}\n </Typography>\n);\n\nfunction humanize(value: string): string {\n return value.replaceAll('_', ' ');\n}\n"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -2,11 +2,12 @@ import * as react from 'react';
|
|
|
2
2
|
import * as _backstage_filter_predicates from '@backstage/filter-predicates';
|
|
3
3
|
import * as _backstage_frontend_plugin_api from '@backstage/frontend-plugin-api';
|
|
4
4
|
import { Entity } from '@backstage/catalog-model';
|
|
5
|
-
import { AnyshiftCapabilities, ServiceBrief, BriefSectionName, BriefSectionResponse, EstateInsights, DatadogSignals, CatalogSummary, QueryResult, CatalogSyncReport } from '@anyshift/backstage-plugin-anyshift-common';
|
|
6
|
-
export { ANYSHIFT_KIND_ANNOTATION, ANYSHIFT_OBSERVED_SERVICE_ANNOTATION, ANYSHIFT_RESOURCE_ID_ANNOTATION, ANYSHIFT_RESOURCE_TYPE_ANNOTATION, ANYSHIFT_TARGET_ANNOTATION, BriefSection, CatalogSummary, EstateInsights, GITHUB_PROJECT_SLUG_ANNOTATION, QueryResult, ServiceBrief, anyshiftQueryPermission, anyshiftViewPermission } from '@anyshift/backstage-plugin-anyshift-common';
|
|
5
|
+
import { AnyshiftCapabilities, ServiceBrief, BriefSectionName, BriefSectionResponse, EstateInsights, DatadogSignals, CloudResourceBrief, CatalogSummary, QueryResult, CatalogSyncReport } from '@anyshift/backstage-plugin-anyshift-common';
|
|
6
|
+
export { ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION, ANYSHIFT_CLOUD_PROVIDER_ANNOTATION, ANYSHIFT_CLOUD_REGION_ANNOTATION, ANYSHIFT_CLOUD_RESOURCE_ARN_ANNOTATION, ANYSHIFT_KIND_ANNOTATION, ANYSHIFT_OBSERVED_SERVICE_ANNOTATION, ANYSHIFT_RESOURCE_ID_ANNOTATION, ANYSHIFT_RESOURCE_TYPE_ANNOTATION, ANYSHIFT_TARGET_ANNOTATION, BriefSection, CatalogSummary, CloudEventEvidenceItem, CloudEventsEvidence, CloudResourceBrief, EstateInsights, GITHUB_PROJECT_SLUG_ANNOTATION, IacDriftEvidence, IacEvidence, QueryResult, ServiceBrief, anyshiftQueryPermission, anyshiftViewPermission } from '@anyshift/backstage-plugin-anyshift-common';
|
|
7
7
|
|
|
8
8
|
declare const anyshiftPlugin: _backstage_frontend_plugin_api.OverridableFrontendPlugin<{
|
|
9
9
|
root: _backstage_frontend_plugin_api.RouteRef<undefined>;
|
|
10
|
+
cloud: _backstage_frontend_plugin_api.RouteRef<undefined>;
|
|
10
11
|
}, {}, {
|
|
11
12
|
"api:anyshift": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
|
|
12
13
|
kind: "api";
|
|
@@ -17,6 +18,48 @@ declare const anyshiftPlugin: _backstage_frontend_plugin_api.OverridableFrontend
|
|
|
17
18
|
inputs: {};
|
|
18
19
|
params: <TApi, TImpl extends TApi, TDeps extends { [name in string]: unknown; }>(params: _backstage_frontend_plugin_api.ApiFactory<TApi, TImpl, TDeps>) => _backstage_frontend_plugin_api.ExtensionBlueprintParams<_backstage_frontend_plugin_api.AnyApiFactory>;
|
|
19
20
|
}>;
|
|
21
|
+
"entity-content:anyshift/cloud-resource-content": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
|
|
22
|
+
kind: "entity-content";
|
|
23
|
+
name: "cloud-resource-content";
|
|
24
|
+
config: {
|
|
25
|
+
path: string | undefined;
|
|
26
|
+
title: string | undefined;
|
|
27
|
+
filter: _backstage_filter_predicates.FilterPredicate | undefined;
|
|
28
|
+
group: string | false | undefined;
|
|
29
|
+
icon: string | undefined;
|
|
30
|
+
};
|
|
31
|
+
configInput: {
|
|
32
|
+
path?: string | undefined;
|
|
33
|
+
title?: string | undefined;
|
|
34
|
+
filter?: _backstage_filter_predicates.FilterPredicate | undefined;
|
|
35
|
+
group?: string | false | undefined;
|
|
36
|
+
icon?: string | undefined;
|
|
37
|
+
};
|
|
38
|
+
output: _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
|
|
39
|
+
optional: true;
|
|
40
|
+
}> | _backstage_frontend_plugin_api.ExtensionDataRef<(entity: Entity) => boolean, "catalog.entity-filter-function", {
|
|
41
|
+
optional: true;
|
|
42
|
+
}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "catalog.entity-filter-expression", {
|
|
43
|
+
optional: true;
|
|
44
|
+
}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "catalog.entity-content-title", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "catalog.entity-content-group", {
|
|
45
|
+
optional: true;
|
|
46
|
+
}> | _backstage_frontend_plugin_api.ExtensionDataRef<string | react.ReactElement<any, string | react.JSXElementConstructor<any>>, "catalog.entity-content-icon", {
|
|
47
|
+
optional: true;
|
|
48
|
+
}>;
|
|
49
|
+
inputs: {};
|
|
50
|
+
params: {
|
|
51
|
+
defaultPath?: [Error: `Use the 'path' param instead`];
|
|
52
|
+
path: string;
|
|
53
|
+
defaultTitle?: [Error: `Use the 'title' param instead`];
|
|
54
|
+
title: string;
|
|
55
|
+
defaultGroup?: [Error: `Use the 'group' param instead`];
|
|
56
|
+
group?: ("development" | "deployment" | "overview" | "documentation" | "operation" | "observability") | (string & {});
|
|
57
|
+
icon?: string | react.ReactElement;
|
|
58
|
+
loader: () => Promise<JSX.Element>;
|
|
59
|
+
routeRef?: _backstage_frontend_plugin_api.RouteRef;
|
|
60
|
+
filter?: string | _backstage_filter_predicates.FilterPredicate | ((entity: Entity) => boolean);
|
|
61
|
+
};
|
|
62
|
+
}>;
|
|
20
63
|
"entity-content:anyshift/entity-content": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
|
|
21
64
|
kind: "entity-content";
|
|
22
65
|
name: "entity-content";
|
|
@@ -99,6 +142,46 @@ declare const anyshiftPlugin: _backstage_frontend_plugin_api.OverridableFrontend
|
|
|
99
142
|
noHeader?: boolean;
|
|
100
143
|
};
|
|
101
144
|
}>;
|
|
145
|
+
"page:anyshift/cloud": _backstage_frontend_plugin_api.OverridableExtensionDefinition<{
|
|
146
|
+
kind: "page";
|
|
147
|
+
name: "cloud";
|
|
148
|
+
config: {
|
|
149
|
+
path: string | undefined;
|
|
150
|
+
title: string | undefined;
|
|
151
|
+
};
|
|
152
|
+
configInput: {
|
|
153
|
+
path?: string | undefined;
|
|
154
|
+
title?: string | undefined;
|
|
155
|
+
};
|
|
156
|
+
output: _backstage_frontend_plugin_api.ExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
|
|
157
|
+
optional: true;
|
|
158
|
+
}> | _backstage_frontend_plugin_api.ExtensionDataRef<string, "core.title", {
|
|
159
|
+
optional: true;
|
|
160
|
+
}> | _backstage_frontend_plugin_api.ExtensionDataRef<_backstage_frontend_plugin_api.IconElement, "core.icon", {
|
|
161
|
+
optional: true;
|
|
162
|
+
}>;
|
|
163
|
+
inputs: {
|
|
164
|
+
pages: _backstage_frontend_plugin_api.ExtensionInput<_backstage_frontend_plugin_api.ConfigurableExtensionDataRef<react.JSX.Element, "core.reactElement", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.routing.path", {}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.RouteRef<_backstage_frontend_plugin_api.AnyRouteRefParams>, "core.routing.ref", {
|
|
165
|
+
optional: true;
|
|
166
|
+
}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<string, "core.title", {
|
|
167
|
+
optional: true;
|
|
168
|
+
}> | _backstage_frontend_plugin_api.ConfigurableExtensionDataRef<_backstage_frontend_plugin_api.IconElement, "core.icon", {
|
|
169
|
+
optional: true;
|
|
170
|
+
}>, {
|
|
171
|
+
singleton: false;
|
|
172
|
+
optional: false;
|
|
173
|
+
internal: false;
|
|
174
|
+
}>;
|
|
175
|
+
};
|
|
176
|
+
params: {
|
|
177
|
+
path: string;
|
|
178
|
+
title?: string;
|
|
179
|
+
icon?: _backstage_frontend_plugin_api.IconElement;
|
|
180
|
+
loader?: () => Promise<react.JSX.Element>;
|
|
181
|
+
routeRef?: _backstage_frontend_plugin_api.RouteRef;
|
|
182
|
+
noHeader?: boolean;
|
|
183
|
+
};
|
|
184
|
+
}>;
|
|
102
185
|
}>;
|
|
103
186
|
|
|
104
187
|
interface AnyshiftApi {
|
|
@@ -109,6 +192,7 @@ interface AnyshiftApi {
|
|
|
109
192
|
datadogSignals(options?: {
|
|
110
193
|
refresh?: boolean;
|
|
111
194
|
}): Promise<DatadogSignals>;
|
|
195
|
+
cloudResourceBrief(resource: string, options?: CloudResourceBriefRequestOptions): Promise<CloudResourceBrief>;
|
|
112
196
|
inventory(type: string, options?: {
|
|
113
197
|
limit?: number;
|
|
114
198
|
offset?: number;
|
|
@@ -120,6 +204,12 @@ interface BriefRequestOptions {
|
|
|
120
204
|
namespace?: string;
|
|
121
205
|
refresh?: boolean;
|
|
122
206
|
}
|
|
207
|
+
interface CloudResourceBriefRequestOptions {
|
|
208
|
+
since?: string;
|
|
209
|
+
cursor?: string;
|
|
210
|
+
diff?: boolean;
|
|
211
|
+
refresh?: boolean;
|
|
212
|
+
}
|
|
123
213
|
interface CatalogSyncAccepted {
|
|
124
214
|
accepted: true;
|
|
125
215
|
acceptedAt: string;
|
|
@@ -130,4 +220,4 @@ declare const anyshiftApiRef: _backstage_frontend_plugin_api.ApiRef<AnyshiftApi>
|
|
|
130
220
|
};
|
|
131
221
|
|
|
132
222
|
export { anyshiftApiRef, anyshiftPlugin, anyshiftPlugin as default };
|
|
133
|
-
export type { AnyshiftApi };
|
|
223
|
+
export type { AnyshiftApi, CloudResourceBriefRequestOptions };
|
package/dist/index.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { anyshiftPlugin, anyshiftPlugin as default } from './plugin.esm.js';
|
|
2
2
|
export { anyshiftApiRef } from './api/AnyshiftApi.esm.js';
|
|
3
|
-
export { ANYSHIFT_KIND_ANNOTATION, ANYSHIFT_OBSERVED_SERVICE_ANNOTATION, ANYSHIFT_RESOURCE_ID_ANNOTATION, ANYSHIFT_RESOURCE_TYPE_ANNOTATION, ANYSHIFT_TARGET_ANNOTATION, GITHUB_PROJECT_SLUG_ANNOTATION, anyshiftQueryPermission, anyshiftViewPermission } from '@anyshift/backstage-plugin-anyshift-common';
|
|
3
|
+
export { ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION, ANYSHIFT_CLOUD_PROVIDER_ANNOTATION, ANYSHIFT_CLOUD_REGION_ANNOTATION, ANYSHIFT_CLOUD_RESOURCE_ARN_ANNOTATION, ANYSHIFT_KIND_ANNOTATION, ANYSHIFT_OBSERVED_SERVICE_ANNOTATION, ANYSHIFT_RESOURCE_ID_ANNOTATION, ANYSHIFT_RESOURCE_TYPE_ANNOTATION, ANYSHIFT_TARGET_ANNOTATION, GITHUB_PROJECT_SLUG_ANNOTATION, anyshiftQueryPermission, anyshiftViewPermission } from '@anyshift/backstage-plugin-anyshift-common';
|
|
4
4
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/package.json.esm.js
CHANGED
package/dist/plugin.esm.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { jsx } from 'react/jsx-runtime';
|
|
2
2
|
import DeviceHubIcon from '@material-ui/icons/DeviceHub';
|
|
3
|
+
import CloudQueueIcon from '@material-ui/icons/CloudQueue';
|
|
3
4
|
import { ApiBlueprint, fetchApiRef, discoveryApiRef, PageBlueprint, createFrontendPlugin } from '@backstage/frontend-plugin-api';
|
|
4
5
|
import { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';
|
|
5
|
-
import { anyshiftViewPermission, ANYSHIFT_TARGET_ANNOTATION, GITHUB_PROJECT_SLUG_ANNOTATION, ANYSHIFT_KIND_ANNOTATION, isAnyshiftResourceKind, ANYSHIFT_MANAGED_BY_ANNOTATION } from '@anyshift/backstage-plugin-anyshift-common';
|
|
6
|
+
import { anyshiftViewPermission, ANYSHIFT_TARGET_ANNOTATION, GITHUB_PROJECT_SLUG_ANNOTATION, ANYSHIFT_KIND_ANNOTATION, isAnyshiftResourceKind, ANYSHIFT_MANAGED_BY_ANNOTATION, ANYSHIFT_CLOUD_PROVIDER_ANNOTATION } from '@anyshift/backstage-plugin-anyshift-common';
|
|
6
7
|
import { AnyshiftPermissionBoundary } from './components/AnyshiftPermissionBoundary.esm.js';
|
|
7
8
|
import { AnyshiftClient, anyshiftApiRef } from './api/AnyshiftApi.esm.js';
|
|
8
|
-
import { rootRouteRef } from './routes.esm.js';
|
|
9
|
+
import { rootRouteRef, cloudRouteRef } from './routes.esm.js';
|
|
9
10
|
|
|
10
11
|
const anyshiftApi = ApiBlueprint.make({
|
|
11
12
|
params: (defineParams) => defineParams({
|
|
@@ -24,6 +25,17 @@ const anyshiftPage = PageBlueprint.make({
|
|
|
24
25
|
loader: () => import('./components/explorer/AnyshiftExplorerPage.esm.js').then((module) => /* @__PURE__ */ jsx(AnyshiftPermissionBoundary, { permission: anyshiftViewPermission, children: /* @__PURE__ */ jsx(module.AnyshiftExplorerPage, {}) }))
|
|
25
26
|
}
|
|
26
27
|
});
|
|
28
|
+
const cloudPage = PageBlueprint.make({
|
|
29
|
+
name: "cloud",
|
|
30
|
+
params: {
|
|
31
|
+
path: "/cloud",
|
|
32
|
+
title: "Cloud",
|
|
33
|
+
icon: /* @__PURE__ */ jsx(CloudQueueIcon, { fontSize: "inherit" }),
|
|
34
|
+
routeRef: cloudRouteRef,
|
|
35
|
+
noHeader: true,
|
|
36
|
+
loader: () => import('./components/cloud/CloudPage.esm.js').then((module) => /* @__PURE__ */ jsx(AnyshiftPermissionBoundary, { permission: anyshiftViewPermission, children: /* @__PURE__ */ jsx(module.CloudPage, {}) }))
|
|
37
|
+
}
|
|
38
|
+
});
|
|
27
39
|
const anyshiftEntityContent = EntityContentBlueprint.make({
|
|
28
40
|
name: "entity-content",
|
|
29
41
|
params: {
|
|
@@ -34,6 +46,16 @@ const anyshiftEntityContent = EntityContentBlueprint.make({
|
|
|
34
46
|
loader: () => import('./components/entity/AnyshiftEntityContent.esm.js').then((module) => /* @__PURE__ */ jsx(AnyshiftPermissionBoundary, { permission: anyshiftViewPermission, children: /* @__PURE__ */ jsx(module.AnyshiftEntityContent, {}) }))
|
|
35
47
|
}
|
|
36
48
|
});
|
|
49
|
+
const cloudEntityContent = EntityContentBlueprint.make({
|
|
50
|
+
name: "cloud-resource-content",
|
|
51
|
+
params: {
|
|
52
|
+
path: "/cloud",
|
|
53
|
+
title: "Cloud",
|
|
54
|
+
group: "observability",
|
|
55
|
+
filter: isCloudResourceContentAvailable,
|
|
56
|
+
loader: () => import('./components/cloud/CloudResourceContent.esm.js').then((module) => /* @__PURE__ */ jsx(AnyshiftPermissionBoundary, { permission: anyshiftViewPermission, children: /* @__PURE__ */ jsx(module.CloudResourceContent, {}) }))
|
|
57
|
+
}
|
|
58
|
+
});
|
|
37
59
|
function isAnyshiftEntityContentAvailable(entity) {
|
|
38
60
|
const annotations = entity.metadata.annotations ?? {};
|
|
39
61
|
const hasTarget = Boolean(
|
|
@@ -44,14 +66,23 @@ function isAnyshiftEntityContentAvailable(entity) {
|
|
|
44
66
|
if (kind) return isAnyshiftResourceKind(kind);
|
|
45
67
|
return annotations[ANYSHIFT_MANAGED_BY_ANNOTATION] !== "catalog-provider";
|
|
46
68
|
}
|
|
69
|
+
function isCloudResourceContentAvailable(entity) {
|
|
70
|
+
return entity.kind.toLocaleLowerCase("en-US") === "resource" && Boolean(entity.metadata.annotations?.[ANYSHIFT_CLOUD_PROVIDER_ANNOTATION]);
|
|
71
|
+
}
|
|
47
72
|
const anyshiftPlugin = createFrontendPlugin({
|
|
48
73
|
pluginId: "anyshift",
|
|
49
74
|
info: {
|
|
50
75
|
packageJson: () => import('./package.json.esm.js')
|
|
51
76
|
},
|
|
52
|
-
routes: { root: rootRouteRef },
|
|
53
|
-
extensions: [
|
|
77
|
+
routes: { root: rootRouteRef, cloud: cloudRouteRef },
|
|
78
|
+
extensions: [
|
|
79
|
+
anyshiftApi,
|
|
80
|
+
anyshiftPage,
|
|
81
|
+
cloudPage,
|
|
82
|
+
anyshiftEntityContent,
|
|
83
|
+
cloudEntityContent
|
|
84
|
+
]
|
|
54
85
|
});
|
|
55
86
|
|
|
56
|
-
export { anyshiftPlugin, isAnyshiftEntityContentAvailable };
|
|
87
|
+
export { anyshiftPlugin, isAnyshiftEntityContentAvailable, isCloudResourceContentAvailable };
|
|
57
88
|
//# sourceMappingURL=plugin.esm.js.map
|
package/dist/plugin.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.esm.js","sources":["../src/plugin.tsx"],"sourcesContent":["import DeviceHubIcon from '@material-ui/icons/DeviceHub';\nimport {\n ApiBlueprint,\n PageBlueprint,\n createFrontendPlugin,\n discoveryApiRef,\n fetchApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';\nimport type { Entity } from '@backstage/catalog-model';\nimport {\n ANYSHIFT_KIND_ANNOTATION,\n ANYSHIFT_MANAGED_BY_ANNOTATION,\n ANYSHIFT_TARGET_ANNOTATION,\n GITHUB_PROJECT_SLUG_ANNOTATION,\n anyshiftViewPermission,\n isAnyshiftResourceKind,\n} from '@anyshift/backstage-plugin-anyshift-common';\nimport { AnyshiftPermissionBoundary } from './components/AnyshiftPermissionBoundary';\nimport { anyshiftApiRef, AnyshiftClient } from './api/AnyshiftApi';\nimport { rootRouteRef } from './routes';\n\nconst anyshiftApi = ApiBlueprint.make({\n params: defineParams =>\n defineParams({\n api: anyshiftApiRef,\n deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },\n factory: ({ discoveryApi, fetchApi }) =>\n new AnyshiftClient({ discoveryApi, fetchApi }),\n }),\n});\n\nconst anyshiftPage = PageBlueprint.make({\n params: {\n path: '/anyshift',\n title: 'Anyshift',\n icon: <DeviceHubIcon fontSize=\"inherit\" />,\n routeRef: rootRouteRef,\n noHeader: true,\n loader: () =>\n import('./components/explorer/AnyshiftExplorerPage').then(module => (\n <AnyshiftPermissionBoundary permission={anyshiftViewPermission}>\n <module.AnyshiftExplorerPage />\n </AnyshiftPermissionBoundary>\n )),\n },\n});\n\nconst anyshiftEntityContent = EntityContentBlueprint.make({\n name: 'entity-content',\n params: {\n path: '/anyshift',\n title: 'Anyshift',\n group: 'observability',\n filter: isAnyshiftEntityContentAvailable,\n loader: () =>\n import('./components/entity/AnyshiftEntityContent').then(module => (\n <AnyshiftPermissionBoundary permission={anyshiftViewPermission}>\n <module.AnyshiftEntityContent />\n </AnyshiftPermissionBoundary>\n )),\n },\n});\n\nexport function isAnyshiftEntityContentAvailable(entity: Entity): boolean {\n const annotations = entity.metadata.annotations ?? {};\n const hasTarget = Boolean(\n annotations[ANYSHIFT_TARGET_ANNOTATION] ||\n annotations[GITHUB_PROJECT_SLUG_ANNOTATION],\n );\n if (!hasTarget) return false;\n\n const kind = annotations[ANYSHIFT_KIND_ANNOTATION];\n if (kind) return isAnyshiftResourceKind(kind);\n\n return annotations[ANYSHIFT_MANAGED_BY_ANNOTATION] !== 'catalog-provider';\n}\n\nexport const anyshiftPlugin = createFrontendPlugin({\n pluginId: 'anyshift',\n info: {\n packageJson: () => import('../package.json'),\n },\n routes: { root: rootRouteRef },\n extensions: [anyshiftApi
|
|
1
|
+
{"version":3,"file":"plugin.esm.js","sources":["../src/plugin.tsx"],"sourcesContent":["import DeviceHubIcon from '@material-ui/icons/DeviceHub';\nimport CloudQueueIcon from '@material-ui/icons/CloudQueue';\nimport {\n ApiBlueprint,\n PageBlueprint,\n createFrontendPlugin,\n discoveryApiRef,\n fetchApiRef,\n} from '@backstage/frontend-plugin-api';\nimport { EntityContentBlueprint } from '@backstage/plugin-catalog-react/alpha';\nimport type { Entity } from '@backstage/catalog-model';\nimport {\n ANYSHIFT_KIND_ANNOTATION,\n ANYSHIFT_CLOUD_PROVIDER_ANNOTATION,\n ANYSHIFT_MANAGED_BY_ANNOTATION,\n ANYSHIFT_TARGET_ANNOTATION,\n GITHUB_PROJECT_SLUG_ANNOTATION,\n anyshiftViewPermission,\n isAnyshiftResourceKind,\n} from '@anyshift/backstage-plugin-anyshift-common';\nimport { AnyshiftPermissionBoundary } from './components/AnyshiftPermissionBoundary';\nimport { anyshiftApiRef, AnyshiftClient } from './api/AnyshiftApi';\nimport { cloudRouteRef, rootRouteRef } from './routes';\n\nconst anyshiftApi = ApiBlueprint.make({\n params: defineParams =>\n defineParams({\n api: anyshiftApiRef,\n deps: { discoveryApi: discoveryApiRef, fetchApi: fetchApiRef },\n factory: ({ discoveryApi, fetchApi }) =>\n new AnyshiftClient({ discoveryApi, fetchApi }),\n }),\n});\n\nconst anyshiftPage = PageBlueprint.make({\n params: {\n path: '/anyshift',\n title: 'Anyshift',\n icon: <DeviceHubIcon fontSize=\"inherit\" />,\n routeRef: rootRouteRef,\n noHeader: true,\n loader: () =>\n import('./components/explorer/AnyshiftExplorerPage').then(module => (\n <AnyshiftPermissionBoundary permission={anyshiftViewPermission}>\n <module.AnyshiftExplorerPage />\n </AnyshiftPermissionBoundary>\n )),\n },\n});\n\nconst cloudPage = PageBlueprint.make({\n name: 'cloud',\n params: {\n path: '/cloud',\n title: 'Cloud',\n icon: <CloudQueueIcon fontSize=\"inherit\" />,\n routeRef: cloudRouteRef,\n noHeader: true,\n loader: () =>\n import('./components/cloud/CloudPage').then(module => (\n <AnyshiftPermissionBoundary permission={anyshiftViewPermission}>\n <module.CloudPage />\n </AnyshiftPermissionBoundary>\n )),\n },\n});\n\nconst anyshiftEntityContent = EntityContentBlueprint.make({\n name: 'entity-content',\n params: {\n path: '/anyshift',\n title: 'Anyshift',\n group: 'observability',\n filter: isAnyshiftEntityContentAvailable,\n loader: () =>\n import('./components/entity/AnyshiftEntityContent').then(module => (\n <AnyshiftPermissionBoundary permission={anyshiftViewPermission}>\n <module.AnyshiftEntityContent />\n </AnyshiftPermissionBoundary>\n )),\n },\n});\n\nconst cloudEntityContent = EntityContentBlueprint.make({\n name: 'cloud-resource-content',\n params: {\n path: '/cloud',\n title: 'Cloud',\n group: 'observability',\n filter: isCloudResourceContentAvailable,\n loader: () =>\n import('./components/cloud/CloudResourceContent').then(module => (\n <AnyshiftPermissionBoundary permission={anyshiftViewPermission}>\n <module.CloudResourceContent />\n </AnyshiftPermissionBoundary>\n )),\n },\n});\n\nexport function isAnyshiftEntityContentAvailable(entity: Entity): boolean {\n const annotations = entity.metadata.annotations ?? {};\n const hasTarget = Boolean(\n annotations[ANYSHIFT_TARGET_ANNOTATION] ||\n annotations[GITHUB_PROJECT_SLUG_ANNOTATION],\n );\n if (!hasTarget) return false;\n\n const kind = annotations[ANYSHIFT_KIND_ANNOTATION];\n if (kind) return isAnyshiftResourceKind(kind);\n\n return annotations[ANYSHIFT_MANAGED_BY_ANNOTATION] !== 'catalog-provider';\n}\n\nexport function isCloudResourceContentAvailable(entity: Entity): boolean {\n return (\n entity.kind.toLocaleLowerCase('en-US') === 'resource' &&\n Boolean(entity.metadata.annotations?.[ANYSHIFT_CLOUD_PROVIDER_ANNOTATION])\n );\n}\n\nexport const anyshiftPlugin = createFrontendPlugin({\n pluginId: 'anyshift',\n info: {\n packageJson: () => import('../package.json'),\n },\n routes: { root: rootRouteRef, cloud: cloudRouteRef },\n extensions: [\n anyshiftApi,\n anyshiftPage,\n cloudPage,\n anyshiftEntityContent,\n cloudEntityContent,\n ],\n});\n\nexport default anyshiftPlugin;\n"],"names":[],"mappings":"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"}
|
package/dist/routes.esm.js
CHANGED
package/dist/routes.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.esm.js","sources":["../src/routes.ts"],"sourcesContent":["import { createRouteRef } from '@backstage/frontend-plugin-api';\n\nexport const rootRouteRef = createRouteRef();\n"],"names":[],"mappings":";;AAEO,MAAM,eAAe,cAAA;;;;"}
|
|
1
|
+
{"version":3,"file":"routes.esm.js","sources":["../src/routes.ts"],"sourcesContent":["import { createRouteRef } from '@backstage/frontend-plugin-api';\n\nexport const rootRouteRef = createRouteRef();\nexport const cloudRouteRef = createRouteRef();\n"],"names":[],"mappings":";;AAEO,MAAM,eAAe,cAAA;AACrB,MAAM,gBAAgB,cAAA;;;;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@anyshift/backstage-plugin-anyshift",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.7",
|
|
4
4
|
"description": "Read-only Anyshift infrastructure context for Backstage",
|
|
5
5
|
"main": "dist/index.esm.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"clean": "backstage-cli package clean"
|
|
46
46
|
},
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@anyshift/backstage-plugin-anyshift-common": "^0.1.0-beta.
|
|
48
|
+
"@anyshift/backstage-plugin-anyshift-common": "^0.1.0-beta.7",
|
|
49
49
|
"@backstage/catalog-model": "^1.9.0",
|
|
50
50
|
"@backstage/core-components": "^0.18.11",
|
|
51
51
|
"@backstage/frontend-plugin-api": "^0.17.2",
|