@anyshift/backstage-plugin-anyshift 0.1.0-beta.10 → 0.1.0-beta.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/README.md +7 -0
- package/dist/api/AnyshiftApi.esm.js +38 -3
- package/dist/api/AnyshiftApi.esm.js.map +1 -1
- package/dist/components/ask/AskAnyshiftCard.esm.js +342 -0
- package/dist/components/ask/AskAnyshiftCard.esm.js.map +1 -0
- package/dist/components/ask/askPresenters.esm.js +1174 -0
- package/dist/components/ask/askPresenters.esm.js.map +1 -0
- package/dist/components/ask/askScope.esm.js +25 -0
- package/dist/components/ask/askScope.esm.js.map +1 -0
- package/dist/components/ask/askSuggestions.esm.js +41 -0
- package/dist/components/ask/askSuggestions.esm.js.map +1 -0
- package/dist/components/cloud/CloudImpactSection.esm.js +54 -0
- package/dist/components/cloud/CloudImpactSection.esm.js.map +1 -0
- package/dist/components/cloud/CloudRelationshipSections.esm.js +43 -0
- package/dist/components/cloud/CloudRelationshipSections.esm.js.map +1 -0
- package/dist/components/cloud/CloudResourceContent.esm.js +299 -172
- package/dist/components/cloud/CloudResourceContent.esm.js.map +1 -1
- package/dist/components/entity/AnyshiftEntityContent.esm.js +30 -12
- package/dist/components/entity/AnyshiftEntityContent.esm.js.map +1 -1
- package/dist/components/entity/SectionPresenters.esm.js +14 -7
- package/dist/components/entity/SectionPresenters.esm.js.map +1 -1
- package/dist/components/entity/entityIdentity.esm.js +30 -0
- package/dist/components/entity/entityIdentity.esm.js.map +1 -0
- package/dist/components/explorer/AnyshiftExplorerPage.esm.js +8 -3
- package/dist/components/explorer/AnyshiftExplorerPage.esm.js.map +1 -1
- package/dist/index.d.ts +3 -2
- package/dist/package.json.esm.js +1 -1
- package/dist/plugin.esm.js +6 -2
- package/dist/plugin.esm.js.map +1 -1
- package/package.json +2 -2
|
@@ -1,44 +1,82 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
2
|
-
import { useState, useCallback, useEffect } from 'react';
|
|
2
|
+
import { useState, useRef, useMemo, useLayoutEffect, useCallback, useEffect } from 'react';
|
|
3
3
|
import { Button, Grid, Typography, CircularProgress, Chip } from '@material-ui/core';
|
|
4
4
|
import { Alert } from '@material-ui/lab';
|
|
5
5
|
import { Progress, InfoCard, Link } from '@backstage/core-components';
|
|
6
6
|
import { useApi } from '@backstage/frontend-plugin-api';
|
|
7
|
-
import {
|
|
8
|
-
import { useEntity
|
|
7
|
+
import { 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 } from '@backstage/plugin-catalog-react';
|
|
9
9
|
import { anyshiftApiRef } from '../../api/AnyshiftApi.esm.js';
|
|
10
|
+
import { AskAnyshiftCard } from '../ask/AskAnyshiftCard.esm.js';
|
|
11
|
+
import { askScopeForEntity } from '../ask/askScope.esm.js';
|
|
12
|
+
import { cloudResourceIdentity } from '../entity/entityIdentity.esm.js';
|
|
13
|
+
import { CloudImpactSection } from './CloudImpactSection.esm.js';
|
|
14
|
+
import { CloudDependenciesSection, CloudDependentsSection } from './CloudRelationshipSections.esm.js';
|
|
10
15
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
const CloudResourceContent = () => {
|
|
16
|
+
const CloudResourceContent = () => /* @__PURE__ */ jsx(CloudResourceEvidenceContent, {});
|
|
17
|
+
const CloudAnyshiftEntityContent = () => /* @__PURE__ */ jsx(CloudResourceEvidenceContent, { includeAsk: true });
|
|
18
|
+
const CloudResourceEvidenceContent = ({ includeAsk = false }) => {
|
|
16
19
|
const { entity } = useEntity();
|
|
17
20
|
const api = useApi(anyshiftApiRef);
|
|
18
21
|
const resourceId = cloudResourceIdentity(entity);
|
|
19
|
-
const [
|
|
22
|
+
const [loadedBrief, setLoadedBrief] = useState();
|
|
23
|
+
const brief = loadedBrief && loadedBrief.resourceId === resourceId ? loadedBrief.value : void 0;
|
|
20
24
|
const [error, setError] = useState();
|
|
21
25
|
const [refreshing, setRefreshing] = useState(false);
|
|
22
26
|
const [loadingMore, setLoadingMore] = useState(false);
|
|
27
|
+
const [paginationError, setPaginationError] = useState();
|
|
28
|
+
const [capabilities, setCapabilities] = useState({
|
|
29
|
+
askEnabled: false,
|
|
30
|
+
queryEnabled: false
|
|
31
|
+
});
|
|
32
|
+
const operations = useRef({ identity: 0, primary: 0, pagination: 0 });
|
|
33
|
+
const askScope = useMemo(
|
|
34
|
+
() => includeAsk ? askScopeForEntity(entity) : void 0,
|
|
35
|
+
[entity, includeAsk]
|
|
36
|
+
);
|
|
37
|
+
useLayoutEffect(() => {
|
|
38
|
+
operations.current.identity += 1;
|
|
39
|
+
operations.current.primary += 1;
|
|
40
|
+
operations.current.pagination += 1;
|
|
41
|
+
setLoadedBrief(void 0);
|
|
42
|
+
setError(void 0);
|
|
43
|
+
setRefreshing(false);
|
|
44
|
+
setLoadingMore(false);
|
|
45
|
+
setPaginationError(void 0);
|
|
46
|
+
}, [resourceId]);
|
|
23
47
|
const load = useCallback(
|
|
24
48
|
async (refresh = false) => {
|
|
25
49
|
if (!resourceId) return;
|
|
50
|
+
const identity = operations.current.identity;
|
|
51
|
+
const primary = ++operations.current.primary;
|
|
52
|
+
++operations.current.pagination;
|
|
26
53
|
setRefreshing(refresh);
|
|
54
|
+
setLoadingMore(false);
|
|
55
|
+
setPaginationError(void 0);
|
|
27
56
|
setError(void 0);
|
|
28
57
|
try {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
58
|
+
const next = await api.cloudResourceBrief(resourceId, {
|
|
59
|
+
since: "24h",
|
|
60
|
+
diff: true,
|
|
61
|
+
...refresh ? { refresh: true } : {}
|
|
62
|
+
});
|
|
63
|
+
if (operations.current.identity === identity && operations.current.primary === primary) {
|
|
64
|
+
++operations.current.pagination;
|
|
65
|
+
setLoadingMore(false);
|
|
66
|
+
setLoadedBrief({ resourceId, value: next });
|
|
67
|
+
}
|
|
36
68
|
} catch (loadError) {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
69
|
+
if (operations.current.identity === identity && operations.current.primary === primary) {
|
|
70
|
+
++operations.current.pagination;
|
|
71
|
+
setLoadingMore(false);
|
|
72
|
+
setError(
|
|
73
|
+
loadError instanceof Error ? loadError.message : "Unable to load cloud evidence"
|
|
74
|
+
);
|
|
75
|
+
}
|
|
40
76
|
} finally {
|
|
41
|
-
|
|
77
|
+
if (operations.current.identity === identity && operations.current.primary === primary) {
|
|
78
|
+
setRefreshing(false);
|
|
79
|
+
}
|
|
42
80
|
}
|
|
43
81
|
},
|
|
44
82
|
[api, resourceId]
|
|
@@ -46,41 +84,71 @@ const CloudResourceContent = () => {
|
|
|
46
84
|
useEffect(() => {
|
|
47
85
|
void load();
|
|
48
86
|
}, [load]);
|
|
87
|
+
useEffect(() => {
|
|
88
|
+
if (!includeAsk) return void 0;
|
|
89
|
+
let active = true;
|
|
90
|
+
api.capabilities().then((next) => active && setCapabilities(next)).catch(
|
|
91
|
+
() => active && setCapabilities({ askEnabled: false, queryEnabled: false })
|
|
92
|
+
);
|
|
93
|
+
return () => {
|
|
94
|
+
active = false;
|
|
95
|
+
};
|
|
96
|
+
}, [api, includeAsk]);
|
|
49
97
|
const loadMore = useCallback(async () => {
|
|
50
98
|
const cursor = brief?.changes.data?.page.nextCursor;
|
|
51
99
|
if (!resourceId || !cursor) return;
|
|
100
|
+
const identity = operations.current.identity;
|
|
101
|
+
const primary = operations.current.primary;
|
|
102
|
+
const pagination = ++operations.current.pagination;
|
|
52
103
|
setLoadingMore(true);
|
|
104
|
+
setPaginationError(void 0);
|
|
53
105
|
try {
|
|
54
106
|
const next = await api.cloudResourceBrief(resourceId, {
|
|
55
107
|
since: brief.since,
|
|
56
108
|
diff: true,
|
|
57
109
|
cursor
|
|
58
110
|
});
|
|
59
|
-
|
|
60
|
-
|
|
111
|
+
if (operations.current.identity !== identity || operations.current.primary !== primary || operations.current.pagination !== pagination) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (next.changes.state === "error") {
|
|
115
|
+
setPaginationError(
|
|
116
|
+
next.changes.message || "Unable to load more cloud changes"
|
|
117
|
+
);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
if (next.changes.state === "data" && next.changes.data) {
|
|
121
|
+
setPaginationError(void 0);
|
|
122
|
+
}
|
|
123
|
+
setLoadedBrief((current) => {
|
|
124
|
+
if (!current || current.resourceId !== resourceId || !current.value.changes.data || next.changes.state !== "data" || !next.changes.data) {
|
|
61
125
|
return current;
|
|
62
126
|
}
|
|
63
127
|
return {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
128
|
+
resourceId,
|
|
129
|
+
value: {
|
|
130
|
+
...current.value,
|
|
131
|
+
refreshedAt: next.refreshedAt,
|
|
132
|
+
changes: {
|
|
133
|
+
state: "data",
|
|
134
|
+
data: mergeCloudEventsEvidence(
|
|
135
|
+
current.value.changes.data,
|
|
136
|
+
next.changes.data
|
|
137
|
+
)
|
|
74
138
|
}
|
|
75
139
|
}
|
|
76
140
|
};
|
|
77
141
|
});
|
|
78
142
|
} catch (loadError) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
143
|
+
if (operations.current.identity === identity && operations.current.primary === primary && operations.current.pagination === pagination) {
|
|
144
|
+
setPaginationError(
|
|
145
|
+
loadError instanceof Error ? loadError.message : "Unable to load more cloud changes"
|
|
146
|
+
);
|
|
147
|
+
}
|
|
82
148
|
} finally {
|
|
83
|
-
|
|
149
|
+
if (operations.current.identity === identity && operations.current.primary === primary && operations.current.pagination === pagination) {
|
|
150
|
+
setLoadingMore(false);
|
|
151
|
+
}
|
|
84
152
|
}
|
|
85
153
|
}, [api, brief, resourceId]);
|
|
86
154
|
if (!resourceId) {
|
|
@@ -103,10 +171,50 @@ const CloudResourceContent = () => {
|
|
|
103
171
|
onRefresh: () => void load(true),
|
|
104
172
|
loadingMore,
|
|
105
173
|
onLoadMore: () => void loadMore(),
|
|
106
|
-
error
|
|
174
|
+
error,
|
|
175
|
+
paginationError,
|
|
176
|
+
askEnabled: includeAsk && capabilities.askEnabled && Boolean(askScope),
|
|
177
|
+
askScope
|
|
107
178
|
}
|
|
108
179
|
);
|
|
109
180
|
};
|
|
181
|
+
function mergeCloudEventsEvidence(current, next) {
|
|
182
|
+
return {
|
|
183
|
+
...next,
|
|
184
|
+
availability: mergeCloudEventAvailability(
|
|
185
|
+
current.availability,
|
|
186
|
+
next.availability
|
|
187
|
+
),
|
|
188
|
+
warnings: [.../* @__PURE__ */ new Set([...current.warnings, ...next.warnings])],
|
|
189
|
+
items: [...current.items, ...next.items]
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function mergeCloudEventAvailability(current, next) {
|
|
193
|
+
const availability = {};
|
|
194
|
+
for (const field of /* @__PURE__ */ new Set([
|
|
195
|
+
...Object.keys(current),
|
|
196
|
+
...Object.keys(next)
|
|
197
|
+
])) {
|
|
198
|
+
availability[field] = leastCompleteAvailability(
|
|
199
|
+
current[field],
|
|
200
|
+
next[field]
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
return availability;
|
|
204
|
+
}
|
|
205
|
+
function leastCompleteAvailability(current, next) {
|
|
206
|
+
const normalizedCurrent = normalizeAvailability(current);
|
|
207
|
+
const normalizedNext = normalizeAvailability(next);
|
|
208
|
+
return AVAILABILITY_RANK[normalizedCurrent] <= AVAILABILITY_RANK[normalizedNext] ? normalizedCurrent : normalizedNext;
|
|
209
|
+
}
|
|
210
|
+
const AVAILABILITY_RANK = {
|
|
211
|
+
unavailable: 0,
|
|
212
|
+
partial: 1,
|
|
213
|
+
available: 2
|
|
214
|
+
};
|
|
215
|
+
function normalizeAvailability(value) {
|
|
216
|
+
return value && value in AVAILABILITY_RANK ? value : "unavailable";
|
|
217
|
+
}
|
|
110
218
|
const CloudResourceBriefView = ({
|
|
111
219
|
entity,
|
|
112
220
|
brief,
|
|
@@ -114,12 +222,12 @@ const CloudResourceBriefView = ({
|
|
|
114
222
|
onRefresh,
|
|
115
223
|
loadingMore,
|
|
116
224
|
onLoadMore,
|
|
117
|
-
error
|
|
225
|
+
error,
|
|
226
|
+
paginationError,
|
|
227
|
+
askEnabled = false,
|
|
228
|
+
askScope
|
|
118
229
|
}) => {
|
|
119
230
|
const annotations = entity.metadata.annotations ?? {};
|
|
120
|
-
const relationships = (entity.relations ?? []).filter(
|
|
121
|
-
(relation) => ["dependencyOf", "dependsOn"].includes(relation.type)
|
|
122
|
-
);
|
|
123
231
|
const consoleLink = entity.metadata.links?.find(
|
|
124
232
|
(link) => link.url.startsWith("https://console.aws.amazon.com/")
|
|
125
233
|
);
|
|
@@ -134,7 +242,6 @@ const CloudResourceBriefView = ({
|
|
|
134
242
|
{
|
|
135
243
|
size: "small",
|
|
136
244
|
onClick: onRefresh,
|
|
137
|
-
disabled: refreshing,
|
|
138
245
|
startIcon: refreshing ? /* @__PURE__ */ jsx(CircularProgress, { size: 14 }) : void 0,
|
|
139
246
|
children: "Refresh"
|
|
140
247
|
}
|
|
@@ -148,17 +255,7 @@ const CloudResourceBriefView = ({
|
|
|
148
255
|
] })
|
|
149
256
|
}
|
|
150
257
|
) }),
|
|
151
|
-
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */
|
|
152
|
-
ChangesSection,
|
|
153
|
-
{
|
|
154
|
-
section: brief.changes,
|
|
155
|
-
loadingMore,
|
|
156
|
-
onLoadMore
|
|
157
|
-
}
|
|
158
|
-
) }),
|
|
159
|
-
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */ jsx(ProvenanceSection, { section: brief.provenance }) }),
|
|
160
|
-
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */ jsx(DriftSection, { section: brief.drift }) }),
|
|
161
|
-
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */ jsxs(InfoCard, { title: "Cloud identity", children: [
|
|
258
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsxs(InfoCard, { title: "Cloud identity", children: [
|
|
162
259
|
/* @__PURE__ */ jsx(
|
|
163
260
|
Definition,
|
|
164
261
|
{
|
|
@@ -187,137 +284,167 @@ const CloudResourceBriefView = ({
|
|
|
187
284
|
value: annotations[ANYSHIFT_CLOUD_ACCOUNT_ANNOTATION]
|
|
188
285
|
}
|
|
189
286
|
),
|
|
287
|
+
/* @__PURE__ */ jsx(Definition, { label: "Stable resource ID", value: brief.resourceId }),
|
|
190
288
|
consoleLink && /* @__PURE__ */ jsx(Link, { to: consoleLink.url, children: "Open in AWS Console" })
|
|
191
289
|
] }) }),
|
|
192
|
-
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
value: typeof entity.spec?.owner === "string" ? entity.spec.owner : void 0
|
|
198
|
-
}
|
|
199
|
-
),
|
|
200
|
-
/* @__PURE__ */ jsx(
|
|
201
|
-
Definition,
|
|
202
|
-
{
|
|
203
|
-
label: "Namespace",
|
|
204
|
-
value: entity.metadata.namespace || "default"
|
|
205
|
-
}
|
|
206
|
-
),
|
|
207
|
-
/* @__PURE__ */ jsx(Typography, { variant: "subtitle2", children: "Relationships" }),
|
|
208
|
-
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: [
|
|
209
|
-
/* @__PURE__ */ jsx(
|
|
210
|
-
Chip,
|
|
211
|
-
{
|
|
212
|
-
size: "small",
|
|
213
|
-
label: relation.type,
|
|
214
|
-
style: { marginRight: 8 }
|
|
215
|
-
}
|
|
216
|
-
),
|
|
217
|
-
/* @__PURE__ */ jsx(EntityRefLink, { entityRef: relation.targetRef })
|
|
218
|
-
] }, `${relation.type}:${relation.targetRef}`)) })
|
|
219
|
-
] }) }),
|
|
220
|
-
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(InfoCard, { title: "Stable resource identity", children: /* @__PURE__ */ jsx(
|
|
221
|
-
Typography,
|
|
290
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */ jsx(CloudDependenciesSection, { entity }) }),
|
|
291
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */ jsx(CloudDependentsSection, { entity }) }),
|
|
292
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(CloudImpactSection, { section: brief.impact, onRetry: onRefresh }) }),
|
|
293
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(
|
|
294
|
+
ChangesSection,
|
|
222
295
|
{
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
296
|
+
section: brief.changes,
|
|
297
|
+
loadingMore,
|
|
298
|
+
onLoadMore,
|
|
299
|
+
paginationError
|
|
226
300
|
}
|
|
227
|
-
) })
|
|
301
|
+
) }),
|
|
302
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */ jsx(ProvenanceSection, { section: brief.provenance }) }),
|
|
303
|
+
/* @__PURE__ */ jsx(Grid, { item: true, xs: 12, md: 6, children: /* @__PURE__ */ jsx(DriftSection, { section: brief.drift }) }),
|
|
304
|
+
askEnabled && /* @__PURE__ */ jsx(Grid, { item: true, xs: 12, children: /* @__PURE__ */ jsx(AskAnyshiftCard, { context: "entity", scope: askScope }) })
|
|
228
305
|
] });
|
|
229
306
|
};
|
|
230
307
|
const ChangesSection = ({
|
|
231
308
|
section,
|
|
232
309
|
loadingMore,
|
|
233
|
-
onLoadMore
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
/* @__PURE__ */
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
310
|
+
onLoadMore,
|
|
311
|
+
paginationError
|
|
312
|
+
}) => {
|
|
313
|
+
const { hasEvidenceLimitations, actionableWarnings } = partitionCloudEventWarnings(section.data?.warnings);
|
|
314
|
+
if (section.state === "empty") {
|
|
315
|
+
return /* @__PURE__ */ jsx(InfoCard, { title: "Recent cloud changes", children: /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: section.message || "No cloud changes were found in this window." }) });
|
|
316
|
+
}
|
|
317
|
+
return /* @__PURE__ */ jsx(InfoCard, { title: "Recent cloud changes", children: /* @__PURE__ */ jsxs(SectionState, { section, children: [
|
|
318
|
+
paginationError && /* @__PURE__ */ jsx(Alert, { severity: "error", style: { marginBottom: 8 }, children: paginationError }),
|
|
319
|
+
hasEvidenceLimitations && /* @__PURE__ */ jsx(
|
|
320
|
+
Typography,
|
|
321
|
+
{
|
|
322
|
+
"aria-label": "Cloud event evidence limitations",
|
|
323
|
+
variant: "caption",
|
|
324
|
+
color: "textSecondary",
|
|
325
|
+
display: "block",
|
|
326
|
+
style: { marginBottom: 8 },
|
|
327
|
+
children: "Event source is derived from graph relationships, and event status may be incomplete."
|
|
328
|
+
}
|
|
329
|
+
),
|
|
330
|
+
actionableWarnings.map((warning) => /* @__PURE__ */ jsx(Alert, { severity: "warning", style: { marginBottom: 8 }, children: humanize(warning) }, warning)),
|
|
331
|
+
section.data?.items?.length ? section.data.items.map((event) => /* @__PURE__ */ jsxs("div", { style: { marginBottom: 20 }, children: [
|
|
332
|
+
/* @__PURE__ */ jsx(Typography, { variant: "subtitle1", children: event.summary || humanize(event.type) }),
|
|
333
|
+
/* @__PURE__ */ jsxs(Typography, { color: "textSecondary", children: [
|
|
334
|
+
formatCloudTimestamp(event.timestamp),
|
|
335
|
+
" \xB7 ",
|
|
336
|
+
event.category,
|
|
337
|
+
" \xB7",
|
|
338
|
+
" ",
|
|
339
|
+
event.evidence.source,
|
|
340
|
+
" \xB7 ",
|
|
341
|
+
event.evidence.status,
|
|
342
|
+
event.actor?.name || event.actor?.id ? ` \xB7 ${event.actor.name || event.actor.id}` : ""
|
|
343
|
+
] }),
|
|
344
|
+
event.change.fields.length > 0 && /* @__PURE__ */ jsxs("details", { children: [
|
|
345
|
+
/* @__PURE__ */ jsx("summary", { children: "View sanitized field changes" }),
|
|
346
|
+
/* @__PURE__ */ jsx("ul", { children: event.change.fields.map((field) => {
|
|
347
|
+
const detail = event.change.details.find(
|
|
348
|
+
(candidate) => candidate.field === field
|
|
349
|
+
);
|
|
350
|
+
return /* @__PURE__ */ jsxs("li", { children: [
|
|
351
|
+
/* @__PURE__ */ jsx("code", { children: field }),
|
|
352
|
+
detail ? `: ${detail.before ?? "\u2205"} \u2192 ${detail.after ?? "\u2205"}` : ""
|
|
353
|
+
] }, field);
|
|
354
|
+
}) })
|
|
355
|
+
] })
|
|
356
|
+
] }, event.id)) : /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: "No cloud changes were found in this window." }),
|
|
357
|
+
section.data?.page.hasMore && onLoadMore && /* @__PURE__ */ jsx(
|
|
358
|
+
Button,
|
|
359
|
+
{
|
|
360
|
+
size: "small",
|
|
361
|
+
onClick: onLoadMore,
|
|
362
|
+
disabled: loadingMore,
|
|
363
|
+
startIcon: loadingMore ? /* @__PURE__ */ jsx(CircularProgress, { size: 14 }) : void 0,
|
|
364
|
+
children: "Load more changes"
|
|
365
|
+
}
|
|
366
|
+
)
|
|
367
|
+
] }) });
|
|
368
|
+
};
|
|
369
|
+
const QUIET_EVIDENCE_LIMITATIONS = /* @__PURE__ */ new Set([
|
|
370
|
+
"event_source_derived",
|
|
371
|
+
"event_status_incomplete"
|
|
372
|
+
]);
|
|
373
|
+
function partitionCloudEventWarnings(warnings) {
|
|
374
|
+
const allWarnings = warnings ?? [];
|
|
375
|
+
return {
|
|
376
|
+
hasEvidenceLimitations: allWarnings.some(
|
|
377
|
+
(warning) => QUIET_EVIDENCE_LIMITATIONS.has(warning)
|
|
378
|
+
),
|
|
379
|
+
actionableWarnings: allWarnings.filter(
|
|
380
|
+
(warning) => !QUIET_EVIDENCE_LIMITATIONS.has(warning)
|
|
381
|
+
)
|
|
382
|
+
};
|
|
383
|
+
}
|
|
273
384
|
const ProvenanceSection = ({
|
|
274
385
|
section
|
|
275
|
-
}) =>
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
/* @__PURE__ */ jsx(
|
|
280
|
-
|
|
281
|
-
{
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
386
|
+
}) => {
|
|
387
|
+
if (section.state === "empty") {
|
|
388
|
+
return /* @__PURE__ */ jsx(InfoCard, { title: "IaC provenance", children: /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: section.message || "No Terraform provenance is linked to this resource." }) });
|
|
389
|
+
}
|
|
390
|
+
return /* @__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: [
|
|
391
|
+
/* @__PURE__ */ jsx(Typography, { variant: "subtitle1", children: resource.terraform.address }),
|
|
392
|
+
/* @__PURE__ */ jsx(Chip, { size: "small", label: humanize(resource.status) }),
|
|
393
|
+
/* @__PURE__ */ jsx(
|
|
394
|
+
Definition,
|
|
395
|
+
{
|
|
396
|
+
label: "File",
|
|
397
|
+
value: resource.terraform.fileRef ?? ""
|
|
398
|
+
}
|
|
399
|
+
),
|
|
400
|
+
/* @__PURE__ */ jsx(
|
|
401
|
+
Definition,
|
|
402
|
+
{
|
|
403
|
+
label: "State links",
|
|
404
|
+
value: String(resource.states.length)
|
|
405
|
+
}
|
|
406
|
+
),
|
|
407
|
+
/* @__PURE__ */ jsx(
|
|
408
|
+
Definition,
|
|
409
|
+
{
|
|
410
|
+
label: "Cloud links",
|
|
411
|
+
value: String(resource.clouds.length)
|
|
412
|
+
}
|
|
413
|
+
),
|
|
414
|
+
resource.terraform.githubUrl && /* @__PURE__ */ jsx(Link, { to: resource.terraform.githubUrl, children: "Open source" })
|
|
415
|
+
] }, resource.terraform.hashedID)) : /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: "No Terraform provenance is linked to this resource." }) }) });
|
|
416
|
+
};
|
|
295
417
|
const DriftSection = ({
|
|
296
418
|
section
|
|
297
|
-
}) =>
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
" ",
|
|
314
|
-
difference
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
419
|
+
}) => {
|
|
420
|
+
if (section.state === "empty") {
|
|
421
|
+
return /* @__PURE__ */ jsx(InfoCard, { title: "IaC drift evidence", children: /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: section.message || "No comparable Terraform evidence is available." }) });
|
|
422
|
+
}
|
|
423
|
+
return /* @__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: [
|
|
424
|
+
/* @__PURE__ */ jsx(Typography, { variant: "subtitle1", children: item.terraform.address }),
|
|
425
|
+
/* @__PURE__ */ jsx(
|
|
426
|
+
Chip,
|
|
427
|
+
{
|
|
428
|
+
size: "small",
|
|
429
|
+
color: item.status === "drifted" ? "secondary" : "default",
|
|
430
|
+
label: humanize(item.status)
|
|
431
|
+
}
|
|
432
|
+
),
|
|
433
|
+
/* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: item.status === "unknown" ? `Unknown is not drift: ${humanize(item.reason)}.` : `${item.comparedFields} supported fields compared.` }),
|
|
434
|
+
item.differences.length > 0 && /* @__PURE__ */ jsxs("details", { children: [
|
|
435
|
+
/* @__PURE__ */ jsx("summary", { children: "View sanitized differences" }),
|
|
436
|
+
/* @__PURE__ */ jsx("ul", { children: item.differences.map((difference) => /* @__PURE__ */ jsxs("li", { children: [
|
|
437
|
+
/* @__PURE__ */ jsx("code", { children: difference.field }),
|
|
438
|
+
":",
|
|
439
|
+
" ",
|
|
440
|
+
difference.lastApplied ?? "\u2205",
|
|
441
|
+
" \u2192",
|
|
442
|
+
" ",
|
|
443
|
+
difference.observed ?? "\u2205"
|
|
444
|
+
] }, difference.field)) })
|
|
445
|
+
] })
|
|
446
|
+
] }, item.terraform.hashedID)) : /* @__PURE__ */ jsx(Typography, { color: "textSecondary", children: "No comparable Terraform evidence is available." }) }) });
|
|
447
|
+
};
|
|
321
448
|
const SectionState = ({
|
|
322
449
|
section,
|
|
323
450
|
children
|
|
@@ -354,5 +481,5 @@ function formatCloudTimestamp(value) {
|
|
|
354
481
|
return Number.isNaN(normalizedTimestamp) ? value : new Date(normalizedTimestamp).toLocaleString();
|
|
355
482
|
}
|
|
356
483
|
|
|
357
|
-
export { CloudResourceBriefView, CloudResourceContent,
|
|
484
|
+
export { CloudAnyshiftEntityContent, CloudResourceBriefView, CloudResourceContent, formatCloudTimestamp, mergeCloudEventsEvidence };
|
|
358
485
|
//# sourceMappingURL=CloudResourceContent.esm.js.map
|