@kuadrant/kuadrant-backstage-plugin-frontend 0.0.1-test.1-1593c3ec

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +491 -0
  2. package/dist/components/ApiAccessCard/ApiAccessCard.esm.js +42 -0
  3. package/dist/components/ApiAccessCard/ApiAccessCard.esm.js.map +1 -0
  4. package/dist/components/ApiAccessCard/index.esm.js +2 -0
  5. package/dist/components/ApiAccessCard/index.esm.js.map +1 -0
  6. package/dist/components/ApiKeyManagementTab/ApiKeyManagementTab.esm.js +441 -0
  7. package/dist/components/ApiKeyManagementTab/ApiKeyManagementTab.esm.js.map +1 -0
  8. package/dist/components/ApiKeyManagementTab/index.esm.js +2 -0
  9. package/dist/components/ApiKeyManagementTab/index.esm.js.map +1 -0
  10. package/dist/components/ApiProductInfoCard/ApiProductInfoCard.esm.js +47 -0
  11. package/dist/components/ApiProductInfoCard/ApiProductInfoCard.esm.js.map +1 -0
  12. package/dist/components/ApiProductInfoCard/index.esm.js +2 -0
  13. package/dist/components/ApiProductInfoCard/index.esm.js.map +1 -0
  14. package/dist/components/ApprovalQueueCard/ApprovalQueueCard.esm.js +349 -0
  15. package/dist/components/ApprovalQueueCard/ApprovalQueueCard.esm.js.map +1 -0
  16. package/dist/components/ApprovalQueueCard/index.esm.js +2 -0
  17. package/dist/components/ApprovalQueueCard/index.esm.js.map +1 -0
  18. package/dist/components/CreateAPIProductDialog/CreateAPIProductDialog.esm.js +289 -0
  19. package/dist/components/CreateAPIProductDialog/CreateAPIProductDialog.esm.js.map +1 -0
  20. package/dist/components/KuadrantPage/KuadrantPage.esm.js +198 -0
  21. package/dist/components/KuadrantPage/KuadrantPage.esm.js.map +1 -0
  22. package/dist/components/KuadrantPage/index.esm.js +2 -0
  23. package/dist/components/KuadrantPage/index.esm.js.map +1 -0
  24. package/dist/components/PermissionGate/PermissionGate.esm.js +33 -0
  25. package/dist/components/PermissionGate/PermissionGate.esm.js.map +1 -0
  26. package/dist/components/PlanPolicyDetailPage/PlanPolicyDetailPage.esm.js +89 -0
  27. package/dist/components/PlanPolicyDetailPage/PlanPolicyDetailPage.esm.js.map +1 -0
  28. package/dist/components/PlanPolicyDetailPage/index.esm.js +2 -0
  29. package/dist/components/PlanPolicyDetailPage/index.esm.js.map +1 -0
  30. package/dist/hooks/useUserRole.esm.js +49 -0
  31. package/dist/hooks/useUserRole.esm.js.map +1 -0
  32. package/dist/index.d.ts +31 -0
  33. package/dist/index.esm.js +6 -0
  34. package/dist/index.esm.js.map +1 -0
  35. package/dist/plugin.esm.js +68 -0
  36. package/dist/plugin.esm.js.map +1 -0
  37. package/dist/routes.esm.js +13 -0
  38. package/dist/routes.esm.js.map +1 -0
  39. package/package.json +85 -0
package/README.md ADDED
@@ -0,0 +1,491 @@
1
+ # Kuadrant Plugin for Backstage/RHDH
2
+
3
+ Backstage plugin for Kuadrant - enables developer portals for API access management using Kuadrant Gateway API primitives.
4
+
5
+ ## Features
6
+
7
+ - **API Access Management**: Request API keys for Kuadrant-protected APIs
8
+ - **Tiered Plans**: Support for multiple access tiers with different rate limits via PlanPolicy
9
+ - **User Identity**: Integrates with Backstage identity API for user-specific API keys
10
+ - **Policy Visibility**: View AuthPolicies, RateLimitPolicies, and PlanPolicies
11
+ - **API Key Management**: View, create, and delete API keys with show/hide toggles
12
+ - **Approval Workflow**: Platform engineers can approve/reject API access requests
13
+ - **APIProduct Integration**: Sync APIProduct custom resources from Kubernetes
14
+
15
+ ## Prerequisites
16
+
17
+ - Backstage/RHDH instance (v1.30+)
18
+ - Kubernetes cluster with Kuadrant Gateway API and CRDs installed
19
+ - Backend plugin (`@internal/plugin-kuadrant-backend`) configured and running
20
+
21
+ ## Installation
22
+
23
+ ### 1. Copy Plugins to Your Instance
24
+
25
+ Copy both plugins to your Backstage instance:
26
+ ```bash
27
+ # Frontend plugin
28
+ cp -r plugins/kuadrant /path/to/your/backstage/plugins/
29
+
30
+ # Backend plugin
31
+ cp -r plugins/kuadrant-backend /path/to/your/backstage/plugins/
32
+ ```
33
+
34
+ ### 2. Install Dependencies
35
+
36
+ Add to your root `package.json` workspaces if needed, then install:
37
+ ```bash
38
+ yarn install
39
+ ```
40
+
41
+ ### 3. Configure Backend
42
+
43
+ In `packages/backend/src/index.ts`, add the backend plugins:
44
+
45
+ ```typescript
46
+ // Kuadrant backend plugins
47
+ backend.add(import('@internal/plugin-kuadrant-backend'));
48
+ backend.add(import('@internal/plugin-kuadrant-backend/alpha'));
49
+ ```
50
+
51
+ The backend plugin provides:
52
+ - HTTP API endpoints at `/api/kuadrant/*`
53
+ - APIProduct entity provider for catalog integration
54
+
55
+ ### 4. Configure Frontend
56
+
57
+ #### 4.1. Add Plugin Dependency
58
+
59
+ In `packages/app/package.json`:
60
+ ```json
61
+ {
62
+ "dependencies": {
63
+ "@internal/plugin-kuadrant": "0.1.0"
64
+ }
65
+ }
66
+ ```
67
+
68
+ #### 4.2. Add Route
69
+
70
+ In `packages/app/src/components/AppBase/AppBase.tsx`:
71
+
72
+ ```typescript
73
+ import { KuadrantPage } from '@internal/plugin-kuadrant';
74
+
75
+ // Inside FlatRoutes:
76
+ <Route path="/kuadrant" element={<KuadrantPage />} />
77
+ ```
78
+
79
+ #### 4.3. Add Menu Item
80
+
81
+ In `packages/app/src/consts.ts`:
82
+
83
+ ```typescript
84
+ export const DefaultMainMenuItems = {
85
+ menuItems: {
86
+ // ... existing items
87
+ 'default.kuadrant': {
88
+ title: 'Kuadrant',
89
+ icon: 'extension',
90
+ to: 'kuadrant',
91
+ priority: 55,
92
+ },
93
+ },
94
+ };
95
+ ```
96
+
97
+ #### 4.4. Add Entity Page Components
98
+
99
+ In `packages/app/src/components/catalog/EntityPage/defaultTabs.tsx`:
100
+
101
+ ```typescript
102
+ // Add imports
103
+ import {
104
+ EntityKuadrantApiKeysContent,
105
+ EntityKuadrantApiProductInfoContent,
106
+ } from '@internal/plugin-kuadrant';
107
+
108
+ // Add to defaultTabs object:
109
+ export const defaultTabs = {
110
+ // ... existing tabs
111
+ '/api-keys': {
112
+ title: 'API Keys',
113
+ mountPoint: 'entity.page.api-keys',
114
+ },
115
+ '/api-product-info': {
116
+ title: 'API Product Info',
117
+ mountPoint: 'entity.page.api-product-info',
118
+ },
119
+ };
120
+
121
+ // Add to tabRules object:
122
+ export const tabRules = {
123
+ // ... existing rules
124
+ '/api-keys': {
125
+ if: isKind('api'),
126
+ },
127
+ '/api-product-info': {
128
+ if: (entity: Entity) => entity.kind === 'APIProduct',
129
+ },
130
+ };
131
+
132
+ // Add to tabChildren object:
133
+ export const tabChildren = {
134
+ // ... existing children
135
+ '/api-keys': {
136
+ children: <EntityKuadrantApiKeysContent />,
137
+ },
138
+ '/api-product-info': {
139
+ children: <EntityKuadrantApiProductInfoContent />,
140
+ },
141
+ };
142
+ ```
143
+
144
+ #### 4.5. Add API Access Card to Overview (Optional)
145
+
146
+ In `packages/app/src/components/catalog/EntityPage/OverviewTabContent.tsx`:
147
+
148
+ ```typescript
149
+ import { EntityKuadrantApiAccessCard } from '@internal/plugin-kuadrant';
150
+
151
+ // In the EntitySwitch.Case for isKind('api'), add:
152
+ <Grid
153
+ item
154
+ sx={{
155
+ gridColumn: {
156
+ lg: '5 / -1',
157
+ md: '7 / -1',
158
+ xs: '1 / -1',
159
+ },
160
+ }}
161
+ >
162
+ <EntityKuadrantApiAccessCard />
163
+ </Grid>
164
+ ```
165
+
166
+ ### 5. Configure Catalog
167
+
168
+ Update `app-config.yaml` to allow APIProduct entities:
169
+
170
+ ```yaml
171
+ catalog:
172
+ rules:
173
+ - allow: [Component, System, Group, Resource, Location, Template, API, APIProduct]
174
+ ```
175
+
176
+ ### 6. Configure Kubernetes Access
177
+
178
+ The backend plugin uses `@kubernetes/client-node` which supports multiple authentication methods:
179
+
180
+ #### Production (Explicit Cluster Configuration)
181
+
182
+ For production deployments, configure explicit cluster access in `app-config.yaml`:
183
+
184
+ ```yaml
185
+ kubernetes:
186
+ clusterLocatorMethods:
187
+ - type: config
188
+ clusters:
189
+ - name: production
190
+ url: https://your-k8s-cluster-url
191
+ authProvider: serviceAccount
192
+ serviceAccountToken: ${K8S_CLUSTER_TOKEN}
193
+ skipTLSVerify: false # set to true only for development
194
+ ```
195
+
196
+ Environment variables:
197
+ - `K8S_CLUSTER_TOKEN` - Service account token for cluster access
198
+
199
+ **Required RBAC permissions:**
200
+
201
+ ```yaml
202
+ apiVersion: v1
203
+ kind: ServiceAccount
204
+ metadata:
205
+ name: rhdh-kuadrant
206
+ namespace: rhdh
207
+ ---
208
+ apiVersion: rbac.authorization.k8s.io/v1
209
+ kind: ClusterRole
210
+ metadata:
211
+ name: rhdh-kuadrant
212
+ rules:
213
+ # APIProduct and APIKeyRequest CRDs
214
+ - apiGroups: ["extensions.kuadrant.io"]
215
+ resources: ["apiproducts", "apikeyrequests"]
216
+ verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
217
+ # PlanPolicy CRDs
218
+ - apiGroups: ["kuadrant.io"]
219
+ resources: ["planpolicies"]
220
+ verbs: ["get", "list", "watch"]
221
+ # Secrets for API keys
222
+ - apiGroups: [""]
223
+ resources: ["secrets"]
224
+ verbs: ["get", "list", "create", "delete"]
225
+ ---
226
+ apiVersion: rbac.authorization.k8s.io/v1
227
+ kind: ClusterRoleBinding
228
+ metadata:
229
+ name: rhdh-kuadrant
230
+ roleRef:
231
+ apiGroup: rbac.authorization.k8s.io
232
+ kind: ClusterRole
233
+ name: rhdh-kuadrant
234
+ subjects:
235
+ - kind: ServiceAccount
236
+ name: rhdh-kuadrant
237
+ namespace: rhdh
238
+ ```
239
+
240
+ To get the service account token:
241
+ ```bash
242
+ kubectl create token rhdh-kuadrant -n rhdh --duration=8760h
243
+ ```
244
+
245
+ #### In-Cluster (Automatic)
246
+
247
+ When RHDH runs inside Kubernetes without explicit configuration, it automatically uses the service account mounted at `/var/run/secrets/kubernetes.io/serviceaccount/`. Ensure the pod's service account has the RBAC permissions listed above.
248
+
249
+ #### Local Development
250
+
251
+ For local development, the plugin automatically uses your kubeconfig file (`~/.kube/config`). No configuration needed in `app-config.local.yaml`.
252
+
253
+ Verify access:
254
+ ```bash
255
+ kubectl config current-context # verify cluster
256
+ kubectl get apiproducts -A # test access
257
+ ```
258
+
259
+ ## Configuring API Entities
260
+
261
+ To enable Kuadrant features for an API entity, add annotations:
262
+
263
+ ```yaml
264
+ apiVersion: backstage.io/v1alpha1
265
+ kind: API
266
+ metadata:
267
+ name: toystore-api
268
+ annotations:
269
+ # required: name of the gateway api httproute resource
270
+ kuadrant.io/httproute: toystore
271
+
272
+ # required: kubernetes namespace where the httproute exists
273
+ kuadrant.io/namespace: toystore
274
+
275
+ # optional: gateway name for reference
276
+ kuadrant.io/gateway: external
277
+ spec:
278
+ type: openapi
279
+ lifecycle: production
280
+ owner: team-a
281
+ ```
282
+
283
+ ### Annotation Reference
284
+
285
+ | Annotation | Required | Description | Example |
286
+ |-----------|----------|-------------|---------|
287
+ | `kuadrant.io/httproute` | yes | Name of the Gateway API HTTPRoute resource | `toystore` |
288
+ | `kuadrant.io/namespace` | yes | Kubernetes namespace containing the HTTPRoute | `toystore` |
289
+ | `kuadrant.io/gateway` | no | Gateway name for reference/display | `external` |
290
+
291
+ ## Usage
292
+
293
+ ### For API Consumers
294
+
295
+ 1. Navigate to an API entity in the catalog
296
+ 2. Click the "API Keys" tab
297
+ 3. Click "Request API Access"
298
+ 4. Select a plan tier (bronze, silver, gold) and provide use case
299
+ 5. Wait for approval from platform engineers
300
+ 6. Once approved, your API key will appear in the API Keys tab
301
+
302
+ ### For Platform Engineers
303
+
304
+ 1. Navigate to the Kuadrant page from the sidebar menu
305
+ 2. View "Pending API Key Requests" card
306
+ 3. Review each request with details:
307
+ - Requester information
308
+ - API name and namespace
309
+ - Requested plan tier
310
+ - Use case justification
311
+ 4. Approve or reject with optional comments
312
+ 5. API keys are automatically created in Kubernetes upon approval
313
+
314
+ ### For API Owners
315
+
316
+ 1. Navigate to the Kuadrant page
317
+ 2. View all API products synced from Kubernetes
318
+ 3. Create new API products with:
319
+ - Display name and description
320
+ - Multiple plan tiers with rate limits
321
+ - Associated PlanPolicy references
322
+ - Contact information and documentation links
323
+ 4. API products automatically sync to Backstage catalog as APIProduct entities
324
+
325
+ ## Components
326
+
327
+ ### Pages
328
+
329
+ - **`KuadrantPage`** - Main page showing API products list and approval queue
330
+
331
+ ### Entity Content Components
332
+
333
+ - **`EntityKuadrantApiKeysContent`** - Full API keys management tab for API entities
334
+ - **`EntityKuadrantApiProductInfoContent`** - APIProduct details and plan information tab
335
+ - **`EntityKuadrantApiAccessCard`** - Quick API key request card for API entity overview
336
+
337
+ ### Other Components
338
+
339
+ - **`ApprovalQueueCard`** - Displays pending API key requests for platform engineers
340
+ - **`CreateAPIProductDialog`** - Dialog for creating new API products
341
+
342
+ ### Hooks
343
+
344
+ - **`useUserRole()`** - Determines user role based on Backstage groups:
345
+ - Platform Engineer: member of `platform-engineers` or `platform-admins`
346
+ - API Owner: member of `api-owners` or `app-developers`
347
+ - API Consumer: member of `api-consumers`
348
+
349
+ ## Kubernetes Resources
350
+
351
+ The plugin creates and manages Kubernetes custom resources:
352
+
353
+ ### APIKeyRequest
354
+
355
+ Created when users request API access:
356
+
357
+ ```yaml
358
+ apiVersion: extensions.kuadrant.io/v1alpha1
359
+ kind: APIKeyRequest
360
+ metadata:
361
+ name: guest-toystore-abc123
362
+ namespace: toystore
363
+ spec:
364
+ apiName: toystore
365
+ apiNamespace: toystore
366
+ planTier: silver
367
+ requestedAt: "2025-10-29T08:14:49.412Z"
368
+ requestedBy:
369
+ userId: guest
370
+ email: user@example.com
371
+ useCase: "Testing API integration"
372
+ ```
373
+
374
+ ### API Key Secrets
375
+
376
+ Created upon approval:
377
+
378
+ ```yaml
379
+ apiVersion: v1
380
+ kind: Secret
381
+ metadata:
382
+ name: user-toystore-1234567890
383
+ namespace: toystore
384
+ labels:
385
+ app: toystore # Matches AuthPolicy selector
386
+ annotations:
387
+ secret.kuadrant.io/user-id: john
388
+ secret.kuadrant.io/plan-id: silver
389
+ type: Opaque
390
+ data:
391
+ api_key: <base64-encoded-key>
392
+ ```
393
+
394
+ ### APIProduct
395
+
396
+ Synced from Kubernetes to Backstage catalog:
397
+
398
+ ```yaml
399
+ apiVersion: extensions.kuadrant.io/v1alpha1
400
+ kind: APIProduct
401
+ metadata:
402
+ name: toystore-api
403
+ namespace: toystore
404
+ spec:
405
+ displayName: Toystore API
406
+ description: Simple toy store API for demonstration
407
+ version: v1
408
+ plans:
409
+ - tier: gold
410
+ description: Premium access
411
+ limits:
412
+ daily: 100
413
+ - tier: silver
414
+ description: Standard access
415
+ limits:
416
+ daily: 50
417
+ planPolicyRef:
418
+ name: toystore-plans
419
+ namespace: toystore
420
+ contact:
421
+ team: platform-team
422
+ email: platform@example.com
423
+ ```
424
+
425
+ ## Development
426
+
427
+ ### Running Locally
428
+
429
+ ```bash
430
+ # Frontend plugin
431
+ cd plugins/kuadrant
432
+ yarn start
433
+
434
+ # Backend plugin
435
+ cd plugins/kuadrant-backend
436
+ yarn start
437
+ ```
438
+
439
+ ### Building
440
+
441
+ ```bash
442
+ yarn build
443
+ ```
444
+
445
+ ### Testing
446
+
447
+ ```bash
448
+ yarn test
449
+ ```
450
+
451
+ ### Linting
452
+
453
+ ```bash
454
+ yarn lint:check
455
+ yarn lint:fix
456
+ ```
457
+
458
+ ## Permissions
459
+
460
+ The plugin integrates with Backstage permissions. Different views and actions are available based on user roles:
461
+
462
+ - **Platform Engineers**: Full access including approval queue
463
+ - **API Owners**: Can create and manage API products
464
+ - **API Consumers**: Can request API access and manage their keys
465
+
466
+ Roles are determined by Backstage catalog group membership.
467
+
468
+ ## Troubleshooting
469
+
470
+ ### "No pending requests" shown but requests exist in Kubernetes
471
+
472
+ Ensure the backend is using the correct backend URL. Check browser console for errors about non-JSON responses, which indicates the frontend is hitting the wrong endpoint.
473
+
474
+ ### APIProduct entities not appearing in catalog
475
+
476
+ 1. Check backend logs for APIProduct entity provider sync messages
477
+ 2. Verify Kubernetes connectivity from the backend
478
+ 3. Ensure APIProduct CRDs exist in your cluster
479
+ 4. Check catalog rules allow APIProduct kind
480
+
481
+ ### API key requests failing
482
+
483
+ 1. Verify Kubernetes write permissions for the backend service account
484
+ 2. Check backend logs for detailed error messages
485
+ 3. Ensure the target namespace exists and is accessible
486
+
487
+ ## Related Documentation
488
+
489
+ - [Backend Plugin README](../kuadrant-backend/README.md)
490
+ - [Kuadrant Documentation](https://docs.kuadrant.io/)
491
+ - [Backstage Plugin Development](https://backstage.io/docs/plugins/)
@@ -0,0 +1,42 @@
1
+ import React, { useState } from 'react';
2
+ import { useAsync } from 'react-use';
3
+ import { Progress, ResponseErrorPanel, InfoCard } from '@backstage/core-components';
4
+ import { Box, Typography, Chip } from '@material-ui/core';
5
+ import { useApi, configApiRef, identityApiRef, fetchApiRef } from '@backstage/core-plugin-api';
6
+ import { useEntity } from '@backstage/plugin-catalog-react';
7
+
8
+ const ApiAccessCard = ({ namespace: propNamespace }) => {
9
+ const { entity } = useEntity();
10
+ const config = useApi(configApiRef);
11
+ const identityApi = useApi(identityApiRef);
12
+ const fetchApi = useApi(fetchApiRef);
13
+ const backendUrl = config.getString("backend.baseUrl");
14
+ const [userId, setUserId] = useState("guest");
15
+ const namespace = entity.metadata.annotations?.["kuadrant.io/namespace"] || propNamespace || "default";
16
+ useAsync(async () => {
17
+ const identity = await identityApi.getBackstageIdentity();
18
+ setUserId(identity.userEntityRef.split("/")[1] || "guest");
19
+ }, [identityApi]);
20
+ const { value: apiKeys, loading: keysLoading, error: keysError } = useAsync(async () => {
21
+ const response = await fetchApi.fetch(`${backendUrl}/api/kuadrant/apikeys?namespace=${namespace}&userId=${userId}`);
22
+ if (!response.ok) {
23
+ throw new Error("failed to fetch api keys");
24
+ }
25
+ const data = await response.json();
26
+ return data.items || [];
27
+ }, [namespace, userId, backendUrl, fetchApi]);
28
+ if (keysLoading) {
29
+ return /* @__PURE__ */ React.createElement(Progress, null);
30
+ }
31
+ if (keysError) {
32
+ return /* @__PURE__ */ React.createElement(ResponseErrorPanel, { error: keysError });
33
+ }
34
+ const keys = apiKeys || [];
35
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(InfoCard, { title: "Kuadrant API Keys" }, /* @__PURE__ */ React.createElement(Box, { p: 2 }, keys.length > 0 ? /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", gutterBottom: true }, "You have ", keys.length, " active API key", keys.length !== 1 ? "s" : "", " for this API"), keys.map((key) => {
36
+ const planTier = key.metadata.annotations?.["secret.kuadrant.io/plan-id"] || "Unknown";
37
+ return /* @__PURE__ */ React.createElement(Box, { key: key.metadata.name, mb: 1, p: 1, border: 1, borderColor: "grey.300", borderRadius: 4 }, /* @__PURE__ */ React.createElement(Box, { display: "flex", justifyContent: "space-between", alignItems: "center" }, /* @__PURE__ */ React.createElement(Typography, { variant: "body2" }, key.metadata.name), /* @__PURE__ */ React.createElement(Chip, { label: planTier, color: "primary", size: "small" })));
38
+ }), /* @__PURE__ */ React.createElement(Box, { mt: 2 }, /* @__PURE__ */ React.createElement(Typography, { variant: "caption", color: "textSecondary" }, "Visit the API Keys tab to view keys, make new requests, or manage access"))) : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(Typography, { variant: "body1", gutterBottom: true }, "You don't have any API keys for this API yet"), /* @__PURE__ */ React.createElement(Typography, { variant: "body2", color: "textSecondary" }, "Visit the API Keys tab to request access")))));
39
+ };
40
+
41
+ export { ApiAccessCard };
42
+ //# sourceMappingURL=ApiAccessCard.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ApiAccessCard.esm.js","sources":["../../../src/components/ApiAccessCard/ApiAccessCard.tsx"],"sourcesContent":["import React, { useState } from 'react';\nimport { useAsync } from 'react-use';\nimport {\n InfoCard,\n Progress,\n ResponseErrorPanel,\n} from '@backstage/core-components';\nimport {\n Typography,\n Box,\n Chip,\n} from '@material-ui/core';\nimport { useApi, configApiRef, identityApiRef, fetchApiRef } from '@backstage/core-plugin-api';\nimport { useEntity } from '@backstage/plugin-catalog-react';\n\ninterface ApiKey {\n metadata: {\n name: string;\n namespace: string;\n annotations?: {\n 'secret.kuadrant.io/plan-id'?: string;\n 'secret.kuadrant.io/user-id'?: string;\n };\n };\n data?: {\n api_key?: string;\n };\n}\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 config = useApi(configApiRef);\n const identityApi = useApi(identityApiRef);\n const fetchApi = useApi(fetchApiRef);\n const backendUrl = config.getString('backend.baseUrl');\n const [userId, setUserId] = useState<string>('guest');\n\n // read from entity annotations, fallback to props for backwards compat\n const namespace = entity.metadata.annotations?.['kuadrant.io/namespace'] || propNamespace || 'default';\n\n // get current user identity\n useAsync(async () => {\n const identity = await identityApi.getBackstageIdentity();\n setUserId(identity.userEntityRef.split('/')[1] || 'guest');\n }, [identityApi]);\n\n const { value: apiKeys, loading: keysLoading, error: keysError } = useAsync(async () => {\n const response = await fetchApi.fetch(`${backendUrl}/api/kuadrant/apikeys?namespace=${namespace}&userId=${userId}`);\n if (!response.ok) {\n throw new Error('failed to fetch api keys');\n }\n const data = await response.json();\n return data.items || [];\n }, [namespace, userId, backendUrl, fetchApi]);\n\n if (keysLoading) {\n return <Progress />;\n }\n\n if (keysError) {\n return <ResponseErrorPanel error={keysError} />;\n }\n\n const keys = (apiKeys as ApiKey[]) || [];\n\n return (\n <>\n <InfoCard title=\"Kuadrant API Keys\">\n <Box p={2}>\n {keys.length > 0 ? (\n <>\n <Typography variant=\"body1\" gutterBottom>\n You have {keys.length} active API key{keys.length !== 1 ? 's' : ''} for this API\n </Typography>\n {keys.map((key: ApiKey) => {\n const planTier = key.metadata.annotations?.['secret.kuadrant.io/plan-id'] || 'Unknown';\n\n return (\n <Box key={key.metadata.name} mb={1} p={1} border={1} borderColor=\"grey.300\" borderRadius={4}>\n <Box display=\"flex\" justifyContent=\"space-between\" alignItems=\"center\">\n <Typography variant=\"body2\">\n {key.metadata.name}\n </Typography>\n <Chip label={planTier} color=\"primary\" size=\"small\" />\n </Box>\n </Box>\n );\n })}\n <Box mt={2}>\n <Typography variant=\"caption\" color=\"textSecondary\">\n Visit the API Keys tab to view keys, make new requests, or manage access\n </Typography>\n </Box>\n </>\n ) : (\n <>\n <Typography variant=\"body1\" gutterBottom>\n You don't have any API keys for this API yet\n </Typography>\n <Typography variant=\"body2\" color=\"textSecondary\">\n Visit the API Keys tab to request access\n </Typography>\n </>\n )}\n </Box>\n </InfoCard>\n </>\n );\n};\n"],"names":[],"mappings":";;;;;;;AAkCO,MAAM,aAAgB,GAAA,CAAC,EAAE,SAAA,EAAW,eAAwC,KAAA;AACjF,EAAM,MAAA,EAAE,MAAO,EAAA,GAAI,SAAU,EAAA;AAC7B,EAAM,MAAA,MAAA,GAAS,OAAO,YAAY,CAAA;AAClC,EAAM,MAAA,WAAA,GAAc,OAAO,cAAc,CAAA;AACzC,EAAM,MAAA,QAAA,GAAW,OAAO,WAAW,CAAA;AACnC,EAAM,MAAA,UAAA,GAAa,MAAO,CAAA,SAAA,CAAU,iBAAiB,CAAA;AACrD,EAAA,MAAM,CAAC,MAAA,EAAQ,SAAS,CAAA,GAAI,SAAiB,OAAO,CAAA;AAGpD,EAAA,MAAM,YAAY,MAAO,CAAA,QAAA,CAAS,WAAc,GAAA,uBAAuB,KAAK,aAAiB,IAAA,SAAA;AAG7F,EAAA,QAAA,CAAS,YAAY;AACnB,IAAM,MAAA,QAAA,GAAW,MAAM,WAAA,CAAY,oBAAqB,EAAA;AACxD,IAAA,SAAA,CAAU,SAAS,aAAc,CAAA,KAAA,CAAM,GAAG,CAAE,CAAA,CAAC,KAAK,OAAO,CAAA;AAAA,GAC3D,EAAG,CAAC,WAAW,CAAC,CAAA;AAEhB,EAAM,MAAA,EAAE,OAAO,OAAS,EAAA,OAAA,EAAS,aAAa,KAAO,EAAA,SAAA,EAAc,GAAA,QAAA,CAAS,YAAY;AACtF,IAAM,MAAA,QAAA,GAAW,MAAM,QAAA,CAAS,KAAM,CAAA,CAAA,EAAG,UAAU,CAAmC,gCAAA,EAAA,SAAS,CAAW,QAAA,EAAA,MAAM,CAAE,CAAA,CAAA;AAClH,IAAI,IAAA,CAAC,SAAS,EAAI,EAAA;AAChB,MAAM,MAAA,IAAI,MAAM,0BAA0B,CAAA;AAAA;AAE5C,IAAM,MAAA,IAAA,GAAO,MAAM,QAAA,CAAS,IAAK,EAAA;AACjC,IAAO,OAAA,IAAA,CAAK,SAAS,EAAC;AAAA,KACrB,CAAC,SAAA,EAAW,MAAQ,EAAA,UAAA,EAAY,QAAQ,CAAC,CAAA;AAE5C,EAAA,IAAI,WAAa,EAAA;AACf,IAAA,2CAAQ,QAAS,EAAA,IAAA,CAAA;AAAA;AAGnB,EAAA,IAAI,SAAW,EAAA;AACb,IAAO,uBAAA,KAAA,CAAA,aAAA,CAAC,kBAAmB,EAAA,EAAA,KAAA,EAAO,SAAW,EAAA,CAAA;AAAA;AAG/C,EAAM,MAAA,IAAA,GAAQ,WAAwB,EAAC;AAEvC,EAAA,uBAEI,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,QAAS,EAAA,EAAA,KAAA,EAAM,uCACb,KAAA,CAAA,aAAA,CAAA,GAAA,EAAA,EAAI,CAAG,EAAA,CAAA,EAAA,EACL,IAAK,CAAA,MAAA,GAAS,CACb,mBAAA,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,sCACG,UAAW,EAAA,EAAA,OAAA,EAAQ,OAAQ,EAAA,YAAA,EAAY,IAAC,EAAA,EAAA,WAAA,EAC7B,IAAK,CAAA,MAAA,EAAO,mBAAgB,IAAK,CAAA,MAAA,KAAW,CAAI,GAAA,GAAA,GAAM,IAAG,eACrE,CAAA,EACC,IAAK,CAAA,GAAA,CAAI,CAAC,GAAgB,KAAA;AACzB,IAAA,MAAM,QAAW,GAAA,GAAA,CAAI,QAAS,CAAA,WAAA,GAAc,4BAA4B,CAAK,IAAA,SAAA;AAE7E,IAAA,2CACG,GAAI,EAAA,EAAA,GAAA,EAAK,IAAI,QAAS,CAAA,IAAA,EAAM,IAAI,CAAG,EAAA,CAAA,EAAG,CAAG,EAAA,MAAA,EAAQ,GAAG,WAAY,EAAA,UAAA,EAAW,cAAc,CACxF,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,OAAI,OAAQ,EAAA,MAAA,EAAO,cAAe,EAAA,eAAA,EAAgB,YAAW,QAC5D,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,cAAW,OAAQ,EAAA,OAAA,EAAA,EACjB,IAAI,QAAS,CAAA,IAChB,mBACC,KAAA,CAAA,aAAA,CAAA,IAAA,EAAA,EAAK,OAAO,QAAU,EAAA,KAAA,EAAM,WAAU,IAAK,EAAA,OAAA,EAAQ,CACtD,CACF,CAAA;AAAA,GAEH,CAAA,kBACA,KAAA,CAAA,aAAA,CAAA,GAAA,EAAA,EAAI,IAAI,CACP,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,UAAW,EAAA,EAAA,OAAA,EAAQ,WAAU,KAAM,EAAA,eAAA,EAAA,EAAgB,0EAEpD,CACF,CACF,CAEA,mBAAA,KAAA,CAAA,aAAA,CAAA,KAAA,CAAA,QAAA,EAAA,IAAA,kBACG,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,SAAQ,OAAQ,EAAA,YAAA,EAAY,IAAC,EAAA,EAAA,8CAEzC,mBACC,KAAA,CAAA,aAAA,CAAA,UAAA,EAAA,EAAW,OAAQ,EAAA,OAAA,EAAQ,OAAM,eAAgB,EAAA,EAAA,0CAElD,CACF,CAEJ,CACF,CACF,CAAA;AAEJ;;;;"}
@@ -0,0 +1,2 @@
1
+ export { ApiAccessCard } from './ApiAccessCard.esm.js';
2
+ //# sourceMappingURL=index.esm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}