@kuadrant/kuadrant-backstage-plugin-frontend 0.2.1 → 0.3.0-aaa46b4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.esm.js +40 -2
- package/dist/api.esm.js.map +1 -1
- package/dist/components/ApiAccessCard/ApiAccessCard.esm.js +20 -85
- package/dist/components/ApiAccessCard/ApiAccessCard.esm.js.map +1 -1
- package/dist/components/ApiKeyApprovalPage/ApiKeyApprovalPageWithPermissions.esm.js +18 -0
- package/dist/components/ApiKeyApprovalPage/ApiKeyApprovalPageWithPermissions.esm.js.map +1 -0
- package/dist/components/ApiKeyApprovalPage/index.esm.js +1 -0
- package/dist/components/ApiKeyApprovalPage/index.esm.js.map +1 -1
- package/dist/components/ApiKeyDetailPage/ApiKeyDetailPage.esm.js +19 -60
- package/dist/components/ApiKeyDetailPage/ApiKeyDetailPage.esm.js.map +1 -1
- package/dist/components/ApiKeyManagementTab/ApiKeyManagementTab.esm.js +45 -110
- package/dist/components/ApiKeyManagementTab/ApiKeyManagementTab.esm.js.map +1 -1
- package/dist/components/ApiProductInfoCard/ApiProductInfoCard.esm.js +3 -2
- package/dist/components/ApiProductInfoCard/ApiProductInfoCard.esm.js.map +1 -1
- package/dist/components/ApprovalQueueTable/ApprovalQueueTable.esm.js +16 -15
- package/dist/components/ApprovalQueueTable/ApprovalQueueTable.esm.js.map +1 -1
- package/dist/components/EditAPIProductDialog/EditAPIProductDialog.esm.js +2 -3
- package/dist/components/EditAPIProductDialog/EditAPIProductDialog.esm.js.map +1 -1
- package/dist/components/KuadrantPage/ApiProductsPage.esm.js +5 -18
- package/dist/components/KuadrantPage/ApiProductsPage.esm.js.map +1 -1
- package/dist/components/MyApiKeysTable/MyApiKeysTable.esm.js +28 -138
- package/dist/components/MyApiKeysTable/MyApiKeysTable.esm.js.map +1 -1
- package/dist/components/PermissionGate/PermissionGate.esm.js +2 -2
- package/dist/components/PermissionGate/PermissionGate.esm.js.map +1 -1
- package/dist/components/RequestAccessDialog/RequestAccessDialog.esm.js +32 -18
- package/dist/components/RequestAccessDialog/RequestAccessDialog.esm.js.map +1 -1
- package/dist/components/SimpleRequestAccessDialog/SimpleRequestAccessDialog.esm.js +13 -2
- package/dist/components/SimpleRequestAccessDialog/SimpleRequestAccessDialog.esm.js.map +1 -1
- package/dist/index.d.ts +47 -6
- package/dist/index.esm.js +1 -1
- package/dist/plugin.esm.js +2 -10
- package/dist/plugin.esm.js.map +1 -1
- package/dist/utils/apikeys.esm.js +21 -0
- package/dist/utils/apikeys.esm.js.map +1 -0
- package/dist/utils/styles.esm.js +5 -2
- package/dist/utils/styles.esm.js.map +1 -1
- package/dist-scalprum/internal.plugin-kuadrant.7f1de85661b1b848af15.js +2 -0
- package/dist-scalprum/internal.plugin-kuadrant.7f1de85661b1b848af15.js.map +1 -0
- package/dist-scalprum/plugin-manifest.json +3 -3
- package/dist-scalprum/static/1340.7c4f4722.chunk.js +3 -0
- package/dist-scalprum/static/1340.7c4f4722.chunk.js.map +1 -0
- package/dist-scalprum/static/1719.52ca7daf.chunk.js +2 -0
- package/dist-scalprum/static/1719.52ca7daf.chunk.js.map +1 -0
- package/dist-scalprum/static/{1881.56cde36b.chunk.js → 1931.f6a5edbb.chunk.js} +2 -2
- package/dist-scalprum/static/1931.f6a5edbb.chunk.js.map +1 -0
- package/dist-scalprum/static/20.b3a37d1d.chunk.js +3 -0
- package/dist-scalprum/static/20.b3a37d1d.chunk.js.map +1 -0
- package/dist-scalprum/static/2251.08f3db99.chunk.js +2 -0
- package/dist-scalprum/static/2251.08f3db99.chunk.js.map +1 -0
- package/dist-scalprum/static/2769.e09b0ab0.chunk.js +2 -0
- package/dist-scalprum/static/2769.e09b0ab0.chunk.js.map +1 -0
- package/dist-scalprum/static/2821.1ac360ec.chunk.js +2 -0
- package/dist-scalprum/static/2821.1ac360ec.chunk.js.map +1 -0
- package/dist-scalprum/static/2967.004a950e.chunk.js +2 -0
- package/dist-scalprum/static/2967.004a950e.chunk.js.map +1 -0
- package/dist-scalprum/static/327.74ecd572.chunk.js +2 -0
- package/dist-scalprum/static/327.74ecd572.chunk.js.map +1 -0
- package/dist-scalprum/static/3658.104704cc.chunk.js +2 -0
- package/dist-scalprum/static/3658.104704cc.chunk.js.map +1 -0
- package/dist-scalprum/static/3917.888fba36.chunk.js +2 -0
- package/dist-scalprum/static/3917.888fba36.chunk.js.map +1 -0
- package/dist-scalprum/static/3947.f5320e89.chunk.js +2 -0
- package/dist-scalprum/static/3947.f5320e89.chunk.js.map +1 -0
- package/dist-scalprum/static/438.4335b833.chunk.js +2 -0
- package/dist-scalprum/static/438.4335b833.chunk.js.map +1 -0
- package/dist-scalprum/static/4908.4f21d453.chunk.js +2 -0
- package/dist-scalprum/static/4908.4f21d453.chunk.js.map +1 -0
- package/dist-scalprum/static/5197.53535ff4.chunk.js +2 -0
- package/dist-scalprum/static/5197.53535ff4.chunk.js.map +1 -0
- package/dist-scalprum/static/5289.6c803140.chunk.js +2 -0
- package/dist-scalprum/static/5289.6c803140.chunk.js.map +1 -0
- package/dist-scalprum/static/5943.4e1dedc5.chunk.js +2 -0
- package/dist-scalprum/static/5943.4e1dedc5.chunk.js.map +1 -0
- package/dist-scalprum/static/6311.da1849d1.chunk.js +2 -0
- package/dist-scalprum/static/6311.da1849d1.chunk.js.map +1 -0
- package/dist-scalprum/static/6371.02375475.chunk.js +3 -0
- package/dist-scalprum/static/6371.02375475.chunk.js.map +1 -0
- package/dist-scalprum/static/6837.6252b97a.chunk.js +2 -0
- package/dist-scalprum/static/6837.6252b97a.chunk.js.map +1 -0
- package/dist-scalprum/static/7270.8ee9d452.chunk.js +2 -0
- package/dist-scalprum/static/7270.8ee9d452.chunk.js.map +1 -0
- package/dist-scalprum/static/980.744a4d36.chunk.js +2 -0
- package/dist-scalprum/static/980.744a4d36.chunk.js.map +1 -0
- package/dist-scalprum/static/exposed-KuadrantPage.f123b200.chunk.js +2 -0
- package/dist-scalprum/static/exposed-KuadrantPage.f123b200.chunk.js.map +1 -0
- package/dist-scalprum/static/exposed-PluginRoot.3a5f9157.chunk.js +2 -0
- package/dist-scalprum/static/exposed-PluginRoot.3a5f9157.chunk.js.map +1 -0
- package/package.json +1 -1
- package/dist/components/EntityApiApprovalTab/EntityApiApprovalTab.esm.js +0 -265
- package/dist/components/EntityApiApprovalTab/EntityApiApprovalTab.esm.js.map +0 -1
- package/dist/components/EntityApiApprovalTab/index.esm.js +0 -2
- package/dist/components/EntityApiApprovalTab/index.esm.js.map +0 -1
- package/dist-scalprum/internal.plugin-kuadrant.c4fb9c2347d3ad03a5d0.js +0 -2
- package/dist-scalprum/internal.plugin-kuadrant.c4fb9c2347d3ad03a5d0.js.map +0 -1
- package/dist-scalprum/static/1584.1e45e570.chunk.js +0 -3
- package/dist-scalprum/static/1584.1e45e570.chunk.js.map +0 -1
- package/dist-scalprum/static/1719.246c020d.chunk.js +0 -2
- package/dist-scalprum/static/1719.246c020d.chunk.js.map +0 -1
- package/dist-scalprum/static/1881.56cde36b.chunk.js.map +0 -1
- package/dist-scalprum/static/286.ee43f560.chunk.js +0 -2
- package/dist-scalprum/static/286.ee43f560.chunk.js.map +0 -1
- package/dist-scalprum/static/2967.0c2ef90e.chunk.js +0 -2
- package/dist-scalprum/static/2967.0c2ef90e.chunk.js.map +0 -1
- package/dist-scalprum/static/3581.ebe5968a.chunk.js +0 -2
- package/dist-scalprum/static/3581.ebe5968a.chunk.js.map +0 -1
- package/dist-scalprum/static/369.2374941c.chunk.js +0 -2
- package/dist-scalprum/static/369.2374941c.chunk.js.map +0 -1
- package/dist-scalprum/static/3947.4f4aa8c2.chunk.js +0 -2
- package/dist-scalprum/static/3947.4f4aa8c2.chunk.js.map +0 -1
- package/dist-scalprum/static/3976.e8a8fda2.chunk.js +0 -2
- package/dist-scalprum/static/3976.e8a8fda2.chunk.js.map +0 -1
- package/dist-scalprum/static/4434.f92f6ba4.chunk.js +0 -2
- package/dist-scalprum/static/4434.f92f6ba4.chunk.js.map +0 -1
- package/dist-scalprum/static/4908.196a4425.chunk.js +0 -2
- package/dist-scalprum/static/4908.196a4425.chunk.js.map +0 -1
- package/dist-scalprum/static/512.d592d164.chunk.js +0 -2
- package/dist-scalprum/static/512.d592d164.chunk.js.map +0 -1
- package/dist-scalprum/static/5289.55069930.chunk.js +0 -2
- package/dist-scalprum/static/5289.55069930.chunk.js.map +0 -1
- package/dist-scalprum/static/5605.430f697e.chunk.js +0 -2
- package/dist-scalprum/static/5605.430f697e.chunk.js.map +0 -1
- package/dist-scalprum/static/5978.bf347a09.chunk.js +0 -2
- package/dist-scalprum/static/5978.bf347a09.chunk.js.map +0 -1
- package/dist-scalprum/static/6371.7b57ac27.chunk.js +0 -3
- package/dist-scalprum/static/6371.7b57ac27.chunk.js.map +0 -1
- package/dist-scalprum/static/6426.4c18bab5.chunk.js +0 -2
- package/dist-scalprum/static/6426.4c18bab5.chunk.js.map +0 -1
- package/dist-scalprum/static/6777.a2b77ec5.chunk.js +0 -2
- package/dist-scalprum/static/6777.a2b77ec5.chunk.js.map +0 -1
- package/dist-scalprum/static/6800.a45da2f4.chunk.js +0 -2
- package/dist-scalprum/static/6800.a45da2f4.chunk.js.map +0 -1
- package/dist-scalprum/static/7005.8190ad95.chunk.js +0 -2
- package/dist-scalprum/static/7005.8190ad95.chunk.js.map +0 -1
- package/dist-scalprum/static/7057.fcd688c7.chunk.js +0 -2
- package/dist-scalprum/static/7057.fcd688c7.chunk.js.map +0 -1
- package/dist-scalprum/static/7270.d52407cd.chunk.js +0 -2
- package/dist-scalprum/static/7270.d52407cd.chunk.js.map +0 -1
- package/dist-scalprum/static/7717.6f5b9dd7.chunk.js +0 -3
- package/dist-scalprum/static/7717.6f5b9dd7.chunk.js.map +0 -1
- package/dist-scalprum/static/9079.2ddaea57.chunk.js +0 -2
- package/dist-scalprum/static/9079.2ddaea57.chunk.js.map +0 -1
- package/dist-scalprum/static/9239.1acfe112.chunk.js +0 -2
- package/dist-scalprum/static/9239.1acfe112.chunk.js.map +0 -1
- package/dist-scalprum/static/9625.b0c6610a.chunk.js +0 -2
- package/dist-scalprum/static/9625.b0c6610a.chunk.js.map +0 -1
- package/dist-scalprum/static/exposed-KuadrantPage.5ae00605.chunk.js +0 -2
- package/dist-scalprum/static/exposed-KuadrantPage.5ae00605.chunk.js.map +0 -1
- package/dist-scalprum/static/exposed-PluginRoot.949381c2.chunk.js +0 -2
- package/dist-scalprum/static/exposed-PluginRoot.949381c2.chunk.js.map +0 -1
- /package/dist-scalprum/static/{1584.1e45e570.chunk.js.LICENSE.txt → 1340.7c4f4722.chunk.js.LICENSE.txt} +0 -0
- /package/dist-scalprum/static/{7717.6f5b9dd7.chunk.js.LICENSE.txt → 20.b3a37d1d.chunk.js.LICENSE.txt} +0 -0
- /package/dist-scalprum/static/{6371.7b57ac27.chunk.js.LICENSE.txt → 6371.02375475.chunk.js.LICENSE.txt} +0 -0
|
@@ -2,14 +2,14 @@ import React, { useState } from 'react';
|
|
|
2
2
|
import { useParams } from 'react-router-dom';
|
|
3
3
|
import { useApi, alertApiRef } from '@backstage/core-plugin-api';
|
|
4
4
|
import { kuadrantApiRef } from '../../api.esm.js';
|
|
5
|
+
import { getAPIKeyPhase } from '../../utils/apikeys.esm.js';
|
|
5
6
|
import { useAsync } from 'react-use';
|
|
6
7
|
import { Page, Header, Content, ResponseErrorPanel, Link, Breadcrumbs, InfoCard } from '@backstage/core-components';
|
|
7
|
-
import { makeStyles, Box, Button, Typography, Grid, Chip, Tooltip, IconButton, Tabs, Tab
|
|
8
|
+
import { makeStyles, Box, Button, Typography, Grid, Chip, Tooltip, IconButton, Tabs, Tab } from '@material-ui/core';
|
|
8
9
|
import { Skeleton } from '@material-ui/lab';
|
|
9
10
|
import VisibilityIcon from '@material-ui/icons/Visibility';
|
|
10
11
|
import VisibilityOffIcon from '@material-ui/icons/VisibilityOff';
|
|
11
12
|
import FileCopyIcon from '@material-ui/icons/FileCopy';
|
|
12
|
-
import WarningIcon from '@material-ui/icons/Warning';
|
|
13
13
|
import ArrowBackIcon from '@material-ui/icons/ArrowBack';
|
|
14
14
|
import OpenInNewIcon from '@material-ui/icons/OpenInNew';
|
|
15
15
|
import EmailIcon from '@material-ui/icons/Email';
|
|
@@ -71,8 +71,6 @@ const ApiKeyDetailPage = () => {
|
|
|
71
71
|
const [showApiKey, setShowApiKey] = useState(false);
|
|
72
72
|
const [apiKeyValue, setApiKeyValue] = useState(null);
|
|
73
73
|
const [apiKeyLoading, setApiKeyLoading] = useState(false);
|
|
74
|
-
const [alreadyRead, setAlreadyRead] = useState(false);
|
|
75
|
-
const [showWarning, setShowWarning] = useState(false);
|
|
76
74
|
const {
|
|
77
75
|
value: data,
|
|
78
76
|
loading,
|
|
@@ -82,9 +80,6 @@ const ApiKeyDetailPage = () => {
|
|
|
82
80
|
kuadrantApi.getApiKey(namespace, name),
|
|
83
81
|
kuadrantApi.getApiProducts()
|
|
84
82
|
]);
|
|
85
|
-
if (apiKeyData.status?.canReadSecret === false) {
|
|
86
|
-
setAlreadyRead(true);
|
|
87
|
-
}
|
|
88
83
|
const apiProduct2 = (productsData.items || []).find(
|
|
89
84
|
(p) => p.metadata.name === apiKeyData.spec.apiProductRef?.name && p.metadata.namespace === apiKeyData.metadata.namespace
|
|
90
85
|
);
|
|
@@ -97,25 +92,15 @@ const ApiKeyDetailPage = () => {
|
|
|
97
92
|
try {
|
|
98
93
|
const extractedSecret = await kuadrantApi.getApiKeySecret(namespace, name);
|
|
99
94
|
setApiKeyValue(extractedSecret.apiKey);
|
|
100
|
-
setAlreadyRead(true);
|
|
101
95
|
setShowApiKey(true);
|
|
102
96
|
} catch (err) {
|
|
103
97
|
console.error("Failed to fetch API key:", err);
|
|
104
98
|
const errorMessage = err instanceof Error ? err.message : "unknown error occurred";
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
display: "transient"
|
|
111
|
-
});
|
|
112
|
-
} else {
|
|
113
|
-
alertApi.post({
|
|
114
|
-
message: `Failed to fetch APIKey. ${errorMessage}`,
|
|
115
|
-
severity: "error",
|
|
116
|
-
display: "transient"
|
|
117
|
-
});
|
|
118
|
-
}
|
|
99
|
+
alertApi.post({
|
|
100
|
+
message: `Failed to fetch APIKey. ${errorMessage}`,
|
|
101
|
+
severity: "error",
|
|
102
|
+
display: "transient"
|
|
103
|
+
});
|
|
119
104
|
} finally {
|
|
120
105
|
setApiKeyLoading(false);
|
|
121
106
|
}
|
|
@@ -124,14 +109,10 @@ const ApiKeyDetailPage = () => {
|
|
|
124
109
|
if (showApiKey) {
|
|
125
110
|
setShowApiKey(false);
|
|
126
111
|
setApiKeyValue(null);
|
|
127
|
-
} else
|
|
128
|
-
|
|
112
|
+
} else {
|
|
113
|
+
fetchApiKeySecret();
|
|
129
114
|
}
|
|
130
115
|
};
|
|
131
|
-
const handleConfirmReveal = () => {
|
|
132
|
-
setShowWarning(false);
|
|
133
|
-
fetchApiKeySecret();
|
|
134
|
-
};
|
|
135
116
|
const handleCopy = async (text) => {
|
|
136
117
|
await navigator.clipboard.writeText(text);
|
|
137
118
|
alertApi.post({
|
|
@@ -146,7 +127,7 @@ const ApiKeyDetailPage = () => {
|
|
|
146
127
|
if (error || !apiKey) {
|
|
147
128
|
return /* @__PURE__ */ React.createElement(ResponseErrorPanel, { error: error || new Error("API key not found") });
|
|
148
129
|
}
|
|
149
|
-
const phase = apiKey.status?.
|
|
130
|
+
const phase = getAPIKeyPhase(apiKey.status?.conditions || []);
|
|
150
131
|
const statusLabel = phase === "Approved" ? "Active" : phase;
|
|
151
132
|
const hostname = apiKey.status?.apiHostname || "api.example.com";
|
|
152
133
|
const displayApiKey = apiKeyValue || "<your-api-key>";
|
|
@@ -232,16 +213,12 @@ func main() {
|
|
|
232
213
|
}
|
|
233
214
|
)), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Requester"), /* @__PURE__ */ React.createElement(Typography, { variant: "body1", className: classes.value }, apiKey.spec.requestedBy?.userId), /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Requested"), /* @__PURE__ */ React.createElement(Typography, { variant: "body1", className: classes.value }, apiKey.metadata.creationTimestamp ? new Date(
|
|
234
215
|
apiKey.metadata.creationTimestamp
|
|
235
|
-
).toLocaleDateString() : "-"),
|
|
236
|
-
apiKey.status
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
{
|
|
240
|
-
|
|
241
|
-
color: "disabled",
|
|
242
|
-
style: { marginLeft: 8 }
|
|
243
|
-
}
|
|
244
|
-
))) : /* @__PURE__ */ React.createElement(Box, { className: classes.apiKeyContainer }, /* @__PURE__ */ React.createElement(
|
|
216
|
+
).toLocaleDateString() : "-"), (() => {
|
|
217
|
+
const approvalDate = apiKey.status?.conditions?.find(
|
|
218
|
+
(c) => c.type === "Approved" && c.status === "True"
|
|
219
|
+
)?.lastTransitionTime;
|
|
220
|
+
return approvalDate ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Approved On"), /* @__PURE__ */ React.createElement(Typography, { variant: "body1", className: classes.value }, new Date(approvalDate).toLocaleDateString())) : null;
|
|
221
|
+
})()))), /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12, md: 6 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Use Case" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body1" }, apiKey.spec.useCase || "No use case provided")), phase === "Approved" && /* @__PURE__ */ React.createElement(Box, { mt: 2 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "API Key" }, /* @__PURE__ */ React.createElement(Box, { className: classes.apiKeyContainer }, /* @__PURE__ */ React.createElement(
|
|
245
222
|
Typography,
|
|
246
223
|
{
|
|
247
224
|
variant: "body2",
|
|
@@ -258,14 +235,14 @@ func main() {
|
|
|
258
235
|
)), /* @__PURE__ */ React.createElement(
|
|
259
236
|
Tooltip,
|
|
260
237
|
{
|
|
261
|
-
title: showApiKey ? "Hide API key" : "Reveal API key
|
|
238
|
+
title: showApiKey ? "Hide API key" : "Reveal API key"
|
|
262
239
|
},
|
|
263
240
|
/* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement(
|
|
264
241
|
IconButton,
|
|
265
242
|
{
|
|
266
243
|
size: "small",
|
|
267
244
|
onClick: handleRevealClick,
|
|
268
|
-
disabled: apiKeyLoading
|
|
245
|
+
disabled: apiKeyLoading
|
|
269
246
|
},
|
|
270
247
|
showApiKey ? /* @__PURE__ */ React.createElement(VisibilityOffIcon, { fontSize: "small" }) : /* @__PURE__ */ React.createElement(VisibilityIcon, { fontSize: "small" })
|
|
271
248
|
))
|
|
@@ -284,25 +261,7 @@ func main() {
|
|
|
284
261
|
code: codeExamples[selectedTab].code,
|
|
285
262
|
onCopy: () => handleCopy(codeExamples[selectedTab].code)
|
|
286
263
|
}
|
|
287
|
-
))))), apiKey.status?.limits && /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Rate Limits" }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2 }, apiKey.status.limits.daily && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Daily"), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, apiKey.status.limits.daily.toLocaleString())), apiKey.status.limits.weekly && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Weekly"), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, apiKey.status.limits.weekly.toLocaleString())), apiKey.status.limits.monthly && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Monthly"), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, apiKey.status.limits.monthly.toLocaleString())))))))
|
|
288
|
-
Dialog,
|
|
289
|
-
{
|
|
290
|
-
open: showWarning,
|
|
291
|
-
onClose: () => setShowWarning(false),
|
|
292
|
-
maxWidth: "sm"
|
|
293
|
-
},
|
|
294
|
-
/* @__PURE__ */ React.createElement(DialogTitle, null, /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(WarningIcon, { color: "primary", style: { marginRight: 8 } }), "View API Key")),
|
|
295
|
-
/* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", paragraph: true }, "This API key can only be viewed ", /* @__PURE__ */ React.createElement("strong", null, "once"), ". After you reveal it, you will not be able to retrieve it again."), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Make sure to copy and store it securely before closing this view.")),
|
|
296
|
-
/* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(Button, { onClick: () => setShowWarning(false) }, "Cancel"), /* @__PURE__ */ React.createElement(
|
|
297
|
-
Button,
|
|
298
|
-
{
|
|
299
|
-
variant: "contained",
|
|
300
|
-
color: "primary",
|
|
301
|
-
onClick: handleConfirmReveal
|
|
302
|
-
},
|
|
303
|
-
"Reveal API Key"
|
|
304
|
-
))
|
|
305
|
-
));
|
|
264
|
+
))))), apiKey.status?.limits && /* @__PURE__ */ React.createElement(Grid, { item: true, xs: 12 }, /* @__PURE__ */ React.createElement(InfoCard, { title: "Rate Limits" }, /* @__PURE__ */ React.createElement(Grid, { container: true, spacing: 2 }, apiKey.status.limits.daily && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Daily"), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, apiKey.status.limits.daily.toLocaleString())), apiKey.status.limits.weekly && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Weekly"), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, apiKey.status.limits.weekly.toLocaleString())), apiKey.status.limits.monthly && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", className: classes.label }, "Monthly"), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, apiKey.status.limits.monthly.toLocaleString()))))))));
|
|
306
265
|
};
|
|
307
266
|
|
|
308
267
|
export { ApiKeyDetailPage };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApiKeyDetailPage.esm.js","sources":["../../../src/components/ApiKeyDetailPage/ApiKeyDetailPage.tsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport {\n useApi,\n alertApiRef,\n} from \"@backstage/core-plugin-api\";\nimport { kuadrantApiRef } from '../../api';\nimport { useAsync } from \"react-use\";\nimport {\n Header,\n Page,\n Content,\n ResponseErrorPanel,\n InfoCard,\n Link,\n Breadcrumbs,\n} from \"@backstage/core-components\";\nimport {\n Box,\n Grid,\n Typography,\n Chip,\n IconButton,\n Tooltip,\n Tabs,\n Tab,\n Button,\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n makeStyles,\n} from \"@material-ui/core\";\nimport { Skeleton } from \"@material-ui/lab\";\nimport VisibilityIcon from \"@material-ui/icons/Visibility\";\nimport VisibilityOffIcon from \"@material-ui/icons/VisibilityOff\";\nimport FileCopyIcon from \"@material-ui/icons/FileCopy\";\nimport WarningIcon from \"@material-ui/icons/Warning\";\nimport ArrowBackIcon from \"@material-ui/icons/ArrowBack\";\nimport OpenInNewIcon from \"@material-ui/icons/OpenInNew\";\nimport EmailIcon from \"@material-ui/icons/Email\";\nimport { APIKey, APIProduct } from \"../../types/api-management\";\nimport { getApprovalQueueStatusChipStyle } from \"../../utils/styles\";\n\nconst useStyles = makeStyles((theme) => ({\n label: {\n fontWeight: 600,\n color: theme.palette.text.secondary,\n marginBottom: theme.spacing(0.5),\n },\n value: {\n marginBottom: theme.spacing(2),\n },\n codeBlock: {\n backgroundColor: theme.palette.background.default,\n padding: theme.spacing(2),\n borderRadius: theme.shape.borderRadius,\n fontFamily: \"monospace\",\n fontSize: \"0.875rem\",\n overflow: \"auto\",\n whiteSpace: \"pre-wrap\",\n wordBreak: \"break-all\",\n },\n apiKeyContainer: {\n display: \"flex\",\n alignItems: \"center\",\n gap: theme.spacing(1),\n padding: theme.spacing(1.5),\n backgroundColor: theme.palette.background.default,\n borderRadius: theme.shape.borderRadius,\n fontFamily: \"monospace\",\n },\n tabPanel: {\n marginTop: theme.spacing(2),\n },\n}));\n\nconst CodeExample = ({\n code,\n onCopy,\n}: {\n code: string;\n onCopy: () => void;\n}) => {\n const classes = useStyles();\n\n return (\n <Box position=\"relative\">\n <Box className={classes.codeBlock}>{code}</Box>\n <Tooltip title=\"Copy code\">\n <IconButton\n size=\"small\"\n style={{ position: \"absolute\", top: 8, right: 8 }}\n onClick={onCopy}\n >\n <FileCopyIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n </Box>\n );\n};\n\nexport const ApiKeyDetailPage = () => {\n const classes = useStyles();\n const { namespace, name } = useParams<{ namespace: string; name: string }>();\n const kuadrantApi = useApi(kuadrantApiRef);\n const alertApi = useApi(alertApiRef);\n\n const [selectedTab, setSelectedTab] = useState(0);\n const [showApiKey, setShowApiKey] = useState(false);\n const [apiKeyValue, setApiKeyValue] = useState<string | null>(null);\n const [apiKeyLoading, setApiKeyLoading] = useState(false);\n const [alreadyRead, setAlreadyRead] = useState(false);\n const [showWarning, setShowWarning] = useState(false);\n\n const {\n value: data,\n loading,\n error,\n } = useAsync(async () => {\n const [apiKeyData, productsData] = await Promise.all([\n kuadrantApi.getApiKey(namespace!, name!),\n kuadrantApi.getApiProducts(),\n ]);\n\n // check if key has already been read\n if (apiKeyData.status?.canReadSecret === false) {\n setAlreadyRead(true);\n }\n\n // find matching api product to get contact info\n const apiProduct = (productsData.items || []).find(\n (p: APIProduct) =>\n p.metadata.name === apiKeyData.spec.apiProductRef?.name &&\n p.metadata.namespace === apiKeyData.metadata.namespace,\n );\n\n return { apiKey: apiKeyData as APIKey, apiProduct };\n }, [namespace, name, kuadrantApi]);\n\n const apiKey = data?.apiKey;\n const apiProduct = data?.apiProduct;\n\n const fetchApiKeySecret = async () => {\n setApiKeyLoading(true);\n try {\n const extractedSecret = await kuadrantApi.getApiKeySecret(namespace!, name!);\n setApiKeyValue(extractedSecret.apiKey);\n setAlreadyRead(true);\n setShowApiKey(true);\n } catch (err) {\n console.error(\"Failed to fetch API key:\", err);\n const errorMessage = err instanceof Error ? err.message : \"unknown error occurred\";\n if (errorMessage.includes(\"403\") || errorMessage.includes(\"already been viewed\")) {\n setAlreadyRead(true);\n alertApi.post({\n message:\n \"This API key has already been viewed and cannot be retrieved again.\",\n severity: \"warning\",\n display: \"transient\",\n });\n } else {\n alertApi.post({\n message: `Failed to fetch APIKey. ${errorMessage}`,\n severity: 'error',\n display: 'transient',\n });\n }\n } finally {\n setApiKeyLoading(false);\n }\n };\n\n const handleRevealClick = () => {\n if (showApiKey) {\n setShowApiKey(false);\n setApiKeyValue(null);\n } else if (!alreadyRead) {\n setShowWarning(true);\n }\n };\n\n const handleConfirmReveal = () => {\n setShowWarning(false);\n fetchApiKeySecret();\n };\n\n const handleCopy = async (text: string) => {\n await navigator.clipboard.writeText(text);\n alertApi.post({\n message: \"Copied to clipboard\",\n severity: \"success\",\n display: \"transient\",\n });\n };\n\n if (loading) {\n return (\n <Page themeId=\"tool\">\n <Header title=\"Loading...\" />\n <Content>\n <Box p={2}>\n {[...Array(5)].map((_, i) => (\n <Box key={i} p={2}>\n <Skeleton variant=\"text\" width=\"100%\" />\n </Box>\n ))}\n </Box>\n </Content>\n </Page>\n );\n }\n\n if (error || !apiKey) {\n return (\n <ResponseErrorPanel error={error || new Error(\"API key not found\")} />\n );\n }\n\n const phase = apiKey.status?.phase || \"Pending\";\n const statusLabel = phase === \"Approved\" ? \"Active\" : phase;\n const hostname = apiKey.status?.apiHostname || \"api.example.com\";\n const displayApiKey = apiKeyValue || \"<your-api-key>\";\n\n // code examples\n const curlExample = `curl -H \"Authorization: Bearer ${displayApiKey}\" \\\\\n https://${hostname}/`;\n\n const nodeExample = `const response = await fetch('https://${hostname}/', {\n headers: {\n 'Authorization': 'Bearer ${displayApiKey}'\n }\n});\nconst data = await response.json();`;\n\n const pythonExample = `import requests\n\nresponse = requests.get(\n 'https://${hostname}/',\n headers={'Authorization': 'Bearer ${displayApiKey}'}\n)\ndata = response.json()`;\n\n const goExample = `package main\n\nimport (\n \"net/http\"\n)\n\nfunc main() {\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", \"https://${hostname}/\", nil)\n req.Header.Set(\"Authorization\", \"Bearer ${displayApiKey}\")\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n}`;\n\n const codeExamples = [\n { label: \"cURL\", code: curlExample },\n { label: \"Node.js\", code: nodeExample },\n { label: \"Python\", code: pythonExample },\n { label: \"Go\", code: goExample },\n ];\n\n return (\n <Page themeId=\"tool\">\n <Header\n title={apiKey.metadata.name}\n subtitle={`API Key for ${apiKey.spec.apiProductRef?.name || \"unknown\"}`}\n >\n <Link to=\"/kuadrant/my-api-keys\">\n <Button startIcon={<ArrowBackIcon />}>Back to API Keys</Button>\n </Link>\n </Header>\n <Content>\n <Box mb={2}>\n <Breadcrumbs aria-label=\"breadcrumb\">\n <Link to=\"/kuadrant/my-api-keys\">API keys</Link>\n <Typography>{apiKey.metadata.name}</Typography>\n </Breadcrumbs>\n </Box>\n\n <Box mb={3} display=\"flex\" style={{ gap: 8 }}>\n <Link to={`/catalog/default/api/${apiKey.spec.apiProductRef?.name}`}>\n <Button\n variant=\"outlined\"\n startIcon={<OpenInNewIcon />}\n data-testid=\"view-api-button\"\n >\n View API\n </Button>\n </Link>\n {apiProduct?.spec?.contact &&\n (apiProduct.spec.contact.email ||\n apiProduct.spec.contact.url ||\n apiProduct.spec.contact.slack) && (\n <Button\n variant=\"outlined\"\n startIcon={<EmailIcon />}\n href={\n apiProduct.spec.contact.email\n ? `mailto:${apiProduct.spec.contact.email}`\n : apiProduct.spec.contact.slack\n ? apiProduct.spec.contact.slack\n : apiProduct.spec.contact.url || \"#\"\n }\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n Contact Owner\n </Button>\n )}\n </Box>\n\n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n <InfoCard title=\"API Key Details\">\n <Box>\n <Typography variant=\"caption\" className={classes.label}>\n Status\n </Typography>\n <Box className={classes.value}>\n <Chip\n label={statusLabel}\n size=\"small\"\n style={getApprovalQueueStatusChipStyle(phase)}\n data-testid=\"api-key-status-chip\"\n />\n </Box>\n\n <Typography variant=\"caption\" className={classes.label}>\n API Product\n </Typography>\n <Typography variant=\"body1\" className={classes.value}>\n <Link\n to={`/catalog/default/api/${apiKey.spec.apiProductRef?.name}/api-keys`}\n >\n {apiKey.spec.apiProductRef?.name || \"unknown\"}\n </Link>\n </Typography>\n\n <Typography variant=\"caption\" className={classes.label}>\n Tier\n </Typography>\n <Box className={classes.value}>\n <Chip\n label={apiKey.spec.planTier}\n size=\"small\"\n variant=\"outlined\"\n />\n </Box>\n\n <Typography variant=\"caption\" className={classes.label}>\n Requester\n </Typography>\n <Typography variant=\"body1\" className={classes.value}>\n {apiKey.spec.requestedBy?.userId}\n </Typography>\n\n <Typography variant=\"caption\" className={classes.label}>\n Requested\n </Typography>\n <Typography variant=\"body1\" className={classes.value}>\n {apiKey.metadata.creationTimestamp\n ? new Date(\n apiKey.metadata.creationTimestamp,\n ).toLocaleDateString()\n : \"-\"}\n </Typography>\n\n {apiKey.status?.reviewedBy && (\n <>\n <Typography variant=\"caption\" className={classes.label}>\n Reviewed By\n </Typography>\n <Typography variant=\"body1\" className={classes.value}>\n {apiKey.status.reviewedBy.replace(/^user:default\\//, \"\")}\n {apiKey.status.reviewedAt && (\n <Typography variant=\"caption\" color=\"textSecondary\">\n {\" \"}\n on{\" \"}\n {new Date(\n apiKey.status.reviewedAt,\n ).toLocaleDateString()}\n </Typography>\n )}\n </Typography>\n </>\n )}\n </Box>\n </InfoCard>\n </Grid>\n\n <Grid item xs={12} md={6}>\n <InfoCard title=\"Use Case\">\n <Typography variant=\"body1\">\n {apiKey.spec.useCase || \"No use case provided\"}\n </Typography>\n </InfoCard>\n\n {phase === \"Approved\" && (\n <Box mt={2}>\n <InfoCard title=\"API Key\">\n {alreadyRead && !apiKeyValue ? (\n <Tooltip title=\"This API key has already been viewed and cannot be retrieved again\">\n <Box display=\"flex\" alignItems=\"center\">\n <Typography variant=\"body2\" color=\"textSecondary\">\n Already viewed - cannot be retrieved again\n </Typography>\n <VisibilityOffIcon\n fontSize=\"small\"\n color=\"disabled\"\n style={{ marginLeft: 8 }}\n />\n </Box>\n </Tooltip>\n ) : (\n <Box className={classes.apiKeyContainer}>\n <Typography\n variant=\"body2\"\n style={{ fontFamily: \"monospace\", flex: 1 }}\n >\n {apiKeyLoading\n ? \"Loading...\"\n : showApiKey && apiKeyValue\n ? apiKeyValue\n : \"•\".repeat(32)}\n </Typography>\n {showApiKey && apiKeyValue && (\n <Tooltip title=\"Copy to clipboard\">\n <IconButton\n size=\"small\"\n onClick={() => handleCopy(apiKeyValue)}\n >\n <FileCopyIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n <Tooltip\n title={\n showApiKey\n ? \"Hide API key\"\n : \"Reveal API key (one-time only)\"\n }\n >\n <span>\n <IconButton\n size=\"small\"\n onClick={handleRevealClick}\n disabled={\n apiKeyLoading || (alreadyRead && !apiKeyValue)\n }\n >\n {showApiKey ? (\n <VisibilityOffIcon fontSize=\"small\" />\n ) : (\n <VisibilityIcon fontSize=\"small\" />\n )}\n </IconButton>\n </span>\n </Tooltip>\n </Box>\n )}\n </InfoCard>\n </Box>\n )}\n </Grid>\n\n {phase === \"Approved\" && (\n <Grid item xs={12}>\n <InfoCard title=\"Code Examples\">\n <Box>\n <Tabs\n value={selectedTab}\n onChange={(_, newValue) => setSelectedTab(newValue)}\n indicatorColor=\"primary\"\n textColor=\"primary\"\n >\n {codeExamples.map((ex) => (\n <Tab key={ex.label} label={ex.label} />\n ))}\n </Tabs>\n <Box className={classes.tabPanel}>\n <CodeExample\n code={codeExamples[selectedTab].code}\n onCopy={() => handleCopy(codeExamples[selectedTab].code)}\n />\n </Box>\n </Box>\n </InfoCard>\n </Grid>\n )}\n\n {apiKey.status?.limits && (\n <Grid item xs={12}>\n <InfoCard title=\"Rate Limits\">\n <Grid container spacing={2}>\n {apiKey.status.limits.daily && (\n <Grid item>\n <Typography variant=\"caption\" className={classes.label}>\n Daily\n </Typography>\n <Typography variant=\"h6\">\n {apiKey.status.limits.daily.toLocaleString()}\n </Typography>\n </Grid>\n )}\n {apiKey.status.limits.weekly && (\n <Grid item>\n <Typography variant=\"caption\" className={classes.label}>\n Weekly\n </Typography>\n <Typography variant=\"h6\">\n {apiKey.status.limits.weekly.toLocaleString()}\n </Typography>\n </Grid>\n )}\n {apiKey.status.limits.monthly && (\n <Grid item>\n <Typography variant=\"caption\" className={classes.label}>\n Monthly\n </Typography>\n <Typography variant=\"h6\">\n {apiKey.status.limits.monthly.toLocaleString()}\n </Typography>\n </Grid>\n )}\n </Grid>\n </InfoCard>\n </Grid>\n )}\n </Grid>\n </Content>\n\n <Dialog\n open={showWarning}\n onClose={() => setShowWarning(false)}\n maxWidth=\"sm\"\n >\n <DialogTitle>\n <Box display=\"flex\" alignItems=\"center\">\n <WarningIcon color=\"primary\" style={{ marginRight: 8 }} />\n View API Key\n </Box>\n </DialogTitle>\n <DialogContent>\n <Typography variant=\"body1\" paragraph>\n This API key can only be viewed <strong>once</strong>. After you\n reveal it, you will not be able to retrieve it again.\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Make sure to copy and store it securely before closing this view.\n </Typography>\n </DialogContent>\n <DialogActions>\n <Button onClick={() => setShowWarning(false)}>Cancel</Button>\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleConfirmReveal}\n >\n Reveal API Key\n </Button>\n </DialogActions>\n </Dialog>\n </Page>\n );\n};\n"],"names":["apiProduct"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ApiKeyDetailPage.esm.js","sources":["../../../src/components/ApiKeyDetailPage/ApiKeyDetailPage.tsx"],"sourcesContent":["import React, { useState } from \"react\";\nimport { useParams } from \"react-router-dom\";\nimport {\n useApi,\n alertApiRef,\n} from \"@backstage/core-plugin-api\";\nimport { kuadrantApiRef } from '../../api';\nimport { getAPIKeyPhase } from '../../utils/apikeys';\nimport { useAsync } from \"react-use\";\nimport {\n Header,\n Page,\n Content,\n ResponseErrorPanel,\n InfoCard,\n Link,\n Breadcrumbs,\n} from \"@backstage/core-components\";\nimport {\n Box,\n Grid,\n Typography,\n Chip,\n IconButton,\n Tooltip,\n Tabs,\n Tab,\n Button,\n makeStyles,\n} from \"@material-ui/core\";\nimport { Skeleton } from \"@material-ui/lab\";\nimport VisibilityIcon from \"@material-ui/icons/Visibility\";\nimport VisibilityOffIcon from \"@material-ui/icons/VisibilityOff\";\nimport FileCopyIcon from \"@material-ui/icons/FileCopy\";\nimport ArrowBackIcon from \"@material-ui/icons/ArrowBack\";\nimport OpenInNewIcon from \"@material-ui/icons/OpenInNew\";\nimport EmailIcon from \"@material-ui/icons/Email\";\nimport { APIKey, APIProduct } from \"../../types/api-management\";\nimport { getApprovalQueueStatusChipStyle } from \"../../utils/styles\";\n\nconst useStyles = makeStyles((theme) => ({\n label: {\n fontWeight: 600,\n color: theme.palette.text.secondary,\n marginBottom: theme.spacing(0.5),\n },\n value: {\n marginBottom: theme.spacing(2),\n },\n codeBlock: {\n backgroundColor: theme.palette.background.default,\n padding: theme.spacing(2),\n borderRadius: theme.shape.borderRadius,\n fontFamily: \"monospace\",\n fontSize: \"0.875rem\",\n overflow: \"auto\",\n whiteSpace: \"pre-wrap\",\n wordBreak: \"break-all\",\n },\n apiKeyContainer: {\n display: \"flex\",\n alignItems: \"center\",\n gap: theme.spacing(1),\n padding: theme.spacing(1.5),\n backgroundColor: theme.palette.background.default,\n borderRadius: theme.shape.borderRadius,\n fontFamily: \"monospace\",\n },\n tabPanel: {\n marginTop: theme.spacing(2),\n },\n}));\n\nconst CodeExample = ({\n code,\n onCopy,\n}: {\n code: string;\n onCopy: () => void;\n}) => {\n const classes = useStyles();\n\n return (\n <Box position=\"relative\">\n <Box className={classes.codeBlock}>{code}</Box>\n <Tooltip title=\"Copy code\">\n <IconButton\n size=\"small\"\n style={{ position: \"absolute\", top: 8, right: 8 }}\n onClick={onCopy}\n >\n <FileCopyIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n </Box>\n );\n};\n\nexport const ApiKeyDetailPage = () => {\n const classes = useStyles();\n const { namespace, name } = useParams<{ namespace: string; name: string }>();\n const kuadrantApi = useApi(kuadrantApiRef);\n const alertApi = useApi(alertApiRef);\n\n const [selectedTab, setSelectedTab] = useState(0);\n const [showApiKey, setShowApiKey] = useState(false);\n const [apiKeyValue, setApiKeyValue] = useState<string | null>(null);\n const [apiKeyLoading, setApiKeyLoading] = useState(false);\n\n const {\n value: data,\n loading,\n error,\n } = useAsync(async () => {\n const [apiKeyData, productsData] = await Promise.all([\n kuadrantApi.getApiKey(namespace!, name!),\n kuadrantApi.getApiProducts(),\n ]);\n\n // find matching api product to get contact info\n const apiProduct = (productsData.items || []).find(\n (p: APIProduct) =>\n p.metadata.name === apiKeyData.spec.apiProductRef?.name &&\n p.metadata.namespace === apiKeyData.metadata.namespace,\n );\n\n return { apiKey: apiKeyData as APIKey, apiProduct };\n }, [namespace, name, kuadrantApi]);\n\n const apiKey = data?.apiKey;\n const apiProduct = data?.apiProduct;\n\n const fetchApiKeySecret = async () => {\n setApiKeyLoading(true);\n try {\n const extractedSecret = await kuadrantApi.getApiKeySecret(namespace!, name!);\n setApiKeyValue(extractedSecret.apiKey);\n setShowApiKey(true);\n } catch (err) {\n console.error(\"Failed to fetch API key:\", err);\n const errorMessage = err instanceof Error ? err.message : \"unknown error occurred\";\n alertApi.post({\n message: `Failed to fetch APIKey. ${errorMessage}`,\n severity: 'error',\n display: 'transient',\n });\n } finally {\n setApiKeyLoading(false);\n }\n };\n\n const handleRevealClick = () => {\n if (showApiKey) {\n setShowApiKey(false);\n setApiKeyValue(null);\n } else {\n fetchApiKeySecret();\n }\n };\n\n const handleCopy = async (text: string) => {\n await navigator.clipboard.writeText(text);\n alertApi.post({\n message: \"Copied to clipboard\",\n severity: \"success\",\n display: \"transient\",\n });\n };\n\n if (loading) {\n return (\n <Page themeId=\"tool\">\n <Header title=\"Loading...\" />\n <Content>\n <Box p={2}>\n {[...Array(5)].map((_, i) => (\n <Box key={i} p={2}>\n <Skeleton variant=\"text\" width=\"100%\" />\n </Box>\n ))}\n </Box>\n </Content>\n </Page>\n );\n }\n\n if (error || !apiKey) {\n return (\n <ResponseErrorPanel error={error || new Error(\"API key not found\")} />\n );\n }\n\n const phase = getAPIKeyPhase(apiKey.status?.conditions || []);\n const statusLabel = phase === \"Approved\" ? \"Active\" : phase;\n const hostname = apiKey.status?.apiHostname || \"api.example.com\";\n const displayApiKey = apiKeyValue || \"<your-api-key>\";\n\n // code examples\n const curlExample = `curl -H \"Authorization: Bearer ${displayApiKey}\" \\\\\n https://${hostname}/`;\n\n const nodeExample = `const response = await fetch('https://${hostname}/', {\n headers: {\n 'Authorization': 'Bearer ${displayApiKey}'\n }\n});\nconst data = await response.json();`;\n\n const pythonExample = `import requests\n\nresponse = requests.get(\n 'https://${hostname}/',\n headers={'Authorization': 'Bearer ${displayApiKey}'}\n)\ndata = response.json()`;\n\n const goExample = `package main\n\nimport (\n \"net/http\"\n)\n\nfunc main() {\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", \"https://${hostname}/\", nil)\n req.Header.Set(\"Authorization\", \"Bearer ${displayApiKey}\")\n resp, _ := client.Do(req)\n defer resp.Body.Close()\n}`;\n\n const codeExamples = [\n { label: \"cURL\", code: curlExample },\n { label: \"Node.js\", code: nodeExample },\n { label: \"Python\", code: pythonExample },\n { label: \"Go\", code: goExample },\n ];\n\n return (\n <Page themeId=\"tool\">\n <Header\n title={apiKey.metadata.name}\n subtitle={`API Key for ${apiKey.spec.apiProductRef?.name || \"unknown\"}`}\n >\n <Link to=\"/kuadrant/my-api-keys\">\n <Button startIcon={<ArrowBackIcon />}>Back to API Keys</Button>\n </Link>\n </Header>\n <Content>\n <Box mb={2}>\n <Breadcrumbs aria-label=\"breadcrumb\">\n <Link to=\"/kuadrant/my-api-keys\">API keys</Link>\n <Typography>{apiKey.metadata.name}</Typography>\n </Breadcrumbs>\n </Box>\n\n <Box mb={3} display=\"flex\" style={{ gap: 8 }}>\n <Link to={`/catalog/default/api/${apiKey.spec.apiProductRef?.name}`}>\n <Button\n variant=\"outlined\"\n startIcon={<OpenInNewIcon />}\n data-testid=\"view-api-button\"\n >\n View API\n </Button>\n </Link>\n {apiProduct?.spec?.contact &&\n (apiProduct.spec.contact.email ||\n apiProduct.spec.contact.url ||\n apiProduct.spec.contact.slack) && (\n <Button\n variant=\"outlined\"\n startIcon={<EmailIcon />}\n href={\n apiProduct.spec.contact.email\n ? `mailto:${apiProduct.spec.contact.email}`\n : apiProduct.spec.contact.slack\n ? apiProduct.spec.contact.slack\n : apiProduct.spec.contact.url || \"#\"\n }\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n Contact Owner\n </Button>\n )}\n </Box>\n\n <Grid container spacing={3}>\n <Grid item xs={12} md={6}>\n <InfoCard title=\"API Key Details\">\n <Box>\n <Typography variant=\"caption\" className={classes.label}>\n Status\n </Typography>\n <Box className={classes.value}>\n <Chip\n label={statusLabel}\n size=\"small\"\n style={getApprovalQueueStatusChipStyle(phase)}\n data-testid=\"api-key-status-chip\"\n />\n </Box>\n\n <Typography variant=\"caption\" className={classes.label}>\n API Product\n </Typography>\n <Typography variant=\"body1\" className={classes.value}>\n <Link\n to={`/catalog/default/api/${apiKey.spec.apiProductRef?.name}/api-keys`}\n >\n {apiKey.spec.apiProductRef?.name || \"unknown\"}\n </Link>\n </Typography>\n\n <Typography variant=\"caption\" className={classes.label}>\n Tier\n </Typography>\n <Box className={classes.value}>\n <Chip\n label={apiKey.spec.planTier}\n size=\"small\"\n variant=\"outlined\"\n />\n </Box>\n\n <Typography variant=\"caption\" className={classes.label}>\n Requester\n </Typography>\n <Typography variant=\"body1\" className={classes.value}>\n {apiKey.spec.requestedBy?.userId}\n </Typography>\n\n <Typography variant=\"caption\" className={classes.label}>\n Requested\n </Typography>\n <Typography variant=\"body1\" className={classes.value}>\n {apiKey.metadata.creationTimestamp\n ? new Date(\n apiKey.metadata.creationTimestamp,\n ).toLocaleDateString()\n : \"-\"}\n </Typography>\n\n {(() => {\n const approvalDate = apiKey.status?.conditions?.find(\n c => c.type === 'Approved' && c.status === 'True'\n )?.lastTransitionTime;\n return approvalDate ? (\n <>\n <Typography variant=\"caption\" className={classes.label}>\n Approved On\n </Typography>\n <Typography variant=\"body1\" className={classes.value}>\n {new Date(approvalDate).toLocaleDateString()}\n </Typography>\n </>\n ) : null;\n })()}\n </Box>\n </InfoCard>\n </Grid>\n\n <Grid item xs={12} md={6}>\n <InfoCard title=\"Use Case\">\n <Typography variant=\"body1\">\n {apiKey.spec.useCase || \"No use case provided\"}\n </Typography>\n </InfoCard>\n\n {phase === \"Approved\" && (\n <Box mt={2}>\n <InfoCard title=\"API Key\">\n <Box className={classes.apiKeyContainer}>\n <Typography\n variant=\"body2\"\n style={{ fontFamily: \"monospace\", flex: 1 }}\n >\n {apiKeyLoading\n ? \"Loading...\"\n : showApiKey && apiKeyValue\n ? apiKeyValue\n : \"•\".repeat(32)}\n </Typography>\n {showApiKey && apiKeyValue && (\n <Tooltip title=\"Copy to clipboard\">\n <IconButton\n size=\"small\"\n onClick={() => handleCopy(apiKeyValue)}\n >\n <FileCopyIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n <Tooltip\n title={\n showApiKey\n ? \"Hide API key\"\n : \"Reveal API key\"\n }\n >\n <span>\n <IconButton\n size=\"small\"\n onClick={handleRevealClick}\n disabled={apiKeyLoading}\n >\n {showApiKey ? (\n <VisibilityOffIcon fontSize=\"small\" />\n ) : (\n <VisibilityIcon fontSize=\"small\" />\n )}\n </IconButton>\n </span>\n </Tooltip>\n </Box>\n </InfoCard>\n </Box>\n )}\n </Grid>\n\n {phase === \"Approved\" && (\n <Grid item xs={12}>\n <InfoCard title=\"Code Examples\">\n <Box>\n <Tabs\n value={selectedTab}\n onChange={(_, newValue) => setSelectedTab(newValue)}\n indicatorColor=\"primary\"\n textColor=\"primary\"\n >\n {codeExamples.map((ex) => (\n <Tab key={ex.label} label={ex.label} />\n ))}\n </Tabs>\n <Box className={classes.tabPanel}>\n <CodeExample\n code={codeExamples[selectedTab].code}\n onCopy={() => handleCopy(codeExamples[selectedTab].code)}\n />\n </Box>\n </Box>\n </InfoCard>\n </Grid>\n )}\n\n {apiKey.status?.limits && (\n <Grid item xs={12}>\n <InfoCard title=\"Rate Limits\">\n <Grid container spacing={2}>\n {apiKey.status.limits.daily && (\n <Grid item>\n <Typography variant=\"caption\" className={classes.label}>\n Daily\n </Typography>\n <Typography variant=\"h6\">\n {apiKey.status.limits.daily.toLocaleString()}\n </Typography>\n </Grid>\n )}\n {apiKey.status.limits.weekly && (\n <Grid item>\n <Typography variant=\"caption\" className={classes.label}>\n Weekly\n </Typography>\n <Typography variant=\"h6\">\n {apiKey.status.limits.weekly.toLocaleString()}\n </Typography>\n </Grid>\n )}\n {apiKey.status.limits.monthly && (\n <Grid item>\n <Typography variant=\"caption\" className={classes.label}>\n Monthly\n </Typography>\n <Typography variant=\"h6\">\n {apiKey.status.limits.monthly.toLocaleString()}\n </Typography>\n </Grid>\n )}\n </Grid>\n </InfoCard>\n </Grid>\n )}\n </Grid>\n </Content>\n </Page>\n );\n};\n"],"names":["apiProduct"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState, useMemo } from 'react';
|
|
2
2
|
import { useAsync } from 'react-use';
|
|
3
3
|
import { CodeSnippet, ResponseErrorPanel, Table } from '@backstage/core-components';
|
|
4
|
-
import { Box, Typography, Tabs, Tab, Grid, Button, Menu, MenuItem,
|
|
4
|
+
import { Box, Typography, Tabs, Tab, Grid, Button, Menu, MenuItem, Chip, Tooltip, IconButton, CircularProgress } from '@material-ui/core';
|
|
5
5
|
import { Skeleton } from '@material-ui/lab';
|
|
6
6
|
import { useApi, identityApiRef, alertApiRef } from '@backstage/core-plugin-api';
|
|
7
7
|
import { kuadrantApiRef } from '../../api.esm.js';
|
|
@@ -13,9 +13,9 @@ import CancelIcon from '@material-ui/icons/Cancel';
|
|
|
13
13
|
import AddIcon from '@material-ui/icons/Add';
|
|
14
14
|
import MoreVertIcon from '@material-ui/icons/MoreVert';
|
|
15
15
|
import FileCopyIcon from '@material-ui/icons/FileCopy';
|
|
16
|
-
import WarningIcon from '@material-ui/icons/Warning';
|
|
17
16
|
import { kuadrantApiKeyCreatePermission, kuadrantApiKeyDeleteOwnPermission, kuadrantApiKeyDeleteAllPermission, kuadrantApiKeyUpdateOwnPermission } from '../../permissions.esm.js';
|
|
18
17
|
import { useKuadrantPermission, canDeleteResource } from '../../utils/permissions.esm.js';
|
|
18
|
+
import { getAPIKeyPhase } from '../../utils/apikeys.esm.js';
|
|
19
19
|
import { EditAPIKeyDialog } from '../EditAPIKeyDialog/EditAPIKeyDialog.esm.js';
|
|
20
20
|
import { ConfirmDeleteDialog } from '../ConfirmDeleteDialog/ConfirmDeleteDialog.esm.js';
|
|
21
21
|
import { generateAuthCodeSnippets } from '../../utils/codeSnippets.esm.js';
|
|
@@ -45,11 +45,6 @@ const ApiKeyManagementTab = ({
|
|
|
45
45
|
/* @__PURE__ */ new Map()
|
|
46
46
|
);
|
|
47
47
|
const [apiKeyLoading, setApiKeyLoading] = useState(/* @__PURE__ */ new Set());
|
|
48
|
-
const [alreadyReadKeys, setAlreadyReadKeys] = useState(
|
|
49
|
-
/* @__PURE__ */ new Set()
|
|
50
|
-
);
|
|
51
|
-
const [showOnceWarningOpen, setShowOnceWarningOpen] = useState(false);
|
|
52
|
-
const [pendingKeyReveal, setPendingKeyReveal] = useState(null);
|
|
53
48
|
const apiProductName = entity.metadata.annotations?.["kuadrant.io/apiproduct"] || entity.metadata.name;
|
|
54
49
|
const namespace = entity.metadata.annotations?.["kuadrant.io/namespace"] || propNamespace || "default";
|
|
55
50
|
useAsync(async () => {
|
|
@@ -70,11 +65,8 @@ const ApiKeyManagementTab = ({
|
|
|
70
65
|
loading: requestsLoading,
|
|
71
66
|
error: requestsError
|
|
72
67
|
} = useAsync(async () => {
|
|
73
|
-
const data = await kuadrantApi.
|
|
74
|
-
return
|
|
75
|
-
(r) => r.spec.apiProductRef.name === apiProductName && r.metadata.namespace === namespace
|
|
76
|
-
// APIProducts and APIKeys (and its Secret) will be in the same NS
|
|
77
|
-
);
|
|
68
|
+
const data = await kuadrantApi.getRequestsByApiProduct(apiProductName, namespace);
|
|
69
|
+
return data.items || [];
|
|
78
70
|
}, [apiProductName, namespace, refresh, kuadrantApi]);
|
|
79
71
|
const {
|
|
80
72
|
value: apiProduct,
|
|
@@ -108,11 +100,11 @@ const ApiKeyManagementTab = ({
|
|
|
108
100
|
loading: updateRequestPermissionLoading,
|
|
109
101
|
error: updateRequestPermissionError
|
|
110
102
|
} = useKuadrantPermission(kuadrantApiKeyUpdateOwnPermission);
|
|
111
|
-
const handleDeleteRequest = async (
|
|
112
|
-
setOptimisticallyDeleted((prev) => new Set(prev).add(
|
|
113
|
-
setDeleting(
|
|
103
|
+
const handleDeleteRequest = async (deleteNamespace, deleteName) => {
|
|
104
|
+
setOptimisticallyDeleted((prev) => new Set(prev).add(deleteName));
|
|
105
|
+
setDeleting(deleteName);
|
|
114
106
|
try {
|
|
115
|
-
await kuadrantApi.deleteRequest(
|
|
107
|
+
await kuadrantApi.deleteRequest(deleteNamespace, deleteName);
|
|
116
108
|
alertApi.post({
|
|
117
109
|
message: "API key deleted successfully",
|
|
118
110
|
severity: "success",
|
|
@@ -123,7 +115,7 @@ const ApiKeyManagementTab = ({
|
|
|
123
115
|
const errorMessage = err instanceof Error ? err.message : "unknown error occurred";
|
|
124
116
|
setOptimisticallyDeleted((prev) => {
|
|
125
117
|
const next = new Set(prev);
|
|
126
|
-
next.delete(
|
|
118
|
+
next.delete(deleteName);
|
|
127
119
|
return next;
|
|
128
120
|
});
|
|
129
121
|
alertApi.post({
|
|
@@ -144,23 +136,14 @@ const ApiKeyManagementTab = ({
|
|
|
144
136
|
try {
|
|
145
137
|
const data = await kuadrantApi.getApiKeySecret(requestNamespace, requestName);
|
|
146
138
|
setApiKeyValues((prev) => new Map(prev).set(key, data.apiKey));
|
|
147
|
-
|
|
139
|
+
toggleVisibility(requestName);
|
|
148
140
|
} catch (err) {
|
|
149
141
|
const errorMessage = err instanceof Error ? err.message : "unknown error occurred";
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
display: "transient"
|
|
156
|
-
});
|
|
157
|
-
} else {
|
|
158
|
-
alertApi.post({
|
|
159
|
-
message: `Failed to fetch api key: ${errorMessage}`,
|
|
160
|
-
severity: "error",
|
|
161
|
-
display: "transient"
|
|
162
|
-
});
|
|
163
|
-
}
|
|
142
|
+
alertApi.post({
|
|
143
|
+
message: `Failed to fetch api key: ${errorMessage}`,
|
|
144
|
+
severity: "error",
|
|
145
|
+
display: "transient"
|
|
146
|
+
});
|
|
164
147
|
} finally {
|
|
165
148
|
setApiKeyLoading((prev) => {
|
|
166
149
|
const next = new Set(prev);
|
|
@@ -208,7 +191,10 @@ const ApiKeyManagementTab = ({
|
|
|
208
191
|
};
|
|
209
192
|
const handleDeleteConfirm = async () => {
|
|
210
193
|
if (!deleteDialogState.request) return;
|
|
211
|
-
await handleDeleteRequest(
|
|
194
|
+
await handleDeleteRequest(
|
|
195
|
+
deleteDialogState.request.metadata.namespace,
|
|
196
|
+
deleteDialogState.request.metadata.name
|
|
197
|
+
);
|
|
212
198
|
setDeleteDialogState({ open: false, request: null });
|
|
213
199
|
};
|
|
214
200
|
const handleDeleteCancel = () => {
|
|
@@ -352,13 +338,13 @@ const ApiKeyManagementTab = ({
|
|
|
352
338
|
);
|
|
353
339
|
const plans = apiProduct?.status?.discoveredPlans || [];
|
|
354
340
|
const pendingRequests = myRequests.filter(
|
|
355
|
-
(r) =>
|
|
341
|
+
(r) => getAPIKeyPhase(r.status?.conditions) === "Pending"
|
|
356
342
|
);
|
|
357
343
|
const approvedRequests = myRequests.filter(
|
|
358
|
-
(r) => r.status?.
|
|
344
|
+
(r) => getAPIKeyPhase(r.status?.conditions) === "Approved"
|
|
359
345
|
);
|
|
360
346
|
const rejectedRequests = myRequests.filter(
|
|
361
|
-
(r) => r.status?.
|
|
347
|
+
(r) => getAPIKeyPhase(r.status?.conditions) === "Denied"
|
|
362
348
|
);
|
|
363
349
|
const approvedColumns = [
|
|
364
350
|
{
|
|
@@ -368,8 +354,13 @@ const ApiKeyManagementTab = ({
|
|
|
368
354
|
},
|
|
369
355
|
{
|
|
370
356
|
title: "Approved",
|
|
371
|
-
field: "status.
|
|
372
|
-
render: (row) =>
|
|
357
|
+
field: "status.conditions",
|
|
358
|
+
render: (row) => {
|
|
359
|
+
const approvalDate = row.status?.conditions?.find(
|
|
360
|
+
(c) => c.type === "Approved" && c.status === "True"
|
|
361
|
+
)?.lastTransitionTime;
|
|
362
|
+
return /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, approvalDate ? new Date(approvalDate).toLocaleDateString() : "-");
|
|
363
|
+
}
|
|
373
364
|
},
|
|
374
365
|
{
|
|
375
366
|
title: "API Key",
|
|
@@ -381,33 +372,16 @@ const ApiKeyManagementTab = ({
|
|
|
381
372
|
const isVisible = visibleKeys.has(row.metadata.name);
|
|
382
373
|
const isLoading = apiKeyLoading.has(key);
|
|
383
374
|
const apiKeyValue = apiKeyValues.get(key);
|
|
384
|
-
const hasSecretRef = row.
|
|
385
|
-
const canReadSecret = row.status?.canReadSecret !== false;
|
|
386
|
-
const isAlreadyRead = alreadyReadKeys.has(key) || !canReadSecret;
|
|
375
|
+
const hasSecretRef = Boolean(row.spec?.secretRef?.name);
|
|
387
376
|
if (!hasSecretRef) {
|
|
388
377
|
return /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Awaiting secret...");
|
|
389
378
|
}
|
|
390
|
-
if (isAlreadyRead && !apiKeyValue) {
|
|
391
|
-
return /* @__PURE__ */ React.createElement(Tooltip, { title: "This API key has already been viewed and cannot be retrieved again" }, /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(
|
|
392
|
-
Typography,
|
|
393
|
-
{
|
|
394
|
-
variant: "body2",
|
|
395
|
-
color: "textSecondary",
|
|
396
|
-
style: { fontFamily: "monospace", marginRight: 8 }
|
|
397
|
-
},
|
|
398
|
-
"Already viewed"
|
|
399
|
-
), /* @__PURE__ */ React.createElement(VisibilityOffIcon, { fontSize: "small", color: "disabled" })));
|
|
400
|
-
}
|
|
401
379
|
const handleRevealClick = () => {
|
|
402
380
|
if (isVisible) {
|
|
403
381
|
clearApiKeyValue(row.metadata.namespace, row.metadata.name);
|
|
404
382
|
toggleVisibility(row.metadata.name);
|
|
405
|
-
} else
|
|
406
|
-
|
|
407
|
-
namespace: row.metadata.namespace,
|
|
408
|
-
name: row.metadata.name
|
|
409
|
-
});
|
|
410
|
-
setShowOnceWarningOpen(true);
|
|
383
|
+
} else {
|
|
384
|
+
fetchApiKeyFromSecret(row.metadata.namespace, row.metadata.name);
|
|
411
385
|
}
|
|
412
386
|
};
|
|
413
387
|
const handleCopy = async () => {
|
|
@@ -433,14 +407,14 @@ const ApiKeyManagementTab = ({
|
|
|
433
407
|
), isVisible && apiKeyValue && /* @__PURE__ */ React.createElement(Tooltip, { title: "Copy to clipboard" }, /* @__PURE__ */ React.createElement(IconButton, { size: "small", onClick: handleCopy }, /* @__PURE__ */ React.createElement(FileCopyIcon, { fontSize: "small" }))), /* @__PURE__ */ React.createElement(
|
|
434
408
|
Tooltip,
|
|
435
409
|
{
|
|
436
|
-
title: isVisible ? "Hide API key" : "Reveal API key
|
|
410
|
+
title: isVisible ? "Hide API key" : "Reveal API key"
|
|
437
411
|
},
|
|
438
412
|
/* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement(
|
|
439
413
|
IconButton,
|
|
440
414
|
{
|
|
441
415
|
size: "small",
|
|
442
416
|
onClick: handleRevealClick,
|
|
443
|
-
disabled: isLoading
|
|
417
|
+
disabled: isLoading
|
|
444
418
|
},
|
|
445
419
|
isVisible ? /* @__PURE__ */ React.createElement(VisibilityOffIcon, null) : /* @__PURE__ */ React.createElement(VisibilityIcon, null)
|
|
446
420
|
))
|
|
@@ -487,9 +461,9 @@ const ApiKeyManagementTab = ({
|
|
|
487
461
|
const requestColumns = [
|
|
488
462
|
{
|
|
489
463
|
title: "Status",
|
|
490
|
-
field: "status.
|
|
464
|
+
field: "status.conditions",
|
|
491
465
|
render: (row) => {
|
|
492
|
-
const phase = row.status?.
|
|
466
|
+
const phase = getAPIKeyPhase(row.status?.conditions || []);
|
|
493
467
|
const isPending = phase === "Pending";
|
|
494
468
|
return /* @__PURE__ */ React.createElement(
|
|
495
469
|
Chip,
|
|
@@ -536,11 +510,12 @@ const ApiKeyManagementTab = ({
|
|
|
536
510
|
},
|
|
537
511
|
{
|
|
538
512
|
title: "Reviewed",
|
|
539
|
-
field: "status.
|
|
513
|
+
field: "status.conditions",
|
|
540
514
|
render: (row) => {
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
515
|
+
const approvedDate = row.status?.conditions?.find(
|
|
516
|
+
(c) => (c.type === "Approved" || c.type === "Denied") && c.status === "True"
|
|
517
|
+
)?.lastTransitionTime;
|
|
518
|
+
return /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, approvedDate ? new Date(approvedDate).toLocaleDateString() : "-");
|
|
544
519
|
}
|
|
545
520
|
},
|
|
546
521
|
{
|
|
@@ -553,7 +528,7 @@ const ApiKeyManagementTab = ({
|
|
|
553
528
|
if (isDeleting) {
|
|
554
529
|
return /* @__PURE__ */ React.createElement(CircularProgress, { size: 20 });
|
|
555
530
|
}
|
|
556
|
-
const isPending =
|
|
531
|
+
const isPending = getAPIKeyPhase(row.status?.conditions || []) === "Pending";
|
|
557
532
|
const ownerId = row.spec.requestedBy.userId;
|
|
558
533
|
const canDelete = canDeleteResource(
|
|
559
534
|
ownerId,
|
|
@@ -659,7 +634,7 @@ const ApiKeyManagementTab = ({
|
|
|
659
634
|
)), rejectedRequests.length > 0 && /* @__PURE__ */ React.createElement(Grid, { item: true }, /* @__PURE__ */ React.createElement(
|
|
660
635
|
Table,
|
|
661
636
|
{
|
|
662
|
-
title: "
|
|
637
|
+
title: "Denied Requests",
|
|
663
638
|
options: {
|
|
664
639
|
paging: rejectedRequests.length > 5,
|
|
665
640
|
pageSize: 20,
|
|
@@ -714,7 +689,7 @@ const ApiKeyManagementTab = ({
|
|
|
714
689
|
anchorPosition: menuAnchor || { top: 0, left: 0 }
|
|
715
690
|
},
|
|
716
691
|
menuRequest && (() => {
|
|
717
|
-
const isPending =
|
|
692
|
+
const isPending = getAPIKeyPhase(menuRequest.status?.conditions || []) === "Pending";
|
|
718
693
|
const ownerId = menuRequest.spec.requestedBy.userId;
|
|
719
694
|
const canEdit = canUpdateRequest && ownerId === userId && isPending;
|
|
720
695
|
const items = [];
|
|
@@ -745,51 +720,11 @@ const ApiKeyManagementTab = ({
|
|
|
745
720
|
{
|
|
746
721
|
open: deleteDialogState.open,
|
|
747
722
|
title: "Delete Request",
|
|
748
|
-
description: `Are you sure you want to delete this ${deleteDialogState.request?.status?.
|
|
723
|
+
description: `Are you sure you want to delete this ${getAPIKeyPhase(deleteDialogState.request?.status?.conditions || []) === "Approved" ? "API key" : "request"}?`,
|
|
749
724
|
deleting: deleting !== null,
|
|
750
725
|
onConfirm: handleDeleteConfirm,
|
|
751
726
|
onCancel: handleDeleteCancel
|
|
752
727
|
}
|
|
753
|
-
), /* @__PURE__ */ React.createElement(
|
|
754
|
-
Dialog,
|
|
755
|
-
{
|
|
756
|
-
open: showOnceWarningOpen,
|
|
757
|
-
onClose: () => {
|
|
758
|
-
setShowOnceWarningOpen(false);
|
|
759
|
-
setPendingKeyReveal(null);
|
|
760
|
-
},
|
|
761
|
-
maxWidth: "sm"
|
|
762
|
-
},
|
|
763
|
-
/* @__PURE__ */ React.createElement(DialogTitle, null, /* @__PURE__ */ React.createElement(Box, { display: "flex", alignItems: "center" }, /* @__PURE__ */ React.createElement(WarningIcon, { color: "primary", style: { marginRight: 8 } }), "View API Key")),
|
|
764
|
-
/* @__PURE__ */ React.createElement(DialogContent, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", paragraph: true }, "This API key can only be viewed ", /* @__PURE__ */ React.createElement("strong", null, "once"), ". After you reveal it, you will not be able to retrieve it again."), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Make sure to copy and store it securely before closing this view.")),
|
|
765
|
-
/* @__PURE__ */ React.createElement(DialogActions, null, /* @__PURE__ */ React.createElement(
|
|
766
|
-
Button,
|
|
767
|
-
{
|
|
768
|
-
onClick: () => {
|
|
769
|
-
setShowOnceWarningOpen(false);
|
|
770
|
-
setPendingKeyReveal(null);
|
|
771
|
-
}
|
|
772
|
-
},
|
|
773
|
-
"Cancel"
|
|
774
|
-
), /* @__PURE__ */ React.createElement(
|
|
775
|
-
Button,
|
|
776
|
-
{
|
|
777
|
-
variant: "contained",
|
|
778
|
-
color: "primary",
|
|
779
|
-
onClick: () => {
|
|
780
|
-
if (pendingKeyReveal) {
|
|
781
|
-
fetchApiKeyFromSecret(
|
|
782
|
-
pendingKeyReveal.namespace,
|
|
783
|
-
pendingKeyReveal.name
|
|
784
|
-
);
|
|
785
|
-
toggleVisibility(pendingKeyReveal.name);
|
|
786
|
-
}
|
|
787
|
-
setShowOnceWarningOpen(false);
|
|
788
|
-
setPendingKeyReveal(null);
|
|
789
|
-
}
|
|
790
|
-
},
|
|
791
|
-
"Reveal API Key"
|
|
792
|
-
))
|
|
793
728
|
));
|
|
794
729
|
};
|
|
795
730
|
|