Skip to content

feat(apikey): add expiration support to key management UI - #689

Merged
emmaaroche merged 1 commit into
Kuadrant:mainfrom
emmaaroche:feat/apikey-expiration-ui
Jul 29, 2026
Merged

feat(apikey): add expiration support to key management UI#689
emmaaroche merged 1 commit into
Kuadrant:mainfrom
emmaaroche:feat/apikey-expiration-ui

Conversation

@emmaaroche

@emmaaroche emmaaroche commented Jul 28, 2026

Copy link
Copy Markdown
Member

Description

Adds API key expiration support to the console plugin UI, implementing the consumer-facing and owner-facing expiration features that depend on developer-portal-controller #88.

Changes

  • Request modal: New Expiration field with preset options (7/30/60/90 days, Custom date picker, No expiration). Sets spec.expiresAt on the created APIKey. Validates incomplete dates and disables submit on invalid input
  • Approval table: New Expiration column showing requested expiry date ( if none)
  • Approve/Deny modals: Expiration shown in both single and bulk views
  • My API Keys: New Expires column showing "X days left (date)", "Expired (date)" in grey, or "No expiration"
  • Key detail page: Expiration field in Details tab
  • Expired status: New grey BanIcon badge for Expired condition (controller sets this instead of Approved when expiresAt has passed)
image
  • Types: expiresAt?: string added to APIKeyRequestSpec and APIKey.spec

Testing

  • yarn lint and yarn i18n pass
  • E2E test added to apikey-lifecycle.spec.ts: creates key with future expiresAt, verifies date appears in approval table and modal, approves, verifies "days left" in My API Keys, patches expiresAt to past (fast-forward), waits for controller to set Expired=True, verifies Expired badge and expired text
  • Manually tested against cluster with updated controller CRDs applied

Closes #659

Summary by CodeRabbit

  • New Features
    • Added API key expiration support across request, approvals, and API key listings, including preset durations, custom dates, and “no expiration”.
    • Introduced an Expiration/Expires column and consistent expiry display in details, modals, and tables.
    • Extended status badges with an Expired state and updated UI copy/date labels to support relative and absolute expiry text.
  • Bug Fixes
    • Improved expiry status detection so Expired keys are identified and shown consistently.
  • Tests
    • Added end-to-end coverage for the API key expiry lifecycle.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds optional expiresAt fields, an Expired phase, expiry formatting, request configuration, expiry displays across key-management views, locale strings, and end-to-end lifecycle coverage.

Changes

API key expiration feature

Layer / File(s) Summary
Expiry contracts and formatting
src/utils/resources.ts, src/components/apikey/types.ts, src/components/apikey/utils.ts
Adds expiry fields, Expired phase detection, and relative/absolute expiry formatting helpers.
Expiry selection in API key requests
src/components/apikey/RequestAPIKeyModal.tsx, locales/en/...json
Adds preset and custom-date selection, validation, payload wiring, and supporting locale strings.
Approval and rejection expiry display
src/components/apikey/APIKeyApprovalTable.tsx, src/components/apikey/ApprovalModal.tsx, src/components/apikey/RejectionModal.tsx
Adds formatted expiration columns and fields to approval and rejection views.
Key details and status rendering
src/components/apikey/APIKeyDetailsTab.tsx, src/components/apikey/MyAPIKeysPage.tsx, src/components/apikey/APIKeyStatusBadge.tsx
Displays expiry information and renders an Expired status badge.
Expiry lifecycle e2e coverage
e2e/tests/apikey-lifecycle.spec.ts
Creates, approves, expires, and verifies an API key through the UI and Kubernetes resource state.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Possibly related issues

Possibly related PRs

Suggested reviewers: anton-fil, eguzki, r-lawton

Poem

A rabbit watched the expiry clock,
Then patched the key around the block.
It bloomed, was approved, then turned grey,
As “Expired” marked the final day. 🐰
Dates now hop through every view,
With tests to prove the journey true.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the main change: API key expiration support in the key management UI.
Linked Issues check ✅ Passed The changes satisfy the linked issue's core acceptance criteria for approval display, My API Keys expiry display, and Expired status handling.
Out of Scope Changes check ✅ Passed No clear out-of-scope work is evident; the diff stays focused on API key expiration UI, types, helpers, and end-to-end coverage.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (1)
e2e/tests/apikey-lifecycle.spec.ts (1)

433-438: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Namespace cleanup is skipped when stopImpersonation throws.

If the test fails mid-flow (e.g. impersonation state is inconsistent), stopImpersonation can reject and the kubectl delete namespace never runs, leaking a namespace per failed run.

♻️ Guarantee cleanup with try/finally
 test.afterEach(async ({ page }) => {
-    await stopImpersonation(page);
-    execSync(`kubectl delete namespace ${consumerNs} --ignore-not-found=true`, {
-      stdio: 'inherit',
-    });
+    try {
+      await stopImpersonation(page);
+    } finally {
+      execSync(`kubectl delete namespace ${consumerNs} --ignore-not-found=true`, {
+        stdio: 'inherit',
+      });
+    }
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/tests/apikey-lifecycle.spec.ts` around lines 433 - 438, Update the
test.afterEach cleanup around stopImpersonation so namespace deletion always
executes, even when stopImpersonation rejects. Use a try/finally structure and
keep the existing kubectl delete command in the finally block.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@e2e/tests/apikey-lifecycle.spec.ts`:
- Around line 459-461: Reformat the alert visibility assertions in the API key
lifecycle test, including the matching assertion around the other referenced
location, to comply with the repository’s Prettier configuration. Preserve the
existing locator, success message, and timeout behavior.
- Around line 364-365: Update the expiry constants and assertions in the API-key
lifecycle test to avoid fixed dates and timezone-sensitive labels. Derive
FUTURE_EXPIRES_AT from Date.now() with sufficient future validity, keep the past
timestamp, and compute FUTURE_LABEL and PAST_LABEL using the same locale/date
formatting options as formatExpiry/formatExpiryDate; replace all hard-coded
display labels in the affected assertions with these derived values.

In `@src/components/apikey/APIKeyApprovalTable.tsx`:
- Line 184: Update the empty Th in APIKeyApprovalTable to include an accessible
label for the approve/deny actions column, preserving the column’s screen-reader
header while keeping it visually empty.

In `@src/components/apikey/RequestAPIKeyModal.tsx`:
- Around line 170-178: The expiration label in getExpirationPresetLabel must use
useTranslation’s t() with an interpolated translation key instead of assembling
English text directly. Update src/components/apikey/RequestAPIKeyModal.tsx lines
170-178 accordingly, and add the matching {{days}} days ({{date}}) translation
key in locales/en/plugin__kuadrant-console-plugin.json lines 114-117.
- Around line 180-202: Use a single UTC-based calendar-date convention across
custom expiry parsing, preset calculation, and the related
formatExpiry/formatExpiryDate display helpers. Ensure selecting a date such as
Aug 4 serializes and renders as Aug 4 in every timezone by using UTC date
components and arithmetic consistently, while preserving the existing validation
and expiration behavior.

In `@src/components/apikey/utils.ts`:
- Around line 45-70: Update formatExpiry to stop returning hard-coded
user-facing labels; return an expiry status/key and the necessary values such as
date and remaining days, then translate those states at the consuming component
boundary with useTranslation. Preserve the existing expired, today,
no-expiration, and pluralization behavior while moving all displayed text into
i18n resources.

---

Nitpick comments:
In `@e2e/tests/apikey-lifecycle.spec.ts`:
- Around line 433-438: Update the test.afterEach cleanup around
stopImpersonation so namespace deletion always executes, even when
stopImpersonation rejects. Use a try/finally structure and keep the existing
kubectl delete command in the finally block.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1fac7e44-eac2-430a-bc37-79cb3dce90b0

📥 Commits

Reviewing files that changed from the base of the PR and between 7014686 and 02f7600.

📒 Files selected for processing (12)
  • e2e/tests/apikey-lifecycle.spec.ts
  • locales/en/plugin__kuadrant-console-plugin.json
  • src/components/apikey/APIKeyApprovalTable.tsx
  • src/components/apikey/APIKeyDetailsTab.tsx
  • src/components/apikey/APIKeyStatusBadge.tsx
  • src/components/apikey/ApprovalModal.tsx
  • src/components/apikey/MyAPIKeysPage.tsx
  • src/components/apikey/RejectionModal.tsx
  • src/components/apikey/RequestAPIKeyModal.tsx
  • src/components/apikey/types.ts
  • src/components/apikey/utils.ts
  • src/utils/resources.ts

Comment thread e2e/tests/apikey-lifecycle.spec.ts Outdated
Comment thread e2e/tests/apikey-lifecycle.spec.ts Outdated
Comment thread src/components/apikey/APIKeyApprovalTable.tsx Outdated
Comment thread src/components/apikey/RequestAPIKeyModal.tsx
Comment thread src/components/apikey/RequestAPIKeyModal.tsx
Comment thread src/components/apikey/utils.ts Outdated
@emmaaroche
emmaaroche force-pushed the feat/apikey-expiration-ui branch from 02f7600 to af67183 Compare July 28, 2026 12:03
@emmaaroche
emmaaroche requested review from Anton-Fil and R-Lawton July 28, 2026 14:07
@R-Lawton

Copy link
Copy Markdown
Contributor

👀

@R-Lawton R-Lawton left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks great overall. couple of small suggestions below, nothing blocking.

Comment thread src/utils/resources.ts Outdated
Comment thread src/components/apikey/RequestAPIKeyModal.tsx Outdated
Signed-off-by: emmaaroche <eroche@redhat.com>
@emmaaroche
emmaaroche force-pushed the feat/apikey-expiration-ui branch from af67183 to 1372492 Compare July 28, 2026 16:30
@emmaaroche
emmaaroche requested a review from R-Lawton July 28, 2026 16:31

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
e2e/tests/apikey-lifecycle.spec.ts (1)

447-455: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider making stopImpersonation truly best-effort.

A throw here fails the test in teardown even though the cleanup ran. Wrapping it in .catch(() => {}) keeps the failure signal on the test body itself.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/tests/apikey-lifecycle.spec.ts` around lines 447 - 455, Update the
test.afterEach teardown to make stopImpersonation(page) best-effort by
swallowing its rejection, while preserving the finally block that deletes
consumerNs. Ensure cleanup errors do not fail the test after the test body has
completed.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@e2e/tests/apikey-lifecycle.spec.ts`:
- Around line 505-510: Format the assertions in the expired-row check using the
repository’s Prettier style, specifically the locator containing PAST_LABEL in
the expiredRow expectations, so the file passes lint without changing test
behavior.

---

Nitpick comments:
In `@e2e/tests/apikey-lifecycle.spec.ts`:
- Around line 447-455: Update the test.afterEach teardown to make
stopImpersonation(page) best-effort by swallowing its rejection, while
preserving the finally block that deletes consumerNs. Ensure cleanup errors do
not fail the test after the test body has completed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f60c3e9d-b8a3-4772-9d12-9a913f8c9954

📥 Commits

Reviewing files that changed from the base of the PR and between 02f7600 and 1372492.

📒 Files selected for processing (12)
  • e2e/tests/apikey-lifecycle.spec.ts
  • locales/en/plugin__kuadrant-console-plugin.json
  • src/components/apikey/APIKeyApprovalTable.tsx
  • src/components/apikey/APIKeyDetailsTab.tsx
  • src/components/apikey/APIKeyStatusBadge.tsx
  • src/components/apikey/ApprovalModal.tsx
  • src/components/apikey/MyAPIKeysPage.tsx
  • src/components/apikey/RejectionModal.tsx
  • src/components/apikey/RequestAPIKeyModal.tsx
  • src/components/apikey/types.ts
  • src/components/apikey/utils.ts
  • src/utils/resources.ts
🚧 Files skipped from review as they are similar to previous changes (10)
  • src/components/apikey/types.ts
  • src/components/apikey/APIKeyStatusBadge.tsx
  • src/components/apikey/APIKeyDetailsTab.tsx
  • src/components/apikey/utils.ts
  • src/components/apikey/MyAPIKeysPage.tsx
  • src/components/apikey/ApprovalModal.tsx
  • src/components/apikey/RejectionModal.tsx
  • src/utils/resources.ts
  • src/components/apikey/RequestAPIKeyModal.tsx
  • src/components/apikey/APIKeyApprovalTable.tsx

Comment thread e2e/tests/apikey-lifecycle.spec.ts

@R-Lawton R-Lawton left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm 🌻

@emmaaroche
emmaaroche merged commit 6024a62 into Kuadrant:main Jul 29, 2026
10 checks passed
@emmaaroche
emmaaroche deleted the feat/apikey-expiration-ui branch July 29, 2026 10:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

APIKey: display expiration in key management UI

2 participants