@kuadrant/kuadrant-backstage-plugin-frontend 0.3.0 → 0.4.0-dev1
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/components/ApiProductDetails/ApiProductDetails.esm.js +2 -1
- package/dist/components/ApiProductDetails/ApiProductDetails.esm.js.map +1 -1
- package/dist/components/ApiProductPolicies/ApiProductPolicies.esm.js +2 -1
- package/dist/components/ApiProductPolicies/ApiProductPolicies.esm.js.map +1 -1
- package/dist/components/EditAPIKeyDialog/EditAPIKeyDialog.esm.js +2 -1
- package/dist/components/EditAPIKeyDialog/EditAPIKeyDialog.esm.js.map +1 -1
- package/dist/components/RequestAccessDialog/RequestAccessDialog.esm.js +2 -1
- package/dist/components/RequestAccessDialog/RequestAccessDialog.esm.js.map +1 -1
- package/dist/components/SimpleRequestAccessDialog/SimpleRequestAccessDialog.esm.js +2 -1
- package/dist/components/SimpleRequestAccessDialog/SimpleRequestAccessDialog.esm.js.map +1 -1
- package/dist/utils/policies.esm.js +16 -1
- package/dist/utils/policies.esm.js.map +1 -1
- package/dist-scalprum/{internal.plugin-kuadrant.7f1de85661b1b848af15.js → internal.plugin-kuadrant.88f7f9d244e0e8c2b613.js} +2 -2
- package/dist-scalprum/{internal.plugin-kuadrant.7f1de85661b1b848af15.js.map → internal.plugin-kuadrant.88f7f9d244e0e8c2b613.js.map} +1 -1
- package/dist-scalprum/plugin-manifest.json +3 -3
- package/dist-scalprum/static/1719.430c122c.chunk.js +2 -0
- package/dist-scalprum/static/1719.430c122c.chunk.js.map +1 -0
- package/dist-scalprum/static/2251.1b3cdd55.chunk.js +2 -0
- package/dist-scalprum/static/2251.1b3cdd55.chunk.js.map +1 -0
- package/dist-scalprum/static/2821.7a436d84.chunk.js +2 -0
- package/dist-scalprum/static/2821.7a436d84.chunk.js.map +1 -0
- package/dist-scalprum/static/3947.4ae8ccd7.chunk.js +2 -0
- package/dist-scalprum/static/3947.4ae8ccd7.chunk.js.map +1 -0
- package/dist-scalprum/static/5289.be544370.chunk.js +2 -0
- package/dist-scalprum/static/5289.be544370.chunk.js.map +1 -0
- package/dist-scalprum/static/5922.d56dc5a5.chunk.js +2 -0
- package/dist-scalprum/static/5922.d56dc5a5.chunk.js.map +1 -0
- package/dist-scalprum/static/7270.bf7ae167.chunk.js +2 -0
- package/dist-scalprum/static/7270.bf7ae167.chunk.js.map +1 -0
- package/dist-scalprum/static/7791.4c486f1d.chunk.js +2 -0
- package/dist-scalprum/static/{7791.ad31cf6d.chunk.js.map → 7791.4c486f1d.chunk.js.map} +1 -1
- package/dist-scalprum/static/{exposed-KuadrantPage.f123b200.chunk.js → exposed-KuadrantPage.f9303a62.chunk.js} +2 -2
- package/dist-scalprum/static/{exposed-KuadrantPage.f123b200.chunk.js.map → exposed-KuadrantPage.f9303a62.chunk.js.map} +1 -1
- package/dist-scalprum/static/{exposed-PluginRoot.3a5f9157.chunk.js → exposed-PluginRoot.992ab15b.chunk.js} +2 -2
- package/dist-scalprum/static/{exposed-PluginRoot.3a5f9157.chunk.js.map → exposed-PluginRoot.992ab15b.chunk.js.map} +1 -1
- package/package.json +1 -1
- package/dist-scalprum/static/1719.52ca7daf.chunk.js +0 -2
- package/dist-scalprum/static/1719.52ca7daf.chunk.js.map +0 -1
- package/dist-scalprum/static/2251.08f3db99.chunk.js +0 -2
- package/dist-scalprum/static/2251.08f3db99.chunk.js.map +0 -1
- package/dist-scalprum/static/2821.1ac360ec.chunk.js +0 -2
- package/dist-scalprum/static/2821.1ac360ec.chunk.js.map +0 -1
- package/dist-scalprum/static/3947.f5320e89.chunk.js +0 -2
- package/dist-scalprum/static/3947.f5320e89.chunk.js.map +0 -1
- package/dist-scalprum/static/5289.6c803140.chunk.js +0 -2
- package/dist-scalprum/static/5289.6c803140.chunk.js.map +0 -1
- package/dist-scalprum/static/7270.8ee9d452.chunk.js +0 -2
- package/dist-scalprum/static/7270.8ee9d452.chunk.js.map +0 -1
- package/dist-scalprum/static/7791.ad31cf6d.chunk.js +0 -2
- package/dist-scalprum/static/980.744a4d36.chunk.js +0 -2
- package/dist-scalprum/static/980.744a4d36.chunk.js.map +0 -1
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "internal.plugin-kuadrant",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0-dev1",
|
|
4
4
|
"extensions": [],
|
|
5
5
|
"registrationMethod": "callback",
|
|
6
6
|
"baseURL": "auto",
|
|
7
7
|
"loadScripts": [
|
|
8
|
-
"internal.plugin-kuadrant.
|
|
8
|
+
"internal.plugin-kuadrant.88f7f9d244e0e8c2b613.js"
|
|
9
9
|
],
|
|
10
|
-
"buildHash": "
|
|
10
|
+
"buildHash": "88f7f9d244e0e8c2b61398a83e52f1b2"
|
|
11
11
|
}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";(self.webpackChunkinternal_plugin_kuadrant=self.webpackChunkinternal_plugin_kuadrant||[]).push([[1719],{11719:(e,t,a)=>{a.r(t),a.d(t,{ApiAccessCard:()=>s.z});var s=a(57349)},14933:(e,t,a)=>{a.d(t,{c:()=>b});var s=a(31085),n=a(95478),r=a(76891),i=a(61477),l=a(46805),o=a(10394),d=a(72501),c=a(95061),u=a(48543),p=a(81215),m=a(26343),h=a(16249),y=a(93453),A=a(64947),x=a(78467),v=a(39053),f=a(22097),g=a(70980),j=a(97859);const b=({open:e,onClose:t,onSuccess:a,apiProductName:b,namespace:w,userEmail:S,plans:k})=>{const P=(0,f.useApi)(g.s),I=(0,f.useApi)(f.alertApiRef),[C,$]=(0,n.useState)(""),[R,z]=(0,n.useState)(""),[K,q]=(0,n.useState)(!1),[T,E]=(0,n.useState)(null),F=()=>{$(""),z(""),E(null),t()};return(0,s.jsxs)(r.A,{open:e,onClose:F,maxWidth:"sm",fullWidth:!0,children:[(0,s.jsx)(i.A,{children:"Request API Access"}),(0,s.jsxs)(l.A,{children:[(0,s.jsxs)(o.A,{mb:2,p:1.5,bgcolor:"info.light",borderRadius:1,display:"flex",alignItems:"flex-start",style:{gap:8},children:[(0,s.jsx)(v.A,{color:"primary",fontSize:"small",style:{marginTop:2}}),(0,s.jsx)(d.A,{variant:"body2",children:"Your request will be reviewed by an API owner before access is granted."})]}),T&&(0,s.jsx)(o.A,{mb:2,p:2,bgcolor:"error.main",color:"error.contrastText",borderRadius:1,children:(0,s.jsx)(d.A,{variant:"body2",children:T})}),(0,s.jsxs)(c.A,{fullWidth:!0,margin:"normal",disabled:K,"data-testid":"tier-select-form",children:[(0,s.jsx)(u.A,{id:"tier-select-label",children:"Select Tier"}),(0,s.jsx)(p.A,{labelId:"tier-select-label","data-testid":"tier-select",value:C,onChange:e=>$(e.target.value),disabled:K,children:k.map(e=>{const t=(0,j.lK)(e.limits);return(0,s.jsxs)(m.A,{value:e.tier,"data-testid":`tier-option-${e.tier}`,children:[e.tier," ",t?`(${t})`:""]},e.tier)})})]}),(0,s.jsx)(h.A,{label:"Use Case (optional)",placeholder:"Describe how you plan to use this API",multiline:!0,rows:3,fullWidth:!0,margin:"normal",value:R,onChange:e=>z(e.target.value),helperText:"Explain your intended use of this API for admin review",disabled:K})]}),(0,s.jsxs)(y.A,{children:[(0,s.jsx)(A.A,{onClick:F,disabled:K,children:"Cancel"}),(0,s.jsx)(A.A,{onClick:async()=>{if(C){q(!0),E(null);try{const e=Math.random().toString(36).substring(2,10),t=`${b}-${e}-secret`.toLowerCase().replace(/[^a-z0-9-]/g,"-"),s=crypto.randomUUID().replace(/-/g,"");await P.createSecret(t,s);try{await P.createRequest({apiProductName:b,namespace:w,planTier:C,useCase:R.trim()||"",userEmail:S,secretName:t}),I.post({message:"Request submitted successfully. Pending API owner approval.",severity:"info",display:"transient"}),$(""),z(""),a()}catch(e){try{await P.deleteSecret(t)}catch(e){console.warn("Failed to cleanup orphaned secret:",e)}throw e}}catch(e){const t=e instanceof Error?e.message:"unknown error occurred";I.post({message:`Failed to create request: ${t}`,severity:"error",display:"transient"}),E(t)}finally{q(!1)}}},color:"primary",variant:"contained",disabled:!C||K,startIcon:K?(0,s.jsx)(x.A,{size:16,color:"inherit"}):void 0,children:K?"Submitting...":"Submit Request"})]})]})}},57349:(e,t,a)=>{a.d(t,{z:()=>k});var s=a(31085),n=a(95478),r=a(35015),i=a(42367),l=a(96040),o=a(10394),d=a(72501),c=a(67720),u=a(71677),p=a(29365),m=a(64947),h=a(4387),y=a(18466),A=a(32269),x=a(61524),v=a(99594),f=a(22097),g=a(70980),j=a(25467),b=a(14933),w=a(46205),S=a(34955);const k=({namespace:e})=>{var t,a,k;const{entity:P}=(0,j.tN)(),I=(0,f.useApi)(g.s),C=(0,f.useApi)(f.identityApiRef),$=(0,f.useApi)(f.alertApiRef),[R,z]=(0,n.useState)(""),[K,q]=(0,n.useState)(!1),[T,E]=(0,n.useState)(0),[F,N]=(0,n.useState)(new Set),[W,_]=(0,n.useState)(new Map),[B,M]=(0,n.useState)(new Set),U=(null===(t=P.metadata.annotations)||void 0===t?void 0:t["kuadrant.io/apiproduct"])||P.metadata.name,D=(null===(a=P.metadata.annotations)||void 0===a?void 0:a["kuadrant.io/namespace"])||e||"default";(0,r.A)(async()=>{const e=await C.getProfileInfo();z(e.email||"")},[C]);const{value:H,loading:L,error:O}=(0,r.A)(async()=>((await I.getRequestsByApiProduct(U,D)).items||[]).filter(e=>{var t;return"Approved"===(null===(t=e.status)||void 0===t?void 0:t.phase)}),[D,U,I,T]),{value:Y,loading:V}=(0,r.A)(async()=>{try{var e;return null===(e=(await I.getApiProducts()).items)||void 0===e?void 0:e.find(e=>e.metadata.namespace===D&&e.metadata.name===U)}catch(e){return null}},[D,U,I]),G=Y?`apiproduct:${Y.metadata.namespace}/${Y.metadata.name}`:void 0,{allowed:J,loading:Q}=(0,w.l)(S.dp,G),X=e=>{N(t=>{const a=new Set(t);return a.has(e)?a.delete(e):a.add(e),a})};if(L||V||Q)return(0,s.jsx)(l.n,{title:"Kuadrant API Keys",children:(0,s.jsxs)(o.A,{p:2,children:[(0,s.jsx)(h.A,{variant:"text",width:"80%"}),(0,s.jsx)(h.A,{variant:"text",width:"60%"}),(0,s.jsx)(h.A,{variant:"text",width:"70%"})]})});if(O)return(0,s.jsx)(i._,{error:O});const Z=H||[],ee=(null==Y||null===(k=Y.status)||void 0===k?void 0:k.discoveredPlans)||[],te=J&&ee.length>0;return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(l.n,{title:"Kuadrant API Keys",children:(0,s.jsxs)(o.A,{p:2,children:[Z.length>0?(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)(d.A,{variant:"body2",color:"textSecondary",gutterBottom:!0,children:[Z.length," active key",1!==Z.length?"s":""]}),Z.map(e=>{var t,a;const n=`${e.metadata.namespace}/${e.metadata.name}`,r=F.has(e.metadata.name),i=B.has(n),l=W.get(n),m=Boolean(null===(a=e.spec)||void 0===a||null===(t=a.secretRef)||void 0===t?void 0:t.name);return(0,s.jsxs)(o.A,{mb:1,p:1.5,border:1,borderColor:"grey.300",borderRadius:4,children:[(0,s.jsxs)(o.A,{display:"flex",justifyContent:"space-between",alignItems:"center",mb:1,children:[(0,s.jsx)(d.A,{variant:"body2",style:{fontWeight:500},children:e.metadata.name}),(0,s.jsx)(c.A,{label:e.spec.planTier,color:"primary",size:"small"})]}),m&&(0,s.jsxs)(o.A,{display:"flex",alignItems:"center",children:[(0,s.jsx)(d.A,{variant:"body2",style:{fontFamily:"monospace",fontSize:"0.8rem",flex:1,overflow:"hidden",textOverflow:"ellipsis"},children:i?"Loading...":r&&l?l:"••••••••••••••••"}),r&&l&&(0,s.jsx)(u.Ay,{title:"Copy to clipboard",children:(0,s.jsx)(p.A,{size:"small",onClick:async()=>{l&&(await navigator.clipboard.writeText(l),$.post({message:"API key copied to clipboard",severity:"success",display:"transient"}))},children:(0,s.jsx)(v.A,{fontSize:"small"})})}),(0,s.jsx)(u.Ay,{title:r?"Hide API key":"Reveal API key",children:(0,s.jsx)("span",{children:(0,s.jsx)(p.A,{size:"small",onClick:()=>{r?(((e,t)=>{const a=`${e}/${t}`;_(e=>{const t=new Map(e);return t.delete(a),t})})(e.metadata.namespace,e.metadata.name),X(e.metadata.name)):(async(e,t)=>{const a=`${e}/${t}`;if(!B.has(a)){M(e=>new Set(e).add(a));try{const s=await I.getApiKeySecret(e,t);_(e=>new Map(e).set(a,s.apiKey)),X(t)}catch(e){console.error("failed to fetch api key:",e);const t=e instanceof Error?e.message:"unknown error occurred";$.post({message:`Failed to fetch APIKey. ${t}`,severity:"error",display:"transient"})}finally{M(e=>{const t=new Set(e);return t.delete(a),t})}}})(e.metadata.namespace,e.metadata.name)},disabled:i,children:r?(0,s.jsx)(x.A,{fontSize:"small"}):(0,s.jsx)(A.A,{fontSize:"small"})})})})]})]},e.metadata.name)})]}):(0,s.jsx)(d.A,{variant:"body1",gutterBottom:!0,children:"You don't have any API keys for this API yet"}),(0,s.jsx)(o.A,{mt:2,children:te?(0,s.jsx)(m.A,{variant:"contained",color:"primary",size:"small",startIcon:(0,s.jsx)(y.A,{}),onClick:()=>q(!0),"data-testid":"request-api-access-button",children:"Request API Access"}):(0,s.jsx)(d.A,{variant:"caption",color:"textSecondary",children:"Visit the API Keys tab to view and manage access"})})]})}),(0,s.jsx)(b.c,{open:K,onClose:()=>q(!1),onSuccess:()=>{q(!1),E(e=>e+1)},apiProductName:U,namespace:D,userEmail:R,plans:ee})]})}},97859:(e,t,a)=>{a.d(t,{K5:()=>s,dO:()=>r,lK:()=>n});const s=e=>{if(!e)return[];const t=[];if(e.daily&&t.push(`${e.daily} per day`),e.weekly&&t.push(`${e.weekly} per week`),e.monthly&&t.push(`${e.monthly} per month`),e.yearly&&t.push(`${e.yearly} per year`),e.custom)for(const a of e.custom)t.push(`${a.limit} per ${a.window}`);return t},n=e=>s(e).join(", "),r=(e,t,a)=>e&&e.find(e=>{const s=e.spec.targetRef;var n;const r=null!==(n=null==s?void 0:s.namespace)&&void 0!==n?n:e.metadata.namespace;return"HTTPRoute"===(null==s?void 0:s.kind)&&(null==s?void 0:s.name)===a&&r===t})||null}}]);
|
|
2
|
+
//# sourceMappingURL=1719.430c122c.chunk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static/1719.430c122c.chunk.js","mappings":"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","sources":["webpack://internal.plugin-kuadrant/./src/components/RequestAccessDialog/RequestAccessDialog.tsx","webpack://internal.plugin-kuadrant/./src/components/ApiAccessCard/ApiAccessCard.tsx","webpack://internal.plugin-kuadrant/./src/utils/policies.ts"],"sourcesContent":["import React, { useState } from 'react';\nimport {\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n Button,\n TextField,\n Select,\n MenuItem,\n FormControl,\n InputLabel,\n Box,\n Typography,\n CircularProgress,\n} from '@material-ui/core';\nimport InfoIcon from '@material-ui/icons/Info';\nimport {\n useApi,\n alertApiRef,\n} from '@backstage/core-plugin-api';\nimport { kuadrantApiRef } from '../../api';\nimport { Plan } from \"../../types/api-management.ts\";\nimport { formatPlanLimits } from '../../utils/policies';\n\nexport interface RequestAccessDialogProps {\n open: boolean;\n onClose: () => void;\n onSuccess: () => void;\n apiProductName: string;\n namespace: string;\n userEmail: string;\n plans: Plan[];\n}\n\nexport const RequestAccessDialog = ({\n open,\n onClose,\n onSuccess,\n apiProductName,\n namespace,\n userEmail,\n plans,\n}: RequestAccessDialogProps) => {\n const kuadrantApi = useApi(kuadrantApiRef);\n const alertApi = useApi(alertApiRef);\n\n const [selectedPlan, setSelectedPlan] = useState('');\n const [useCase, setUseCase] = useState('');\n const [creating, setCreating] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n\n const handleClose = () => {\n setSelectedPlan('');\n setUseCase('');\n setCreateError(null);\n onClose();\n };\n\n const handleRequestAccess = async () => {\n if (!selectedPlan) return;\n\n setCreating(true);\n setCreateError(null);\n\n try {\n // 1. generate secret name and API key value\n const randomSuffix = Math.random().toString(36).substring(2, 10);\n const secretName = `${apiProductName}-${randomSuffix}-secret`\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, '-');\n const apiKeyValue = crypto.randomUUID().replace(/-/g, '');\n\n // 2. create secret in consumer's namespace first (design doc: secret before APIKey)\n await kuadrantApi.createSecret(secretName, apiKeyValue);\n\n try {\n // 3. create APIKey referencing the pre-existing secret\n await kuadrantApi.createRequest({\n apiProductName,\n namespace,\n planTier: selectedPlan,\n useCase: useCase.trim() || '',\n userEmail,\n secretName,\n });\n\n alertApi.post({\n message: `Request submitted successfully. Pending API owner approval.`,\n severity: 'info',\n display: 'transient',\n });\n\n setSelectedPlan('');\n setUseCase('');\n onSuccess();\n\n } catch (apiKeyError) {\n // cleanup orphaned secret if APIKey creation fails\n try {\n await kuadrantApi.deleteSecret(secretName);\n } catch (deleteError) {\n console.warn('Failed to cleanup orphaned secret:', deleteError);\n }\n throw apiKeyError;\n }\n\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'unknown error occurred';\n alertApi.post({\n message: `Failed to create request: ${errorMessage}`,\n severity: 'error',\n display: 'transient',\n });\n setCreateError(errorMessage);\n } finally {\n setCreating(false);\n }\n };\n\n return (\n <Dialog open={open} onClose={handleClose} maxWidth=\"sm\" fullWidth>\n <DialogTitle>Request API Access</DialogTitle>\n <DialogContent>\n <Box\n mb={2}\n p={1.5}\n bgcolor=\"info.light\"\n borderRadius={1}\n display=\"flex\"\n alignItems=\"flex-start\"\n style={{ gap: 8 }}\n >\n <InfoIcon\n color=\"primary\"\n fontSize=\"small\"\n style={{ marginTop: 2 }}\n />\n <Typography variant=\"body2\">\n Your request will be reviewed by an API owner before access is\n granted.\n </Typography>\n </Box>\n {createError && (\n <Box\n mb={2}\n p={2}\n bgcolor=\"error.main\"\n color=\"error.contrastText\"\n borderRadius={1}\n >\n <Typography variant=\"body2\">{createError}</Typography>\n </Box>\n )}\n <FormControl\n fullWidth\n margin=\"normal\"\n disabled={creating}\n data-testid=\"tier-select-form\"\n >\n <InputLabel id=\"tier-select-label\">Select Tier</InputLabel>\n <Select\n labelId=\"tier-select-label\"\n data-testid=\"tier-select\"\n value={selectedPlan}\n onChange={(e) => setSelectedPlan(e.target.value as string)}\n disabled={creating}\n >\n {plans.map((plan: Plan) => {\n const limitDesc = formatPlanLimits(plan.limits);\n return (\n <MenuItem\n key={plan.tier}\n value={plan.tier}\n data-testid={`tier-option-${plan.tier}`}\n >\n {plan.tier} {limitDesc ? `(${limitDesc})` : ''}\n </MenuItem>\n );\n })}\n </Select>\n </FormControl>\n <TextField\n label=\"Use Case (optional)\"\n placeholder=\"Describe how you plan to use this API\"\n multiline\n rows={3}\n fullWidth\n margin=\"normal\"\n value={useCase}\n onChange={(e) => setUseCase(e.target.value)}\n helperText=\"Explain your intended use of this API for admin review\"\n disabled={creating}\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={handleClose} disabled={creating}>\n Cancel\n </Button>\n <Button\n onClick={handleRequestAccess}\n color=\"primary\"\n variant=\"contained\"\n disabled={!selectedPlan || creating}\n startIcon={\n creating ? (\n <CircularProgress size={16} color=\"inherit\" />\n ) : undefined\n }\n >\n {creating ? 'Submitting...' : 'Submit Request'}\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import React, { useState } from 'react';\nimport { useAsync } from 'react-use';\nimport {\n InfoCard,\n ResponseErrorPanel,\n} from '@backstage/core-components';\nimport {\n Typography,\n Box,\n Chip,\n Button,\n IconButton,\n Tooltip,\n} from '@material-ui/core';\nimport { Skeleton } from '@material-ui/lab';\nimport AddIcon from '@material-ui/icons/Add';\nimport VisibilityIcon from '@material-ui/icons/Visibility';\nimport VisibilityOffIcon from '@material-ui/icons/VisibilityOff';\nimport FileCopyIcon from '@material-ui/icons/FileCopy';\nimport { useApi, identityApiRef, alertApiRef } from '@backstage/core-plugin-api';\nimport { kuadrantApiRef } from '../../api';\nimport { useEntity } from '@backstage/plugin-catalog-react';\nimport { RequestAccessDialog } from '../RequestAccessDialog';\nimport { useKuadrantPermission } from '../../utils/permissions';\nimport { kuadrantApiKeyCreatePermission } from '../../permissions';\nimport { APIKey, APIProduct, Plan } from \"../../types/api-management\";\n\nexport interface ApiAccessCardProps {\n // deprecated: use entity annotations instead\n namespace?: string;\n}\n\nexport const ApiAccessCard = ({ namespace: propNamespace }: ApiAccessCardProps) => {\n const { entity } = useEntity();\n const kuadrantApi = useApi(kuadrantApiRef);\n const identityApi = useApi(identityApiRef);\n const alertApi = useApi(alertApiRef);\n const [userEmail, setUserEmail] = useState<string>('');\n const [requestDialogOpen, setRequestDialogOpen] = useState(false);\n const [refresh, setRefresh] = useState(0);\n\n // key reveal state\n const [visibleKeys, setVisibleKeys] = useState<Set<string>>(new Set());\n const [apiKeyValues, setApiKeyValues] = useState<Map<string, string>>(new Map());\n const [apiKeyLoading, setApiKeyLoading] = useState<Set<string>>(new Set());\n\n // get apiproduct name from entity annotation (set by entity provider)\n const apiProductName = entity.metadata.annotations?.['kuadrant.io/apiproduct'] || entity.metadata.name;\n const namespace = entity.metadata.annotations?.['kuadrant.io/namespace'] || propNamespace || 'default';\n\n // get current user identity\n useAsync(async () => {\n const profile = await identityApi.getProfileInfo();\n setUserEmail(profile.email || '');\n }, [identityApi]);\n\n // fetch user's approved keys for this API product\n // backend filters by apiProductRef.name and scopes to the user's consumer namespace\n const { value: requests, loading: keysLoading, error: keysError } = useAsync(async () => {\n const data = await kuadrantApi.getRequestsByApiProduct(apiProductName, namespace);\n const allRequests = data.items || [];\n return allRequests.filter((r: APIKey) =>\n r.status?.phase === 'Approved'\n );\n }, [namespace, apiProductName, kuadrantApi, refresh]);\n\n // fetch apiproduct to get available plans\n const { value: apiProduct, loading: productLoading } = useAsync(async () => {\n try {\n const data = await kuadrantApi.getApiProducts();\n return data.items?.find(\n (p: APIProduct) =>\n p.metadata.namespace === namespace &&\n p.metadata.name === apiProductName,\n );\n } catch (err) {\n return null;\n }\n }, [namespace, apiProductName, kuadrantApi]);\n\n const resourceRef = apiProduct\n ? `apiproduct:${apiProduct.metadata.namespace}/${apiProduct.metadata.name}`\n : undefined;\n\n const { allowed: canCreateRequest, loading: permissionLoading } = useKuadrantPermission(\n kuadrantApiKeyCreatePermission,\n resourceRef,\n );\n\n const fetchApiKeyFromSecret = async (requestNamespace: string, requestName: string) => {\n const key = `${requestNamespace}/${requestName}`;\n if (apiKeyLoading.has(key)) {\n return;\n }\n\n setApiKeyLoading((prev) => new Set(prev).add(key));\n try {\n const data = await kuadrantApi.getApiKeySecret(requestNamespace, requestName);\n setApiKeyValues((prev) => new Map(prev).set(key, data.apiKey));\n toggleVisibility(requestName);\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((prev) => {\n const next = new Set(prev);\n next.delete(key);\n return next;\n });\n }\n };\n\n const clearApiKeyValue = (requestNamespace: string, requestName: string) => {\n const key = `${requestNamespace}/${requestName}`;\n setApiKeyValues((prev) => {\n const next = new Map(prev);\n next.delete(key);\n return next;\n });\n };\n\n const toggleVisibility = (keyName: string) => {\n setVisibleKeys((prev) => {\n const newSet = new Set(prev);\n if (newSet.has(keyName)) {\n newSet.delete(keyName);\n } else {\n newSet.add(keyName);\n }\n return newSet;\n });\n };\n\n const loading = keysLoading || productLoading || permissionLoading;\n\n if (loading) {\n return (\n <InfoCard title=\"Kuadrant API Keys\">\n <Box p={2}>\n <Skeleton variant=\"text\" width=\"80%\" />\n <Skeleton variant=\"text\" width=\"60%\" />\n <Skeleton variant=\"text\" width=\"70%\" />\n </Box>\n </InfoCard>\n );\n }\n\n if (keysError) {\n return <ResponseErrorPanel error={keysError} />;\n }\n\n const keys = (requests as APIKey[]) || [];\n const plans = (apiProduct?.status?.discoveredPlans || []) as Plan[];\n const canRequest = canCreateRequest && plans.length > 0;\n\n const renderKeyRow = (request: APIKey) => {\n const key = `${request.metadata.namespace}/${request.metadata.name}`;\n const isVisible = visibleKeys.has(request.metadata.name);\n const isLoading = apiKeyLoading.has(key);\n const apiKeyValue = apiKeyValues.get(key);\n const hasSecretRef = Boolean(request.spec?.secretRef?.name);\n\n const handleRevealClick = () => {\n if (isVisible) {\n clearApiKeyValue(request.metadata.namespace, request.metadata.name);\n toggleVisibility(request.metadata.name);\n } else {\n fetchApiKeyFromSecret(request.metadata.namespace, request.metadata.name);\n }\n };\n\n const handleCopy = async () => {\n if (apiKeyValue) {\n await navigator.clipboard.writeText(apiKeyValue);\n alertApi.post({\n message: 'API key copied to clipboard',\n severity: 'success',\n display: 'transient',\n });\n }\n };\n\n return (\n <Box\n key={request.metadata.name}\n mb={1}\n p={1.5}\n border={1}\n borderColor=\"grey.300\"\n borderRadius={4}\n >\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\" mb={1}>\n <Typography variant=\"body2\" style={{ fontWeight: 500 }}>\n {request.metadata.name}\n </Typography>\n <Chip label={request.spec.planTier} color=\"primary\" size=\"small\" />\n </Box>\n {hasSecretRef && (\n <Box display=\"flex\" alignItems=\"center\">\n <Typography\n variant=\"body2\"\n style={{\n fontFamily: 'monospace',\n fontSize: '0.8rem',\n flex: 1,\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n }}\n >\n {isLoading\n ? 'Loading...'\n : isVisible && apiKeyValue\n ? apiKeyValue\n : '••••••••••••••••'}\n </Typography>\n {isVisible && apiKeyValue && (\n <Tooltip title=\"Copy to clipboard\">\n <IconButton size=\"small\" onClick={handleCopy}>\n <FileCopyIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n <Tooltip title={isVisible ? 'Hide API key' : 'Reveal API key'}>\n <span>\n <IconButton\n size=\"small\"\n onClick={handleRevealClick}\n disabled={isLoading}\n >\n {isVisible ? <VisibilityOffIcon fontSize=\"small\" /> : <VisibilityIcon fontSize=\"small\" />}\n </IconButton>\n </span>\n </Tooltip>\n </Box>\n )}\n </Box>\n );\n };\n\n return (\n <>\n <InfoCard title=\"Kuadrant API Keys\">\n <Box p={2}>\n {keys.length > 0 ? (\n <>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n {keys.length} active key{keys.length !== 1 ? 's' : ''}\n </Typography>\n {keys.map(renderKeyRow)}\n </>\n ) : (\n <Typography variant=\"body1\" gutterBottom>\n You don't have any API keys for this API yet\n </Typography>\n )}\n <Box mt={2}>\n {canRequest ? (\n <Button\n variant=\"contained\"\n color=\"primary\"\n size=\"small\"\n startIcon={<AddIcon />}\n onClick={() => setRequestDialogOpen(true)}\n data-testid=\"request-api-access-button\"\n >\n Request API Access\n </Button>\n ) : (\n <Typography variant=\"caption\" color=\"textSecondary\">\n Visit the API Keys tab to view and manage access\n </Typography>\n )}\n </Box>\n </Box>\n </InfoCard>\n\n <RequestAccessDialog\n open={requestDialogOpen}\n onClose={() => setRequestDialogOpen(false)}\n onSuccess={() => {\n setRequestDialogOpen(false);\n setRefresh((r) => r + 1);\n }}\n apiProductName={apiProductName}\n namespace={namespace}\n userEmail={userEmail}\n plans={plans}\n />\n\n </>\n );\n};\n","import { PlanLimits } from '../types/api-management';\n\n/**\n * Return PlanLimits as an array of human-readable strings.\n * Handles standard period fields (daily/weekly/monthly/yearly) and\n * custom limits (array of { limit, window }).\n */\nexport const getPlanLimitLines = (limits: PlanLimits | undefined): string[] => {\n if (!limits) return [];\n const parts: string[] = [];\n if (limits.daily) parts.push(`${limits.daily} per day`);\n if (limits.weekly) parts.push(`${limits.weekly} per week`);\n if (limits.monthly) parts.push(`${limits.monthly} per month`);\n if (limits.yearly) parts.push(`${limits.yearly} per year`);\n if (limits.custom) {\n for (const item of limits.custom) {\n parts.push(`${item.limit} per ${item.window}`);\n }\n }\n return parts;\n};\n\n/** Convenience wrapper that joins limit lines with a comma for single-line display. */\nexport const formatPlanLimits = (limits: PlanLimits | undefined): string =>\n getPlanLimitLines(limits).join(', ');\n\n/**\n * Find a policy that targets a specific APIProduct\n *\n * @param policies - Array of policies with targetRef\n * @param routeNamespace - Namespace of the target HTTPRoute\n * @param routeName - Name of the target HTTPRoute\n * @returns The matching policy or null if not found\n *\n * @remarks\n * The function matches a policy if:\n * - The targetRef.kind is 'HTTPRoute'\n * - The targetRef.name matches the routeName parameter\n * - The targetRef.namespace (or policy's metadata.namespace if not specified) matches the routeNamespace parameter\n */\nexport const getPolicyForRoute = (\n policies: any[] | undefined,\n routeNamespace: string,\n routeName: string,\n) => {\n if (!policies) return null;\n\n return policies.find((pp: any) => {\n const ref = pp.spec.targetRef;\n const targetNamespace = ref?.namespace ?? pp.metadata.namespace;\n\n return (\n ref?.kind === 'HTTPRoute' &&\n ref?.name === routeName &&\n targetNamespace === routeNamespace\n );\n }) || null;\n};\n"],"names":["RequestAccessDialog","open","onClose","onSuccess","apiProductName","namespace","userEmail","plans","kuadrantApi","useApi","kuadrantApiRef","alertApi","alertApiRef","selectedPlan","setSelectedPlan","useState","useCase","setUseCase","creating","setCreating","createError","setCreateError","handleClose","Dialog","maxWidth","fullWidth","DialogTitle","DialogContent","Box","mb","p","bgcolor","borderRadius","display","alignItems","style","gap","InfoIcon","color","fontSize","marginTop","Typography","variant","FormControl","margin","disabled","data-testid","InputLabel","id","Select","labelId","value","onChange","e","target","map","plan","limitDesc","formatPlanLimits","limits","MenuItem","tier","TextField","label","placeholder","multiline","rows","helperText","DialogActions","Button","onClick","async","randomSuffix","Math","random","toString","substring","secretName","toLowerCase","replace","apiKeyValue","crypto","randomUUID","createSecret","createRequest","planTier","trim","post","message","severity","apiKeyError","deleteSecret","deleteError","console","warn","error","errorMessage","Error","startIcon","CircularProgress","size","undefined","ApiAccessCard","propNamespace","entity","apiProduct","useEntity","identityApi","identityApiRef","setUserEmail","requestDialogOpen","setRequestDialogOpen","refresh","setRefresh","visibleKeys","setVisibleKeys","Set","apiKeyValues","setApiKeyValues","Map","apiKeyLoading","setApiKeyLoading","metadata","annotations","name","useAsync","profile","getProfileInfo","email","requests","loading","keysLoading","keysError","getRequestsByApiProduct","items","filter","r","status","phase","productLoading","data","getApiProducts","find","err","resourceRef","allowed","canCreateRequest","permissionLoading","useKuadrantPermission","kuadrantApiKeyCreatePermission","toggleVisibility","keyName","prev","newSet","has","delete","add","InfoCard","title","Skeleton","width","ResponseErrorPanel","keys","discoveredPlans","canRequest","length","gutterBottom","request","key","isVisible","isLoading","get","hasSecretRef","Boolean","spec","secretRef","border","borderColor","justifyContent","fontWeight","Chip","fontFamily","flex","overflow","textOverflow","Tooltip","IconButton","navigator","clipboard","writeText","FileCopyIcon","span","requestNamespace","requestName","next","clearApiKeyValue","getApiKeySecret","set","apiKey","fetchApiKeyFromSecret","VisibilityOffIcon","VisibilityIcon","mt","AddIcon","getPlanLimitLines","parts","daily","push","weekly","monthly","yearly","custom","item","limit","window","join","getPolicyForRoute","policies","routeNamespace","routeName","pp","ref","targetRef","targetNamespace","kind"],"sourceRoot":""}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";(self.webpackChunkinternal_plugin_kuadrant=self.webpackChunkinternal_plugin_kuadrant||[]).push([[2251],{14933:(e,n,t)=>{t.d(n,{c:()=>b});var r=t(31085),a=t(95478),s=t(76891),i=t(61477),o=t(46805),l=t(10394),d=t(72501),c=t(95061),u=t(48543),p=t(81215),h=t(26343),m=t(16249),y=t(93453),g=t(64947),A=t(78467),v=t(39053),x=t(22097),f=t(70980),j=t(97859);const b=({open:e,onClose:n,onSuccess:t,apiProductName:b,namespace:w,userEmail:k,plans:C})=>{const $=(0,x.useApi)(f.s),P=(0,x.useApi)(x.alertApiRef),[S,q]=(0,a.useState)(""),[T,R]=(0,a.useState)(""),[E,I]=(0,a.useState)(!1),[z,B]=(0,a.useState)(null),K=()=>{q(""),R(""),B(null),n()};return(0,r.jsxs)(s.A,{open:e,onClose:K,maxWidth:"sm",fullWidth:!0,children:[(0,r.jsx)(i.A,{children:"Request API Access"}),(0,r.jsxs)(o.A,{children:[(0,r.jsxs)(l.A,{mb:2,p:1.5,bgcolor:"info.light",borderRadius:1,display:"flex",alignItems:"flex-start",style:{gap:8},children:[(0,r.jsx)(v.A,{color:"primary",fontSize:"small",style:{marginTop:2}}),(0,r.jsx)(d.A,{variant:"body2",children:"Your request will be reviewed by an API owner before access is granted."})]}),z&&(0,r.jsx)(l.A,{mb:2,p:2,bgcolor:"error.main",color:"error.contrastText",borderRadius:1,children:(0,r.jsx)(d.A,{variant:"body2",children:z})}),(0,r.jsxs)(c.A,{fullWidth:!0,margin:"normal",disabled:E,"data-testid":"tier-select-form",children:[(0,r.jsx)(u.A,{id:"tier-select-label",children:"Select Tier"}),(0,r.jsx)(p.A,{labelId:"tier-select-label","data-testid":"tier-select",value:S,onChange:e=>q(e.target.value),disabled:E,children:C.map(e=>{const n=(0,j.lK)(e.limits);return(0,r.jsxs)(h.A,{value:e.tier,"data-testid":`tier-option-${e.tier}`,children:[e.tier," ",n?`(${n})`:""]},e.tier)})})]}),(0,r.jsx)(m.A,{label:"Use Case (optional)",placeholder:"Describe how you plan to use this API",multiline:!0,rows:3,fullWidth:!0,margin:"normal",value:T,onChange:e=>R(e.target.value),helperText:"Explain your intended use of this API for admin review",disabled:E})]}),(0,r.jsxs)(y.A,{children:[(0,r.jsx)(g.A,{onClick:K,disabled:E,children:"Cancel"}),(0,r.jsx)(g.A,{onClick:async()=>{if(S){I(!0),B(null);try{const e=Math.random().toString(36).substring(2,10),n=`${b}-${e}-secret`.toLowerCase().replace(/[^a-z0-9-]/g,"-"),r=crypto.randomUUID().replace(/-/g,"");await $.createSecret(n,r);try{await $.createRequest({apiProductName:b,namespace:w,planTier:S,useCase:T.trim()||"",userEmail:k,secretName:n}),P.post({message:"Request submitted successfully. Pending API owner approval.",severity:"info",display:"transient"}),q(""),R(""),t()}catch(e){try{await $.deleteSecret(n)}catch(e){console.warn("Failed to cleanup orphaned secret:",e)}throw e}}catch(e){const n=e instanceof Error?e.message:"unknown error occurred";P.post({message:`Failed to create request: ${n}`,severity:"error",display:"transient"}),B(n)}finally{I(!1)}}},color:"primary",variant:"contained",disabled:!S||E,startIcon:E?(0,r.jsx)(A.A,{size:16,color:"inherit"}):void 0,children:E?"Submitting...":"Submit Request"})]})]})}},16325:(e,n,t)=>{function r(e){return e&&0!==e.length?e.find(e=>"Approved"===e.type&&"True"===e.status)?"Approved":e.find(e=>"Denied"===e.type&&"True"===e.status)?"Denied":e.find(e=>"Failed"===e.type&&"True"===e.status)?"Failed":"Pending":"Pending"}t.d(n,{v:()=>r})},63221:(e,n,t)=>{t.d(n,{K:()=>g});var r=t(31085),a=t(95478),s=t(76891),i=t(61477),o=t(10394),l=t(46805),d=t(59461),c=t(72501),u=t(16249),p=t(93453),h=t(64947),m=t(78467),y=t(77225);const g=({open:e,title:n,description:t,confirmText:g,severity:A="normal",deleting:v=!1,onConfirm:x,onCancel:f})=>{const[j,b]=(0,a.useState)("");(0,a.useEffect)(()=>{e||b("")},[e]);const w="high"===A&&g,k=!w||j===g;return(0,r.jsxs)(s.A,{open:e,onClose:v?void 0:f,maxWidth:"sm",fullWidth:!0,children:[(0,r.jsxs)(i.A,{children:["high"===A&&(0,r.jsxs)(o.A,{display:"flex",alignItems:"center",style:{gap:8},children:[(0,r.jsx)(y.A,{color:"error"}),(0,r.jsx)("span",{children:n})]}),"high"!==A&&n]}),(0,r.jsxs)(l.A,{children:[(0,r.jsx)(d.A,{style:{whiteSpace:"pre-line"},children:t}),w&&(0,r.jsxs)(o.A,{mt:2,children:[(0,r.jsxs)(c.A,{variant:"body2",color:"textSecondary",gutterBottom:!0,children:["Type ",(0,r.jsx)("strong",{children:g})," to confirm:"]}),(0,r.jsx)(u.A,{fullWidth:!0,variant:"outlined",size:"small",value:j,onChange:e=>b(e.target.value),disabled:v,autoFocus:!0,placeholder:g})]})]}),(0,r.jsxs)(p.A,{children:[(0,r.jsx)(h.A,{onClick:f,disabled:v,children:"Cancel"}),(0,r.jsx)(h.A,{onClick:()=>{k&&x()},color:"secondary",variant:"contained",disabled:v||!k,startIcon:v?(0,r.jsx)(m.A,{size:16,color:"inherit"}):void 0,children:v?"Deleting...":"Delete"})]})]})}},82251:(e,n,t)=>{t.d(n,{_:()=>V});var r=t(31085),a=t(95478),s=t(35015),i=t(82300),o=t(42367),l=t(25010),d=t(10394),c=t(72501),u=t(31653),p=t(38605),h=t(67720),m=t(71677),y=t(29365),g=t(78467),A=t(42899),v=t(64947),x=t(37757),f=t(26343),j=t(4387),b=t(22097),w=t(70980),k=t(25467),C=t(84692),$=t(32269),P=t(61524),S=t(93246),q=t(92399),T=t(18466),R=t(71407),E=t(99594),I=t(34955),z=t(46205),B=t(16325),K=t(76891),D=t(61477),_=t(46805),W=t(95061),N=t(48543),G=t(81215),H=t(16249),U=t(93453),F=t(97859);const L=({open:e,onClose:n,onSuccess:t,request:s,availablePlans:i})=>{const o=(0,b.useApi)(w.s),[l,u]=(0,a.useState)(""),[p,h]=(0,a.useState)(""),[m,y]=(0,a.useState)(!1),[A,x]=(0,a.useState)("");(0,a.useEffect)(()=>{e&&s&&(u(s.spec.planTier||""),h(s.spec.useCase||""),x(""))},[e,s]);const j=()=>{m||(x(""),n())};return(0,r.jsxs)(K.A,{open:e,onClose:j,maxWidth:"sm",fullWidth:!0,children:[(0,r.jsx)(D.A,{children:"Edit API Key"}),(0,r.jsxs)(_.A,{children:[A&&(0,r.jsx)(d.A,{mb:2,p:2,bgcolor:"error.main",color:"error.contrastText",borderRadius:1,children:(0,r.jsx)(c.A,{variant:"body2",children:A})}),(0,r.jsxs)(W.A,{fullWidth:!0,margin:"normal",children:[(0,r.jsx)(N.A,{children:"Tier"}),(0,r.jsx)(G.A,{value:l,onChange:e=>u(e.target.value),disabled:m,children:i.map(e=>{const n=(0,F.lK)(e.limits);return(0,r.jsxs)(f.A,{value:e.tier,children:[e.tier," ",n?`(${n})`:""]},e.tier)})})]}),(0,r.jsx)(H.A,{label:"Use Case",placeholder:"Describe how you plan to use this API",multiline:!0,rows:3,fullWidth:!0,margin:"normal",value:p,onChange:e=>h(e.target.value),disabled:m,helperText:"Explain your intended use of this API for admin review"})]}),(0,r.jsxs)(U.A,{children:[(0,r.jsx)(v.A,{onClick:j,disabled:m,children:"Cancel"}),(0,r.jsx)(v.A,{onClick:async()=>{if(l){x(""),y(!0);try{const e={spec:{planTier:l,useCase:p.trim()}};await o.updateRequest(s.metadata.name,s.metadata.namespace,e),t(),n()}catch(e){console.error("Error updating API key request:",e),x(e instanceof Error?e.message:"Unknown error occurred")}finally{y(!1)}}else x("Please select a tier")},color:"primary",variant:"contained",disabled:!l||m,startIcon:m?(0,r.jsx)(g.A,{size:16,color:"inherit"}):void 0,children:m?"Saving...":"Save Changes"})]})]})};var X=t(63221);function M(e,n,t){const r=t?`${t} `:"";return{curl:`curl -X GET ${e} \\\n -H "Authorization: ${t?`${t} ${n}`:n}"`,nodejs:`const fetch = require('node-fetch');\n\nconst apiKey = '${n}';\nconst endpoint = '${e}';\n\nfetch(endpoint, {\n method: 'GET',\n headers: {\n 'Authorization': '${r}' + apiKey\n }\n})\n .then(response => response.json())\n .then(data => console.log(data))\n .catch(error => console.error('Error:', error));`,python:`import requests\n\napi_key = '${n}'\nendpoint = '${e}'\n\nheaders = {\n 'Authorization': '${r}' + api_key\n}\n\nresponse = requests.get(endpoint, headers=headers)\nprint(response.json())`,go:`package main\n\nimport (\n "fmt"\n "net/http"\n "io"\n)\n\nfunc main() {\n apiKey := "${n}"\n endpoint := "${e}"\n\n client := &http.Client{}\n req, _ := http.NewRequest("GET", endpoint, nil)\n req.Header.Add("Authorization", "${r}" + apiKey)\n\n resp, err := client.Do(req)\n if err != nil {\n fmt.Println("Error:", err)\n return\n }\n defer resp.Body.Close()\n\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}`}}var O=t(14933);const V=({namespace:e})=>{var n,t,K,D,_;const{entity:W}=(0,k.tN)(),N=(0,b.useApi)(C.v),G=(0,b.useApi)(w.s),H=(0,b.useApi)(b.identityApiRef),U=(0,b.useApi)(b.alertApiRef),[F,V]=(0,a.useState)(new Set),[Y,J]=(0,a.useState)(0),[Q,Z]=(0,a.useState)(""),[ee,ne]=(0,a.useState)(""),[te,re]=(0,a.useState)(!1),[ae,se]=(0,a.useState)(!1),[ie,oe]=(0,a.useState)(null),[le,de]=(0,a.useState)(null),[ce,ue]=(0,a.useState)(null),[pe,he]=(0,a.useState)(null),[me,ye]=(0,a.useState)(new Set),[ge,Ae]=(0,a.useState)({open:!1,request:null}),[ve,xe]=(0,a.useState)(new Map),[fe,je]=(0,a.useState)(new Set),be=(null===(n=W.metadata.annotations)||void 0===n?void 0:n["kuadrant.io/apiproduct"])||W.metadata.name,we=(null===(t=W.metadata.annotations)||void 0===t?void 0:t["kuadrant.io/namespace"])||e||"default";(0,s.A)(async()=>{const e=await H.getBackstageIdentity(),n=await H.getProfileInfo();Z(e.userEntityRef);let t=n.email||"";if(!t&&e.userEntityRef){var r,a;const n=await N.getEntityByRef(e.userEntityRef);(null==n||null===(a=n.spec)||void 0===a||null===(r=a.profile)||void 0===r?void 0:r.email)&&(t=n.spec.profile.email)}ne(t)},[H]);const{value:ke,loading:Ce,error:$e}=(0,s.A)(async()=>(await G.getRequestsByApiProduct(be,we)).items||[],[be,we,Y,G]),{value:Pe,loading:Se,error:qe}=(0,s.A)(async()=>{var e;return null===(e=(await G.getApiProducts()).items)||void 0===e?void 0:e.find(e=>e.metadata.namespace===we&&e.metadata.name===be)},[we,be,G]),Te=Pe?`apiproduct:${Pe.metadata.namespace}/${Pe.metadata.name}`:void 0,{allowed:Re,loading:Ee,error:Ie}=(0,z.l)(I.dp,Te),{allowed:ze,loading:Be,error:Ke}=(0,z.l)(I.uL),{allowed:De,loading:_e,error:We}=(0,z.l)(I.jH),{allowed:Ne,loading:Ge,error:He}=(0,z.l)(I.q0),Ue=()=>{de(null),ue(null)},Fe=()=>{ce&&(oe(ce),se(!0),Ue())},Le=()=>{if(!ce)return;const e=ce;Ue(),Ae({open:!0,request:e})},Xe=e=>{V(n=>{const t=new Set(n);return t.has(e)?t.delete(e):t.add(e),t})},Me=(0,a.useMemo)(()=>[{render:e=>{var n;const t=e.rowData;if(!(null==t||null===(n=t.metadata)||void 0===n?void 0:n.name))return(0,r.jsx)(d.A,{});const a=`${t.metadata.namespace}/${t.metadata.name}`,s=ve.get(a);return(0,r.jsx)(Oe,{request:t,apiName:be,revealedApiKey:s})}}],[be,ve]),Oe=({request:e,apiName:n,revealedApiKey:t})=>{var s,o,l;const[h,m]=(0,a.useState)(0),y=(null===(s=e.status)||void 0===s?void 0:s.apiHostname)||`${n}.apps.example.com`,g=t||"<your-api-key>",A=function(e,n,t){const r=`https://${n}/api/v1/endpoint`;return e?e.authorizationHeader?M(r,t,e.authorizationHeader.prefix||""):e.customHeader?function(e,n,t,r){return{curl:`curl -X GET ${e} \\\n -H "${t}: ${r?`${r}${n}`:n}"`,nodejs:`const fetch = require('node-fetch');\n\nconst apiKey = '${n}';\nconst endpoint = '${e}';\n\nfetch(endpoint, {\n method: 'GET',\n headers: {\n '${t}': '${r}' + apiKey\n }\n})\n .then(response => response.json())\n .then(data => console.log(data))\n .catch(error => console.error('Error:', error));`,python:`import requests\n\napi_key = '${n}'\nendpoint = '${e}'\n\nheaders = {\n '${t}': '${r}' + api_key\n}\n\nresponse = requests.get(endpoint, headers=headers)\nprint(response.json())`,go:`package main\n\nimport (\n "fmt"\n "net/http"\n "io"\n)\n\nfunc main() {\n apiKey := "${n}"\n endpoint := "${e}"\n\n client := &http.Client{}\n req, _ := http.NewRequest("GET", endpoint, nil)\n req.Header.Add("${t}", "${r}" + apiKey)\n\n resp, err := client.Do(req)\n if err != nil {\n fmt.Println("Error:", err)\n return\n }\n defer resp.Body.Close()\n\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}`}}(r,t,e.customHeader.name,e.customHeader.prefix||""):e.queryString?function(e,n,t){return{curl:`curl -X GET "${e}?${t}=${n}"`,nodejs:`const fetch = require('node-fetch');\n\nconst apiKey = '${n}';\nconst endpoint = '${e}?${t}=' + apiKey;\n\nfetch(endpoint, {\n method: 'GET'\n})\n .then(response => response.json())\n .then(data => console.log(data))\n .catch(error => console.error('Error:', error));`,python:`import requests\n\napi_key = '${n}'\nendpoint = '${e}'\n\nparams = {\n '${t}': api_key\n}\n\nresponse = requests.get(endpoint, params=params)\nprint(response.json())`,go:`package main\n\nimport (\n "fmt"\n "net/http"\n "io"\n)\n\nfunc main() {\n apiKey := "${n}"\n endpoint := "${e}?${t}=" + apiKey\n\n client := &http.Client{}\n req, _ := http.NewRequest("GET", endpoint, nil)\n\n resp, err := client.Do(req)\n if err != nil {\n fmt.Println("Error:", err)\n return\n }\n defer resp.Body.Close()\n\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}`}}(r,t,e.queryString.name):e.cookie?function(e,n,t){return{curl:`curl -X GET ${e} \\\n --cookie "${t}=${n}"`,nodejs:`const fetch = require('node-fetch');\n\nconst apiKey = '${n}';\nconst endpoint = '${e}';\n\nfetch(endpoint, {\n method: 'GET',\n headers: {\n 'Cookie': '${t}=' + apiKey\n }\n})\n .then(response => response.json())\n .then(data => console.log(data))\n .catch(error => console.error('Error:', error));`,python:`import requests\n\napi_key = '${n}'\nendpoint = '${e}'\n\ncookies = {\n '${t}': api_key\n}\n\nresponse = requests.get(endpoint, cookies=cookies)\nprint(response.json())`,go:`package main\n\nimport (\n "fmt"\n "net/http"\n "io"\n)\n\nfunc main() {\n apiKey := "${n}"\n endpoint := "${e}"\n\n client := &http.Client{}\n req, _ := http.NewRequest("GET", endpoint, nil)\n req.AddCookie(&http.Cookie{\n Name: "${t}",\n Value: apiKey,\n })\n\n resp, err := client.Do(req)\n if err != nil {\n fmt.Println("Error:", err)\n return\n }\n defer resp.Body.Close()\n\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}`}}(r,t,e.cookie.name):M(r,t,"Bearer"):M(r,t,"Bearer")}(null===(l=e.status)||void 0===l||null===(o=l.authScheme)||void 0===o?void 0:o.credentials,y,g);return(0,r.jsxs)(d.A,{p:3,bgcolor:"background.default",onClick:e=>e.stopPropagation(),children:[e.spec.useCase&&(0,r.jsxs)(d.A,{mb:3,children:[(0,r.jsx)(c.A,{variant:"h6",gutterBottom:!0,children:"Use Case"}),(0,r.jsx)(d.A,{p:2,bgcolor:"background.paper",borderRadius:1,border:"1px solid rgba(0, 0, 0, 0.12)",children:(0,r.jsx)(c.A,{variant:"body2",style:{whiteSpace:"pre-wrap",wordBreak:"break-word",overflowWrap:"break-word"},children:e.spec.useCase})})]}),(0,r.jsx)(c.A,{variant:"h6",gutterBottom:!0,children:"Usage Examples"}),(0,r.jsxs)(c.A,{variant:"body2",paragraph:!0,children:["Use these code examples to test the API with your"," ",e.spec.planTier," tier key."]}),(0,r.jsx)(d.A,{onClick:e=>e.stopPropagation(),children:(0,r.jsxs)(u.A,{value:h,onChange:(e,n)=>{e.stopPropagation(),m(n)},indicatorColor:"primary",children:[(0,r.jsx)(p.A,{label:"cURL",onClick:e=>e.stopPropagation()}),(0,r.jsx)(p.A,{label:"Node.js",onClick:e=>e.stopPropagation()}),(0,r.jsx)(p.A,{label:"Python",onClick:e=>e.stopPropagation()}),(0,r.jsx)(p.A,{label:"Go",onClick:e=>e.stopPropagation()})]})}),(0,r.jsxs)(d.A,{mt:2,children:[0===h&&(0,r.jsx)(i.z,{text:A.curl,language:"bash",showCopyCodeButton:!0}),1===h&&(0,r.jsx)(i.z,{text:A.nodejs,language:"javascript",showCopyCodeButton:!0}),2===h&&(0,r.jsx)(i.z,{text:A.python,language:"python",showCopyCodeButton:!0}),3===h&&(0,r.jsx)(i.z,{text:A.go,language:"go",showCopyCodeButton:!0})]})]})},Ve=$e||qe,Ye=Ie||Ke||We||He;if(Ce||Se||Ee||Be||_e||Ge)return(0,r.jsx)(d.A,{p:2,children:[...Array(5)].map((e,n)=>(0,r.jsx)(d.A,{p:2,children:(0,r.jsx)(j.A,{variant:"text",width:"100%"})},n))});if(Ve)return(0,r.jsx)(o._,{error:Ve});if(Ye){const e=Ie?"kuadrant.apikey.create":Ke?"kuadrant.apikey.delete.own":We?"kuadrant.apikey.delete.all":He?"kuadrant.apikey.update.own":"unknown";return(0,r.jsxs)(d.A,{p:2,children:[(0,r.jsxs)(c.A,{color:"error",children:["Unable to check permissions: ",Ye.message]}),(0,r.jsxs)(c.A,{variant:"body2",color:"textSecondary",children:["Permission: ",e]}),(0,r.jsx)(c.A,{variant:"body2",color:"textSecondary",children:"Please try again or contact your administrator"})]})}const Je=(ke||[]).filter(e=>!me.has(e.metadata.name)),Qe=(null==Pe||null===(K=Pe.status)||void 0===K?void 0:K.discoveredPlans)||[],Ze=Je.filter(e=>{var n;return"Pending"===(0,B.v)(null===(n=e.status)||void 0===n?void 0:n.conditions)}),en=Je.filter(e=>{var n;return"Approved"===(0,B.v)(null===(n=e.status)||void 0===n?void 0:n.conditions)}),nn=Je.filter(e=>{var n;return"Denied"===(0,B.v)(null===(n=e.status)||void 0===n?void 0:n.conditions)}),tn=[{title:"Tier",field:"spec.planTier",render:e=>(0,r.jsx)(h.A,{label:e.spec.planTier,color:"primary",size:"small"})},{title:"Approved",field:"status.conditions",render:e=>{var n,t,a;const s=null===(a=e.status)||void 0===a||null===(t=a.conditions)||void 0===t||null===(n=t.find(e=>"Approved"===e.type&&"True"===e.status))||void 0===n?void 0:n.lastTransitionTime;return(0,r.jsx)(c.A,{variant:"body2",children:s?new Date(s).toLocaleDateString():"-"})}},{title:"API Key",field:"status.secretRef",searchable:!1,filtering:!1,render:e=>{var n,t;const a=`${e.metadata.namespace}/${e.metadata.name}`,s=F.has(e.metadata.name),i=fe.has(a),o=ve.get(a);return Boolean(null===(t=e.spec)||void 0===t||null===(n=t.secretRef)||void 0===n?void 0:n.name)?(0,r.jsxs)(d.A,{display:"flex",alignItems:"center",children:[(0,r.jsx)(c.A,{variant:"body2",style:{fontFamily:"monospace",marginRight:8},children:i?"Loading...":s&&o?o:"••••••••••••••••"}),s&&o&&(0,r.jsx)(m.Ay,{title:"Copy to clipboard",children:(0,r.jsx)(y.A,{size:"small",onClick:async()=>{o&&(await navigator.clipboard.writeText(o),U.post({message:"API key copied to clipboard",severity:"success",display:"transient"}))},children:(0,r.jsx)(E.A,{fontSize:"small"})})}),(0,r.jsx)(m.Ay,{title:s?"Hide API key":"Reveal API key",children:(0,r.jsx)("span",{children:(0,r.jsx)(y.A,{size:"small",onClick:()=>{s?(((e,n)=>{const t=`${e}/${n}`;xe(e=>{const n=new Map(e);return n.delete(t),n})})(e.metadata.namespace,e.metadata.name),Xe(e.metadata.name)):(async(e,n)=>{const t=`${e}/${n}`;if(!fe.has(t)){je(e=>new Set(e).add(t));try{const r=await G.getApiKeySecret(e,n);xe(e=>new Map(e).set(t,r.apiKey)),Xe(n)}catch(e){const n=e instanceof Error?e.message:"unknown error occurred";U.post({message:`Failed to fetch api key: ${n}`,severity:"error",display:"transient"})}finally{je(e=>{const n=new Set(e);return n.delete(t),n})}}})(e.metadata.namespace,e.metadata.name)},disabled:i,children:s?(0,r.jsx)(P.A,{}):(0,r.jsx)($.A,{})})})})]}):(0,r.jsx)(c.A,{variant:"body2",color:"textSecondary",children:"Awaiting secret..."})}},{title:"",field:"actions",searchable:!1,filtering:!1,render:e=>{if(pe===e.metadata.name)return(0,r.jsx)(g.A,{size:20});const n=e.spec.requestedBy.userId;return(0,z.W)(n,Q,ze,De)?(0,r.jsx)(y.A,{size:"small",onClick:n=>{n.stopPropagation();const t=n.currentTarget.getBoundingClientRect();de({top:t.bottom,left:t.left}),ue(e)},title:"Actions","aria-controls":le?"actions-menu":void 0,"aria-haspopup":"true",children:(0,r.jsx)(R.A,{})}):null}}],rn=[{title:"Status",field:"status.conditions",render:e=>{var n;const t=(0,B.v)((null===(n=e.status)||void 0===n?void 0:n.conditions)||[]),a="Pending"===t;return(0,r.jsx)(h.A,{label:t,size:"small",icon:a?(0,r.jsx)(S.A,{}):(0,r.jsx)(q.A,{}),color:a?"default":"secondary"})}},{title:"Tier",field:"spec.planTier",render:e=>(0,r.jsx)(h.A,{label:e.spec.planTier,color:"primary",size:"small"})},{title:"Use Case",field:"spec.useCase",render:e=>e.spec.useCase?(0,r.jsx)(m.Ay,{title:e.spec.useCase,placement:"top",children:(0,r.jsx)(c.A,{variant:"body2",style:{maxWidth:"200px",overflow:"hidden",textOverflow:"ellipsis",whiteSpace:"nowrap"},children:e.spec.useCase})}):(0,r.jsx)(c.A,{variant:"body2",children:"-"})},{title:"Requested",field:"metadata.creationTimestamp",render:e=>(0,r.jsx)(c.A,{variant:"body2",children:e.metadata.creationTimestamp?new Date(e.metadata.creationTimestamp).toLocaleDateString():"-"})},{title:"Reviewed",field:"status.conditions",render:e=>{var n,t,a;const s=null===(a=e.status)||void 0===a||null===(t=a.conditions)||void 0===t||null===(n=t.find(e=>("Approved"===e.type||"Denied"===e.type)&&"True"===e.status))||void 0===n?void 0:n.lastTransitionTime;return(0,r.jsx)(c.A,{variant:"body2",children:s?new Date(s).toLocaleDateString():"-"})}},{title:"",field:"actions",searchable:!1,filtering:!1,render:e=>{var n;if(pe===e.metadata.name)return(0,r.jsx)(g.A,{size:20});const t="Pending"===(0,B.v)((null===(n=e.status)||void 0===n?void 0:n.conditions)||[]),a=e.spec.requestedBy.userId,s=(0,z.W)(a,Q,ze,De);return t&&(Ne&&a===Q||s)?(0,r.jsx)(y.A,{size:"small",onClick:n=>{n.stopPropagation();const t=n.currentTarget.getBoundingClientRect();de({top:t.bottom,left:t.left}),ue(e)},title:"Actions","aria-controls":le?"actions-menu":void 0,"aria-haspopup":"true",children:(0,r.jsx)(R.A,{})}):null}}],an=rn.filter(e=>"Reviewed"!==e.title&&"Reason"!==e.title);return(0,r.jsxs)(d.A,{p:2,children:[(0,r.jsxs)(A.A,{container:!0,spacing:3,direction:"column",children:[Re&&(0,r.jsx)(A.A,{item:!0,children:(0,r.jsxs)(d.A,{display:"flex",flexDirection:"column",alignItems:"flex-end",mb:2,children:[(0,r.jsx)(v.A,{variant:"contained",color:"primary",startIcon:(0,r.jsx)(T.A,{}),onClick:()=>re(!0),disabled:0===Qe.length||!ee,"data-testid":"request-api-access-button","data-plans-count":Qe.length,children:"Request API Access"}),!ee&&(0,r.jsx)(c.A,{variant:"caption",color:"textSecondary",style:{marginTop:4},"data-testid":"no-email-message",children:'"Email address is required"'}),0===Qe.length&&(0,r.jsx)(c.A,{variant:"caption",color:"textSecondary",style:{marginTop:4},"data-testid":"no-plans-message",children:Pe?(()=>{var e,n,t,r;const a=null===(n=Pe.status)||void 0===n||null===(e=n.conditions)||void 0===e?void 0:e.find(e=>"Ready"===e.type),s=null===(r=Pe.status)||void 0===r||null===(t=r.conditions)||void 0===t?void 0:t.find(e=>"PlanPolicyDiscovered"===e.type);return"True"!==(null==a?void 0:a.status)?`HTTPRoute not ready: ${(null==a?void 0:a.message)||"unknown"}`:"True"!==(null==s?void 0:s.status)?`No plans discovered: ${(null==s?void 0:s.message)||"no PlanPolicy found"}`:"No plans available"})():"API product not found"})]})}),0===Ze.length&&0===nn.length&&0===en.length&&(0,r.jsx)(A.A,{item:!0,children:(0,r.jsx)(d.A,{p:3,textAlign:"center",children:(0,r.jsx)(c.A,{variant:"body1",color:"textSecondary",children:"No API keys yet. Request access to get started."})})}),Ze.length>0&&(0,r.jsx)(A.A,{item:!0,children:(0,r.jsx)(l.X,{title:"Pending Requests",options:{paging:Ze.length>5,pageSize:20,search:!0,filtering:!0,debounceInterval:300,toolbar:!0,emptyRowsWhenPaging:!1},columns:an,data:Ze})}),nn.length>0&&(0,r.jsx)(A.A,{item:!0,children:(0,r.jsx)(l.X,{title:"Denied Requests",options:{paging:nn.length>5,pageSize:20,search:!0,filtering:!0,debounceInterval:300,toolbar:!0,emptyRowsWhenPaging:!1},columns:rn,data:nn})}),en.length>0&&(0,r.jsx)(A.A,{item:!0,children:(0,r.jsx)(l.X,{title:"API Keys",options:{paging:en.length>5,pageSize:20,search:!0,filtering:!0,debounceInterval:300,toolbar:!0,emptyRowsWhenPaging:!1},columns:tn,data:en,detailPanel:Me},"api-keys-table")})]}),(0,r.jsx)(O.c,{open:te,onClose:()=>re(!1),onSuccess:()=>{re(!1),J(e=>e+1)},apiProductName:be,namespace:we,userEmail:ee,plans:Qe}),(0,r.jsx)(x.A,{id:"actions-menu",open:Boolean(le),onClose:Ue,anchorReference:"anchorPosition",anchorPosition:le||{top:0,left:0},children:ce&&(()=>{var e;const n="Pending"===(0,B.v)((null===(e=ce.status)||void 0===e?void 0:e.conditions)||[]),t=ce.spec.requestedBy.userId,a=[];return Ne&&t===Q&&n&&a.push((0,r.jsx)(f.A,{onClick:Fe,children:"Edit"},"edit")),a.push((0,r.jsx)(f.A,{onClick:Le,children:"Delete"},"delete")),a})()}),ie&&(0,r.jsx)(L,{open:ae,onClose:()=>{se(!1),oe(null)},onSuccess:()=>{J(e=>e+1),se(!1),U.post({message:"API key updated",severity:"success",display:"transient"}),oe(null)},request:ie,availablePlans:Qe}),(0,r.jsx)(X.K,{open:ge.open,title:"Delete Request",description:`Are you sure you want to delete this ${"Approved"===(0,B.v)((null===(_=ge.request)||void 0===_||null===(D=_.status)||void 0===D?void 0:D.conditions)||[])?"API key":"request"}?`,deleting:null!==pe,onConfirm:async()=>{ge.request&&(await(async(e,n)=>{ye(e=>new Set(e).add(n)),he(n);try{await G.deleteRequest(e,n),U.post({message:"API key deleted successfully",severity:"success",display:"transient"}),J(e=>e+1)}catch(e){const t=e instanceof Error?e.message:"unknown error occurred";ye(e=>{const t=new Set(e);return t.delete(n),t}),U.post({message:`Failed to delete API key: ${t}`,severity:"error",display:"transient"})}finally{he(null)}})(ge.request.metadata.namespace,ge.request.metadata.name),Ae({open:!1,request:null}))},onCancel:()=>{Ae({open:!1,request:null})}})]})}}}]);
|
|
2
|
+
//# sourceMappingURL=2251.1b3cdd55.chunk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static/2251.1b3cdd55.chunk.js","mappings":"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","sources":["webpack://internal.plugin-kuadrant/./src/components/RequestAccessDialog/RequestAccessDialog.tsx","webpack://internal.plugin-kuadrant/./src/utils/apikeys.ts","webpack://internal.plugin-kuadrant/./src/components/ConfirmDeleteDialog/ConfirmDeleteDialog.tsx","webpack://internal.plugin-kuadrant/./src/components/EditAPIKeyDialog/EditAPIKeyDialog.tsx","webpack://internal.plugin-kuadrant/./src/utils/codeSnippets.ts","webpack://internal.plugin-kuadrant/./src/components/ApiKeyManagementTab/ApiKeyManagementTab.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport {\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n Button,\n TextField,\n Select,\n MenuItem,\n FormControl,\n InputLabel,\n Box,\n Typography,\n CircularProgress,\n} from '@material-ui/core';\nimport InfoIcon from '@material-ui/icons/Info';\nimport {\n useApi,\n alertApiRef,\n} from '@backstage/core-plugin-api';\nimport { kuadrantApiRef } from '../../api';\nimport { Plan } from \"../../types/api-management.ts\";\nimport { formatPlanLimits } from '../../utils/policies';\n\nexport interface RequestAccessDialogProps {\n open: boolean;\n onClose: () => void;\n onSuccess: () => void;\n apiProductName: string;\n namespace: string;\n userEmail: string;\n plans: Plan[];\n}\n\nexport const RequestAccessDialog = ({\n open,\n onClose,\n onSuccess,\n apiProductName,\n namespace,\n userEmail,\n plans,\n}: RequestAccessDialogProps) => {\n const kuadrantApi = useApi(kuadrantApiRef);\n const alertApi = useApi(alertApiRef);\n\n const [selectedPlan, setSelectedPlan] = useState('');\n const [useCase, setUseCase] = useState('');\n const [creating, setCreating] = useState(false);\n const [createError, setCreateError] = useState<string | null>(null);\n\n const handleClose = () => {\n setSelectedPlan('');\n setUseCase('');\n setCreateError(null);\n onClose();\n };\n\n const handleRequestAccess = async () => {\n if (!selectedPlan) return;\n\n setCreating(true);\n setCreateError(null);\n\n try {\n // 1. generate secret name and API key value\n const randomSuffix = Math.random().toString(36).substring(2, 10);\n const secretName = `${apiProductName}-${randomSuffix}-secret`\n .toLowerCase()\n .replace(/[^a-z0-9-]/g, '-');\n const apiKeyValue = crypto.randomUUID().replace(/-/g, '');\n\n // 2. create secret in consumer's namespace first (design doc: secret before APIKey)\n await kuadrantApi.createSecret(secretName, apiKeyValue);\n\n try {\n // 3. create APIKey referencing the pre-existing secret\n await kuadrantApi.createRequest({\n apiProductName,\n namespace,\n planTier: selectedPlan,\n useCase: useCase.trim() || '',\n userEmail,\n secretName,\n });\n\n alertApi.post({\n message: `Request submitted successfully. Pending API owner approval.`,\n severity: 'info',\n display: 'transient',\n });\n\n setSelectedPlan('');\n setUseCase('');\n onSuccess();\n\n } catch (apiKeyError) {\n // cleanup orphaned secret if APIKey creation fails\n try {\n await kuadrantApi.deleteSecret(secretName);\n } catch (deleteError) {\n console.warn('Failed to cleanup orphaned secret:', deleteError);\n }\n throw apiKeyError;\n }\n\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : 'unknown error occurred';\n alertApi.post({\n message: `Failed to create request: ${errorMessage}`,\n severity: 'error',\n display: 'transient',\n });\n setCreateError(errorMessage);\n } finally {\n setCreating(false);\n }\n };\n\n return (\n <Dialog open={open} onClose={handleClose} maxWidth=\"sm\" fullWidth>\n <DialogTitle>Request API Access</DialogTitle>\n <DialogContent>\n <Box\n mb={2}\n p={1.5}\n bgcolor=\"info.light\"\n borderRadius={1}\n display=\"flex\"\n alignItems=\"flex-start\"\n style={{ gap: 8 }}\n >\n <InfoIcon\n color=\"primary\"\n fontSize=\"small\"\n style={{ marginTop: 2 }}\n />\n <Typography variant=\"body2\">\n Your request will be reviewed by an API owner before access is\n granted.\n </Typography>\n </Box>\n {createError && (\n <Box\n mb={2}\n p={2}\n bgcolor=\"error.main\"\n color=\"error.contrastText\"\n borderRadius={1}\n >\n <Typography variant=\"body2\">{createError}</Typography>\n </Box>\n )}\n <FormControl\n fullWidth\n margin=\"normal\"\n disabled={creating}\n data-testid=\"tier-select-form\"\n >\n <InputLabel id=\"tier-select-label\">Select Tier</InputLabel>\n <Select\n labelId=\"tier-select-label\"\n data-testid=\"tier-select\"\n value={selectedPlan}\n onChange={(e) => setSelectedPlan(e.target.value as string)}\n disabled={creating}\n >\n {plans.map((plan: Plan) => {\n const limitDesc = formatPlanLimits(plan.limits);\n return (\n <MenuItem\n key={plan.tier}\n value={plan.tier}\n data-testid={`tier-option-${plan.tier}`}\n >\n {plan.tier} {limitDesc ? `(${limitDesc})` : ''}\n </MenuItem>\n );\n })}\n </Select>\n </FormControl>\n <TextField\n label=\"Use Case (optional)\"\n placeholder=\"Describe how you plan to use this API\"\n multiline\n rows={3}\n fullWidth\n margin=\"normal\"\n value={useCase}\n onChange={(e) => setUseCase(e.target.value)}\n helperText=\"Explain your intended use of this API for admin review\"\n disabled={creating}\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={handleClose} disabled={creating}>\n Cancel\n </Button>\n <Button\n onClick={handleRequestAccess}\n color=\"primary\"\n variant=\"contained\"\n disabled={!selectedPlan || creating}\n startIcon={\n creating ? (\n <CircularProgress size={16} color=\"inherit\" />\n ) : undefined\n }\n >\n {creating ? 'Submitting...' : 'Submit Request'}\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import { StatusCondition } from '../types/api-management';\n\n/**\n * Derives the APIKey approval phase from Kubernetes conditions.\n *\n * Maps conditions to phases:\n * - Empty conditions array → Pending\n * - Approved condition (status: True) → Approved\n * - Denied condition (status: True) → Denied\n * - Failed condition (status: True) → Failed\n *\n * @param conditions - Array of Kubernetes status conditions\n * @returns Current approval phase\n */\nexport function getAPIKeyPhase(\n conditions?: StatusCondition[]\n): 'Pending' | 'Approved' | 'Denied' | 'Failed' {\n if (!conditions || conditions.length === 0) {\n return 'Pending';\n }\n\n const approved = conditions.find(\n c => c.type === 'Approved' && c.status === 'True'\n );\n if (approved) return 'Approved';\n\n const denied = conditions.find(\n c => c.type === 'Denied' && c.status === 'True'\n );\n if (denied) return 'Denied';\n\n const failed = conditions.find(\n c => c.type === 'Failed' && c.status === 'True'\n );\n if (failed) return 'Failed';\n\n return 'Pending';\n}\n","import React, { useState, useEffect } from 'react';\nimport {\n Dialog,\n DialogTitle,\n DialogContent,\n DialogContentText,\n DialogActions,\n Button,\n TextField,\n Typography,\n Box,\n CircularProgress,\n} from '@material-ui/core';\nimport WarningIcon from '@material-ui/icons/Warning';\n\nexport interface ConfirmDeleteDialogProps {\n open: boolean;\n title: string;\n description: string;\n // for dangerous deletes, require typing this text to confirm\n confirmText?: string;\n // severity affects styling - 'high' shows warning icon and requires text confirmation\n severity?: 'normal' | 'high';\n deleting?: boolean;\n onConfirm: () => void;\n onCancel: () => void;\n}\n\nexport const ConfirmDeleteDialog = ({\n open,\n title,\n description,\n confirmText,\n severity = 'normal',\n deleting = false,\n onConfirm,\n onCancel,\n}: ConfirmDeleteDialogProps) => {\n const [inputValue, setInputValue] = useState('');\n\n // reset input when dialog opens/closes\n useEffect(() => {\n if (!open) {\n setInputValue('');\n }\n }, [open]);\n\n const requiresTextConfirmation = severity === 'high' && confirmText;\n const canConfirm = requiresTextConfirmation ? inputValue === confirmText : true;\n\n const handleConfirm = () => {\n if (canConfirm) {\n onConfirm();\n }\n };\n\n return (\n <Dialog\n open={open}\n onClose={deleting ? undefined : onCancel}\n maxWidth=\"sm\"\n fullWidth\n >\n <DialogTitle>\n {severity === 'high' && (\n <Box display=\"flex\" alignItems=\"center\" style={{ gap: 8 }}>\n <WarningIcon color=\"error\" />\n <span>{title}</span>\n </Box>\n )}\n {severity !== 'high' && title}\n </DialogTitle>\n <DialogContent>\n <DialogContentText style={{ whiteSpace: 'pre-line' }}>\n {description}\n </DialogContentText>\n {requiresTextConfirmation && (\n <Box mt={2}>\n <Typography variant=\"body2\" color=\"textSecondary\" gutterBottom>\n Type <strong>{confirmText}</strong> to confirm:\n </Typography>\n <TextField\n fullWidth\n variant=\"outlined\"\n size=\"small\"\n value={inputValue}\n onChange={e => setInputValue(e.target.value)}\n disabled={deleting}\n autoFocus\n placeholder={confirmText}\n />\n </Box>\n )}\n </DialogContent>\n <DialogActions>\n <Button onClick={onCancel} disabled={deleting}>\n Cancel\n </Button>\n <Button\n onClick={handleConfirm}\n color=\"secondary\"\n variant=\"contained\"\n disabled={deleting || !canConfirm}\n startIcon={deleting ? <CircularProgress size={16} color=\"inherit\" /> : undefined}\n >\n {deleting ? 'Deleting...' : 'Delete'}\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import React, { useState, useEffect } from \"react\";\nimport {\n Dialog,\n DialogTitle,\n DialogContent,\n DialogActions,\n Button,\n TextField,\n Box,\n Typography,\n FormControl,\n InputLabel,\n Select,\n MenuItem,\n CircularProgress,\n} from \"@material-ui/core\";\nimport { useApi } from \"@backstage/core-plugin-api\";\nimport { kuadrantApiRef } from '../../api';\nimport { APIKey } from \"../../types/api-management\";\nimport { formatPlanLimits } from '../../utils/policies';\n\ninterface EditAPIKeyDialogProps {\n open: boolean;\n onClose: () => void;\n onSuccess: () => void;\n request: APIKey;\n availablePlans: Array<{\n tier: string;\n description?: string;\n limits?: any;\n }>;\n}\n\nexport const EditAPIKeyDialog = ({\n open,\n onClose,\n onSuccess,\n request,\n availablePlans,\n}: EditAPIKeyDialogProps) => {\n const kuadrantApi = useApi(kuadrantApiRef);\n\n const [planTier, setPlanTier] = useState(\"\");\n const [useCase, setUseCase] = useState(\"\");\n const [saving, setSaving] = useState(false);\n const [error, setError] = useState(\"\");\n\n useEffect(() => {\n if (open && request) {\n setPlanTier(request.spec.planTier || \"\");\n setUseCase(request.spec.useCase || \"\");\n setError(\"\");\n }\n }, [open, request]);\n\n const handleSave = async () => {\n if (!planTier) {\n setError(\"Please select a tier\");\n return;\n }\n\n setError(\"\");\n setSaving(true);\n\n try {\n const patch = {\n spec: {\n planTier,\n useCase: useCase.trim(),\n },\n };\n\n await kuadrantApi.updateRequest(\n request.metadata.name,\n request.metadata.namespace,\n // @ts-ignore Applying a partial obj\n patch,\n );\n\n onSuccess();\n onClose();\n } catch (err) {\n console.error(\"Error updating API key request:\", err);\n setError(err instanceof Error ? err.message : \"Unknown error occurred\");\n } finally {\n setSaving(false);\n }\n };\n\n const handleClose = () => {\n if (!saving) {\n setError(\"\");\n onClose();\n }\n };\n\n return (\n <Dialog open={open} onClose={handleClose} maxWidth=\"sm\" fullWidth>\n <DialogTitle>Edit API Key</DialogTitle>\n <DialogContent>\n {error && (\n <Box\n mb={2}\n p={2}\n bgcolor=\"error.main\"\n color=\"error.contrastText\"\n borderRadius={1}\n >\n <Typography variant=\"body2\">{error}</Typography>\n </Box>\n )}\n\n <FormControl fullWidth margin=\"normal\">\n <InputLabel>Tier</InputLabel>\n <Select\n value={planTier}\n onChange={(e) => setPlanTier(e.target.value as string)}\n disabled={saving}\n >\n {availablePlans.map((plan) => {\n const limitDesc = formatPlanLimits(plan.limits);\n return (\n <MenuItem key={plan.tier} value={plan.tier}>\n {plan.tier} {limitDesc ? `(${limitDesc})` : \"\"}\n </MenuItem>\n );\n })}\n </Select>\n </FormControl>\n\n <TextField\n label=\"Use Case\"\n placeholder=\"Describe how you plan to use this API\"\n multiline\n rows={3}\n fullWidth\n margin=\"normal\"\n value={useCase}\n onChange={(e) => setUseCase(e.target.value)}\n disabled={saving}\n helperText=\"Explain your intended use of this API for admin review\"\n />\n </DialogContent>\n <DialogActions>\n <Button onClick={handleClose} disabled={saving}>\n Cancel\n </Button>\n <Button\n onClick={handleSave}\n color=\"primary\"\n variant=\"contained\"\n disabled={!planTier || saving}\n startIcon={\n saving ? <CircularProgress size={16} color=\"inherit\" /> : undefined\n }\n >\n {saving ? \"Saving...\" : \"Save Changes\"}\n </Button>\n </DialogActions>\n </Dialog>\n );\n};\n","import { Credentials } from '../types/api-management';\n\nexport interface CodeSnippets {\n curl: string;\n nodejs: string;\n python: string;\n go: string;\n}\n\nexport function generateAuthCodeSnippets(\n credentials: Credentials | undefined,\n hostname: string,\n apiKey: string,\n): CodeSnippets {\n const baseUrl = `https://${hostname}/api/v1/endpoint`;\n\n if (!credentials) {\n return generateAuthorizationHeaderSnippets(baseUrl, apiKey, 'Bearer');\n }\n\n if (credentials.authorizationHeader) {\n const prefix = credentials!.authorizationHeader!.prefix || '';\n return generateAuthorizationHeaderSnippets(baseUrl, apiKey, prefix);\n }\n\n if (credentials.customHeader) {\n const headerName = credentials!.customHeader!.name;\n const prefix = credentials!.customHeader!.prefix || '';\n return generateCustomHeaderSnippets(baseUrl, apiKey, headerName, prefix);\n }\n\n\n if (credentials.queryString) {\n const paramName = credentials!.queryString!.name;\n return generateQueryStringSnippets(baseUrl, apiKey, paramName);\n }\n\n if (credentials.cookie) {\n const cookieName = credentials!.cookie!.name;\n return generateCookieSnippets(baseUrl, apiKey, cookieName);\n }\n // Default to Authorization Bearer if no authScheme specified\n return generateAuthorizationHeaderSnippets(baseUrl, apiKey, 'Bearer');\n}\n\nfunction generateAuthorizationHeaderSnippets(\n baseUrl: string,\n apiKey: string,\n prefix: string,\n): CodeSnippets {\n const authValue = prefix ? `${prefix} ${apiKey}` : apiKey;\n const prefixWithSpace = prefix ? `${prefix} ` : '';\n\n return {\n curl: `curl -X GET ${baseUrl} \\\\\n -H \"Authorization: ${authValue}\"`,\n\n nodejs: `const fetch = require('node-fetch');\n\nconst apiKey = '${apiKey}';\nconst endpoint = '${baseUrl}';\n\nfetch(endpoint, {\n method: 'GET',\n headers: {\n 'Authorization': '${prefixWithSpace}' + apiKey\n }\n})\n .then(response => response.json())\n .then(data => console.log(data))\n .catch(error => console.error('Error:', error));`,\n\n python: `import requests\n\napi_key = '${apiKey}'\nendpoint = '${baseUrl}'\n\nheaders = {\n 'Authorization': '${prefixWithSpace}' + api_key\n}\n\nresponse = requests.get(endpoint, headers=headers)\nprint(response.json())`,\n\n go: `package main\n\nimport (\n \"fmt\"\n \"net/http\"\n \"io\"\n)\n\nfunc main() {\n apiKey := \"${apiKey}\"\n endpoint := \"${baseUrl}\"\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", endpoint, nil)\n req.Header.Add(\"Authorization\", \"${prefixWithSpace}\" + apiKey)\n\n resp, err := client.Do(req)\n if err != nil {\n fmt.Println(\"Error:\", err)\n return\n }\n defer resp.Body.Close()\n\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}`,\n };\n}\n\nfunction generateCustomHeaderSnippets(\n baseUrl: string,\n apiKey: string,\n headerName: string,\n prefix: string,\n): CodeSnippets {\n const headerValue = prefix ? `${prefix}${apiKey}` : apiKey;\n\n return {\n curl: `curl -X GET ${baseUrl} \\\\\n -H \"${headerName}: ${headerValue}\"`,\n\n nodejs: `const fetch = require('node-fetch');\n\nconst apiKey = '${apiKey}';\nconst endpoint = '${baseUrl}';\n\nfetch(endpoint, {\n method: 'GET',\n headers: {\n '${headerName}': '${prefix}' + apiKey\n }\n})\n .then(response => response.json())\n .then(data => console.log(data))\n .catch(error => console.error('Error:', error));`,\n\n python: `import requests\n\napi_key = '${apiKey}'\nendpoint = '${baseUrl}'\n\nheaders = {\n '${headerName}': '${prefix}' + api_key\n}\n\nresponse = requests.get(endpoint, headers=headers)\nprint(response.json())`,\n\n go: `package main\n\nimport (\n \"fmt\"\n \"net/http\"\n \"io\"\n)\n\nfunc main() {\n apiKey := \"${apiKey}\"\n endpoint := \"${baseUrl}\"\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", endpoint, nil)\n req.Header.Add(\"${headerName}\", \"${prefix}\" + apiKey)\n\n resp, err := client.Do(req)\n if err != nil {\n fmt.Println(\"Error:\", err)\n return\n }\n defer resp.Body.Close()\n\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}`,\n };\n}\n\nfunction generateQueryStringSnippets(\n baseUrl: string,\n apiKey: string,\n paramName: string,\n): CodeSnippets {\n const urlWithParam = `${baseUrl}?${paramName}=${apiKey}`;\n\n return {\n curl: `curl -X GET \"${urlWithParam}\"`,\n\n nodejs: `const fetch = require('node-fetch');\n\nconst apiKey = '${apiKey}';\nconst endpoint = '${baseUrl}?${paramName}=' + apiKey;\n\nfetch(endpoint, {\n method: 'GET'\n})\n .then(response => response.json())\n .then(data => console.log(data))\n .catch(error => console.error('Error:', error));`,\n\n python: `import requests\n\napi_key = '${apiKey}'\nendpoint = '${baseUrl}'\n\nparams = {\n '${paramName}': api_key\n}\n\nresponse = requests.get(endpoint, params=params)\nprint(response.json())`,\n\n go: `package main\n\nimport (\n \"fmt\"\n \"net/http\"\n \"io\"\n)\n\nfunc main() {\n apiKey := \"${apiKey}\"\n endpoint := \"${baseUrl}?${paramName}=\" + apiKey\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", endpoint, nil)\n\n resp, err := client.Do(req)\n if err != nil {\n fmt.Println(\"Error:\", err)\n return\n }\n defer resp.Body.Close()\n\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}`,\n };\n}\n\nfunction generateCookieSnippets(\n baseUrl: string,\n apiKey: string,\n cookieName: string,\n): CodeSnippets {\n return {\n curl: `curl -X GET ${baseUrl} \\\\\n --cookie \"${cookieName}=${apiKey}\"`,\n\n nodejs: `const fetch = require('node-fetch');\n\nconst apiKey = '${apiKey}';\nconst endpoint = '${baseUrl}';\n\nfetch(endpoint, {\n method: 'GET',\n headers: {\n 'Cookie': '${cookieName}=' + apiKey\n }\n})\n .then(response => response.json())\n .then(data => console.log(data))\n .catch(error => console.error('Error:', error));`,\n\n python: `import requests\n\napi_key = '${apiKey}'\nendpoint = '${baseUrl}'\n\ncookies = {\n '${cookieName}': api_key\n}\n\nresponse = requests.get(endpoint, cookies=cookies)\nprint(response.json())`,\n\n go: `package main\n\nimport (\n \"fmt\"\n \"net/http\"\n \"io\"\n)\n\nfunc main() {\n apiKey := \"${apiKey}\"\n endpoint := \"${baseUrl}\"\n\n client := &http.Client{}\n req, _ := http.NewRequest(\"GET\", endpoint, nil)\n req.AddCookie(&http.Cookie{\n Name: \"${cookieName}\",\n Value: apiKey,\n })\n\n resp, err := client.Do(req)\n if err != nil {\n fmt.Println(\"Error:\", err)\n return\n }\n defer resp.Body.Close()\n\n body, _ := io.ReadAll(resp.Body)\n fmt.Println(string(body))\n}`,\n };\n}\n","import React, { useState, useMemo } from \"react\";\nimport { useAsync } from \"react-use\";\nimport {\n Table,\n TableColumn,\n ResponseErrorPanel,\n CodeSnippet,\n} from \"@backstage/core-components\";\nimport {\n IconButton,\n Typography,\n Box,\n Chip,\n Grid,\n Button,\n Tabs,\n Tab,\n Menu,\n MenuItem,\n Tooltip,\n CircularProgress,\n} from \"@material-ui/core\";\nimport { Skeleton } from \"@material-ui/lab\";\nimport {\n useApi,\n identityApiRef,\n alertApiRef,\n} from \"@backstage/core-plugin-api\";\nimport { kuadrantApiRef } from '../../api';\nimport { UserEntity } from '@backstage/catalog-model';\nimport { useEntity, catalogApiRef } from \"@backstage/plugin-catalog-react\";\nimport VisibilityIcon from \"@material-ui/icons/Visibility\";\nimport VisibilityOffIcon from \"@material-ui/icons/VisibilityOff\";\nimport HourglassEmptyIcon from \"@material-ui/icons/HourglassEmpty\";\nimport CancelIcon from \"@material-ui/icons/Cancel\";\nimport AddIcon from \"@material-ui/icons/Add\";\nimport MoreVertIcon from \"@material-ui/icons/MoreVert\";\nimport FileCopyIcon from \"@material-ui/icons/FileCopy\";\nimport { APIKey, APIProduct, Plan } from \"../../types/api-management\";\nimport {\n kuadrantApiKeyCreatePermission,\n kuadrantApiKeyDeleteOwnPermission,\n kuadrantApiKeyDeleteAllPermission,\n kuadrantApiKeyUpdateOwnPermission,\n} from \"../../permissions\";\nimport {\n useKuadrantPermission,\n canDeleteResource,\n} from \"../../utils/permissions\";\nimport { getAPIKeyPhase } from \"../../utils/apikeys\";\nimport { EditAPIKeyDialog } from \"../EditAPIKeyDialog\";\nimport { ConfirmDeleteDialog } from \"../ConfirmDeleteDialog\";\nimport { generateAuthCodeSnippets } from \"../../utils/codeSnippets\";\nimport { RequestAccessDialog } from \"../RequestAccessDialog\";\n\nexport interface ApiKeyManagementTabProps {\n namespace?: string;\n}\n\nexport const ApiKeyManagementTab = ({\n namespace: propNamespace,\n}: ApiKeyManagementTabProps) => {\n const { entity } = useEntity();\n const catalogAPI = useApi(catalogApiRef);\n const kuadrantApi = useApi(kuadrantApiRef);\n const identityApi = useApi(identityApiRef);\n const alertApi = useApi(alertApiRef);\n const [visibleKeys, setVisibleKeys] = useState<Set<string>>(new Set());\n const [refresh, setRefresh] = useState(0);\n const [userId, setUserId] = useState<string>(\"\");\n const [userEmail, setUserEmail] = useState<string>(\"\");\n const [requestDialogOpen, setRequestDialogOpen] = useState(false);\n const [editDialogOpen, setEditDialogOpen] = useState(false);\n const [requestToEdit, setRequestToEdit] = useState<APIKey | null>(null);\n const [menuAnchor, setMenuAnchor] = useState<{\n top: number;\n left: number;\n } | null>(null);\n const [menuRequest, setMenuRequest] = useState<APIKey | null>(null);\n const [deleting, setDeleting] = useState<string | null>(null);\n const [optimisticallyDeleted, setOptimisticallyDeleted] = useState<\n Set<string>\n >(new Set());\n const [deleteDialogState, setDeleteDialogState] = useState<{\n open: boolean;\n request: APIKey | null;\n }>({ open: false, request: null });\n const [apiKeyValues, setApiKeyValues] = useState<Map<string, string>>(\n new Map(),\n );\n const [apiKeyLoading, setApiKeyLoading] = useState<Set<string>>(new Set());\n\n // get apiproduct name from entity annotation (set by entity provider)\n const apiProductName =\n entity.metadata.annotations?.[\"kuadrant.io/apiproduct\"] ||\n entity.metadata.name;\n const namespace =\n entity.metadata.annotations?.[\"kuadrant.io/namespace\"] ||\n propNamespace ||\n \"default\";\n\n useAsync(async () => {\n const identity = await identityApi.getBackstageIdentity();\n const profile = await identityApi.getProfileInfo();\n setUserId(identity.userEntityRef);\n\n // Try profile email first (works for OAuth providers)\n let email = profile.email || \"\";\n // Fallback to catalog entity for guest/other providers\n if (!email && identity.userEntityRef) {\n const userEntity = await catalogAPI.getEntityByRef(identity.userEntityRef) as UserEntity;\n if (userEntity?.spec?.profile?.email) {\n email = userEntity.spec.profile.email as string;\n }\n }\n\n setUserEmail(email);\n }, [identityApi]);\n\n const {\n value: requests,\n loading: requestsLoading,\n error: requestsError,\n } = useAsync(async () => {\n // backend filters by apiProductRef.name and scopes to user's consumer namespace\n const data = await kuadrantApi.getRequestsByApiProduct(apiProductName, namespace);\n return data.items || [];\n }, [apiProductName, namespace, refresh, kuadrantApi]);\n\n const {\n value: apiProduct,\n loading: plansLoading,\n error: plansError,\n } = useAsync(async () => {\n const data = await kuadrantApi.getApiProducts();\n\n const product = data.items?.find(\n (p: APIProduct) =>\n p.metadata.namespace === namespace &&\n p.metadata.name === apiProductName,\n );\n\n return product;\n }, [namespace, apiProductName, kuadrantApi]);\n\n // check permissions with resource reference once we have the apiproduct\n const resourceRef = apiProduct\n ? `apiproduct:${apiProduct.metadata.namespace}/${apiProduct.metadata.name}`\n : undefined;\n\n const {\n allowed: canCreateRequest,\n loading: createRequestPermissionLoading,\n error: createRequestPermissionError,\n } = useKuadrantPermission(kuadrantApiKeyCreatePermission, resourceRef);\n\n const {\n allowed: canDeleteOwnKey,\n loading: deleteOwnPermissionLoading,\n error: deleteOwnPermissionError,\n } = useKuadrantPermission(kuadrantApiKeyDeleteOwnPermission);\n\n const {\n allowed: canDeleteAllKeys,\n loading: deleteAllPermissionLoading,\n error: deleteAllPermissionError,\n } = useKuadrantPermission(kuadrantApiKeyDeleteAllPermission);\n\n const {\n allowed: canUpdateRequest,\n loading: updateRequestPermissionLoading,\n error: updateRequestPermissionError,\n } = useKuadrantPermission(kuadrantApiKeyUpdateOwnPermission);\n\n const handleDeleteRequest = async (deleteNamespace: string, deleteName: string) => {\n // optimistic update - remove from UI immediately\n setOptimisticallyDeleted((prev) => new Set(prev).add(deleteName));\n setDeleting(deleteName);\n try {\n await kuadrantApi.deleteRequest(deleteNamespace, deleteName);\n alertApi.post({\n message: \"API key deleted successfully\",\n severity: \"success\",\n display: \"transient\",\n });\n setRefresh((r) => r + 1);\n } catch (err) {\n const errorMessage =\n err instanceof Error ? err.message : \"unknown error occurred\";\n // rollback optimistic update on error\n setOptimisticallyDeleted((prev) => {\n const next = new Set(prev);\n next.delete(deleteName);\n return next;\n });\n alertApi.post({\n message: `Failed to delete API key: ${errorMessage}`,\n severity: \"error\",\n display: \"transient\",\n });\n } finally {\n setDeleting(null);\n }\n };\n\n const fetchApiKeyFromSecret = async (\n requestNamespace: string,\n requestName: string,\n ) => {\n const key = `${requestNamespace}/${requestName}`;\n if (apiKeyLoading.has(key)) {\n return;\n }\n\n setApiKeyLoading((prev) => new Set(prev).add(key));\n try {\n const data = await kuadrantApi.getApiKeySecret(requestNamespace, requestName);\n setApiKeyValues((prev) => new Map(prev).set(key, data.apiKey));\n toggleVisibility(requestName);\n } catch (err) {\n const errorMessage =\n err instanceof Error ? err.message : \"unknown error occurred\";\n alertApi.post({\n message: `Failed to fetch api key: ${errorMessage}`,\n severity: \"error\",\n display: \"transient\",\n });\n } finally {\n setApiKeyLoading((prev) => {\n const next = new Set(prev);\n next.delete(key);\n return next;\n });\n }\n };\n\n const clearApiKeyValue = (requestNamespace: string, requestName: string) => {\n const key = `${requestNamespace}/${requestName}`;\n setApiKeyValues((prev) => {\n const next = new Map(prev);\n next.delete(key);\n return next;\n });\n };\n\n const handleEditRequest = (request: APIKey) => {\n setRequestToEdit(request);\n setEditDialogOpen(true);\n };\n\n const handleEditSuccess = () => {\n setRefresh((r) => r + 1);\n setEditDialogOpen(false);\n alertApi.post({\n message: \"API key updated\",\n severity: \"success\",\n display: \"transient\",\n });\n setRequestToEdit(null);\n };\n\n const handleMenuClose = () => {\n setMenuAnchor(null);\n setMenuRequest(null);\n };\n\n const handleMenuEdit = () => {\n if (!menuRequest) return;\n handleEditRequest(menuRequest);\n handleMenuClose();\n };\n\n const handleMenuDeleteClick = () => {\n if (!menuRequest) return;\n const request = menuRequest;\n handleMenuClose();\n setDeleteDialogState({ open: true, request });\n };\n\n const handleDeleteConfirm = async () => {\n if (!deleteDialogState.request) return;\n await handleDeleteRequest(\n deleteDialogState.request.metadata.namespace,\n deleteDialogState.request.metadata.name,\n );\n setDeleteDialogState({ open: false, request: null });\n };\n\n const handleDeleteCancel = () => {\n setDeleteDialogState({ open: false, request: null });\n };\n\n const toggleVisibility = (keyName: string) => {\n setVisibleKeys((prev) => {\n const newSet = new Set(prev);\n if (newSet.has(keyName)) {\n newSet.delete(keyName);\n } else {\n newSet.add(keyName);\n }\n return newSet;\n });\n };\n\n const detailPanelConfig = useMemo(\n () => [\n {\n render: (data: any) => {\n // backstage Table wraps the data in { rowData: actualData }\n const request = data.rowData as APIKey;\n if (!request?.metadata?.name) {\n return <Box />;\n }\n\n // pass already-revealed key from parent state (don't auto-fetch - that consumes show-once)\n const key = `${request.metadata.namespace}/${request.metadata.name}`;\n const revealedKey = apiKeyValues.get(key);\n return (\n <DetailPanelContent\n request={request}\n apiName={apiProductName}\n revealedApiKey={revealedKey}\n />\n );\n },\n },\n ],\n [apiProductName, apiKeyValues],\n );\n\n // separate component to isolate state\n const DetailPanelContent = ({\n request,\n apiName: api,\n revealedApiKey,\n }: {\n request: APIKey;\n apiName: string;\n revealedApiKey?: string;\n }) => {\n const [selectedLanguage, setSelectedLanguage] = useState(0);\n const hostname = request.status?.apiHostname || `${api}.apps.example.com`;\n\n // use revealed key if available, otherwise show placeholder\n const displayApiKey = revealedApiKey || \"<your-api-key>\";\n\n // Generate code snippets based on authScheme credentials\n const credentials = request.status?.authScheme?.credentials;\n const snippets = generateAuthCodeSnippets(credentials, hostname, displayApiKey);\n\n return (\n <Box\n p={3}\n bgcolor=\"background.default\"\n onClick={(e) => e.stopPropagation()}\n >\n {request.spec.useCase && (\n <Box mb={3}>\n <Typography variant=\"h6\" gutterBottom>\n Use Case\n </Typography>\n <Box\n p={2}\n bgcolor=\"background.paper\"\n borderRadius={1}\n border=\"1px solid rgba(0, 0, 0, 0.12)\"\n >\n <Typography\n variant=\"body2\"\n style={{\n whiteSpace: \"pre-wrap\",\n wordBreak: \"break-word\",\n overflowWrap: \"break-word\",\n }}\n >\n {request.spec.useCase}\n </Typography>\n </Box>\n </Box>\n )}\n <Typography variant=\"h6\" gutterBottom>\n Usage Examples\n </Typography>\n <Typography variant=\"body2\" paragraph>\n Use these code examples to test the API with your{\" \"}\n {request.spec.planTier} tier key.\n </Typography>\n <Box onClick={(e) => e.stopPropagation()}>\n <Tabs\n value={selectedLanguage}\n onChange={(e, newValue) => {\n e.stopPropagation();\n setSelectedLanguage(newValue);\n }}\n indicatorColor=\"primary\"\n >\n <Tab label=\"cURL\" onClick={(e) => e.stopPropagation()} />\n <Tab label=\"Node.js\" onClick={(e) => e.stopPropagation()} />\n <Tab label=\"Python\" onClick={(e) => e.stopPropagation()} />\n <Tab label=\"Go\" onClick={(e) => e.stopPropagation()} />\n </Tabs>\n </Box>\n <Box mt={2}>\n {selectedLanguage === 0 && (\n <CodeSnippet\n text={snippets.curl}\n language=\"bash\"\n showCopyCodeButton\n />\n )}\n {selectedLanguage === 1 && (\n <CodeSnippet\n text={snippets.nodejs}\n language=\"javascript\"\n showCopyCodeButton\n />\n )}\n {selectedLanguage === 2 && (\n <CodeSnippet\n text={snippets.python}\n language=\"python\"\n showCopyCodeButton\n />\n )}\n {selectedLanguage === 3 && (\n <CodeSnippet\n text={snippets.go}\n language=\"go\"\n showCopyCodeButton\n />\n )}\n </Box>\n </Box>\n );\n };\n\n const loading =\n requestsLoading ||\n plansLoading ||\n createRequestPermissionLoading ||\n deleteOwnPermissionLoading ||\n deleteAllPermissionLoading ||\n updateRequestPermissionLoading;\n const error = requestsError || plansError;\n const permissionError =\n createRequestPermissionError ||\n deleteOwnPermissionError ||\n deleteAllPermissionError ||\n updateRequestPermissionError;\n\n if (loading) {\n return (\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 );\n }\n\n if (error) {\n return <ResponseErrorPanel error={error} />;\n }\n\n if (permissionError) {\n const failedPermission = createRequestPermissionError\n ? \"kuadrant.apikey.create\"\n : deleteOwnPermissionError\n ? \"kuadrant.apikey.delete.own\"\n : deleteAllPermissionError\n ? \"kuadrant.apikey.delete.all\"\n : updateRequestPermissionError\n ? \"kuadrant.apikey.update.own\"\n : \"unknown\";\n return (\n <Box p={2}>\n <Typography color=\"error\">\n Unable to check permissions: {permissionError.message}\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Permission: {failedPermission}\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Please try again or contact your administrator\n </Typography>\n </Box>\n );\n }\n\n const myRequests = ((requests || []) as APIKey[]).filter(\n (r) => !optimisticallyDeleted.has(r.metadata.name),\n );\n const plans = (apiProduct?.status?.discoveredPlans || []) as Plan[];\n\n const pendingRequests = myRequests.filter(\n (r) => getAPIKeyPhase(r.status?.conditions) === \"Pending\",\n );\n const approvedRequests = myRequests.filter(\n (r) => getAPIKeyPhase(r.status?.conditions) === \"Approved\",\n );\n const rejectedRequests = myRequests.filter(\n (r) => getAPIKeyPhase(r.status?.conditions) === \"Denied\",\n );\n\n const approvedColumns: TableColumn<APIKey>[] = [\n {\n title: \"Tier\",\n field: \"spec.planTier\",\n render: (row: APIKey) => (\n <Chip label={row.spec.planTier} color=\"primary\" size=\"small\" />\n ),\n },\n {\n title: \"Approved\",\n field: \"status.conditions\",\n render: (row: APIKey) => {\n const approvalDate = row.status?.conditions?.find(\n c => c.type === 'Approved' && c.status === 'True'\n )?.lastTransitionTime;\n return (\n <Typography variant=\"body2\">\n {approvalDate ? new Date(approvalDate).toLocaleDateString() : \"-\"}\n </Typography>\n );\n },\n },\n {\n title: \"API Key\",\n field: \"status.secretRef\",\n searchable: false,\n filtering: false,\n render: (row: APIKey) => {\n const key = `${row.metadata.namespace}/${row.metadata.name}`;\n const isVisible = visibleKeys.has(row.metadata.name);\n const isLoading = apiKeyLoading.has(key);\n const apiKeyValue = apiKeyValues.get(key);\n const hasSecretRef = Boolean(row.spec?.secretRef?.name);\n\n if (!hasSecretRef) {\n return (\n <Typography variant=\"body2\" color=\"textSecondary\">\n Awaiting secret...\n </Typography>\n );\n }\n\n const handleRevealClick = () => {\n if (isVisible) {\n clearApiKeyValue(row.metadata.namespace, row.metadata.name);\n toggleVisibility(row.metadata.name);\n } else {\n fetchApiKeyFromSecret(row.metadata.namespace, row.metadata.name);\n }\n };\n\n const handleCopy = async () => {\n if (apiKeyValue) {\n await navigator.clipboard.writeText(apiKeyValue);\n alertApi.post({\n message: \"API key copied to clipboard\",\n severity: \"success\",\n display: \"transient\",\n });\n }\n };\n\n return (\n <Box display=\"flex\" alignItems=\"center\">\n <Typography\n variant=\"body2\"\n style={{\n fontFamily: \"monospace\",\n marginRight: 8,\n }}\n >\n {isLoading\n ? \"Loading...\"\n : isVisible && apiKeyValue\n ? apiKeyValue\n : \"••••••••••••••••\"}\n </Typography>\n {isVisible && apiKeyValue && (\n <Tooltip title=\"Copy to clipboard\">\n <IconButton size=\"small\" onClick={handleCopy}>\n <FileCopyIcon fontSize=\"small\" />\n </IconButton>\n </Tooltip>\n )}\n <Tooltip\n title={isVisible ? \"Hide API key\" : \"Reveal API key\"}\n >\n <span>\n <IconButton\n size=\"small\"\n onClick={handleRevealClick}\n disabled={isLoading}\n >\n {isVisible ? <VisibilityOffIcon /> : <VisibilityIcon />}\n </IconButton>\n </span>\n </Tooltip>\n </Box>\n );\n },\n },\n {\n title: \"\",\n field: \"actions\",\n searchable: false,\n filtering: false,\n render: (row: APIKey) => {\n const isDeleting = deleting === row.metadata.name;\n if (isDeleting) {\n return <CircularProgress size={20} />;\n }\n const ownerId = row.spec.requestedBy.userId;\n const canDelete = canDeleteResource(\n ownerId,\n userId,\n canDeleteOwnKey,\n canDeleteAllKeys,\n );\n if (!canDelete) return null;\n return (\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n const rect = e.currentTarget.getBoundingClientRect();\n setMenuAnchor({ top: rect.bottom, left: rect.left });\n setMenuRequest(row);\n }}\n title=\"Actions\"\n aria-controls={menuAnchor ? \"actions-menu\" : undefined}\n aria-haspopup=\"true\"\n >\n <MoreVertIcon />\n </IconButton>\n );\n },\n },\n ];\n\n const requestColumns: TableColumn<APIKey>[] = [\n {\n title: \"Status\",\n field: \"status.conditions\",\n render: (row: APIKey) => {\n const phase = getAPIKeyPhase(row.status?.conditions || []);\n const isPending = phase === \"Pending\";\n return (\n <Chip\n label={phase}\n size=\"small\"\n icon={isPending ? <HourglassEmptyIcon /> : <CancelIcon />}\n color={isPending ? \"default\" : \"secondary\"}\n />\n );\n },\n },\n {\n title: \"Tier\",\n field: \"spec.planTier\",\n render: (row: APIKey) => (\n <Chip label={row.spec.planTier} color=\"primary\" size=\"small\" />\n ),\n },\n {\n title: \"Use Case\",\n field: \"spec.useCase\",\n render: (row: APIKey) => {\n if (!row.spec.useCase) {\n return <Typography variant=\"body2\">-</Typography>;\n }\n return (\n <Tooltip title={row.spec.useCase} placement=\"top\">\n <Typography\n variant=\"body2\"\n style={{\n maxWidth: \"200px\",\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n whiteSpace: \"nowrap\",\n }}\n >\n {row.spec.useCase}\n </Typography>\n </Tooltip>\n );\n },\n },\n {\n title: \"Requested\",\n field: \"metadata.creationTimestamp\",\n render: (row: APIKey) => (\n <Typography variant=\"body2\">\n {row.metadata.creationTimestamp\n ? new Date(row.metadata.creationTimestamp).toLocaleDateString()\n : \"-\"}\n </Typography>\n ),\n },\n {\n title: \"Reviewed\",\n field: \"status.conditions\",\n render: (row: APIKey) => {\n const approvedDate = row.status?.conditions?.find(\n c => (c.type === 'Approved' || c.type === 'Denied') && c.status === 'True'\n )?.lastTransitionTime;\n return (\n <Typography variant=\"body2\">\n {approvedDate ? new Date(approvedDate).toLocaleDateString() : \"-\"}\n </Typography>\n );\n },\n },\n {\n title: \"\",\n field: \"actions\",\n searchable: false,\n filtering: false,\n render: (row: APIKey) => {\n const isDeleting = deleting === row.metadata.name;\n if (isDeleting) {\n return <CircularProgress size={20} />;\n }\n const isPending = getAPIKeyPhase(row.status?.conditions || []) === \"Pending\";\n const ownerId = row.spec.requestedBy.userId;\n const canDelete = canDeleteResource(\n ownerId,\n userId,\n canDeleteOwnKey,\n canDeleteAllKeys,\n );\n const canEdit = canUpdateRequest && ownerId === userId;\n if (!isPending || (!canEdit && !canDelete)) return null;\n return (\n <IconButton\n size=\"small\"\n onClick={(e) => {\n e.stopPropagation();\n const rect = e.currentTarget.getBoundingClientRect();\n setMenuAnchor({ top: rect.bottom, left: rect.left });\n setMenuRequest(row);\n }}\n title=\"Actions\"\n aria-controls={menuAnchor ? \"actions-menu\" : undefined}\n aria-haspopup=\"true\"\n >\n <MoreVertIcon />\n </IconButton>\n );\n },\n },\n ];\n\n // Filter columns for pending requests (no Reviewed or Reason)\n const pendingRequestColumns = requestColumns.filter(\n (col) => col.title !== \"Reviewed\" && col.title !== \"Reason\",\n );\n\n return (\n <Box p={2}>\n <Grid container spacing={3} direction=\"column\">\n {canCreateRequest && (\n <Grid item>\n <Box\n display=\"flex\"\n flexDirection=\"column\"\n alignItems=\"flex-end\"\n mb={2}\n >\n <Button\n variant=\"contained\"\n color=\"primary\"\n startIcon={<AddIcon />}\n onClick={() => setRequestDialogOpen(true)}\n disabled={plans.length === 0 || !userEmail}\n data-testid=\"request-api-access-button\"\n data-plans-count={plans.length}\n >\n Request API Access\n </Button>\n {!userEmail && (\n <Typography\n variant=\"caption\"\n color=\"textSecondary\"\n style={{ marginTop: 4 }}\n data-testid=\"no-email-message\"\n >\n \"Email address is required\"\n </Typography>\n )}\n {plans.length === 0 && (\n <Typography\n variant=\"caption\"\n color=\"textSecondary\"\n style={{ marginTop: 4 }}\n data-testid=\"no-plans-message\"\n >\n {!apiProduct\n ? \"API product not found\"\n : (() => {\n const readyCondition =\n apiProduct.status?.conditions?.find(\n (c: any) => c.type === \"Ready\",\n );\n const planCondition =\n apiProduct.status?.conditions?.find(\n (c: any) => c.type === \"PlanPolicyDiscovered\",\n );\n\n if (readyCondition?.status !== \"True\") {\n return `HTTPRoute not ready: ${readyCondition?.message || \"unknown\"}`;\n }\n if (planCondition?.status !== \"True\") {\n return `No plans discovered: ${planCondition?.message || \"no PlanPolicy found\"}`;\n }\n return \"No plans available\";\n })()}\n </Typography>\n )}\n </Box>\n </Grid>\n )}\n {pendingRequests.length === 0 &&\n rejectedRequests.length === 0 &&\n approvedRequests.length === 0 && (\n <Grid item>\n <Box p={3} textAlign=\"center\">\n <Typography variant=\"body1\" color=\"textSecondary\">\n No API keys yet. Request access to get started.\n </Typography>\n </Box>\n </Grid>\n )}\n {pendingRequests.length > 0 && (\n <Grid item>\n <Table\n title=\"Pending Requests\"\n options={{\n paging: pendingRequests.length > 5,\n pageSize: 20,\n search: true,\n filtering: true,\n debounceInterval: 300,\n toolbar: true,\n emptyRowsWhenPaging: false,\n }}\n columns={pendingRequestColumns}\n data={pendingRequests}\n />\n </Grid>\n )}\n {rejectedRequests.length > 0 && (\n <Grid item>\n <Table\n title=\"Denied Requests\"\n options={{\n paging: rejectedRequests.length > 5,\n pageSize: 20,\n search: true,\n filtering: true,\n debounceInterval: 300,\n toolbar: true,\n emptyRowsWhenPaging: false,\n }}\n columns={requestColumns}\n data={rejectedRequests}\n />\n </Grid>\n )}\n {approvedRequests.length > 0 && (\n <Grid item>\n <Table\n key=\"api-keys-table\"\n title=\"API Keys\"\n options={{\n paging: approvedRequests.length > 5,\n pageSize: 20,\n search: true,\n filtering: true,\n debounceInterval: 300,\n toolbar: true,\n emptyRowsWhenPaging: false,\n }}\n columns={approvedColumns}\n data={approvedRequests}\n detailPanel={detailPanelConfig}\n />\n </Grid>\n )}\n </Grid>\n\n <RequestAccessDialog\n open={requestDialogOpen}\n onClose={() => setRequestDialogOpen(false)}\n onSuccess={() => {\n setRequestDialogOpen(false);\n setRefresh((r) => r + 1);\n }}\n apiProductName={apiProductName}\n namespace={namespace}\n userEmail={userEmail}\n plans={plans}\n />\n\n <Menu\n id=\"actions-menu\"\n open={Boolean(menuAnchor)}\n onClose={handleMenuClose}\n anchorReference=\"anchorPosition\"\n anchorPosition={menuAnchor || { top: 0, left: 0 }}\n >\n {menuRequest &&\n (() => {\n const isPending = getAPIKeyPhase(menuRequest.status?.conditions || []) === \"Pending\";\n const ownerId = menuRequest.spec.requestedBy.userId;\n const canEdit = canUpdateRequest && ownerId === userId && isPending;\n\n const items = [];\n if (canEdit) {\n items.push(\n <MenuItem key=\"edit\" onClick={handleMenuEdit}>\n Edit\n </MenuItem>,\n );\n }\n items.push(\n <MenuItem key=\"delete\" onClick={handleMenuDeleteClick}>\n Delete\n </MenuItem>,\n );\n return items;\n })()}\n </Menu>\n\n {requestToEdit && (\n <EditAPIKeyDialog\n open={editDialogOpen}\n onClose={() => {\n setEditDialogOpen(false);\n setRequestToEdit(null);\n }}\n onSuccess={handleEditSuccess}\n request={requestToEdit}\n availablePlans={plans}\n />\n )}\n\n <ConfirmDeleteDialog\n open={deleteDialogState.open}\n title=\"Delete Request\"\n description={`Are you sure you want to delete this ${getAPIKeyPhase(deleteDialogState.request?.status?.conditions || []) === \"Approved\" ? \"API key\" : \"request\"}?`}\n deleting={deleting !== null}\n onConfirm={handleDeleteConfirm}\n onCancel={handleDeleteCancel}\n />\n </Box>\n );\n};\n"],"names":["RequestAccessDialog","open","onClose","onSuccess","apiProductName","namespace","userEmail","plans","kuadrantApi","useApi","kuadrantApiRef","alertApi","alertApiRef","selectedPlan","setSelectedPlan","useState","useCase","setUseCase","creating","setCreating","createError","setCreateError","handleClose","Dialog","maxWidth","fullWidth","DialogTitle","DialogContent","Box","mb","p","bgcolor","borderRadius","display","alignItems","style","gap","InfoIcon","color","fontSize","marginTop","Typography","variant","FormControl","margin","disabled","data-testid","InputLabel","id","Select","labelId","value","onChange","e","target","map","plan","limitDesc","formatPlanLimits","limits","MenuItem","tier","TextField","label","placeholder","multiline","rows","helperText","DialogActions","Button","onClick","async","randomSuffix","Math","random","toString","substring","secretName","toLowerCase","replace","apiKeyValue","crypto","randomUUID","createSecret","createRequest","planTier","trim","post","message","severity","apiKeyError","deleteSecret","deleteError","console","warn","error","errorMessage","Error","startIcon","CircularProgress","size","undefined","getAPIKeyPhase","conditions","length","find","c","type","status","ConfirmDeleteDialog","title","description","confirmText","deleting","onConfirm","onCancel","inputValue","setInputValue","useEffect","requiresTextConfirmation","canConfirm","WarningIcon","span","DialogContentText","whiteSpace","mt","gutterBottom","strong","autoFocus","EditAPIKeyDialog","request","availablePlans","setPlanTier","saving","setSaving","setError","spec","patch","updateRequest","metadata","name","err","generateAuthorizationHeaderSnippets","baseUrl","apiKey","prefix","prefixWithSpace","curl","nodejs","python","go","ApiKeyManagementTab","propNamespace","entity","apiProduct","deleteDialogState","useEntity","catalogAPI","catalogApiRef","identityApi","identityApiRef","visibleKeys","setVisibleKeys","Set","refresh","setRefresh","userId","setUserId","setUserEmail","requestDialogOpen","setRequestDialogOpen","editDialogOpen","setEditDialogOpen","requestToEdit","setRequestToEdit","menuAnchor","setMenuAnchor","menuRequest","setMenuRequest","setDeleting","optimisticallyDeleted","setOptimisticallyDeleted","setDeleteDialogState","apiKeyValues","setApiKeyValues","Map","apiKeyLoading","setApiKeyLoading","annotations","useAsync","identity","getBackstageIdentity","profile","getProfileInfo","userEntityRef","email","userEntity","getEntityByRef","requests","loading","requestsLoading","requestsError","getRequestsByApiProduct","items","plansLoading","plansError","data","getApiProducts","resourceRef","allowed","canCreateRequest","createRequestPermissionLoading","createRequestPermissionError","useKuadrantPermission","kuadrantApiKeyCreatePermission","canDeleteOwnKey","deleteOwnPermissionLoading","deleteOwnPermissionError","kuadrantApiKeyDeleteOwnPermission","canDeleteAllKeys","deleteAllPermissionLoading","deleteAllPermissionError","kuadrantApiKeyDeleteAllPermission","canUpdateRequest","updateRequestPermissionLoading","updateRequestPermissionError","kuadrantApiKeyUpdateOwnPermission","handleMenuClose","handleMenuEdit","handleMenuDeleteClick","toggleVisibility","keyName","prev","newSet","has","delete","add","detailPanelConfig","useMemo","render","rowData","key","revealedKey","get","DetailPanelContent","apiName","revealedApiKey","api","selectedLanguage","setSelectedLanguage","hostname","apiHostname","displayApiKey","snippets","credentials","authorizationHeader","customHeader","headerName","generateCustomHeaderSnippets","queryString","paramName","generateQueryStringSnippets","cookie","cookieName","generateCookieSnippets","generateAuthCodeSnippets","authScheme","stopPropagation","border","wordBreak","overflowWrap","paragraph","Tabs","newValue","indicatorColor","Tab","CodeSnippet","text","language","showCopyCodeButton","permissionError","Array","_","i","Skeleton","width","ResponseErrorPanel","failedPermission","myRequests","filter","r","discoveredPlans","pendingRequests","approvedRequests","rejectedRequests","approvedColumns","field","row","Chip","approvalDate","lastTransitionTime","Date","toLocaleDateString","searchable","filtering","isVisible","isLoading","Boolean","secretRef","fontFamily","marginRight","Tooltip","IconButton","navigator","clipboard","writeText","FileCopyIcon","requestNamespace","requestName","next","clearApiKeyValue","getApiKeySecret","set","fetchApiKeyFromSecret","VisibilityOffIcon","VisibilityIcon","ownerId","requestedBy","canDeleteResource","rect","currentTarget","getBoundingClientRect","top","bottom","left","aria-controls","aria-haspopup","MoreVertIcon","requestColumns","phase","isPending","icon","HourglassEmptyIcon","CancelIcon","placement","overflow","textOverflow","creationTimestamp","approvedDate","canDelete","pendingRequestColumns","col","Grid","container","spacing","direction","item","flexDirection","AddIcon","data-plans-count","readyCondition","planCondition","textAlign","Table","options","paging","pageSize","search","debounceInterval","toolbar","emptyRowsWhenPaging","columns","detailPanel","Menu","anchorReference","anchorPosition","push","deleteNamespace","deleteName","deleteRequest","handleDeleteRequest"],"sourceRoot":""}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";(self.webpackChunkinternal_plugin_kuadrant=self.webpackChunkinternal_plugin_kuadrant||[]).push([[2821],{12229:(e,t,a)=>{a.d(t,{Z:()=>x});var s=a(31085),n=a(95478),l=a.n(n),i=a(10394),r=a(72501),o=a(64947),c=a(37197),d=a(69621),u=a(12981),p=a(86901),m=a(69076),h=a(58837),v=a(6924),g=a(23164);const A=(0,h.A)(e=>({root:{width:240,minWidth:240,padding:e.spacing(2),borderRight:`1px solid ${e.palette.divider}`,backgroundColor:e.palette.background.paper,height:"100%",overflowY:"auto"},sectionTitle:{fontWeight:600,fontSize:"0.75rem",textTransform:"uppercase",letterSpacing:"0.05em",color:e.palette.text.secondary,marginBottom:e.spacing(1),display:"flex",alignItems:"center",justifyContent:"space-between",cursor:"pointer",userSelect:"none"},filterSection:{marginBottom:e.spacing(2)},checkbox:{padding:e.spacing(.5)},checkboxLabel:{fontSize:"0.875rem"},clearButton:{marginTop:e.spacing(2)},count:{fontSize:"0.75rem",color:e.palette.text.secondary,marginLeft:e.spacing(1)}})),x=({sections:e,filters:t,onChange:a,onClear:n})=>{const h=A(),[x,f]=l().useState(new Set(e.filter(e=>e.collapsed).map(e=>e.id))),y=Object.values(t).some(e=>e.length>0);return(0,s.jsxs)(i.A,{className:h.root,children:[(0,s.jsxs)(i.A,{display:"flex",justifyContent:"space-between",alignItems:"center",mb:2,children:[(0,s.jsx)(r.A,{variant:"subtitle2",children:"Filters"}),y&&(0,s.jsx)(o.A,{size:"small",color:"primary",onClick:()=>{const t={};e.forEach(e=>{t[e.id]=[]}),a(t),null==n||n()},children:"Clear all"})]}),(0,s.jsx)(c.A,{}),e.map(e=>{const n=x.has(e.id),l=(t[e.id]||[]).length;return(0,s.jsxs)(i.A,{className:h.filterSection,mt:2,children:[(0,s.jsxs)(i.A,{className:h.sectionTitle,onClick:()=>{return t=e.id,void f(e=>{const a=new Set(e);return a.has(t)?a.delete(t):a.add(t),a});var t},children:[(0,s.jsxs)(i.A,{display:"flex",alignItems:"center",children:[(0,s.jsx)("span",{children:e.title}),l>0&&(0,s.jsxs)("span",{className:h.count,children:["(",l,")"]})]}),n?(0,s.jsx)(v.A,{fontSize:"small"}):(0,s.jsx)(g.A,{fontSize:"small"})]}),(0,s.jsx)(d.A,{in:!n,children:(0,s.jsx)(u.A,{children:e.options.map(n=>(0,s.jsx)(p.A,{control:(0,s.jsx)(m.A,{checked:(t[e.id]||[]).includes(n.value),onChange:()=>((e,s)=>{const n=t[e]||[],l=n.includes(s)?n.filter(e=>e!==s):[...n,s];a({...t,[e]:l})})(e.id,n.value),size:"small",className:h.checkbox,color:"primary"}),label:(0,s.jsxs)(i.A,{display:"flex",alignItems:"center",children:[(0,s.jsx)("span",{className:h.checkboxLabel,children:n.label}),void 0!==n.count&&(0,s.jsxs)("span",{className:h.count,children:["(",n.count,")"]})]})},n.value))})})]},e.id)})]})}},38599:(e,t,a)=>{a.d(t,{B:()=>o});var s=a(31085),n=(a(95478),a(10394)),l=a(72501),i=a(4387),r=a(46205);const o=({children:e,permission:t,fallback:a,errorMessage:o})=>{const{allowed:c,loading:d,error:u}=(0,r.l)(t);return d?(0,s.jsx)(n.A,{p:2,children:[...Array(5)].map((e,t)=>(0,s.jsx)(n.A,{p:2,children:(0,s.jsx)(i.A,{variant:"text",width:"100%"})},t))}):u?(0,s.jsxs)(n.A,{p:4,children:[(0,s.jsxs)(l.A,{color:"error",children:["Unable to check permissions: ",u.message]}),(0,s.jsx)(l.A,{variant:"body2",color:"textSecondary",children:"Please try again or contact your administrator"})]}):c?(0,s.jsx)(s.Fragment,{children:e}):a?(0,s.jsx)(s.Fragment,{children:a}):(0,s.jsxs)(n.A,{p:4,children:[(0,s.jsx)(l.A,{color:"textSecondary",children:o||"You don't have permission to view this page"}),(0,s.jsx)(n.A,{mt:1,children:(0,s.jsxs)(l.A,{variant:"caption",color:"textSecondary",children:["Required permission: ",t.name]})})]})}},46299:(e,t,a)=>{a.d(t,{ee:()=>l,g9:()=>s,uU:()=>n});const s=e=>{const t={border:"none"};switch(e){case"Approved":return{...t,backgroundColor:"#1976d2",color:"#fff"};case"Denied":return{...t,backgroundColor:"#d32f2f",color:"#fff"};case"Failed":return{...t,backgroundColor:"#ed6c02",color:"#fff"};default:return{...t,backgroundColor:"#9c27b0",color:"#fff"}}},n=e=>{const t={border:"none"};switch(e){case"Approved":return{...t,backgroundColor:"#2e7d32",color:"#fff"};case"Denied":return{...t,backgroundColor:"#d32f2f",color:"#fff"};default:return{...t,backgroundColor:"#ed6c02",color:"#fff"}}},l=e=>{switch(e){case"production":return{backgroundColor:"#1976d2",color:"#fff"};case"experimental":return{backgroundColor:"#9c27b0",color:"#fff"};case"deprecated":return{backgroundColor:"#ff9800",color:"#fff"};case"retired":return{backgroundColor:"#d32f2f",color:"#fff"};default:return{}}}},80243:(e,t,a)=>{a.r(t),a.d(t,{ApiProductsPage:()=>le});var s=a(31085),n=a(95478),l=a(58837),i=a(10394),r=a(67720),o=a(72501),c=a(64947),d=a(29365),u=a(4387),p=a(18466),m=a(39590),h=a(75625),v=a(21702),g=a(85142),A=a(12229),x=a(37725),f=a(289),y=a(15831),j=a(45210),b=a(46681),P=a(42367),S=a(25010),w=a(91638),C=a(22097),k=a(84692),I=a(70980),T=a(38599),N=a(76891),R=a(61477),z=a(46805),$=a(42899),D=a(16249),L=a(34839),H=a(71677),M=a(26343),W=a(95061),E=a(29635),K=a(86901),O=a(30285),B=a(93453),q=a(78467),F=a(89031),U=a(48351),_=a(86197),Y=a(24170),V=a(97859);const Z=(0,l.A)(e=>({asterisk:{color:"#f44336"},sectionHeader:{display:"flex",alignItems:"center",gap:e.spacing(.5),marginTop:e.spacing(2),marginBottom:e.spacing(1)},infoIcon:{fontSize:18,color:e.palette.text.secondary},tagChip:{marginRight:e.spacing(.5),marginBottom:e.spacing(.5)}})),G=({open:e,onClose:t,onSuccess:a})=>{var l,u,m,h,v,g;const A=Z(),x=(0,C.useApi)(I.s),[f,y]=(0,n.useState)(""),[j,b]=(0,n.useState)(""),[P,S]=(0,n.useState)(""),[k,T]=(0,n.useState)("v1"),[G,J]=(0,n.useState)("manual"),[X,Q]=(0,n.useState)("Published"),[ee,te]=(0,n.useState)("production"),[ae,se]=(0,n.useState)([]),[ne,le]=(0,n.useState)(""),[ie,re]=(0,n.useState)(""),[oe,ce]=(0,n.useState)(""),[de,ue]=(0,n.useState)(""),[pe,me]=(0,n.useState)(""),[he,ve]=(0,n.useState)(""),[ge,Ae]=(0,n.useState)(""),[xe,fe]=(0,n.useState)(!1),[ye,je]=(0,n.useState)(0),[be,Pe]=(0,n.useState)(null),[Se,we]=(0,n.useState)(null),[Ce,ke]=(0,n.useState)(""),[Ie,Te]=(0,n.useState)("name"),{value:Ne,loading:Re,error:ze}=(0,w.A)(async()=>(await x.getHttpRoutes()).items||[],[x,e,ye]),{value:$e,error:De}=(0,w.A)(async()=>await x.getPlanPolicies(),[x,e]),{value:Le,error:He}=(0,w.A)(async()=>await x.getAuthPolicies(),[x,e]),{value:Me,error:We}=(0,w.A)(async()=>await x.getRateLimitPolicies(),[x,e]),Ee=(e,t)=>(0,V.dO)(null==$e?void 0:$e.items,e,t),Ke=ie?ie.split("/"):null,Oe=Ke?Ee(Ke[0],Ke[1]):null,Be=null==Oe||null===(u=Oe.status)||void 0===u||null===(l=u.conditions)||void 0===l?void 0:l.find(e=>"Accepted"===e.type),qe=Ke?(Fe=Ke[0],Ue=Ke[1],(0,V.dO)(null==Le?void 0:Le.items,Fe,Ue)):null;var Fe,Ue;const _e=null==qe||null===(h=qe.status)||void 0===h||null===(m=h.conditions)||void 0===m?void 0:m.find(e=>"Accepted"===e.type),Ye=Ke?((e,t)=>(0,V.dO)(null==Me?void 0:Me.items,e,t))(Ke[0],Ke[1]):null,Ve=null==Ye||null===(g=Ye.status)||void 0===g||null===(v=g.conditions)||void 0===v?void 0:v.find(e=>"Accepted"===e.type),Ze={statusCondition:Be,discoveredPlans:null==Oe?void 0:Oe.spec.plans},Ge={namespacedName:{namespace:null==qe?void 0:qe.metadata.namespace,name:null==qe?void 0:qe.metadata.name},statusCondition:_e},Je={namespacedName:{namespace:null==Ye?void 0:Ye.metadata.namespace,name:null==Ye?void 0:Ye.metadata.name},statusCondition:Ve},Xe=(e,t)=>{const a=Ee(e,t);return a?`${a.metadata.name}${(e=>{var t;if(!(null==e||null===(t=e.spec)||void 0===t?void 0:t.plans))return"";const a=Object.entries(e.spec.plans).map(([e,t])=>{var a;const s=null==t||null===(a=t.limits)||void 0===a?void 0:a.requests;return s?`${e}: ${s.count}/${s.period}`:e}).join("; ");return a?` (${a})`:""})(a)}`:"N/A"};(0,n.useEffect)(()=>{e&&(Pe(null),we(null))},[e]);const Qe=()=>{ne.trim()&&!ae.includes(ne.trim())&&(se([...ae,ne.trim()]),le(""))},et=()=>{y(""),b(""),S(""),T("v1"),J("manual"),Q("Published"),te("production"),se([]),le(""),re(""),ce(""),ue(""),me(""),ve(""),Ae(""),Pe(null),we(null),t()},tt=!!be||!!Se;return(0,s.jsxs)(N.A,{open:e,onClose:et,maxWidth:"md",fullWidth:!0,children:[(0,s.jsx)(R.A,{children:"Create API Product"}),(0,s.jsxs)(z.A,{children:[ge&&(0,s.jsx)(U.A,{severity:"error",style:{marginBottom:16},children:ge}),ze&&(0,s.jsxs)(U.A,{severity:"error",style:{marginBottom:16},children:[(0,s.jsx)("strong",{children:"Failed to load HTTPRoutes:"})," ",ze.message,(0,s.jsx)(i.A,{mt:1,children:(0,s.jsx)(c.A,{size:"small",variant:"outlined",onClick:()=>je(e=>e+1),children:"Retry"})})]}),De&&(0,s.jsxs)(U.A,{severity:"warning",style:{marginBottom:16},children:[(0,s.jsx)("strong",{children:"Failed to load PlanPolicies:"})," ",De.message,(0,s.jsx)(o.A,{variant:"body2",style:{marginTop:8},children:"You can still create the API Product, but plan information may be incomplete."})]}),He&&(0,s.jsxs)(U.A,{severity:"warning",style:{marginBottom:16},children:[(0,s.jsx)("strong",{children:"Failed to load AuthPolicies:"})," ",He.message]}),We&&(0,s.jsxs)(U.A,{severity:"warning",style:{marginBottom:16},children:[(0,s.jsx)("strong",{children:"Failed to load RateLimitPolicies:"})," ",We.message]}),(0,s.jsx)(i.A,{className:A.sectionHeader,children:(0,s.jsx)(o.A,{variant:"subtitle1",children:(0,s.jsx)("strong",{children:"API product info"})})}),(0,s.jsxs)($.A,{container:!0,spacing:2,children:[(0,s.jsx)($.A,{item:!0,xs:6,children:(0,s.jsx)(D.A,{fullWidth:!0,label:"API product name",value:j,onChange:e=>(e=>{if(b(e),!f||f.match(/-[a-f0-9]{6}$/)){const t=`${e.toLowerCase().replace(/[^a-z0-9-]/g,"-").replace(/-+/g,"-").replace(/^-|-$/g,"")}-${Math.floor(16777215*Math.random()).toString(16).padStart(6,"0")}`;y(t),Pe((0,Y.o)(t))}})(e.target.value),placeholder:"My API",helperText:"Display name for your API product (shown to users)",margin:"normal",required:!0,disabled:xe,InputLabelProps:{classes:{asterisk:A.asterisk}}})}),(0,s.jsx)($.A,{item:!0,xs:6,children:(0,s.jsx)(D.A,{fullWidth:!0,label:"Kubernetes resource name",value:f,onChange:e=>{return t=e.target.value,y(t),void Pe((0,Y.o)(t));var t},placeholder:"my-api",helperText:be||"Auto-generated from product name. Only lowercase, numbers, and hyphens allowed.",error:!!be,margin:"normal",required:!0,disabled:xe,InputLabelProps:{classes:{asterisk:A.asterisk}}})}),(0,s.jsx)($.A,{item:!0,xs:6,children:(0,s.jsx)(D.A,{fullWidth:!0,label:"Version",value:k,onChange:e=>T(e.target.value),placeholder:"v1",helperText:"Give a version to your API product",margin:"normal",required:!0,disabled:xe,InputLabelProps:{classes:{asterisk:A.asterisk}}})}),(0,s.jsx)($.A,{item:!0,xs:6,children:(0,s.jsx)(D.A,{fullWidth:!0,label:"Tag",value:ne,onChange:e=>le(e.target.value),onKeyPress:e=>{"Enter"===e.key&&(e.preventDefault(),Qe())},placeholder:"Add tag",helperText:"Add a tag to your API product",margin:"normal",disabled:xe,InputProps:{endAdornment:ne?(0,s.jsx)(L.A,{position:"end",children:(0,s.jsx)(d.A,{size:"small",onClick:Qe,disabled:xe,children:(0,s.jsx)(p.A,{fontSize:"small"})})}):void 0}})}),ae.length>0&&(0,s.jsx)($.A,{item:!0,xs:12,children:(0,s.jsx)(i.A,{display:"flex",flexWrap:"wrap",children:ae.map(e=>(0,s.jsx)(r.A,{label:e,onDelete:xe?void 0:()=>{return t=e,void se(ae.filter(e=>e!==t));var t},size:"small",className:A.tagChip,disabled:xe},e))})}),(0,s.jsx)($.A,{item:!0,xs:12,children:(0,s.jsx)(D.A,{fullWidth:!0,label:"Description",value:P,onChange:e=>S(e.target.value),placeholder:"API description",margin:"normal",multiline:!0,rows:2,required:!0,disabled:xe,InputLabelProps:{classes:{asterisk:A.asterisk}}})})]}),(0,s.jsxs)(i.A,{className:A.sectionHeader,children:[(0,s.jsx)(o.A,{variant:"subtitle1",children:(0,s.jsx)("strong",{children:"Add API and Associate route"})}),(0,s.jsx)(H.Ay,{title:"Register an existing API and associate HTTPRoute for your API product",children:(0,s.jsx)(F.A,{className:A.infoIcon})})]}),(0,s.jsxs)($.A,{container:!0,spacing:2,children:[(0,s.jsx)($.A,{item:!0,xs:12,children:(0,s.jsx)(D.A,{fullWidth:!0,label:"OpenAPI Spec URL",value:he,onChange:e=>{return t=e.target.value,ve(t),void we((0,Y.q)(t));var t},placeholder:"https://api.example.com/openapi.json",helperText:Se||"Enter the full path to your API spec file",error:!!Se,margin:"normal",disabled:xe})}),(0,s.jsx)($.A,{item:!0,xs:12,children:(0,s.jsx)(D.A,{fullWidth:!0,label:"Documentation URL",value:pe,onChange:e=>me(e.target.value),placeholder:"https://docs.example.com/api",helperText:"Link to external documentation for this API",margin:"normal",disabled:xe})}),(0,s.jsx)($.A,{item:!0,xs:12,children:(0,s.jsxs)(D.A,{fullWidth:!0,select:!0,label:"HTTPRoute",value:ie,onChange:e=>re(e.target.value),margin:"normal",required:!0,helperText:ze?"Unable to load HTTPRoutes. Please retry.":"Select an HTTPRoute. APIProduct will be created in the same namespace.",error:!!ze,disabled:Re||xe||!!ze,InputLabelProps:{classes:{asterisk:A.asterisk}},SelectProps:{"data-testid":"httproute-select",MenuProps:{PaperProps:{style:{maxHeight:400}},anchorOrigin:{vertical:"bottom",horizontal:"left"},transformOrigin:{vertical:"top",horizontal:"left"},getContentAnchorEl:null}},children:[(0,s.jsx)(i.A,{px:2,pt:1,pb:1,style:{position:"sticky",top:0,zIndex:1},children:(0,s.jsx)(D.A,{fullWidth:!0,size:"small",placeholder:"Search...",value:Ce,onChange:e=>ke(e.target.value),onKeyDown:e=>e.stopPropagation(),onClick:e=>e.stopPropagation(),InputProps:{endAdornment:(0,s.jsx)(L.A,{position:"end",children:(0,s.jsxs)(D.A,{select:!0,size:"small",value:Ie,onChange:e=>Te(e.target.value),onKeyDown:e=>e.stopPropagation(),onClick:e=>e.stopPropagation(),style:{minWidth:120},variant:"standard",children:[(0,s.jsx)(M.A,{value:"name",children:"Name"}),(0,s.jsx)(M.A,{value:"namespace",children:"Namespace"}),(0,s.jsx)(M.A,{value:"planpolicy",children:"PlanPolicy"})]})})}})}),Re&&(0,s.jsx)(M.A,{value:"",children:"Loading..."}),ze&&(0,s.jsx)(M.A,{value:"",children:"Error loading routes"}),!Re&&!ze&&Ne&&0===Ne.length&&(0,s.jsx)(M.A,{value:"",children:"No HTTPRoutes available"}),!Re&&!ze&&Ne&&Ne.filter(e=>{if(!Ce)return!0;const t=e.metadata.namespace,a=e.metadata.name,s=Xe(t,a),n=Ce.toLowerCase();switch(Ie){case"name":return a.toLowerCase().includes(n);case"namespace":return t.toLowerCase().includes(n);case"planpolicy":return s.toLowerCase().includes(n);default:return!0}}).map(e=>{const t=e.metadata.namespace,a=e.metadata.name,n=Xe(t,a);return(0,s.jsx)(M.A,{value:`${t}/${a}`,children:(0,s.jsxs)(i.A,{children:[(0,s.jsx)(o.A,{variant:"body1",children:a}),(0,s.jsxs)(o.A,{variant:"caption",color:"textSecondary",children:["Associated PlanPolicy: ",n]})]})},`${t}/${a}`)})]})})]}),ie&&(0,s.jsxs)(s.Fragment,{children:[(0,s.jsxs)(i.A,{className:A.sectionHeader,children:[(0,s.jsx)(o.A,{variant:"subtitle1",children:(0,s.jsx)("strong",{children:"HTTPRoute policies"})}),(0,s.jsx)(H.Ay,{title:"Shows the associated policies and rate limit tiers for the selected HTTPRoute",children:(0,s.jsx)(F.A,{className:A.infoIcon})})]}),(0,s.jsx)(_.K,{planPolicy:Ze,authPolicy:Ge,rateLimitPolicy:Je,includeTopMargin:!1})]}),(0,s.jsxs)(i.A,{className:A.sectionHeader,children:[(0,s.jsx)(o.A,{variant:"subtitle1",children:(0,s.jsx)("strong",{children:"Lifecycle and Visibility"})}),(0,s.jsx)(H.Ay,{title:"Control the lifecycle state and catalog visibility of this API product",children:(0,s.jsx)(F.A,{className:A.infoIcon})})]}),(0,s.jsxs)($.A,{container:!0,spacing:2,children:[(0,s.jsx)($.A,{item:!0,xs:6,children:(0,s.jsxs)(D.A,{fullWidth:!0,select:!0,label:"Lifecycle",value:ee,onChange:e=>te(e.target.value),margin:"normal",helperText:"API lifecycle state",disabled:xe,children:[(0,s.jsx)(M.A,{value:"experimental",children:"Experimental"}),(0,s.jsx)(M.A,{value:"production",children:"Production"}),(0,s.jsx)(M.A,{value:"deprecated",children:"Deprecated"}),(0,s.jsx)(M.A,{value:"retired",children:"Retired"})]})}),(0,s.jsx)($.A,{item:!0,xs:6,children:(0,s.jsxs)(D.A,{fullWidth:!0,select:!0,label:"Publish Status",value:X,onChange:e=>Q(e.target.value),margin:"normal",helperText:"Controls catalog visibility (Draft = hidden from consumers)",disabled:xe,children:[(0,s.jsx)(M.A,{value:"Draft",children:"Draft"}),(0,s.jsx)(M.A,{value:"Published",children:"Published"})]})})]}),(0,s.jsxs)(i.A,{className:A.sectionHeader,children:[(0,s.jsx)(o.A,{variant:"subtitle1",children:(0,s.jsx)("strong",{children:"API Key approval"})}),(0,s.jsx)(H.Ay,{title:"Choose how API key requests are handled for this product",children:(0,s.jsx)(F.A,{className:A.infoIcon})})]}),(0,s.jsx)(W.A,{component:"fieldset",disabled:xe,children:(0,s.jsxs)(E.A,{row:!0,value:G,onChange:e=>J(e.target.value),children:[(0,s.jsx)(K.A,{value:"manual",control:(0,s.jsx)(O.A,{color:"primary"}),label:(0,s.jsxs)(i.A,{children:[(0,s.jsx)(o.A,{variant:"body2",children:"Need manual approval"}),(0,s.jsx)(o.A,{variant:"caption",color:"textSecondary",children:"Requires approval for requesting this API"})]})}),(0,s.jsx)(K.A,{value:"automatic",control:(0,s.jsx)(O.A,{color:"primary"}),label:(0,s.jsxs)(i.A,{children:[(0,s.jsx)(o.A,{variant:"body2",children:"Automatic"}),(0,s.jsx)(o.A,{variant:"caption",color:"textSecondary",children:"Keys are created without need to be approved"})]})})]})})]}),(0,s.jsxs)(B.A,{children:[(0,s.jsx)(c.A,{onClick:et,disabled:xe,children:"Cancel"}),(0,s.jsx)(c.A,{onClick:async()=>{Ae(""),fe(!0);try{if(!ie)throw new Error("Please select an HTTPRoute");const[e,t]=ie.split("/"),s=e,n={apiVersion:"devportal.kuadrant.io/v1alpha1",kind:"APIProduct",metadata:{name:f,namespace:s,labels:{lifecycle:ee}},spec:{displayName:j,description:P,version:k,approvalMode:G,publishStatus:X,tags:ae,targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:t,namespace:e},...oe||de?{contact:{...oe&&{email:oe},...de&&{team:de}}}:{},...pe||he?{documentation:{...pe&&{docsURL:pe},...he&&{openAPISpecURL:he}}}:{}}};await x.createApiProduct(n),a({namespace:s,name:f,displayName:j}),et()}catch(e){Ae(e instanceof Error?e.message:String(e))}finally{fe(!1)}},color:"primary",variant:"contained",disabled:xe||!f||!j||!P||!ie||tt,startIcon:xe?(0,s.jsx)(q.A,{size:16,color:"inherit"}):void 0,children:xe?"Creating...":"Create"})]})]})};var J=a(34955),X=a(46205),Q=a(26997),ee=a(63221),te=a(46299);const ae=a.p+"static/empty-state-illustration.7e3ad5a9..png",se=(0,l.A)(e=>({container:{display:"flex",height:"100%",minHeight:400},tableContainer:{flex:1,overflow:"auto",padding:10},emptyState:{display:"flex",alignItems:"center",justifyContent:"center",padding:e.spacing(6),minHeight:400},emptyStateContent:{display:"flex",alignItems:"center",gap:e.spacing(6),maxWidth:900},emptyStateText:{flex:1},emptyStateTitle:{marginBottom:e.spacing(2)},emptyStateDescription:{marginBottom:e.spacing(3),color:e.palette.text.secondary},emptyStateImage:{maxWidth:400,height:"auto"}})),ne=()=>{const e=se(),t=(0,C.useApi)(I.s),a=(0,C.useApi)(C.alertApiRef),l=(0,C.useApi)(C.identityApiRef),T=(0,C.useApi)(k.v),[N,R]=(0,n.useState)(""),[z,$]=(0,n.useState)(!1),[D,L]=(0,n.useState)(!1),[H,M]=(0,n.useState)(0),[W,E]=(0,n.useState)(!1),[K,O]=(0,n.useState)(null),[B,q]=(0,n.useState)(null),[F,U]=(0,n.useState)(!1),[_,Y]=(0,n.useState)(null),[V,Z]=(0,n.useState)({status:[],lifecycle:[],policy:[],route:[],namespace:[],tags:[],authentication:[]}),{allowed:ne,loading:le,error:ie}=(0,X.l)(J.FL),{allowed:re,loading:oe}=(0,X.l)(J.EM),{allowed:ce,loading:de,error:ue}=(0,X.l)(J.R_),{allowed:pe}=(0,X.l)(J.U3),{allowed:me}=(0,X.l)(J.v_),he=oe||de,{allowed:ve,loading:ge,error:Ae}=(0,X.l)(J.J);(0,w.A)(async()=>{const e=await l.getBackstageIdentity();R(e.userEntityRef)},[l]);const{value:xe,loading:fe,error:ye}=(0,w.A)(async()=>t.getApiProducts(),[t,H]),{value:je,loading:be,error:Pe}=(0,w.A)(async()=>ve?t.getPlanPolicies():{items:[]},[t,H,ve]),{value:Se}=(0,w.A)(async()=>(await T.getEntities({filter:{kind:"API","metadata.labels.kuadrant.io/synced":"true"},fields:["metadata.name","spec.lifecycle"]})).items,[T,H]),we=(0,n.useMemo)(()=>{const e=new Map;if(Se)for(const a of Se){var t;const s=null===(t=a.spec)||void 0===t?void 0:t.lifecycle;s&&e.set(a.metadata.name,s)}return e},[Se]),Ce=(0,n.useCallback)(e=>we.get(e.metadata.name),[we]),ke=(0,n.useCallback)(e=>{var t;if(!(null==je?void 0:je.items))return null;const a=null===(t=e.spec)||void 0===t?void 0:t.targetRef;if(!a)return null;const s=je.items.find(t=>{const s=t.spec.targetRef;return"HTTPRoute"===(null==s?void 0:s.kind)&&(null==s?void 0:s.name)===a.name&&(!(null==s?void 0:s.namespace)||(null==s?void 0:s.namespace)===(a.namespace||e.metadata.namespace))});return(null==s?void 0:s.metadata.name)||null},[je]),Ie=(0,n.useCallback)(e=>{var t,a;const s=(null===(a=e.status)||void 0===a||null===(t=a.discoveredAuthScheme)||void 0===t?void 0:t.authentication)||{},n=Object.values(s),l=[];return n.some(e=>e.hasOwnProperty("apiKey"))&&l.push("API Key"),n.some(e=>e.hasOwnProperty("jwt"))&&l.push("OIDC"),0===l.length&&l.push("Unknown"),l},[]),Te=fe||be||le||he||ge,Ne=ye||Pe,Re=ie||ue||Ae,ze=(0,n.useMemo)(()=>{const e=(null==xe?void 0:xe.items)||[];return ne||pe||me?e:e.filter(e=>{var t;return"Published"===((null===(t=e.spec)||void 0===t?void 0:t.publishStatus)||"Draft")})},[xe,ne,pe,me]),$e=(0,n.useMemo)(()=>{const e={Draft:0,Published:0},t=new Map,a=new Map,s=new Map,n=new Map,l=new Map,i=new Map;ze.forEach(r=>{var o,c,d,u;const p=(null===(o=r.spec)||void 0===o?void 0:o.publishStatus)||"Draft";e[p]++;const m=Ce(r);m&&t.set(m,(t.get(m)||0)+1);const h=ke(r)||"N/A";a.set(h,(a.get(h)||0)+1);const v=(null===(d=r.spec)||void 0===d||null===(c=d.targetRef)||void 0===c?void 0:c.name)||"unknown";s.set(v,(s.get(v)||0)+1);const g=r.metadata.namespace;n.set(g,(n.get(g)||0)+1),((null===(u=r.spec)||void 0===u?void 0:u.tags)||[]).forEach(e=>{l.set(e,(l.get(e)||0)+1)}),Ie(r).forEach(e=>{i.set(e,(i.get(e)||0)+1)})});const r=[{id:"status",title:"Publish Status",options:[{value:"Draft",label:"Draft",count:e.Draft},{value:"Published",label:"Published",count:e.Published}]},{id:"lifecycle",title:"Lifecycle",options:Array.from(t.entries()).map(([e,t])=>({value:e,label:e.charAt(0).toUpperCase()+e.slice(1),count:t}))},{id:"authentication",title:"Authentication",options:Array.from(i.entries()).map(([e,t])=>({value:e,label:e,count:t}))},{id:"route",title:"Route",options:Array.from(s.entries()).map(([e,t])=>({value:e,label:e,count:t})),collapsed:s.size>5},{id:"namespace",title:"Namespace",options:Array.from(n.entries()).map(([e,t])=>({value:e,label:e,count:t})),collapsed:n.size>5},{id:"tags",title:"Tags",options:Array.from(l.entries()).map(([e,t])=>({value:e,label:e,count:t})),collapsed:l.size>5}];return ve&&r.splice(2,0,{id:"policy",title:"Policy",options:Array.from(a.entries()).map(([e,t])=>({value:e,label:e,count:t})),collapsed:a.size>5}),r},[ze,ke,Ie,Ce,ve]),De=(0,n.useMemo)(()=>ze.filter(e=>{if(V.status.length>0){var t;const a=(null===(t=e.spec)||void 0===t?void 0:t.publishStatus)||"Draft";if(!V.status.includes(a))return!1}if(V.lifecycle&&V.lifecycle.length>0){const t=Ce(e);if(!t||!V.lifecycle.includes(t))return!1}if(V.authentication.length>0){const t=Ie(e);if(!V.authentication.some(e=>t.includes(e)))return!1}if(V.policy.length>0){const t=ke(e)||"N/A";if(!V.policy.includes(t))return!1}if(V.route.length>0){var a,s;const t=(null===(s=e.spec)||void 0===s||null===(a=s.targetRef)||void 0===a?void 0:a.name)||"unknown";if(!V.route.includes(t))return!1}if(V.namespace.length>0&&!V.namespace.includes(e.metadata.namespace))return!1;if(V.tags.length>0){var n;const t=(null===(n=e.spec)||void 0===n?void 0:n.tags)||[];if(!V.tags.some(e=>t.includes(e)))return!1}return!0}),[ze,V,ke,Ie,Ce]),Le=[{title:"Name",field:"spec.displayName",render:e=>{var t,a;const n=null!==(a=null===(t=e.spec)||void 0===t?void 0:t.displayName)&&void 0!==a?a:e.metadata.name;return(0,s.jsx)(x.N_,{to:`/kuadrant/api-products/${e.metadata.namespace}/${e.metadata.name}`,children:(0,s.jsx)("strong",{children:n})})},customFilterAndSearch:(e,t)=>{var a;return((null===(a=t.spec)||void 0===a?void 0:a.displayName)||t.metadata.name||"").toLowerCase().includes(e.toLowerCase())}},{title:"Version",field:"spec.version",render:e=>{var t;return(null===(t=e.spec)||void 0===t?void 0:t.version)||"-"}},{title:"Route",field:"spec.targetRef.name",render:e=>{var t,a;return(null===(a=e.spec)||void 0===a||null===(t=a.targetRef)||void 0===t?void 0:t.name)||"-"}},...ve?[{title:"Policy",field:"policy",render:e=>ke(e)||"N/A"}]:[],{title:"Tags",field:"spec.tags",render:e=>{var t;const a=(null===(t=e.spec)||void 0===t?void 0:t.tags)||[];return 0===a.length?"-":(0,s.jsx)(i.A,{display:"flex",style:{gap:4,flexWrap:"wrap"},children:a.map(e=>(0,s.jsx)(r.A,{label:e,size:"small",variant:"outlined"},e))})}},{title:"Status",field:"spec.publishStatus",render:e=>{var t;const a=(null===(t=e.spec)||void 0===t?void 0:t.publishStatus)||"Draft";return(0,s.jsx)(r.A,{label:a,size:"small",color:"Published"===a?"primary":"default"})}},{title:"Lifecycle",render:e=>{const t=Ce(e);return t?(0,s.jsx)(r.A,{label:t.charAt(0).toUpperCase()+t.slice(1),size:"small",style:(0,te.ee)(t)}):"-"}},{title:"Authentication",field:"status.discoveredAuthScheme",render:e=>{var t,a;const n=(null===(a=e.status)||void 0===a||null===(t=a.discoveredAuthScheme)||void 0===t?void 0:t.authentication)||{},l=Object.values(n),c=l.some(e=>e.hasOwnProperty("apiKey")),d=l.some(e=>e.hasOwnProperty("jwt"));return c||d?(0,s.jsxs)(i.A,{display:"flex",style:{gap:4},children:[c&&(0,s.jsx)(r.A,{icon:(0,s.jsx)(v.A,{}),label:"API Key",size:"small",color:"primary"}),d&&(0,s.jsx)(r.A,{icon:(0,s.jsx)(g.A,{}),label:"OIDC",size:"small",color:"secondary"})]}):(0,s.jsx)(o.A,{variant:"body2",style:{fontStyle:"italic"},children:"unknown"})}},{title:"Namespace",field:"metadata.namespace"},{title:"Actions",field:"actions",filtering:!1,render:e=>{var n,l,r;const o=(null===(l=e.metadata)||void 0===l||null===(n=l.annotations)||void 0===n?void 0:n["backstage.io/owner"])===N,u=me||pe&&o,p=ce||re&&o,v="Published"===(null===(r=e.spec)||void 0===r?void 0:r.publishStatus);return(0,s.jsxs)(i.A,{display:"flex",alignItems:"center",style:{gap:4},children:[u&&(0,s.jsx)(c.A,{size:"small",color:"primary",onClick:()=>(async e=>{var s,n;const l=e.metadata.namespace,i=e.metadata.name,r=(null===(s=e.spec)||void 0===s?void 0:s.displayName)||i,o="Published"===((null===(n=e.spec)||void 0===n?void 0:n.publishStatus)||"Draft")?"Draft":"Published";if("Published"===o&&"retired"===Ce(e))return void a.post({message:"Cannot publish a retired API product. Please change the lifecycle status first.",severity:"error",display:"transient"});const c={spec:{publishStatus:o}};try{await t.updateApiProduct(l,i,c),M(e=>e+1),a.post({message:`"${r}" ${"Published"===o?"published":"unpublished"} successfully`,severity:"success",display:"transient"})}catch(e){const t=e instanceof Error?e.message:"unknown error occurred";a.post({message:`Failed to update publish status: ${t}`,severity:"error",display:"transient"})}})(e),style:{marginRight:4,textTransform:"none"},children:v?"Unpublish":"Publish"}),u&&(0,s.jsx)(d.A,{size:"small",onClick:()=>{return t=e.metadata.namespace,a=e.metadata.name,q({namespace:t,name:a}),void L(!0);var t,a},title:"Edit API Product",children:(0,s.jsx)(h.A,{fontSize:"small"})}),p&&(0,s.jsx)(d.A,{size:"small",onClick:()=>(async(e,s)=>{O({namespace:e,name:s}),Y(null);try{const a=(await t.getAllRequestsByApiProduct(s,e)).items||[],n=a.filter(e=>{var t;return"Approved"===(null===(t=e.status)||void 0===t?void 0:t.phase)}).length;Y({requests:a.length,secrets:n})}catch(e){const t=e instanceof Error?e.message:"unknown error occurred";a.post({message:`Failed to delete access request: ${t}`,severity:"error",display:"transient"})}finally{E(!0)}})(e.metadata.namespace,e.metadata.name),title:"Delete API Product",children:(0,s.jsx)(m.A,{fontSize:"small"})})]})}}];return(0,s.jsxs)(f.Y,{themeId:"tool",children:[(0,s.jsx)(y.Y,{title:"API Products",subtitle:"Manage API products for Kubernetes",children:(0,s.jsx)(j.Y,{children:"Manage API products and plan policies"})}),(0,s.jsxs)(b.U,{children:[Te&&(0,s.jsx)(i.A,{p:2,children:[...Array(5)].map((e,t)=>(0,s.jsx)(i.A,{p:2,children:(0,s.jsx)(u.A,{variant:"text",width:"100%"})},t))}),Ne&&(0,s.jsx)(P._,{error:Ne}),Re&&(0,s.jsxs)(i.A,{p:2,children:[(0,s.jsxs)(o.A,{color:"error",children:["unable to check permissions: ",Re.message]}),(0,s.jsxs)(o.A,{variant:"body2",color:"textSecondary",children:["permission:"," ",ie?"kuadrant.apiproduct.create":ue?"kuadrant.apiproduct.delete":Ae?"kuadrant.planpolicy.list":"unknown"]}),(0,s.jsx)(o.A,{variant:"body2",color:"textSecondary",children:"please try again or contact your administrator"})]}),!Te&&!Ne&&!Re&&0===ze.length&&(0,s.jsx)(i.A,{className:e.emptyState,children:(0,s.jsxs)(i.A,{className:e.emptyStateContent,children:[(0,s.jsxs)(i.A,{className:e.emptyStateText,children:[(0,s.jsx)(o.A,{variant:"h4",className:e.emptyStateTitle,children:"API Product"}),(0,s.jsx)(o.A,{variant:"body1",className:e.emptyStateDescription,children:"Create API product by registering existing API, associate route and policy"}),ne&&(0,s.jsx)(c.A,{variant:"contained",color:"primary",startIcon:(0,s.jsx)(p.A,{}),onClick:()=>$(!0),children:"Create API Product"})]}),(0,s.jsx)("img",{src:ae,alt:"API Product illustration",className:e.emptyStateImage})]})}),!Te&&!Ne&&!Re&&ze.length>0&&(0,s.jsxs)(i.A,{className:e.container,children:[(0,s.jsx)(A.Z,{sections:$e,filters:V,onChange:Z}),(0,s.jsxs)(i.A,{className:e.tableContainer,children:[(0,s.jsx)(i.A,{display:"flex",justifyContent:"flex-end",mb:2,children:ne&&(0,s.jsx)(c.A,{variant:"contained",color:"primary",size:"small",startIcon:(0,s.jsx)(p.A,{}),onClick:()=>$(!0),children:"Create API Product"})}),0===De.length?(0,s.jsx)(i.A,{p:4,textAlign:"center",children:(0,s.jsx)(o.A,{variant:"body1",color:"textSecondary",children:"No API products match the selected filters."})}):(0,s.jsx)(S.X,{options:{paging:De.length>10,pageSize:20,search:!0,filtering:!1,debounceInterval:300,toolbar:!0,emptyRowsWhenPaging:!1},columns:Le,data:De})]})]}),(0,s.jsx)(G,{open:z,onClose:()=>$(!1),onSuccess:e=>{M(e=>e+1),a.post({message:`"${e.displayName}" created successfully`,severity:"success",display:"transient"})}}),(0,s.jsx)(Q.C,{open:D,onClose:()=>L(!1),onSuccess:()=>{M(e=>e+1);const e=(null==B?void 0:B.name)||"API Product";a.post({message:`"${e}" updated successfully`,severity:"success",display:"transient"})},namespace:(null==B?void 0:B.namespace)||"",name:(null==B?void 0:B.name)||""}),(0,s.jsx)(ee.K,{open:W,title:"Delete API Product",description:_?`Deleting "${null==K?void 0:K.name}" will also remove:\n\n• ${_.requests} API Key(s)\n• ${_.secrets} API Key Secret(s)\n\nThis action cannot be undone.`:`Deleting "${null==K?void 0:K.name}" will also remove all associated API Keys and Secrets.\nThis action cannot be undone.`,confirmText:null==K?void 0:K.name,severity:"high",deleting:F,onConfirm:async()=>{if(K){U(!0);try{await t.deleteApiProduct(K.namespace,K.name);const e=(null==K?void 0:K.name)||"API Product";M(e=>e+1),a.post({message:`"${e}" deleted successfully`,severity:"success",display:"transient"})}catch(e){const t=e instanceof Error?e.message:"unknown error occurred";a.post({message:`Failed to delete API Product: ${t}`,severity:"error",display:"transient"})}finally{U(!1),E(!1),O(null)}}},onCancel:()=>{E(!1),O(null)}})]})]})},le=()=>(0,s.jsx)(T.B,{permission:J.vs,errorMessage:"you don't have permission to view the Kuadrant page",children:(0,s.jsx)(ne,{})})},97859:(e,t,a)=>{a.d(t,{K5:()=>s,dO:()=>l,lK:()=>n});const s=e=>{if(!e)return[];const t=[];if(e.daily&&t.push(`${e.daily} per day`),e.weekly&&t.push(`${e.weekly} per week`),e.monthly&&t.push(`${e.monthly} per month`),e.yearly&&t.push(`${e.yearly} per year`),e.custom)for(const a of e.custom)t.push(`${a.limit} per ${a.window}`);return t},n=e=>s(e).join(", "),l=(e,t,a)=>e&&e.find(e=>{const s=e.spec.targetRef;var n;const l=null!==(n=null==s?void 0:s.namespace)&&void 0!==n?n:e.metadata.namespace;return"HTTPRoute"===(null==s?void 0:s.kind)&&(null==s?void 0:s.name)===a&&l===t})||null}}]);
|
|
2
|
+
//# sourceMappingURL=2821.7a436d84.chunk.js.map
|