Skip to content

Conversation

@Thushani-Jayasekera
Copy link
Contributor

@Thushani-Jayasekera Thushani-Jayasekera commented Dec 20, 2025

Purpose

Design Doc: https://docs.google.com/document/d/1JtHmyJ3IFMZTa8hdwPdnyOSfO_hKzgkLSDdgHeWnCy8/edit?tab=t.0#heading=h.7h46fu1s6qhy

Application overview page

image

Application overview page with themeing

image
  • After production API Key Generation and Production oauth2 generation
image image

Manage Keys page with themeing

image
  • Generating Production API Key
image
  • Generated API Key
image
  • Revoking API Key
image
  • Sandbox Keys
image

✏️ Tip: You can customize this high-level summary in your review settings.

Summary by CodeRabbit

  • New Features

    • New Manage Keys page with Production/Sandbox tabs for API and OAuth2 keys and a dedicated route.
    • Applications menu now exposes Overview and Manage Keys submenu items.
  • UI Changes

    • Per-environment (Production/Sandbox) key flows across app: Generate, View, Modify, Instructions, Regenerate/Revoke, and environment-tagged key displays.
    • Updated key modals and pages to show separate Production/Sandbox sections and improved layouts/styles.
  • Bug Fixes

    • Better validation and error handling for key generation and key routes; missing-page guards and safer modal behavior.
    • Input sanitization for warning/dialog flows and more robust DOM checks.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Dec 20, 2025

Walkthrough

Adds a Manage Keys feature with per-keyType (PRODUCTION/SANDBOX) scoping across controllers, routes, templates, and client scripts; hardens warning modal handling and input sanitization; extends adminService.mapKeys to accept keyType; and adds OpenAPI server descriptions "Production" and "Sandbox".

Changes

Cohort / File(s) Summary
Controller updates
src/controllers/applicationsContentController.js, src/controllers/apiContentController.js, src/controllers/customContentController.js, src/controllers/devportalController.js
Added loadApplicationKeys; consolidated application data loader; getAPIKeys now accepts keyType; OpenAPI servers entries include description: "Production"/"Sandbox"; custom content loader short-circuits invalid manage-keys paths; devportal key generation validates and includes keyType in names.
Routes
src/routes/applicationsContentRoute.js, src/routes/designModeRoute.js
Added GET route for manage-keys (.../applications/:applicationId/manage-keys); normalized param name to :applicationId.
Manage Keys UI & templates
src/pages/manage-keys/page.hbs, src/defaultContent/pages/manage-keys/page.hbs, src/defaultContent/partials/sidebar.hbs, src/pages/application/page.hbs, src/pages/application/partials/manage-keys.hbs
New Manage Keys page and partial; sidebar adds Applications submenu; keys-related modals moved/removed from application page into manage-keys composition.
Per-keyType template refactor
src/pages/application/partials/*.hbs (e.g., api-key.hbs, apis.hbs, generate-oauth-key.hbs, keys-*.hbs, overview.hbs, oauth2-key-generation.hbs)
Templates refactored to iterate applicationKeys and render per-keyType (PRODUCTION/SANDBOX); IDs, modals, forms, onclicks and payloads suffixed with -{{keyType}}; generation calls include subscriptions and keyType.
Client scripts
src/scripts/api-key-generation.js, src/scripts/oauth2-key-generation.js, src/scripts/common.js, src/scripts/warning.js
Client functions extended to accept and propagate keyType (generate/regenerate/revoke/open modals); stronger DOM guards; openWarningModal gains sixth param and sanitization; inline onclicks replaced with safe handlers.
Admin service
src/services/adminService.js
mapKeys signature extended to accept keyType; callers updated to pass dynamic keyType.
Styles
src/styles/manage-keys.css, src/defaultContent/styles/manage-keys.css
New Manage Keys styles: tab-like key buttons, accordion UI, form/table styling, scrollbars, and interaction states.
Page wiring & auxiliary scripts
src/scripts/api-key-generation.js, src/scripts/oauth2-key-generation.js, src/scripts/common.js
KeyType-aware modal and DOM handling across generate/revoke/regenerate flows; applications submenu wiring updated in common.js.

Sequence Diagram(s)

sequenceDiagram
    participant Browser as Client (Browser)
    participant Router as Router
    participant Controller as ApplicationsController
    participant Service as Backend Service
    participant API as Backend API

    Browser->>Router: GET /.../applications/:applicationId/manage-keys
    Router->>Controller: loadApplicationKeys(req)
    Controller->>Service: getAPIMKeyManagers(req)
    Service->>API: fetch key managers
    API-->>Service: key managers
    Service-->>Controller: key managers

    Controller->>Service: getAPIKeys(apiId, appId, "PRODUCTION")
    Service->>API: fetch production keys
    API-->>Service: production keys
    Service-->>Controller: production keys

    Controller->>Service: getAPIKeys(apiId, appId, "SANDBOX")
    Service->>API: fetch sandbox keys
    API-->>Service: sandbox keys
    Service-->>Controller: sandbox keys

    Controller-->>Browser: render manage-keys page (per-keyType UI)
    Browser->>Router: POST /generateKey (payload includes keyType)
    Router->>Controller: generateAPIKeys(req)
    Controller->>Service: createAppKeyMapping(..., keyType)
    Service->>API: create/map key with keyType
    API-->>Service: success
    Service-->>Controller: key data
    Controller-->>Browser: updated key state (keyType-scoped)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I hopped through code with keys in my paws,
Two burrows now — Prod and Sandbox — neat claws,
Modals scoped tidy, IDs snug in their slots,
Tabs click, tokens glow, no tangled knots,
A rabbit's cheer for tidy builds and lots of carrots! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is largely incomplete. While it references the design doc and includes UI screenshots, it lacks most required sections including Goals, Approach, User stories, Release notes, Documentation, Training, Certification, Marketing, Automation tests, Security checks, and others from the template. Complete the PR description by filling in all required template sections: Goals, Approach, User stories, Release notes, Documentation, Training, Certification, Marketing, Automation tests, Security checks, Samples, Related PRs, Migrations, Test environment, and Learning.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add initial changes for sandbox key management' accurately describes the main objective of the PR, which introduces sandbox key management functionality alongside production key management.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 14

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/scripts/api-key-generation.js (1)

333-336: Inconsistent error message in regenerateAPIKey.

The error message says "Failed to generate API Key" but this is the regenerateAPIKey function. Should be "Failed to regenerate API Key" for consistency with line 331.

🔎 Proposed fix
   } catch (error) {
     console.log(error);
-    await showAlert(`Failed to generate API Key. Please try again.\n${error}`, 'error');
+    await showAlert(`Failed to regenerate API Key. Please try again.\n${error}`, 'error');
   }
src/pages/application/partials/generate-oauth-key.hbs (3)

91-91: Inconsistent ID suffix: tokenDisplay_{{../name}} missing keyType.

The tokenDisplay_{{../name}} ID on line 91 does not include the keyType suffix, unlike similar elements such as curlDisplay_{{../name}}_{{keyType}} on line 125 and token_{{../name}}_{{keyType}} on line 112. This could cause ID collisions when both PRODUCTION and SANDBOX keys are present.

🔎 Suggested fix
-                                <div id="tokenDisplay_{{../name}}" style="display: none;">
+                                <div id="tokenDisplay_{{../name}}_{{keyType}}" style="display: none;">

142-142: Inconsistent ID suffix: curl_{{../name}} missing keyType.

The curl_{{../name}} ID does not include the keyType suffix, which will cause collisions when rendering both PRODUCTION and SANDBOX keys.

🔎 Suggested fix
-                                        <code id="curl_{{../name}}"></code>
+                                        <code id="curl_{{../name}}_{{keyType}}"></code>

341-342: Inconsistent ID suffix: showAdvanced('KMData_{{../name}}') missing keyType.

On line 342, the showAdvanced call uses KMData_{{../name}} without the keyType suffix, but the container on line 352 uses KMData_{{../name}} as well. This is inconsistent with the pattern used elsewhere (e.g., line 182 uses KMData_{{../name}}_{{keyType}}). For the "else" branch (when consumer key doesn't exist), ensure consistency with the per-keyType pattern.

🔎 Suggested fix
-                                    onclick="showAdvanced('KMData_{{../name}}')">Advanced Configuration
+                                    onclick="showAdvanced('KMData_{{../name}}_{{keyType}}')">Advanced Configuration

And update the container ID on line 352:

-                            <div class="container KMConfig mt-4" id="KMData_{{../name}}" style="display: none;">
+                            <div class="container KMConfig mt-4" id="KMData_{{../name}}_{{keyType}}" style="display: none;">
src/controllers/applicationsContentController.js (1)

306-312: Same issue exists in loadApplication.

The same scoping issue exists in loadApplication where applicationId is declared at line 132 inside the try block but referenced at line 359 in the catch block.

🔎 Suggested fix

Move the declaration before the try block:

 const loadApplication = async (req, res) => {
     let html, templateContent, metaData, kMmetaData;
     const viewName = req.params.viewName;
     const orgName = req.params.orgName;
     const orgDetails = await adminDao.getOrganization(orgName);
     const devportalMode = orgDetails.ORG_CONFIG?.devportalMode || constants.API_TYPE.DEFAULT;
+    const applicationId = req.params.applicationId;
     try {
-        const applicationId = req.params.applicationId;
         if (config.mode === constants.DEV_MODE) {
🧹 Nitpick comments (15)
src/styles/manage-keys.css (2)

70-99: Consider reducing !important usage.

Multiple !important declarations on .accordion-button states suggest potential specificity conflicts with Bootstrap or other frameworks. While functional, this can make future maintenance harder.

If Bootstrap's accordion styles are the issue, consider increasing selector specificity instead (e.g., .keys-section .accordion-button) to avoid !important overuse.


124-140: Webkit-only scrollbar styles won't apply in Firefox.

The ::-webkit-scrollbar pseudo-elements only work in Chromium-based browsers and Safari. Firefox users will see the default scrollbar.

For broader support, consider adding scrollbar-width and scrollbar-color properties for Firefox:

.accordion-body {
    /* Firefox */
    scrollbar-width: thin;
    scrollbar-color: var(--primary-light-color) var(--secondary-bg-color);
}
src/defaultContent/styles/manage-keys.css (1)

1-189: Duplicate stylesheet detected.

This file is identical to src/styles/manage-keys.css. Maintaining two copies increases the risk of divergence during future updates.

If both locations are required by the architecture (e.g., default content vs. customizable content), consider importing from a single source or documenting why duplication is necessary.

src/pages/application/partials/keys-generate.hbs (2)

196-196: Potential ID collision for grant-types-{{keyType}}.

The IDs grant-types-{{keyType}} and grant-types-adv-{{keyType}} don't include the key manager name ({{../name}}). If multiple key managers are enabled, this could cause duplicate IDs when the outer {{#each keyManagersMetadata}} loop iterates.

Consider including the key manager identifier:

🔎 Proposed fix
-<div class="keygen-checkbox-group" id="grant-types-{{keyType}}">
+<div class="keygen-checkbox-group" id="grant-types-{{../name}}-{{keyType}}">

And similarly for line 355:

-<div class="keygen-checkbox-group" id="grant-types-adv-{{keyType}}">
+<div class="keygen-checkbox-group" id="grant-types-adv-{{../name}}-{{keyType}}">

Also applies to: 355-355


326-328: Long inline onclick handler reduces readability.

The generateApplicationKey call on line 328 has many parameters, making it hard to maintain. While functional, consider extracting this to a helper function that accepts an options object in future refactoring.

src/pages/application/page.hbs (1)

19-28: Approve the modal relocation; consider removing commented-out code.

The refactor to serve key modals from the dedicated manage-keys page is clean. However, the commented-out code at lines 23-28 appears to be stale (oauth2-key-generation, throttling-reset-modal, api-key-modal, delete-confirmation). Consider removing this dead code to improve maintainability.

src/defaultContent/partials/sidebar.hbs (1)

65-70: Consider using a key-related icon for "Manage Keys".

The current icon (documentation.svg) is semantically appropriate for documentation links but less intuitive for key management. Consider using a key or lock icon to better convey the functionality.

src/pages/manage-keys/page.hbs (1)

1-17: LGTM! Consider reviewing necessity of all included scripts.

The page structure is correct. However, scripts like subscription.js, application-dashboard.js, and edit-application-form.js may not be needed for a dedicated key management page. If these are unused on this page, consider removing them to reduce page load overhead.

src/controllers/customContentController.js (1)

66-79: Path normalization and existence check logic is sound, with minor redundancy.

The normalization correctly redirects application-specific manage-keys paths to the shared template. However, the check on line 75 (filePath.includes('manage-keys')) will always be true after normalization on line 69, making the conditional somewhat redundant. Consider simplifying:

🔎 Optional simplification
             // Check if the file exists before attempting to render
             const resolvedPagePath = path.join(process.cwd(), filePrefix + filePath + '/page.hbs');
             if (!fs.existsSync(resolvedPagePath)) {
-                // If it's a manage-keys route that doesn't exist, return 404 or redirect
-                if (filePath.includes('manage-keys')) {
-                    throw new Error(`Manage keys page not found. This route should be handled by the application controller.`);
-                }
-                throw new Error(`Content page not found at ${resolvedPagePath}`);
+                throw new Error(`Content page not found at ${resolvedPagePath}`);
             }

The manage-keys specific message isn't strictly necessary since the safety check at lines 36-48 should catch these cases first in the request flow.

src/pages/application/partials/keys-instructions.hbs (1)

14-16: Minor: Inconsistent ID separator convention.

Element IDs use underscore (tokenGenerationButtons_{{../name}}_{{keyType}}, requestDetails_{{../name}}_{{keyType}}) while other IDs in this file and related templates use hyphens. Consider standardizing for consistency across the codebase.

src/scripts/api-key-generation.js (1)

146-159: Potential issue: URL path manipulation may fail for nested routes.

The regex /\/[^\/]*$/ replaces only the last path segment. If the current path has query parameters or the structure differs, the generated href could be incorrect.

Consider using URL API for safer path manipulation:

🔎 Proposed fix
-          manageLink.href = window.location.pathname.replace(/\/[^\/]*$/, '/manage-keys') + envHash;
+          const basePath = window.location.pathname.split('/').slice(0, -1).join('/');
+          manageLink.href = basePath + '/manage-keys' + envHash;
src/pages/application/partials/manage-keys.hbs (2)

1-6: Consider: External CDN dependency for Bootstrap Icons.

Line 5 loads Bootstrap Icons from a CDN. If the CDN is unavailable, icons won't render. Consider bundling this locally for reliability, or ensure a fallback is in place.


31-149: Consider: Significant code duplication between Production and Sandbox sections.

The OAuth2 Keys sections for Production (lines 31-149) and Sandbox (lines 264-388) are nearly identical, differing only in key type references. Consider extracting a shared partial to reduce maintenance burden and ensure consistency.

Also applies to: 264-388

src/controllers/applicationsContentController.js (1)

381-650: Significant code duplication between loadApplication and loadApplicationKeys.

The loadApplicationKeys function (lines 381-650) duplicates most of the logic from loadApplication (lines 125-379). Both functions share nearly identical code for:

  • Fetching subscribed APIs and building subList
  • Processing key managers and building production/sandbox keys
  • Constructing template content

Consider extracting common logic into shared helper functions to improve maintainability and reduce the risk of divergence.

src/scripts/oauth2-key-generation.js (1)

926-950: validateGrantTypes keyType parameter unused.

The keyType parameter is passed to validateGrantTypes but not used in the function body. The function uses modal-scoped selectors instead. Consider removing the unused parameter or documenting why it's passed for future use.

🔎 Suggested fix

If the parameter is not needed:

-function validateGrantTypes(modal, keyType) {
-    // Find the update button with keyType suffix
+function validateGrantTypes(modal) {
     const updateButton = modal.querySelector('[id^="applicationKeyUpdateButton"]');

And update the call sites at lines 915 and 921:

-    validateGrantTypes(modal, keyType);
+    validateGrantTypes(modal);
-            validateGrantTypes(modal, keyType);
+            validateGrantTypes(modal);
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 98aa7c9 and 8d1bb00.

📒 Files selected for processing (28)
  • src/controllers/apiContentController.js (1 hunks)
  • src/controllers/applicationsContentController.js (7 hunks)
  • src/controllers/customContentController.js (2 hunks)
  • src/controllers/devportalController.js (1 hunks)
  • src/defaultContent/pages/manage-keys/page.hbs (1 hunks)
  • src/defaultContent/partials/sidebar.hbs (1 hunks)
  • src/defaultContent/styles/manage-keys.css (1 hunks)
  • src/pages/application/page.hbs (1 hunks)
  • src/pages/application/partials/api-key.hbs (2 hunks)
  • src/pages/application/partials/apis.hbs (3 hunks)
  • src/pages/application/partials/generate-oauth-key.hbs (21 hunks)
  • src/pages/application/partials/keys-generate.hbs (20 hunks)
  • src/pages/application/partials/keys-instructions.hbs (2 hunks)
  • src/pages/application/partials/keys-modify.hbs (8 hunks)
  • src/pages/application/partials/keys-token.hbs (3 hunks)
  • src/pages/application/partials/keys-view.hbs (7 hunks)
  • src/pages/application/partials/manage-keys.hbs (1 hunks)
  • src/pages/application/partials/oauth2-key-generation.hbs (2 hunks)
  • src/pages/application/partials/overview.hbs (5 hunks)
  • src/pages/manage-keys/page.hbs (1 hunks)
  • src/routes/applicationsContentRoute.js (1 hunks)
  • src/routes/designModeRoute.js (1 hunks)
  • src/scripts/api-key-generation.js (9 hunks)
  • src/scripts/common.js (3 hunks)
  • src/scripts/oauth2-key-generation.js (16 hunks)
  • src/scripts/warning.js (1 hunks)
  • src/services/adminService.js (2 hunks)
  • src/styles/manage-keys.css (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (6)
src/controllers/devportalController.js (2)
src/services/adminService.js (1)
  • cpAppID (1154-1154)
src/middlewares/registerPartials.js (1)
  • apiHandle (165-165)
src/routes/designModeRoute.js (4)
src/routes/applicationsContentRoute.js (2)
  • router (2-2)
  • registerPartials (4-4)
src/routes/devportalRoute.js (1)
  • router (19-19)
src/routes/apiContentRoute.js (2)
  • router (19-19)
  • registerPartials (21-21)
src/middlewares/registerPartials.js (1)
  • registerPartials (35-92)
src/controllers/customContentController.js (1)
src/controllers/applicationsContentController.js (4)
  • logger (21-21)
  • path (24-24)
  • filePrefix (29-29)
  • fs (25-25)
src/routes/applicationsContentRoute.js (2)
src/controllers/customContentController.js (1)
  • req (34-34)
src/middlewares/ensureAuthenticated.js (1)
  • ensureAuthenticated (114-239)
src/scripts/api-key-generation.js (1)
src/middlewares/ensureAuthenticated.js (1)
  • keyType (427-427)
src/scripts/oauth2-key-generation.js (1)
src/scripts/api-key-generation.js (12)
  • generateBtn (140-140)
  • generateBtn (238-238)
  • normalState (88-88)
  • normalState (208-208)
  • normalState (286-286)
  • tokenBtn (87-87)
  • tokenBtn (285-285)
  • scopeContainer (45-45)
  • scopeContainer (91-91)
  • scopeContainer (310-310)
  • modal (38-38)
  • modal (124-124)
🪛 Biome (2.1.2)
src/controllers/applicationsContentController.js

[error] 570-570: Unsafe usage of optional chaining.

If it short-circuits with 'undefined' the evaluation will throw TypeError here:

(lint/correctness/noUnsafeOptionalChaining)

🔇 Additional comments (36)
src/styles/manage-keys.css (1)

53-56: Focus state accessibility is preserved.

The :focus state removes the default outline but provides a visible box-shadow ring, which maintains keyboard accessibility. This approach is acceptable.

src/services/adminService.js (1)

1280-1298: Function signature update looks correct.

The mapKeys function now properly accepts and uses the keyType parameter in the request body. This change supports the per-key-type (PRODUCTION/SANDBOX) flow.

src/pages/application/partials/apis.hbs (3)

48-49: Per-key-type partial inclusion looks correct.

The api-key partial is now rendered twice per API (once for PRODUCTION, once for SANDBOX), enabling key-type-specific handling. This aligns with the broader per-key-type architecture.


70-117: Production/Sandbox key UI structure is well-implemented.

The conditional rendering properly handles four states:

  • Production key exists → show manage link
  • Production key missing → show generate button
  • Sandbox key exists → show manage link
  • Sandbox key missing → show generate button

The keyType parameter is correctly passed to openApiKeyModal as the last argument.


219-219: MCP Servers section should support dual-key handling like REST/WS APIs.

The MCP Servers section hardcodes 'PRODUCTION' as the keyType parameter. REST/WS APIs provide both PRODUCTION and SANDBOX key options via the api-key partial. If MCP servers should support sandbox keys for testing (similar to REST/WS APIs), implement dual-key generation to match the existing REST/WS pattern.

src/pages/application/partials/keys-generate.hbs (2)

28-30: Per-keyType iteration structure is correctly implemented.

The {{#each applicationKeys}} loop with {{#let "keys" keys}} properly establishes the per-key-type context, enabling separate rendering for PRODUCTION and SANDBOX environments.


67-138: Token generation and display sections properly scoped.

The token generation buttons, token display areas, and cURL sections all correctly include {{keyType}} in their IDs, ensuring isolation between PRODUCTION and SANDBOX environments.

src/controllers/apiContentController.js (1)

878-889: LGTM! Clear server descriptions improve API definition clarity.

Adding "Production" and "Sandbox" descriptions to the OpenAPI server entries is a good enhancement that helps API consumers distinguish between environments.

src/defaultContent/pages/manage-keys/page.hbs (1)

1-14: LGTM! Verify asset parity with the other manage-keys page.

The page structure is clean. Note that this template includes fewer assets compared to src/pages/manage-keys/page.hbs (missing application.css, subscription.js, application-dashboard.js, edit-application-form.js). Verify this is intentional for the defaultContent context, or align the assets if both pages require the same functionality.

src/routes/applicationsContentRoute.js (1)

23-28: LGTM! Route follows established patterns.

The new manage-keys route correctly mirrors the existing route structure with the favicon.ico guard, registerPartials, and ensureAuthenticated middleware chain. The loadApplicationKeys function is properly exported from applicationsContentController.js.

src/pages/application/partials/api-key.hbs (1)

1-50: LGTM! Per-key-type scoping correctly implemented.

The template properly scopes all element IDs and event handlers using the {{subID}}-{{keyType}} pattern, preventing ID collisions when multiple modals are rendered for different key types (PRODUCTION/SANDBOX). The change from "Done" to "Cancel" aligns better with the modal's action semantics.

src/pages/application/partials/oauth2-key-generation.hbs (1)

254-254: Good improvement: Environment-specific button ID.

Adding the production/sandbox suffix to the Update button ID prevents DOM ID collisions when both key types are rendered on the same page.

src/scripts/common.js (1)

1010-1013: Good defensive fix for accordion chevron toggle.

Adding the null check before manipulating icon.classList prevents potential runtime errors when the chevron element doesn't exist.

src/controllers/customContentController.js (1)

36-48: Good safety net for routing edge cases.

This defensive check ensures that if the manage-keys route unexpectedly falls through to the catch-all handler (due to Express route ordering or configuration issues), it's handled gracefully with appropriate logging rather than failing silently or rendering incorrect content.

src/routes/designModeRoute.js (1)

43-44: LGTM! Route additions follow established patterns.

The parameter rename from :applicationid to :applicationId improves naming consistency with JavaScript conventions. The new /manage-keys route is correctly placed after the base application route, ensuring proper route matching order. The loadApplicationKeys handler is properly exported from applicationsContentController.

src/pages/application/partials/keys-instructions.hbs (2)

1-11: LGTM! Per-keyType modal structure looks correct.

The nested iteration over keyManagersMetadata and applicationKeys properly scopes modal IDs and handlers by keyType. The modal title clearly indicates the key type context.


48-53: LGTM! Curl copy functionality properly scoped.

The copy button and curl code block IDs correctly include both the key manager id and keyType, preventing collisions between Production and Sandbox modals.

src/pages/application/partials/keys-view.hbs (3)

1-11: LGTM! Modal structure properly supports per-keyType credentials view.

The modal IDs, close handlers, and body containers are correctly scoped by keyType, enabling separate modals for Production and Sandbox keys.


196-234: Verify context navigation is intentional. The ID patterns use different depths based on their containing helpers: {{../../../id}} within {{#if}} blocks and {{../../../../id}} within {{#and}} blocks and attribute contexts. Confirm the {{#and}} helper changes context as expected for these paths to resolve correctly.


138-148: Remove the context depth verification—the code is correct.

Lines 138 and 141 use different context paths because keyType (defined at the applicationKeys loop) and id (defined at the keyManagersMetadata loop) exist at different nesting levels. Handlebars context resolution automatically walks the parent context chain, making {{keyType}} accessible without explicit ../ navigation, while {{../../../id}} correctly navigates to the outer loop context. This is not an inconsistency.

src/scripts/api-key-generation.js (1)

3-29: LGTM! Hash-based tab navigation implementation.

The DOMContentLoaded handler correctly activates Production/Sandbox tabs based on URL hash. Good defensive programming with existence checks for tab elements.

src/scripts/warning.js (3)

1-6: LGTM! Good defensive null check for modal element.

Early return with error logging when the modal element doesn't exist prevents null reference errors downstream.


29-35: LGTM! Good error handling for Bootstrap Modal initialization.

Wrapping the Modal instantiation in try-catch prevents unhandled exceptions if Bootstrap isn't loaded or the modal configuration is invalid.


50-64: LGTM! Improved security with function reference checks.

Using typeof === 'function' checks before invoking regenerateAPIKey and revokeAPIKey prevents errors if these functions aren't defined in the current context. The onclick assignment approach is safer than inline HTML attributes.

src/pages/application/partials/manage-keys.hbs (2)

89-122: LGTM! Key action buttons properly scoped by environment.

The View, Modify, Generate, and Instructions buttons correctly pass the key type ('PRODUCTION' or 'SANDBOX') to their respective modal loader functions. Read-only mode is consistently applied.

Also applies to: 329-362


248-258: LGTM! Empty state handling for no subscriptions.

Both Production and Sandbox sections properly handle the case when no API subscriptions exist, displaying an informative message to the user.

Also applies to: 490-500

src/pages/application/partials/generate-oauth-key.hbs (1)

504-506: Verify template block nesting order.

The closing blocks {{/let}}, {{/if}}, and {{/each}} at lines 504-506 close the applicationKeys iteration, but this appears before the closing {{/each}} for keyManagersMetadata. Ensure the intended nesting is correct—currently {{/if}} closes the {{#if enabled}} block from keyManagersMetadata, not from applicationKeys. The logic seems correct but is worth verifying during testing.

src/pages/application/partials/keys-modify.hbs (2)

1-11: Per-keyType modal structure looks correct.

The iteration over keyManagersMetadata and applicationKeys properly scopes each modal to a specific keyType. The modal ID keysModifyModal-{{keyType}} and the close button referencing the same ID ensure correct per-type modal management.


17-22: Form submission correctly includes keyType.

The form's onsubmit handler properly passes keyType to updateApplicationKey, ensuring the correct key type is updated.

src/pages/application/partials/overview.hbs (2)

67-99: Per-keyType OAuth2 keys section is well-structured.

The iteration over applicationKeys with conditional rendering based on keys.consumerKey existence correctly handles both generated and ungenerated key states for each environment. The keyType-aware button IDs and handler calls ensure proper isolation.


103-156: Hardcoded 'PRODUCTION' keyType in non-internal key manager branch.

The else branch (lines 103-156) for non-internal key managers uses hardcoded PRODUCTION for IDs and handler calls (e.g., generateKeyContainer-PRODUCTION, loadKeysViewModal('PRODUCTION')). Verify this is intentional and that sandbox key support is not required for external key managers. If sandbox support is needed later, this section will need refactoring.

src/scripts/oauth2-key-generation.js (5)

2-6: Good defensive validation for required parameters.

The upfront validation of required parameters prevents silent failures and provides clear error messages for debugging.


51-63: Good error handling for JSON parsing.

Wrapping JSON.parse(subscriptions) in a try-catch with proper error messaging and button state reset is a good practice.


117-118: Verify regex replacement for keyManagerId extraction.

The regex /-(SANDBOX|PRODUCTION)$/ correctly strips the keyType suffix from the formId. However, ensure all formIds follow the expected pattern keysview-{id}-{keyType}, or this extraction could fail silently.


840-860: Modal loading functions properly handle keyType.

The loadKeysViewModal, loadKeysModifyModal, loadKeysTokenModal, and loadKeysInstructionsModal functions correctly construct modal IDs using the keyType parameter and include null checks for missing modals.


431-432: No ID mismatch found—consumer key and secret IDs match template pattern.

The IDs on lines 431-432 use formId.replace("applicationKeyGenerateForm-", ""), which produces {{../id}}-{{keyType}}, matching the template IDs consumer-key-{{../id}}-{{keyType}} and consumer-secret-{{../id}}-{{keyType}}. The regex replacement on line 412 is for a different variable (appKeyManagerId) and does not affect the consumer-key/secret ID construction.

Comment on lines +8 to +19
// Sanitize parameters to prevent XSS
const sanitizeParam = (param) => {
if (param === null || param === undefined) return '';
return String(param).replace(/['"<>]/g, '');
};

const sanitizedParam1 = sanitizeParam(param1);
const sanitizedParam2 = sanitizeParam(param2);
const sanitizedParam3 = sanitizeParam(param3);
const sanitizedParam4 = sanitizeParam(param4);
const sanitizedParam5 = sanitizeParam(param5);
const sanitizedParam6 = sanitizeParam(param6);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for the warning.js file and check its context
find . -type f -name "warning.js" | head -5
# Check apiKeyId usage patterns
rg -n 'apiKeyId|api-key-id|data-api-key-id' --type=js -A 3 2>/dev/null | head -50
# Check sanitization pattern usage
rg -n 'sanitizeParam|sanitize.*param' --type=js -A 2 2>/dev/null | head -50

Repository: wso2/api-developer-portal-core

Length of output: 50386


Sanitization corrupts API key IDs—quotes are data, not XSS attempts.

API key IDs stored in data attributes include literal single quotes as part of their value (e.g., 'abc-123'). The sanitization removes these quotes, corrupting the data. The quotes are part of the intentional data structure, not XSS vectors. Either preserve the original unsanitized values separately or sanitize more carefully to avoid corrupting legitimate quote-wrapped values.

🤖 Prompt for AI Agents
In src/scripts/warning.js around lines 8 to 19 the current sanitizeParam
function strips quote characters and thus corrupts API key IDs that legitimately
include surrounding single quotes; stop removing quotes and preserve original
values for data attributes by (1) keeping rawParamX = paramX for
storage/attribute data, and (2) replace the sanitizeParam implementation used
for HTML insertion with proper escaping (HTML-entity encoding of &, <, >, " and
' to &amp;, &lt;, &gt;, &quot;, &#39;) or only escape when injecting into HTML
attributes rather than mutating the raw value so API key IDs remain intact.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pages/application/partials/api-key.hbs (1)

35-39: Bug: "Done" button uses incorrect modal ID, missing -{{keyType}} suffix.

The closeModal call on line 37 uses 'apiKeyModal-{{subID}}' but the modal ID on line 1 is apiKeyModal-{{subID}}-{{keyType}}. This mismatch will prevent the modal from closing when the "Done" button is clicked.

🔎 Proposed fix
                             <div class="d-flex justify-content-end mt-4 mb-3">
                                 <button class="common-btn-outlined pr-3 me-3" type="button"
-                                    onClick="closeModal('apiKeyModal-{{subID}}')">
+                                    onClick="closeModal('apiKeyModal-{{subID}}-{{keyType}}')">
                                     Done
                                 </button>
♻️ Duplicate comments (8)
src/scripts/common.js (1)

133-153: Duplicate: Apply the past review's suggested fixes.

The concerns flagged in the previous review remain unaddressed:

  1. Comments on lines 138 and 143 still incorrectly reference "API ID" when extracting applicationId.
  2. Line 134 still lacks optional chaining for applicationsSubmenu.classList.add('show'), unlike the guarded apiSubmenu?.classList.add('show') on line 113.

Please apply the diff provided in the past review comment.

src/services/adminService.js (1)

1244-1244: Duplicate: keyType remains undefined—apply the past review's fix.

The critical issue flagged in the previous review persists: keyType at line 1244 is undefined because it was never destructured from req.body at line 1160. This will cause the mapKeys call to fail.

Apply the fix suggested in the past review comment by adding keyType to the destructuring:

-const { applicationName, apis, tokenType, tokenDetails, provider, clientID } = req.body;
+const { applicationName, apis, tokenType, tokenDetails, provider, clientID, keyType } = req.body;
src/scripts/warning.js (1)

8-19: Duplicate: Sanitization still corrupts API key IDs—apply the past review's fix.

The concern raised in the previous review remains unaddressed. The sanitizeParam function strips quote characters that are legitimate parts of API key IDs (e.g., 'abc-123'). This corrupts the data when passed to downstream handlers like regenerateAPIKey and revokeAPIKey.

Apply the fix suggested in the past review: either preserve original values separately for data attributes or use proper HTML entity encoding rather than character removal.

src/pages/application/partials/oauth2-key-generation.hbs (1)

86-86: Duplicate: JSON in onclick attribute risks breakage—apply the past review's fix.

The concern flagged in the previous review remains unaddressed. Passing {{json ../../subAPIs}} directly into the onClick attribute can break the handler when the JSON contains quotes or special characters, and may introduce XSS risks.

Apply the data attribute approach suggested in the past review to safely pass the JSON and keyType to the handler.

src/controllers/applicationsContentController.js (2)

307-309: Unsafe optional chaining in for...of loop.

This issue was flagged in a previous review and remains unaddressed. If cpApplication is undefined, the expression cpApplication?.subscriptionScopes evaluates to undefined, and the for...of loop will throw a TypeError.

🔎 Suggested fix
             let subscriptionScopes = [];
-            if (Array.isArray(cpApplication?.subscriptionScopes)) {
-                for (const scope of cpApplication?.subscriptionScopes) {
+            if (cpApplication && Array.isArray(cpApplication.subscriptionScopes)) {
+                for (const scope of cpApplication.subscriptionScopes) {
                     subscriptionScopes.push(scope.key);
                 }
             }

569-572: Unsafe optional chaining in for...of loop (duplicate in loadApplicationKeys).

The same unsafe optional chaining issue exists in the new loadApplicationKeys function. If cpApplication is undefined, this will throw a TypeError.

🔎 Suggested fix
             let subscriptionScopes = [];
-            if (Array.isArray(cpApplication?.subscriptionScopes)) {
-                for (const scope of cpApplication?.subscriptionScopes) {
+            if (cpApplication && Array.isArray(cpApplication.subscriptionScopes)) {
+                for (const scope of cpApplication.subscriptionScopes) {
                     subscriptionScopes.push(scope.key);
                 }
             }
src/scripts/api-key-generation.js (2)

1-1: Remove unused Express import.

This issue was flagged in a previous review and remains unaddressed. The require("express") statement is dead code in a client-side script.

🔎 Proposed fix
-const e = require("express");
-

257-264: Incorrect loading state text for Generate button.

This issue was flagged in a previous review and remains unaddressed. The loading state incorrectly shows "Revoking..." for a Generate Key button.

🔎 Proposed fix
           generateButton.innerHTML = `
             <span class="button-normal-state">
               <i class="bi bi-key me-1"></i> ${envLabel}
             </span>
             <span class="button-loading-state" style="display: none;">
-              <span class="spinner-border spinner-border-sm me-1" role="status"> Revoking...</span>
+              <span class="spinner-border spinner-border-sm me-1" role="status"></span>
+              Generating...
             </span>
           `;
🧹 Nitpick comments (3)
src/pages/application/partials/keys-generate.hbs (1)

194-210: Context reference {{../id}} works correctly but may benefit from clarity.

At line 199, {{../id}} within the {{#each @root.availableGrantTypes}} loop correctly resolves to the keyManagersMetadata item's id field. While Handlebars' context stack lookup mechanism allows this to work (traversing up through the {{#let}} and {{#if}} blocks), the reference path is not immediately obvious from the code structure. Consider documenting the context hierarchy or using explicit variable passing if templates become more complex, as similar patterns at deeper nesting levels (e.g., line 241 using {{../../../../id}}) already suggest this could be a maintainability concern.

src/pages/application/partials/generate-oauth-key.hbs (2)

81-81: Consider refactoring to reduce parameter count.

The generateOauthKey function call has 8 parameters. While functional, this makes the code harder to maintain and error-prone when parameters need to be reordered or added.

💡 Consider using a data attribute approach

Instead of inline parameters, consider storing configuration in data attributes on the button:

-onClick="generateOauthKey('applicationKeyGenerateForm-{{../id}}-{{keyType}}', '{{keys.appRefID}}', '{{keys.keyMappingId}}', '{{../name}}', '{{keys.clientName}}', '{{keys.consumerSecret}}', '{{json @root.subAPIs}}', '{{keyType}}')"
+data-form-id='applicationKeyGenerateForm-{{../id}}-{{keyType}}'
+data-app-ref-id='{{keys.appRefID}}'
+data-key-mapping-id='{{keys.keyMappingId}}'
+data-key-manager='{{../name}}'
+data-client-name='{{keys.clientName}}'
+data-consumer-secret='{{keys.consumerSecret}}'
+data-sub-apis='{{json @root.subAPIs}}'
+data-key-type='{{keyType}}'
+onClick="generateOauthKey(this)"

Then read from event.target.dataset in the function.


337-337: Consider refactoring to reduce parameter count.

The generateApplicationKey function call has 9 parameters, making it difficult to maintain and prone to errors.

💡 Consider using a data attribute approach

Similar to the previous comment, consider using data attributes instead of inline parameters for better maintainability.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8d1bb00 and 4335b0b.

📒 Files selected for processing (28)
  • src/controllers/apiContentController.js (1 hunks)
  • src/controllers/applicationsContentController.js (7 hunks)
  • src/controllers/customContentController.js (2 hunks)
  • src/controllers/devportalController.js (1 hunks)
  • src/defaultContent/pages/manage-keys/page.hbs (1 hunks)
  • src/defaultContent/partials/sidebar.hbs (1 hunks)
  • src/defaultContent/styles/manage-keys.css (1 hunks)
  • src/pages/application/page.hbs (1 hunks)
  • src/pages/application/partials/api-key.hbs (3 hunks)
  • src/pages/application/partials/apis.hbs (3 hunks)
  • src/pages/application/partials/generate-oauth-key.hbs (21 hunks)
  • src/pages/application/partials/keys-generate.hbs (20 hunks)
  • src/pages/application/partials/keys-instructions.hbs (2 hunks)
  • src/pages/application/partials/keys-modify.hbs (8 hunks)
  • src/pages/application/partials/keys-token.hbs (3 hunks)
  • src/pages/application/partials/keys-view.hbs (7 hunks)
  • src/pages/application/partials/manage-keys.hbs (1 hunks)
  • src/pages/application/partials/oauth2-key-generation.hbs (2 hunks)
  • src/pages/application/partials/overview.hbs (5 hunks)
  • src/pages/manage-keys/page.hbs (1 hunks)
  • src/routes/applicationsContentRoute.js (1 hunks)
  • src/routes/designModeRoute.js (1 hunks)
  • src/scripts/api-key-generation.js (9 hunks)
  • src/scripts/common.js (3 hunks)
  • src/scripts/oauth2-key-generation.js (16 hunks)
  • src/scripts/warning.js (1 hunks)
  • src/services/adminService.js (2 hunks)
  • src/styles/manage-keys.css (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • src/pages/application/partials/manage-keys.hbs
🚧 Files skipped from review as they are similar to previous changes (10)
  • src/defaultContent/pages/manage-keys/page.hbs
  • src/controllers/devportalController.js
  • src/pages/application/partials/keys-token.hbs
  • src/controllers/apiContentController.js
  • src/pages/application/partials/keys-view.hbs
  • src/defaultContent/styles/manage-keys.css
  • src/controllers/customContentController.js
  • src/routes/designModeRoute.js
  • src/defaultContent/partials/sidebar.hbs
  • src/styles/manage-keys.css
🧰 Additional context used
🧬 Code graph analysis (5)
src/services/adminService.js (1)
src/middlewares/ensureAuthenticated.js (1)
  • keyType (427-427)
src/scripts/warning.js (3)
src/scripts/api-key-generation.js (2)
  • modal (38-38)
  • modal (124-124)
src/scripts/oauth2-key-generation.js (8)
  • modal (109-109)
  • modal (506-506)
  • modal (819-819)
  • modal (844-844)
  • modal (864-864)
  • modal (954-954)
  • modal (964-964)
  • modal (1192-1192)
src/scripts/subscription.js (3)
  • modal (10-10)
  • modal (55-55)
  • modal (166-166)
src/routes/applicationsContentRoute.js (3)
src/routes/devportalRoute.js (1)
  • router (19-19)
src/middlewares/registerPartials.js (2)
  • req (203-203)
  • registerPartials (35-92)
src/middlewares/ensureAuthenticated.js (1)
  • ensureAuthenticated (114-239)
src/scripts/api-key-generation.js (2)
src/middlewares/ensureAuthenticated.js (1)
  • keyType (427-427)
src/scripts/oauth2-key-generation.js (1)
  • scopes (579-579)
src/scripts/oauth2-key-generation.js (1)
src/scripts/api-key-generation.js (4)
  • generateBtn (140-140)
  • generateBtn (238-238)
  • modal (38-38)
  • modal (124-124)
🪛 Biome (2.1.2)
src/controllers/applicationsContentController.js

[error] 570-570: Unsafe usage of optional chaining.

If it short-circuits with 'undefined' the evaluation will throw TypeError here:

(lint/correctness/noUnsafeOptionalChaining)

🔇 Additional comments (24)
src/scripts/common.js (1)

1010-1013: LGTM: Null guard prevents runtime errors.

The null check around chevron icon toggling is a good defensive practice.

src/pages/application/partials/apis.hbs (1)

48-49: LGTM: Per-keyType rendering paths implemented correctly.

The introduction of separate production and sandbox key flows with distinct button labels, IDs, and modal invocations aligns with the PR's sandbox key management objectives. The changes correctly scope each key action to its environment.

Also applies to: 77-77, 81-81, 84-107, 210-210

src/pages/application/partials/overview.hbs (2)

67-99: LGTM: Per-keyType OAuth2 key rendering implemented correctly.

The per-keyType iteration and conditional rendering for production/sandbox keys is well-structured. Button IDs and onclick handlers correctly incorporate the keyType parameter.


12-13: LGTM: Proper disabled state handling for read-only mode.

The conditional disabled styling and pointer-events: none for edit buttons when @root.isReadOnlyMode is active provides good UX protection.

Also applies to: 32-33, 37-38

src/pages/application/partials/oauth2-key-generation.hbs (1)

254-254: LGTM: Button ID correctly scoped by environment.

The per-environment button ID suffix aligns with the PR's per-keyType management approach.

src/pages/application/page.hbs (1)

19-28: LGTM: Key modals correctly moved to manage-keys page.

The architectural change to serve key-related modals from the dedicated manage-keys page aligns with the PR's objectives for sandbox key management. Retaining only the keys-view component here is appropriate.

src/routes/applicationsContentRoute.js (1)

23-28: LGTM: Manage-keys route follows established patterns.

The new route for /applications/:applicationId/manage-keys correctly applies the standard middleware stack and aligns with the PR's sandbox key management objectives.

src/pages/application/partials/keys-instructions.hbs (1)

1-65: LGTM - Per-keyType modal structure is consistent.

The template correctly iterates over keyManagersMetadata and applicationKeys to generate per-keyType instruction modals. The modal IDs, close handlers, and content element IDs all consistently use the -{{keyType}} suffix pattern.

src/pages/application/partials/keys-modify.hbs (2)

1-4: LGTM - Per-keyType modal structure implemented correctly.

The template correctly establishes per-keyType modals by iterating through keyManagersMetadata and applicationKeys. The modal IDs, form IDs, error containers, and all interactive elements consistently use the -{{keyType}} suffix pattern.


106-129: File does not exist in the repository.

The file path src/pages/application/partials/keys-modify.hbs does not exist in the repository. The review comment references a non-existent file, making the entire comment invalid. Verify the correct file path before proceeding with the review.

Likely an incorrect or invalid review comment.

src/pages/application/partials/keys-generate.hbs (1)

28-30: Good addition of per-keyType iteration structure.

The introduction of {{#each applicationKeys}} with {{#let "keys" keys}} correctly enables per-keyType rendering of all key-related UI components within the OAuth modal.

src/pages/manage-keys/page.hbs (1)

1-17: LGTM - Page template structure is consistent with per-keyType architecture.

The template correctly loads the required assets and composes the necessary partials for the manage-keys workflow. The structure aligns with the broader per-keyType UI refactoring and follows the established pattern of partial page templates used throughout the codebase.

src/scripts/api-key-generation.js (2)

137-159: Approve the page-aware button management logic.

The logic correctly detects whether the user is on the manage-keys page or APIs page and adjusts the UI accordingly. This separation ensures proper UX flow between the two contexts.


3-29: Approve the hash-based tab navigation.

The DOMContentLoaded handler correctly implements hash-based navigation for production/sandbox tabs on the manage-keys page, improving deep-linking support.

src/controllers/applicationsContentController.js (2)

206-211: Approve the per-keyType API key fetching.

The changes correctly fetch production and sandbox keys separately and organize them into the apiDTO.apiKeys structure, enabling proper isolation between key types.


721-731: All callers of getAPIKeys have been properly updated with the keyType parameter.

Verification confirms all four call sites (lines 206, 207, 468, 469) pass the required keyType parameter with values 'PRODUCTION' or 'SANDBOX'. No action needed.

src/scripts/oauth2-key-generation.js (7)

2-21: Approve the defensive parameter and element validation.

The added validation guards prevent runtime errors by checking for missing parameters, buttons, and button states before proceeding. The early returns ensure graceful failure without breaking the UI.


35-47: Approve the form existence check with user feedback.

The guard prevents attempting to read from a non-existent form and provides clear user guidance to refresh the page. The button state reset ensures the UI remains usable.


49-63: Approve the subscriptions parsing error handling.

The try/catch block safely handles malformed JSON data and provides clear error messaging to the user, preventing the application from crashing on invalid data.


117-129: Approve the safe element updates with existence checks.

The code safely updates form fields only when the elements exist, preventing null reference errors and making the code more resilient to DOM variations.


535-572: Approve the keyType-aware token button selection.

The logic correctly selects the appropriate token button (Sandbox or Production) based on the keyType parameter, and safely handles the scopes data with proper fallbacks.


840-860: Approve the environment-parameterized modal loading.

The loadKeysViewModal function properly accepts an environment parameter and includes error logging when the target modal is not found, improving debuggability.


862-924: Approve the keyType-aware modify modal with grant type validation.

The loadKeysModifyModal function correctly scopes modal operations by keyType and includes validation to ensure at least one grant type is selected before allowing updates.

src/pages/application/partials/generate-oauth-key.hbs (1)

342-342: The onclick and container ID are consistent within each branch, but different patterns between branches should be intentional.

Line 342's showAdvanced('KMData_{{../name}}') correctly matches the container ID at line 352. Similarly, line 181's onclick correctly matches line 192's ID with the keyType suffix. The actual difference is architectural: when keys.consumerKey exists ({{if}} branch at line 36), the ID includes keyType; when generating new credentials ({{else}} branch at line 177), it omits keyType. Verify this branch-level difference is intentional rather than an oversight, as the pattern is unusual compared to keys-generate.hbs which includes keyType in both branches.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/controllers/applicationsContentController.js (1)

731-741: Fix logic bug in getAPIKeys: loop overwrites multiple matching keys instead of collecting them.

The endpoint integration is correct, but the filtering logic has a critical flaw. If the /api-keys?apiId=${apiId}&keyType=${keyType} endpoint returns multiple keys matching the same applicationId, only the last one is preserved because apiKeys.key and apiKeys.scopes are overwritten each iteration. Additionally, there's no null/undefined check on responseData or key.application before use, which will cause errors if the response structure is unexpected. Consider refactoring to collect all matching keys in an array or add proper defensive checks.

♻️ Duplicate comments (3)
src/controllers/applicationsContentController.js (1)

391-660: Significant code duplication remains unaddressed.

The new loadApplicationKeys function (270 lines) duplicates almost all logic from loadApplication (lines 125-389). The functions are nearly identical except for the template name ('manage-keys' vs 'application'). This creates substantial maintenance burden as bug fixes and feature additions must be synchronized across both functions.

💡 Recommended refactoring approach

Extract the shared data-fetching and processing logic into a reusable helper function:

async function loadApplicationData(req, orgName, applicationId, viewName) {
    const orgID = await orgIDValue(orgName);
    let groupList = [];
    if (req.query.groups) {
        groupList.push(req.query.groups.split(" "));
    }
    
    const subAPIs = await adminDao.getSubscribedAPIs(orgID, applicationId);
    const allAPIs = await apiMetadata.getAllAPIMetadata(orgID, groupList, viewName);
    
    // ... all the common data fetching/processing logic ...
    
    return {
        orgID,
        metaData,
        kMmetaData,
        subList,
        nonSubscribedAPIs,
        productionKeys,
        sandboxKeys,
        subscriptionScopes,
        isApiKey,
        otherAPICount,
        mcpAPICount,
        applicationReference
    };
}

Then simplify both functions:

const loadApplication = async (req, res) => {
    const data = await loadApplicationData(req, orgName, applicationId, viewName);
    const templateContent = {
        ...data,
        applicationMetadata: { ...data.metaData, subscriptionCount: data.subList.length },
        // ... application-specific fields ...
    };
    // Render 'application' template
};

const loadApplicationKeys = async (req, res) => {
    const data = await loadApplicationData(req, orgName, applicationId, viewName);
    const templateContent = {
        ...data,
        applicationMetadata: { ...data.metaData, subscriptionCount: data.subList.length },
        // ... manage-keys-specific fields ...
    };
    // Render 'manage-keys' template
};
src/pages/application/partials/manage-keys.hbs (2)

49-50: Duplicate element IDs remain unfixed.

As flagged in the previous review, id="consumerKey" (lines 49, 290) and id="consumerSecret" (lines 68, 309) are duplicated between Production and Sandbox sections. These duplicate IDs violate the HTML specification and will cause getElementById to return only the first match, breaking JavaScript functionality.

🔎 Proposed fix

The IDs should be unique per environment. The inner input fields already use scoped IDs (e.g., consumer-key-{{../id}}-production), but the container divs do not. Since these container IDs don't appear to be referenced in the code, consider removing them entirely or making them unique:

-                                        <div id="consumerKey"
+                                        <div id="consumerKey-production"
                                             class="{{#unless keys.consumerSecret}} col-md-12 {{else}} col-md-6 {{/unless}}">
...
-                                        <div id="consumerSecret" class="col-md-6"
+                                        <div id="consumerSecret-production" class="col-md-6"

And for Sandbox (lines 290, 309):

-                                        <div id="consumerKey"
+                                        <div id="consumerKey-sandbox"
...
-                                        <div id="consumerSecret" class="col-md-6"
+                                        <div id="consumerSecret-sandbox" class="col-md-6"

Also applies to: 68-69


393-401: Duplicate accordion IDs remain unfixed.

As flagged in the previous review, the Sandbox API Keys accordion (lines 393-401) reuses the same IDs (headingFour, collapseFour) as the Production API Keys accordion (lines 151-158). This will cause Bootstrap's collapse functionality to malfunction, potentially causing both accordions to expand/collapse together or only one to work.

🔎 Proposed fix
                     <div class="accordion-item">
-                        <h2 class="accordion-header" id="headingFour">
+                        <h2 class="accordion-header" id="headingSandboxApiKeys">
                             <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
-                                data-bs-target="#collapseFour" aria-expanded="false" aria-controls="collapseFour">
+                                data-bs-target="#collapseSandboxApiKeys" aria-expanded="false" aria-controls="collapseSandboxApiKeys">
                                 API Keys
                             </button>
                         </h2>
-                        <div id="collapseFour" class="accordion-collapse collapse" aria-labelledby="headingFour"
+                        <div id="collapseSandboxApiKeys" class="accordion-collapse collapse" aria-labelledby="headingSandboxApiKeys"
                             data-bs-parent="#sandboxAccordion">
🧹 Nitpick comments (4)
src/pages/application/partials/apis.hbs (2)

84-84: Refactor long parameter lists for better maintainability.

The openApiKeyModal calls pass 9 parameters, making the code difficult to read, maintain, and validate. Consider refactoring to accept a configuration object instead.

🔎 Suggested approach

Refactor the JavaScript function to accept a configuration object:

// In the related JavaScript file
function openApiKeyModal(config) {
  const {
    projectId,
    refID,
    policyName,
    appRefID,
    appID,
    subID,
    subIDs,
    scopes,
    keyType
  } = config;
  // ... implementation
}

Then update the template:

-onclick="openApiKeyModal('{{projectId}}', '{{refID}}', '{{policyName}}', '{{../applicationMetadata.appMap.[0].appRefID}}', '{{../applicationMetadata.id}}', '{{subID}}', '{{getSubIDs ../subAPIs}}', '{{json scopes}}', 'PRODUCTION')"
+onclick="openApiKeyModal({ projectId: '{{projectId}}', refID: '{{refID}}', policyName: '{{policyName}}', appRefID: '{{../applicationMetadata.appMap.[0].appRefID}}', appID: '{{../applicationMetadata.id}}', subID: '{{subID}}', subIDs: '{{getSubIDs ../subAPIs}}', scopes: '{{json scopes}}', keyType: 'PRODUCTION' })"

Also applies to: 107-107


48-49: Render api-key modals conditionally based on key existence.

The api-key partials at lines 48-49 are included unconditionally for each subscribed API (PRODUCTION and SANDBOX), adding 2N modal divs to the DOM. However, the conditional checks at lines 72 and 95 only display management buttons when apiKeys.production.key and apiKeys.sandbox.key exist. Consider wrapping the partial includes in conditional blocks to avoid rendering unused modals in the DOM:

{{#each subAPIs}}
{{#if ../apiKeys.production.key}}
{{> api-key apiID=apiID keyType="PRODUCTION"}}
{{/if}}
{{#if ../apiKeys.sandbox.key}}
{{> api-key apiID=apiID keyType="SANDBOX"}}
{{/if}}
src/pages/application/partials/manage-keys.hbs (1)

106-106: Consider refactoring functions with many inline parameters.

Several onclick handlers pass many parameters inline, making them hard to read and maintain. For example:

  • Line 106: generateOauthKey with 8 parameters
  • Line 129: generateApplicationKey with 10 parameters
  • Line 229, 471: openApiKeyModal with 9 parameters
💡 Suggested approach

Consider using data-* attributes to store parameters on the button elements and reading them in the handler function, similar to how data-scopes is used on line 196:

<button id="tokenKeyBtnProduction"
    class="common-btn-outlined btn-sm"
    data-app-ref-id="{{keys.appRefID}}"
    data-key-mapping-id="{{keys.keyMappingId}}"
    data-km-name="{{../name}}"
    data-app-name="{{../../applicationMetadata/name}}"
    data-consumer-secret="{{keys.consumerSecret}}"
    data-subscription-scopes="{{json @root.subscriptionScopes}}"
    data-key-type="PRODUCTION"
    onclick="generateOauthKey(this)">

Then modify the JavaScript function to extract parameters from the button:

function generateOauthKey(button) {
    const appRefId = button.dataset.appRefId;
    const keyMappingId = button.dataset.keyMappingId;
    // ... etc
}

This improves maintainability and follows modern best practices.

Also applies to: 129-129, 229-229, 347-347, 370-370, 471-471

src/controllers/applicationsContentController.js (1)

579-583: Clarify optional chaining for static analysis.

While the current code is technically safe (the Array.isArray check guards against undefined), static analysis correctly flags that optional chaining in the for...of can throw if it evaluates to undefined. For clarity and to silence the linter, explicitly check that cpApplication exists before accessing its properties.

🔎 Proposed fix
             let subscriptionScopes = [];
-            if (Array.isArray(cpApplication?.subscriptionScopes)) {
-                for (const scope of cpApplication?.subscriptionScopes) {
+            if (cpApplication && Array.isArray(cpApplication.subscriptionScopes)) {
+                for (const scope of cpApplication.subscriptionScopes) {
                     subscriptionScopes.push(scope.key);
                 }
             }

This makes the null/undefined check explicit and matches the pattern used elsewhere in the codebase.

Note: The same pattern at lines 307-311 in loadApplication should also be updated for consistency.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4335b0b and 59e1aa2.

📒 Files selected for processing (5)
  • src/controllers/applicationsContentController.js
  • src/defaultContent/partials/sidebar.hbs
  • src/pages/application/partials/api-key.hbs
  • src/pages/application/partials/apis.hbs
  • src/pages/application/partials/manage-keys.hbs
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/defaultContent/partials/sidebar.hbs
  • src/pages/application/partials/api-key.hbs
🧰 Additional context used
🪛 Biome (2.1.2)
src/controllers/applicationsContentController.js

[error] 580-580: Unsafe usage of optional chaining.

If it short-circuits with 'undefined' the evaluation will throw TypeError here:

(lint/correctness/noUnsafeOptionalChaining)

🔇 Additional comments (8)
src/pages/application/partials/apis.hbs (1)

219-219: Consistent keyType parameter for MCP servers.

The addition of the 'PRODUCTION' parameter to the generateAPIKey call is consistent with the PR's objective of keyType-aware key management. MCP servers appropriately use only production keys.

Ensure this parameter addition is verified as part of the function signature check in the earlier review comment.

src/controllers/applicationsContentController.js (6)

206-211: LGTM! API keys now correctly fetched per environment.

The change to fetch production and sandbox keys separately and structure them as an object with production and sandbox properties aligns well with the new manage-keys UI requirements. This enables environment-specific key display and management.


292-302: LGTM! Well-structured applicationKeys for template consumption.

The per-keyManager applicationKeys array structure with production and sandbox objects provides a clean, consistent interface for the templates. The || {} fallback ensures undefined values don't cause issues.


335-345: LGTM! Template content properly includes sandbox keys and applicationKeys.

The addition of sandboxKeys and applicationKeys to the template content correctly exposes per-environment key data for the UI to render Production and Sandbox sections independently.


367-389: LGTM! Improved error handling with proper context.

The enhanced error handling now differentiates between authentication errors (401) and general errors, providing appropriate error messages and maintaining context (orgName, applicationId, features) for the error page rendering.


804-806: LGTM! Module exports correctly updated.

The new loadApplicationKeys function is properly exported alongside the existing exports, making it available for use in routing.


722-724: No issues found with the endpoint change.

The /key-managers endpoint returns all key managers from the control plane, and client-side filtering adequately handles the selection of appropriate key managers by checking the enabled flag and filtering by name patterns. The claim about removing a devPortalAppEnv filter cannot be substantiated as this parameter does not exist in the codebase.

src/pages/application/partials/manage-keys.hbs (1)

2-4: Resource paths are correctly referenced and files exist in the repository.

The template correctly references static assets at /technical-styles/manage-keys.css, /technical-scripts/oauth2-key-generation.js, and /technical-scripts/api-key-generation.js. The corresponding source files exist in the codebase at src/styles/manage-keys.css and src/scripts/ for both JavaScript files.

@Induwara04
Copy link
Contributor

Screenshot 2025-12-22 at 10 48 24
Screenshot 2025-12-22 at 10 48 41 (1)
Add a title on Generate Button

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/scripts/oauth2-key-generation.js (1)

528-560: Critical: Const variable reassignment.

Line 540 attempts to reassign tokenBtn, which is declared as const on line 530. This will throw a TypeError in strict mode.

🔎 Proposed fix

Change the declaration to let:

 async function generateOauthKey(formId, appId, keyMappingId, keyManager, clientName, clientSecret, subscribedScopes, keyType) {
     // Use the clicked button if provided, otherwise fall back to default buttons
-    const tokenBtn = document.getElementById('tokenKeyBtn-' + keyType);
+    let tokenBtn = document.getElementById('tokenKeyBtn-' + keyType);
     const devAppId = tokenBtn?.dataset?.appId
     const scopeContainer = document.getElementById('scopeContainer-' + devAppId + '-' + keyType);
     const scopeInput = document.getElementById('scope-' + devAppId);
♻️ Duplicate comments (5)
src/pages/application/partials/keys-generate.hbs (1)

328-328: Critical: Handlebars syntax error remains unfixed.

Line 328 still uses {{@root.applicationMetadata/name}} with a forward slash instead of dot notation. This was flagged in a previous review and will cause undefined values or runtime errors.

🔎 Proposed fix
-                                    onClick="generateApplicationKey('applicationKeyGenerateForm-{{../id}}-{{keyType}}', '{{../../applicationMetadata.id}}', '{{keyType}}', '{{../name}}', '{{{../../applicationMetadata/name}}', '{{ json ../../subAPIs}}', '{{../../orgID}}', 'consumer-key-{{../id}}-{{keyType}}', 'consumer-secret-{{../id}}-{{keyType}}')">
+                                    onClick="generateApplicationKey('applicationKeyGenerateForm-{{../id}}-{{keyType}}', '{{../../applicationMetadata.id}}', '{{keyType}}', '{{../name}}', '{{../../applicationMetadata.name}}', '{{ json ../../subAPIs}}', '{{../../orgID}}', 'consumer-key-{{../id}}-{{keyType}}', 'consumer-secret-{{../id}}-{{keyType}}')">
src/pages/application/partials/oauth2-key-generation.hbs (1)

86-86: Major: Potential HTML injection/escaping issue remains unfixed.

Line 86 still passes {{json ../../subAPIs}} directly into the onClick attribute. This was flagged in a previous review as a potential XSS risk and can break the handler if the JSON contains quotes or special characters.

🔎 Recommended approach

Use data attributes instead of inline JSON:

-                                            onClick="generateOauthKey('applicationKeyGenerateForm-{{../id}}-{{#if ../../isProduction}}production{{else}}sandbox{{/if}}', '{{../../applicationMetadata.applicationId}}', '{{keys.keyMappingId}}', '{{../name}}', '{{keys.clientName}}', '{{json ../../subAPIs}}', '{{#if ../../isProduction}}PRODUCTION{{else}}SANDBOX{{/if}}')">
+                                            data-sub-apis='{{json ../../subAPIs}}'
+                                            data-key-type='{{#if ../../isProduction}}PRODUCTION{{else}}SANDBOX{{/if}}'
+                                            onClick="generateOauthKey('applicationKeyGenerateForm-{{../id}}-{{#if ../../isProduction}}production{{else}}sandbox{{/if}}', '{{../../applicationMetadata.applicationId}}', '{{keys.keyMappingId}}', '{{../name}}', '{{keys.clientName}}', this)">

Then update generateOauthKey to read from button.dataset.subApis and button.dataset.keyType.

src/pages/application/partials/manage-keys.hbs (3)

49-50: Duplicate IDs remain unfixed.

The id="consumerKey" (Line 49) and id="consumerSecret" (Line 68) in the Production section are duplicated in the Sandbox section (Lines 290, 309). This violates the HTML spec and will cause getElementById to return only the first match, breaking JavaScript that targets these elements.

🔎 Proposed fix

Since the inner input fields already use environment-scoped IDs like consumer-key-{{../id}}-PRODUCTION, apply the same pattern to these container divs:

-                                        <div id="consumerKey"
+                                        <div id="consumerKey-PRODUCTION"
                                             class="{{#unless keys.consumerSecret}} col-md-12 {{else}} col-md-6 {{/unless}}">
-                                        <div id="consumerSecret" class="col-md-6"
+                                        <div id="consumerSecret-PRODUCTION" class="col-md-6"

Apply corresponding changes in the Sandbox section (Lines 290, 309) using -SANDBOX suffix, and update any JavaScript selectors that reference these IDs.

Also applies to: 68-69


151-158: Duplicate accordion IDs remain unfixed.

The Production API Keys accordion uses id="headingFour" (Line 151) and id="collapseFour" (Line 157), which are duplicated in the Sandbox API Keys accordion (Lines 397, 403). This will cause Bootstrap's collapse functionality to malfunction when toggling either accordion.

🔎 Proposed fix

Rename the Production accordion IDs to be unique:

                     <div class="accordion-item">
-                        <h2 class="accordion-header" id="headingFour">
+                        <h2 class="accordion-header" id="headingProductionApiKeys">
                             <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
-                                data-bs-target="#collapseFour" aria-expanded="false" aria-controls="collapseFour">
+                                data-bs-target="#collapseProductionApiKeys" aria-expanded="false" aria-controls="collapseProductionApiKeys">
                                 API Keys
                             </button>
                         </h2>
-                        <div id="collapseFour" class="accordion-collapse collapse" aria-labelledby="headingFour"
+                        <div id="collapseProductionApiKeys" class="accordion-collapse collapse" aria-labelledby="headingProductionApiKeys"
                             data-bs-parent="#productionAccordion">

Apply corresponding changes to Sandbox accordion IDs (Lines 397-403).


249-250: Duplicate no-subscription ID remains unfixed.

The id="no-subscription" at Line 249 in the Production section is duplicated in the Sandbox section at Line 494. This violates the HTML spec and will cause JavaScript targeting this element to only find the first match.

🔎 Proposed fix

Scope the IDs by environment:

-                                <div class="ps-3" id="no-subscription"
+                                <div class="ps-3" id="no-subscription-production"
                                     style="{{#if subAPIs.length}}display:none{{/if}}">

And for Sandbox at Line 494:

-                                <div class="ps-3" id="no-subscription"
+                                <div class="ps-3" id="no-subscription-sandbox"
                                     style="{{#if subAPIs.length}}display:none{{/if}}">

Update any JavaScript that references #no-subscription to use the environment-specific IDs.

🧹 Nitpick comments (7)
src/controllers/applicationsContentController.js (1)

62-246: Excellent refactoring to eliminate code duplication.

The new loadApplicationData helper successfully addresses the code duplication issue flagged in previous reviews. It centralizes data loading logic for both loadApplication and loadApplicationKeys, making the codebase more maintainable.

The function properly handles:

  • API fetching and filtering
  • Key management data assembly
  • Per-keyType key segregation (PRODUCTION/SANDBOX)
  • Subscription scope gathering

Minor suggestion: Consider adding JSDoc comments documenting the function's parameters and return structure, as this is now a core shared function.

src/pages/application/partials/manage-keys.hbs (6)

124-139: Add tooltip to Production Generate Key button for consistency.

The Sandbox OAuth2 section includes a helpful tooltip explaining "This key is shared among all APIs" (Lines 380-383), but the Production section's Generate Key button (Lines 124-139) lacks this tooltip. This creates an inconsistency in the user experience.

🔎 Proposed fix

Add a tooltip container after the Generate Key button in Production, matching the Sandbox implementation:

                                     </button>
+                                    <div class="tooltip-container ms-2">
+                                        <i class="bi bi-info-circle"></i>
+                                        <span class="tooltip-text">This key is shared among all APIs</span>
+                                    </div>
                                 </div>
                                 </div>

337-342: Use consistent ID naming convention for Sandbox Modify button.

The Sandbox Modify button uses id="modifyKeyBtnSandbox" (Line 337, camelCase with no separator), while the Production Modify button uses id="modifyKeyBtn-PRODUCTION" (Line 96, dash-separated). This inconsistency makes the codebase harder to maintain and could lead to errors when referencing these IDs in JavaScript or CSS.

🔎 Proposed fix

Rename the Sandbox button to match the Production naming pattern:

-                                            <button id="modifyKeyBtnSandbox"
+                                            <button id="modifyKeyBtn-SANDBOX"
                                                 class="common-btn-outlined btn-sm {{#if @root.isReadOnlyMode}}disabled{{/if}}"
                                                 style="{{#if @root.isReadOnlyMode}}pointer-events: none; opacity: 0.5;{{/if}}"
                                                 onclick="loadKeysModifyModal('SANDBOX')">

Update any JavaScript or CSS selectors that reference #modifyKeyBtnSandbox to use #modifyKeyBtn-SANDBOX.


126-137: Consider adding title attributes to Generate Key buttons.

Per the PR feedback from Induwara04, adding a title attribute to the Generate Key buttons would improve accessibility and provide helpful hover tooltips. Currently, none of the Generate Key buttons (OAuth2 at Lines 126-137, 367-379; API Key at Lines 226-238, 471-483) have title attributes.

🔎 Proposed enhancement

Add title attributes to all Generate Key buttons:

For OAuth2 Generate Key buttons (Lines 126, 367):

                                     <button data-cyid="generateKey-PRODUCTION" id="generateKeyBtn-PRODUCTION"
                                         class="common-btn-primary btn-sm {{#if @root.isReadOnlyMode}}disabled{{/if}}"
+                                        title="Generate OAuth2 application key for Production environment"
                                         style="{{#if @root.isReadOnlyMode}}pointer-events: none; opacity: 0.5;{{/if}}"

For API Key Generate Key buttons (Lines 226, 471):

                                                         <button
                                                             class="common-btn-outlined btn-sm me-1 {{#if @root.isReadOnlyMode}}disabled{{/if}}"
                                                             id="generateKeyBtn-{{subID}}-PRODUCTION"
+                                                            title="Generate API Key for this subscription"
                                                             style="{{#if apiKeys.production.key}}display: none;{{else}}display: inline-flex;{{/if}}"

Apply similar titles to Sandbox buttons with appropriate environment labels.

Also applies to: 226-238, 367-379, 471-483


254-255: Align no-subscription message text across environments.

The "no subscriptions" message differs between Production (Lines 254-255: "Please subscribe to an API.") and Sandbox (Lines 499-500: "Please subscribe to an API to generate an API Key."). While the meaning is similar, the inconsistency may cause users to wonder if there's a functional difference between the two environments.

🔎 Proposed fix

Use identical wording in both sections. The more explicit Sandbox version is clearer:

                                             <div class="info-title">
                                                 No subscriptions available for this application. Please subscribe to an
-                                                API.
+                                                API to generate an API Key.
                                             </div>

Also applies to: 499-500


197-197: Remove unnecessary nested quotes from data-api-key-id attributes.

The data-api-key-id attributes contain nested single quotes: data-api-key-id="'{{apiKeys.production.key.[0].id}}'" (Lines 197, 211 for Production; Lines 442, 456 for Sandbox). While the JavaScript correctly sanitizes and strips these quotes before use, the pattern is unnecessarily convoluted. Simplify by removing the quotes from both the template and the dynamic assignment in src/scripts/api-key-generation.js (lines 165, 172) to make the code clearer.

Also applies to: 211-211, 442-442, 456-456


410-487: Add Policy column to Sandbox API Keys table for consistency with Production.

The Sandbox API Keys table (lines 410-487) displays only 3 columns (Name, Version, API Key), while the Production table includes a 4th Policy column (line 189). Both sections use the update-plan partial with subscriptionPolicyDetails and policyName variables, and both reference policyName in the openApiKeyModal function calls. This indicates policy information is available for Sandbox keys but simply not rendered. To maintain consistent UI patterns between environments, consider adding the Policy column to the Sandbox table header and rendering the policy name in each row.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 59e1aa2 and 698b49d.

📒 Files selected for processing (9)
  • src/controllers/applicationsContentController.js
  • src/controllers/devportalController.js
  • src/pages/application/partials/apis.hbs
  • src/pages/application/partials/keys-generate.hbs
  • src/pages/application/partials/manage-keys.hbs
  • src/pages/application/partials/oauth2-key-generation.hbs
  • src/pages/application/partials/overview.hbs
  • src/scripts/common.js
  • src/scripts/oauth2-key-generation.js
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/controllers/devportalController.js
  • src/pages/application/partials/apis.hbs
  • src/scripts/common.js
🧰 Additional context used
🧬 Code graph analysis (1)
src/scripts/oauth2-key-generation.js (1)
src/scripts/api-key-generation.js (10)
  • generateBtn (140-140)
  • generateBtn (238-238)
  • normalState (88-88)
  • normalState (208-208)
  • normalState (286-286)
  • tokenBtn (87-87)
  • tokenBtn (285-285)
  • scopeContainer (45-45)
  • scopeContainer (91-91)
  • scopeContainer (310-310)
🪛 Biome (2.1.2)
src/controllers/applicationsContentController.js

[error] 224-224: Unsafe usage of optional chaining.

If it short-circuits with 'undefined' the evaluation will throw TypeError here:

(lint/correctness/noUnsafeOptionalChaining)

src/scripts/oauth2-key-generation.js

[error] 540-540: Can't assign tokenBtn because it's a constant.

This is where the variable is defined as constant.

Unsafe fix: Replace const with let if you assign it to a new value.

(lint/correctness/noConstAssign)

🔇 Additional comments (4)
src/controllers/applicationsContentController.js (1)

46-56: LGTM: Clean profile builder helper.

The new buildProfile helper cleanly extracts profile construction logic with appropriate null handling.

src/scripts/oauth2-key-generation.js (3)

1-63: Good defensive programming with comprehensive validation.

The added parameter validation, element existence checks, and error handling significantly improve robustness. The function now gracefully handles missing DOM elements and invalid data with clear error messages.


115-159: LGTM: Safe DOM manipulation with proper guards.

The response handling code properly validates element existence before manipulation, preventing potential null reference errors. The environment suffix extraction using regex is clean and maintains the per-keyType scoping pattern.


823-949: Well-structured modal loading with keyType support.

The modal loading functions consistently accept keyType parameters and include proper existence checks with console error logging. This aligns well with the per-environment scoping pattern throughout the PR.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/scripts/oauth2-key-generation.js (1)

528-559: Cannot reassign const variable tokenBtn.

The static analyzer correctly identified this issue. tokenBtn is declared as const on line 529, but line 539 attempts to reassign it, which will throw a TypeError at runtime.

🔎 Proposed fix
 async function generateOauthKey(formId, appId, keyMappingId, keyManager, clientName, clientSecret, subscribedScopes, keyType) {
-    const tokenBtn = document.getElementById('tokenKeyBtn-' + keyType);
+    let tokenBtn = document.getElementById('tokenKeyBtn-' + keyType);
     const devAppId = tokenBtn?.dataset?.appId
     const scopeContainer = document.getElementById('scopeContainer-' + devAppId + '-' + keyType);
     const scopeInput = document.getElementById('scope-' + devAppId);
♻️ Duplicate comments (8)
src/scripts/api-key-generation.js (2)

1-1: Unused Express import still present.

This issue was previously flagged. The require("express") is unused in this client-side script and e is never referenced.


229-236: Loading state text still incorrect for the Generate button.

This was previously discussed - the button being created is a "Generate Key" button (id: generateKeyBtn-, title: "Generate {envLabel} Key"), but the loading state still says "Revoking...". Although this code is inside revokeAPIKey, this button will be clicked by users to generate a new key after revocation, so users will see "Revoking..." when they click "Generate Key".

🔎 Proposed fix
           generateButton.innerHTML = `
             <span class="button-normal-state">
               <i class="bi bi-key me-1"></i> ${envLabel}
             </span>
             <span class="button-loading-state" style="display: none;">
-              <span class="spinner-border spinner-border-sm me-1" role="status"> Revoking...</span>
+              <span class="spinner-border spinner-border-sm me-1" role="status"></span>
+              Generating...
             </span>
           `;
src/pages/application/partials/manage-keys.hbs (5)

15-28: Reversed aria-selected values on tab buttons.

The Production tab is visually active (class="key-btn active") but has aria-selected="false", while the inactive Sandbox tab has aria-selected="true". This accessibility issue was previously flagged and remains unresolved.

🔎 Proposed fix
             <li class="nav-item" role="presentation">
                 <button class="key-btn active" id="production-tab" data-bs-toggle="tab" data-bs-target="#production"
-                    type="button" role="tab" aria-controls="production" aria-selected="false">
+                    type="button" role="tab" aria-controls="production" aria-selected="true">
                     Production
                 </button>
             </li>
             <li class="nav-item" role="presentation">
                 <button class="key-btn" id="sandbox-tab" data-bs-toggle="tab" data-bs-target="#sandbox" type="button"
-                    role="tab" aria-controls="sandbox" aria-selected="true">
+                    role="tab" aria-controls="sandbox" aria-selected="false">
                     Sandbox
                 </button>
             </li>

49-50: Duplicate consumerKey and consumerSecret IDs.

The id="consumerKey" and id="consumerSecret" are used in both Production (lines 49, 68) and Sandbox (lines 290, 309) sections. This violates HTML spec and causes getElementById to return only the first match. This was previously flagged and remains unresolved.

🔎 Proposed fix

For Production section:

-                                        <div id="consumerKey"
+                                        <div id="consumerKey-PRODUCTION"
...
-                                        <div id="consumerSecret" class="col-md-6"
+                                        <div id="consumerSecret-PRODUCTION" class="col-md-6"

For Sandbox section:

-                                        <div id="consumerKey"
+                                        <div id="consumerKey-SANDBOX"
...
-                                        <div id="consumerSecret" class="col-md-6"
+                                        <div id="consumerSecret-SANDBOX" class="col-md-6"

Also applies to: 68-69, 290-291, 309-310


163-172: Missing Policy column header in table.

The table header defines Name, Version, and API Key columns, but the table body includes a Policy column at line 189. This causes column misalignment. This was previously flagged and remains unresolved.

🔎 Proposed fix
                                         <thead>
                                             <tr>
                                                 <th class="app-th">Name</th>
                                                 <th class="app-th">Version</th>
+                                                <th class="app-th">Policy</th>
                                                 <th class="app-th">
                                                     {{#if isApiKey}}
                                                     API Key
                                                     {{/if}}
                                                 </th>
                                             </tr>
                                         </thead>

Apply the same fix to the Sandbox table (lines 409-418).


249-250: Duplicate no-subscription ID.

The id="no-subscription" is used in both Production (line 249) and Sandbox (line 494) sections. This was previously flagged and remains unresolved.

🔎 Proposed fix
-                                <div class="ps-3" id="no-subscription"
+                                <div class="ps-3" id="no-subscription-PRODUCTION"

And for Sandbox:

-                                <div class="ps-3" id="no-subscription"
+                                <div class="ps-3" id="no-subscription-SANDBOX"

Also applies to: 494-495


396-404: Duplicate accordion IDs between Production and Sandbox API Keys sections.

The Sandbox API Keys accordion uses the same IDs (headingFour, collapseFour) as the Production API Keys accordion (lines 151-158). This will cause Bootstrap's collapse functionality to malfunction.

🔎 Proposed fix
                     <div class="accordion-item">
-                        <h2 class="accordion-header" id="headingFour">
+                        <h2 class="accordion-header" id="headingSandboxApiKeys">
                             <button class="accordion-button collapsed" type="button" data-bs-toggle="collapse"
-                                data-bs-target="#collapseFour" aria-expanded="false" aria-controls="collapseFour">
+                                data-bs-target="#collapseSandboxApiKeys" aria-expanded="false" aria-controls="collapseSandboxApiKeys">
                                 API Keys
                             </button>
                         </h2>
-                        <div id="collapseFour" class="accordion-collapse collapse" aria-labelledby="headingFour"
+                        <div id="collapseSandboxApiKeys" class="accordion-collapse collapse" aria-labelledby="headingSandboxApiKeys"
                             data-bs-parent="#sandboxAccordion">
src/controllers/applicationsContentController.js (1)

220-228: Remove redundant optional chaining in for...of loop.

While the Array.isArray guard prevents entering the loop with undefined, the static analyzer correctly flags the optional chaining on line 224 as unsafe. Since the condition already verified cpApplication exists and has an array subscriptionScopes, the optional chaining is redundant and should be removed for clarity.

🔎 Proposed fix
             let subscriptionScopes = [];
             if (applicationReference) {
                 let cpApplication = await getAPIMApplication(req, applicationReference);
-                if (cpApplication && Array.isArray(cpApplication?.subscriptionScopes)) {
-                    for (const scope of cpApplication?.subscriptionScopes) {
+                if (cpApplication && Array.isArray(cpApplication.subscriptionScopes)) {
+                    for (const scope of cpApplication.subscriptionScopes) {
                         subscriptionScopes.push(scope.key);
                     }
                 }
             }
🧹 Nitpick comments (5)
src/services/adminService.js (1)

1280-1298: Consider adding parameter validation and logging for keyType.

The mapKeys function now accepts a keyType parameter, which is a good addition for supporting sandbox vs. production key management. However, the function doesn't validate that keyType is defined or log its value for debugging purposes.

🔎 Suggested improvements
 async function mapKeys(req, clientID, keyManager, applicationId, keyType) {
     logger.info('Mapping existing client ID with application', {
-        applicationId
+        applicationId,
+        keyType
     });
+    if (!keyType) {
+        throw new CustomError(400, "Bad Request", "keyType is required for key mapping");
+    }
     const body = {
         "consumerKey": clientID,
         "keyManager": keyManager,
         "keyType": keyType,
     };
src/styles/manage-keys.css (4)

3-5: Optional: Simplify zero value.

margin-top: 0rem; can be simplified to margin-top: 0; since zero doesn't require a unit.

🔎 Proposed fix
 .keys-section {
-    margin-top: 0rem;
+    margin-top: 0;
 }

61-67: Avoid !important in new stylesheets.

The !important flag on line 64 suggests a specificity conflict. Since this is a new file, consider restructuring selectors to avoid needing !important, which reduces maintainability.


113-118: Prefer class selectors over hardcoded IDs.

Hardcoding specific accordion IDs (#headingOne, #headingTwo, etc.) doesn't scale if additional accordions are added. Consider using a class selector like .accordion-header instead.

🔎 Proposed refactor
-#headingOne.accordion-header,
-#headingTwo.accordion-header,
-#headingThree.accordion-header,
-#headingFour.accordion-header {
+.accordion-header {
     margin-bottom: 0;
 }

130-146: Note: Scrollbar styling is webkit-only.

The custom scrollbar styles (::-webkit-scrollbar-*) only work in Chromium and Safari. Firefox and other browsers will display default scrollbars. This is acceptable as progressive enhancement, but ensure the default scrollbars provide adequate usability.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 698b49d and 34e4559.

📒 Files selected for processing (7)
  • src/controllers/applicationsContentController.js
  • src/defaultContent/styles/manage-keys.css
  • src/pages/application/partials/manage-keys.hbs
  • src/scripts/api-key-generation.js
  • src/scripts/oauth2-key-generation.js
  • src/services/adminService.js
  • src/styles/manage-keys.css
🧰 Additional context used
🧬 Code graph analysis (2)
src/scripts/api-key-generation.js (1)
src/middlewares/ensureAuthenticated.js (1)
  • keyType (427-427)
src/scripts/oauth2-key-generation.js (1)
src/scripts/api-key-generation.js (2)
  • modal (10-10)
  • modal (96-96)
🪛 Biome (2.1.2)
src/controllers/applicationsContentController.js

[error] 224-224: Unsafe usage of optional chaining.

If it short-circuits with 'undefined' the evaluation will throw TypeError here:

(lint/correctness/noUnsafeOptionalChaining)

src/scripts/oauth2-key-generation.js

[error] 539-539: Can't assign tokenBtn because it's a constant.

This is where the variable is defined as constant.

Unsafe fix: Replace const with let if you assign it to a new value.

(lint/correctness/noConstAssign)

🔇 Additional comments (17)
src/scripts/api-key-generation.js (4)

3-55: LGTM - keyType parameter properly propagated in openApiKeyModal.

The function signature update and keyType suffix additions to all DOM element IDs are correctly implemented, enabling per-environment key management.


57-175: LGTM - generateAPIKey function correctly handles keyType.

The keyType is properly passed to the API payload (uppercased), and all DOM element lookups correctly include the keyType suffix. The conditional logic for manage-keys vs apis.hbs pages is well-structured.


177-253: LGTM - revokeAPIKey correctly handles per-environment UI state.

The function properly targets keyType-specific buttons and handles UI state transitions for both manage-keys and apis.hbs pages.


255-311: LGTM - regenerateAPIKey correctly scoped to keyType.

All element references properly include the keyType suffix, and scope handling is correctly per-environment.

src/scripts/oauth2-key-generation.js (5)

1-70: Good defensive parameter validation added.

The upfront validation of required parameters with early returns and clear error messages improves robustness. The form parsing wrapped in try-catch is a good practice.


115-159: LGTM - Environment-scoped element updates.

The keyType suffix is consistently applied to all element IDs (consumerKey, consumerSecret, app-ref, key-map, modalBody, curlDisplay, kmData), and null checks properly guard against missing elements.


388-409: LGTM - updateApplicationKey correctly uses keyType.

The function properly extracts and uses keyType for scoped element lookups and closes the correct modal.


822-948: LGTM - Modal loading functions correctly accept keyType.

All modal loading functions (loadKeysViewModal, loadKeysModifyModal, loadKeysTokenModal, loadKeysInstructionsModal) properly construct modal IDs with keyType suffix and include good error handling for missing modals.


988-1005: LGTM - copyToken updated to use tokenId parameter.

The function now accepts a generic tokenId parameter instead of KMName, enabling environment-specific token copying.

src/pages/application/partials/manage-keys.hbs (2)

89-122: LGTM - Key action containers and buttons correctly scoped by environment.

The keyActionsContainer, viewKeyBtn, tokenKeyBtn, and tokenInstructionsBtn elements now have proper environment-specific IDs (-PRODUCTION/-SANDBOX), addressing previous duplicate ID concerns.

Also applies to: 330-363


143-145: LGTM - Error containers correctly scoped by environment.

Both Production and Sandbox sections now have their own keyGenerationErrorContainer with environment-specific suffixes.

Also applies to: 389-391

src/defaultContent/styles/manage-keys.css (1)

1-201: LGTM - Well-structured CSS with design tokens.

The stylesheet properly uses CSS variables for theming consistency, includes responsive scrollbar styling, and has sensible layout rules for the accordion and tab components. The !important declarations on focus states are appropriate for overriding Bootstrap's default outline styles.

src/controllers/applicationsContentController.js (5)

46-56: LGTM - Clean profile builder helper.

The buildProfile function properly extracts profile construction logic with a null check for missing user data.


62-246: LGTM - Excellent refactor to eliminate code duplication.

The loadApplicationData helper consolidates the shared data loading logic between loadApplication and loadApplicationKeys, addressing the previous code duplication concern. The function properly handles:

  • API subscriptions and key retrieval
  • Key manager metadata processing
  • Per-environment key separation (productionKeys/sandboxKeys)
  • Scope extraction with proper null checks

327-417: LGTM - loadApplication properly refactored.

The function now delegates data loading to loadApplicationData and focuses on template-specific content assembly and rendering. Error handling is comprehensive with proper logging context.


419-515: LGTM - loadApplicationKeys follows the same pattern.

The new function correctly reuses loadApplicationData and assembles the manage-keys specific template content. Error handling mirrors loadApplication for consistency.


586-596: LGTM - getAPIKeys now accepts keyType parameter.

The function signature update enables per-environment API key retrieval, aligning with the broader keyType handling across the codebase.

Copy link

@coderabbitai coderabbitai bot left a comment

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 (2)
src/pages/application/partials/manage-keys.hbs (2)

5-5: Add SRI integrity attribute to the CDN link.

The Bootstrap Icons CDN link lacks Subresource Integrity (SRI) attributes, which protect against compromised CDN content.

🔎 Recommended enhancement
-    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css">
+    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.css" 
+          integrity="sha384-..." crossorigin="anonymous">

You can generate the integrity hash at https://www.srihash.org/ or use a more recent version with built-in SRI support.


106-106: Consider refactoring complex onclick handlers.

The inline onclick handlers pass 8-10 parameters, making them difficult to read and maintain. Consider using data-* attributes to store metadata and delegating to event handlers in JavaScript.

Example approach
<button id="tokenKeyBtn-PRODUCTION" 
        class="common-btn-outlined btn-sm"
        data-form-id="applicationKeyGenerateForm-{{../id}}-PRODUCTION"
        data-app-ref-id="{{keys.appRefID}}"
        data-key-mapping-id="{{keys.keyMappingId}}"
        data-key-manager-name="{{../name}}"
        data-app-name="{{../../applicationMetadata/name}}"
        data-consumer-secret="{{keys.consumerSecret}}"
        data-subscription-scopes="{{json @root.subscriptionScopes}}"
        data-key-type="PRODUCTION">

Then attach a delegated event listener in JavaScript that reads the data-* attributes.

Also applies to: 129-129

Also applies to: 129-129

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 34e4559 and 04b21b9.

📒 Files selected for processing (4)
  • src/defaultContent/styles/manage-keys.css
  • src/pages/application/partials/manage-keys.hbs
  • src/services/adminService.js
  • src/styles/manage-keys.css
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/defaultContent/styles/manage-keys.css
  • src/styles/manage-keys.css
🔇 Additional comments (3)
src/services/adminService.js (2)

1244-1247: LGTM! Validation and call site correctly updated.

The validation check for tokenDetails.keyType has been properly added before calling mapKeys, and the function call now passes the validated keyType parameter. This addresses both previous review comments:

  • The undefined keyType issue has been resolved by passing tokenDetails.keyType.
  • The requested validation has been implemented with an appropriate error message.

The validation placement inside the transaction ensures proper rollback if the check fails.


1283-1290: LGTM! Function signature correctly updated for per-key-type handling.

The mapKeys function signature now accepts keyType as a parameter (line 1283) and correctly uses it in the request body (line 1290). This enables dynamic key-type assignment for PRODUCTION and SANDBOX keys, aligning with the PR objectives for sandbox key management.

src/pages/application/partials/manage-keys.hbs (1)

15-28: Tab navigation accessibility is correctly implemented.

The aria-selected attributes now correctly match the visual state: Production tab (active) has aria-selected="true" and Sandbox tab (inactive) has aria-selected="false".

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (2)
src/scripts/api-key-generation.js (2)

1-1: Remove unused Express import (duplicate of past review).

The const e = require("express") import is still present despite being marked as addressed in commit 4335b0b. This is dead code—Express is a server-side framework and cannot be used in client-side browser scripts. The variable e is never referenced.

🔎 Proposed fix
-const e = require("express");
-

227-234: Incorrect loading state text in dynamically created Generate button.

The Generate Key button created after revocation (Lines 227-234) has loading state text "Revoking..." (Line 232), but this button will be used for generating a new key, not revoking. When a user clicks this button, they should see "Generating..." not "Revoking...".

The loading state text should reflect the button's action (generate), not the context where the button creation code is located (revoke function).

🔎 Proposed fix
             <span class="button-loading-state" style="display: none;">
-              <span class="spinner-border spinner-border-sm me-1" role="status"> Revoking...</span>
+              <span class="spinner-border spinner-border-sm me-1" role="status"></span>
+              Generating...
             </span>
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 04b21b9 and cb0ab28.

📒 Files selected for processing (5)
  • src/pages/application/partials/apis.hbs
  • src/pages/application/partials/keys-view.hbs
  • src/pages/application/partials/manage-keys.hbs
  • src/scripts/api-key-generation.js
  • src/scripts/oauth2-key-generation.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/pages/application/partials/keys-view.hbs
🧰 Additional context used
🧬 Code graph analysis (2)
src/scripts/oauth2-key-generation.js (1)
src/scripts/api-key-generation.js (15)
  • generateBtn (112-112)
  • generateBtn (208-208)
  • normalState (60-60)
  • normalState (178-178)
  • normalState (256-256)
  • loadingState (61-61)
  • loadingState (179-179)
  • loadingState (257-257)
  • tokenBtn (59-59)
  • tokenBtn (255-255)
  • scopeContainer (17-17)
  • scopeContainer (63-63)
  • scopeContainer (280-280)
  • modal (10-10)
  • modal (96-96)
src/scripts/api-key-generation.js (3)
src/controllers/devportalController.js (3)
  • apiID (151-151)
  • apiKeyID (232-232)
  • apiKeyID (248-248)
src/middlewares/ensureAuthenticated.js (1)
  • keyType (427-427)
src/controllers/apiContentController.js (4)
  • apiID (222-222)
  • apiID (450-450)
  • apiID (497-497)
  • apiID (664-664)
🪛 Biome (2.1.2)
src/scripts/oauth2-key-generation.js

[error] 562-562: Can't assign tokenBtn because it's a constant.

This is where the variable is defined as constant.

Unsafe fix: Replace const with let if you assign it to a new value.

(lint/correctness/noConstAssign)

🔇 Additional comments (5)
src/pages/application/partials/manage-keys.hbs (1)

1-520: Past duplicate ID issues have been successfully resolved.

Previous review comments flagged multiple duplicate ID issues (consumerKey/consumerSecret, accordion IDs, data-api-key-id extra quotes, aria-selected). All of these have been properly fixed in the current implementation:

  • Consumer key/secret IDs are now scoped with -PRODUCTION/-SANDBOX suffixes
  • Accordion IDs use unique identifiers per environment
  • Data attributes no longer have extra quotes
  • ARIA attributes are correctly set

The implementation properly supports per-environment key management with distinct UI elements.

src/pages/application/partials/apis.hbs (1)

48-50: LGTM: Per-key-type rendering correctly implemented.

The dual invocation of the api-key partial for both PRODUCTION and SANDBOX key types (Lines 48-49) aligns with the PR's per-environment key management architecture. The addition of WEBSUB to the supported API types (Line 50) properly extends support to WebSub APIs.

src/scripts/api-key-generation.js (1)

3-55: Well-structured per-environment key management.

The addition of the keyType parameter throughout the key lifecycle functions (openApiKeyModal, generateAPIKey, revokeAPIKey, regenerateAPIKey) properly implements per-environment scoping. All DOM selectors are correctly suffixed with keyType (e.g., -PRODUCTION, -SANDBOX), ensuring Production and Sandbox keys are managed independently.

The conditional page detection logic (Lines 109-129, 213-238) appropriately handles UI differences between the manage-keys and APIs pages.

src/scripts/oauth2-key-generation.js (2)

1-310: Excellent defensive programming in generateApplicationKey.

The function has been significantly hardened with:

  • Parameter validation and early returns (Lines 2-6)
  • DOM element existence checks before operations (Lines 10-13, 18-21, 36-47)
  • Error container management with user-friendly messages (Lines 24-28, 280-284, 297-301)
  • Try-catch blocks for JSON operations (Lines 51-63)
  • Consistent keyType-scoped element selectors throughout

This greatly improves reliability and user experience compared to the typical implementation.


845-971: Modal management properly updated for per-environment scoping.

All modal loading functions (loadKeysViewModal, loadKeysModifyModal, loadKeysTokenModal, loadKeysInstructionsModal) correctly accept the keyType parameter and use environment-scoped modal IDs. The null checks with error logging (Lines 847-850, 866-869, 956-959, 966-969) provide good defensive programming.

The copyToken signature change from KMName to tokenId (Line 1011) properly aligns with environment-specific token identifiers.

style="{{#if @root.isReadOnlyMode}}pointer-events: none; opacity: 0.5;{{/if}}"
data-app-ref-id=""
title="Generate Production Key"
onclick="openApiKeyModal('{{projectId}}', '{{refID}}', '{{policyName}}', '{{../applicationMetadata.appMap.[0].appRefID}}', '{{../applicationMetadata.id}}', '{{subID}}', '{{getSubIDs ../subAPIs}}', '{{json scopes}}', 'PRODUCTION')">
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Search for appMap initialization and population to verify it always has at least one element
rg -n "appMap\s*[=:]" --type=js -A5 -B2

Repository: wso2/api-developer-portal-core

Length of output: 507


🏁 Script executed:

# Also check the template file to understand the context of the array access
cat -n src/pages/application/partials/apis.hbs | sed -n '70,120p'

Repository: wso2/api-developer-portal-core

Length of output: 4559


🏁 Script executed:

# Search for any appMap usage patterns to understand if it's guaranteed to have elements
rg -n "appMap" --type=js -B2 -A2 | head -100

Repository: wso2/api-developer-portal-core

Length of output: 2438


Add defensive checks for appMap array access in onclick handlers.

Lines 84 and 107 directly access {{../applicationMetadata.appMap.[0].appRefID}} without verifying that appMap exists or has elements. Since appMap is only initialized when DP_APP_KEY_MAPPINGs is present (see src/dto/application.js:26), accessing index [0] can fail silently. The controller code already guards this access (src/controllers/applicationsContentController.js:83), but the template does not.

Wrap the array access in a Handlebars conditional:

{{#if ../applicationMetadata.appMap}}{{../applicationMetadata.appMap.[0].appRefID}}{{else}}''{{/if}}
🤖 Prompt for AI Agents
In src/pages/application/partials/apis.hbs around line 84 (and apply same fix at
line 107), the template directly reads
../applicationMetadata.appMap.[0].appRefID in onclick handlers which can throw
when appMap is missing or empty; wrap that access in a Handlebars conditional so
you output the value only when appMap exists and has at least one element and
otherwise emit an empty string — e.g. replace the direct expression with a
conditional block that checks ../applicationMetadata.appMap and uses
../applicationMetadata.appMap.[0].appRefID when present, else '' (apply
identical change to the other occurrence).

</td>
<td onclick="window.location.href='{{@root.baseUrl}}/api/{{apiHandle}}'"
class="app-td" style="cursor: pointer;">{{version}}</td>
<td class="app-td">
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add missing id attribute to policy cell for consistency.

The Sandbox API Keys table policy cell (Line 442) is missing the id="policy_{{subID}}" attribute that exists in the Production section (Line 192). This inconsistency may cause issues if JavaScript code expects to query this element by ID.

🔎 Proposed fix
-                                                <td class="app-td">
+                                                <td id="policy_{{subID}}" class="app-td" style="display: none;">
+                                                    {{policyName}}</td>
+                                                <td class="app-td">
                                                     {{#if (contains security 'api_key')}}
🤖 Prompt for AI Agents
In src/pages/application/partials/manage-keys.hbs around line 442, the Sandbox
API Keys table policy cell is missing the id attribute used in Production; add
id="policy_{{subID}}" to the <td> at line 442 so it matches the Production row
and any JS that queries by that id; ensure the template variable (subID)
produces a unique id for each row and that the attribute name exactly matches
the Production implementation.

Comment on lines +552 to +562
const tokenBtn = document.getElementById('tokenKeyBtn-' + keyType);
const devAppId = tokenBtn?.dataset?.appId
const scopeContainer = document.getElementById('scopeContainer-' + devAppId);
const scopeContainer = document.getElementById('scopeContainer-' + devAppId + '-' + keyType);
const scopeInput = document.getElementById('scope-' + devAppId);

if (!(subscribedScopes)) {
// In the regenerate token request, the scopes are fetched from the span tags
const scopeElements = document.querySelectorAll(`#scopeContainer-${devAppId} .span-tag`);
const scopeElements = document.querySelectorAll(`#scopeContainer-${devAppId}-${keyType} .span-tag`);
subscribedScopes = Array.from(scopeElements).map(el => el.textContent.replace('×', '').trim());
scopeContainer.setAttribute('data-scopes', JSON.stringify(subscribedScopes));
tokenBtn = document.getElementById('regenerateKeyBtn');
tokenBtn = document.getElementById('regenerateKeyBtn-' + keyType);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix const reassignment causing runtime error.

Line 552 declares tokenBtn as const, but Line 562 attempts to reassign it in the regenerate token path. This will throw a TypeError at runtime when users regenerate tokens.

🔎 Proposed fix

Change the declaration from const to let:

-    const tokenBtn = document.getElementById('tokenKeyBtn-' + keyType);
+    let tokenBtn = document.getElementById('tokenKeyBtn-' + keyType);

Based on static analysis hints.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const tokenBtn = document.getElementById('tokenKeyBtn-' + keyType);
const devAppId = tokenBtn?.dataset?.appId
const scopeContainer = document.getElementById('scopeContainer-' + devAppId);
const scopeContainer = document.getElementById('scopeContainer-' + devAppId + '-' + keyType);
const scopeInput = document.getElementById('scope-' + devAppId);
if (!(subscribedScopes)) {
// In the regenerate token request, the scopes are fetched from the span tags
const scopeElements = document.querySelectorAll(`#scopeContainer-${devAppId} .span-tag`);
const scopeElements = document.querySelectorAll(`#scopeContainer-${devAppId}-${keyType} .span-tag`);
subscribedScopes = Array.from(scopeElements).map(el => el.textContent.replace('×', '').trim());
scopeContainer.setAttribute('data-scopes', JSON.stringify(subscribedScopes));
tokenBtn = document.getElementById('regenerateKeyBtn');
tokenBtn = document.getElementById('regenerateKeyBtn-' + keyType);
let tokenBtn = document.getElementById('tokenKeyBtn-' + keyType);
const devAppId = tokenBtn?.dataset?.appId
const scopeContainer = document.getElementById('scopeContainer-' + devAppId + '-' + keyType);
const scopeInput = document.getElementById('scope-' + devAppId);
if (!(subscribedScopes)) {
// In the regenerate token request, the scopes are fetched from the span tags
const scopeElements = document.querySelectorAll(`#scopeContainer-${devAppId}-${keyType} .span-tag`);
subscribedScopes = Array.from(scopeElements).map(el => el.textContent.replace('×', '').trim());
scopeContainer.setAttribute('data-scopes', JSON.stringify(subscribedScopes));
tokenBtn = document.getElementById('regenerateKeyBtn-' + keyType);
🧰 Tools
🪛 Biome (2.1.2)

[error] 562-562: Can't assign tokenBtn because it's a constant.

This is where the variable is defined as constant.

Unsafe fix: Replace const with let if you assign it to a new value.

(lint/correctness/noConstAssign)

🤖 Prompt for AI Agents
In src/scripts/oauth2-key-generation.js around lines 552 to 562, tokenBtn is
declared as const but later reassigned in the regenerate token branch which
causes a runtime TypeError; change the tokenBtn declaration from const to let so
it can be reassigned, and keep the rest of the logic the same (ensure any
subsequent references still use tokenBtn and that null/undefined checks remain
intact).

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.

2 participants