Skip to content

Add API Proxy | Add All Tabs to reflect API#2766

Open
asmasarw wants to merge 2 commits intoredhat-developer:mainfrom
asmasarw:feature/api-proxy
Open

Add API Proxy | Add All Tabs to reflect API#2766
asmasarw wants to merge 2 commits intoredhat-developer:mainfrom
asmasarw:feature/api-proxy

Conversation

@asmasarw
Copy link
Copy Markdown
Contributor

@asmasarw asmasarw commented Apr 14, 2026

Overview

Introduces the DCM (Dynamic Configuration Management) plugin for Red Hat Developer Hub — a Backstage plugin that provides a self-service UI for managing infrastructure provisioning workflows through the DCM API Gateway.


Features

Providers management

Register, view, and manage service providers — the infrastructure backends that fulfil provisioning requests. Each provider entry includes its endpoint, service type, schema version, supported operations, and live health status.

Policy management

Create and manage placement policies that control how resources are allocated. Policies can be scoped globally or per-user and toggled enabled/disabled. A priority field controls evaluation order when multiple policies apply.

Catalog Items

Define reusable service blueprints (catalog items) backed by a service type. Each catalog item carries a configurable field schema that controls what parameters end users fill in when provisioning an instance.

Catalog Item Instances

Provision instances from catalog items by filling in the required field configuration. Instances represent active provisioning requests and can be deleted when no longer needed.

Resources

View and manage placement resources — the concrete infrastructure objects created by the Policy Engine. Resources can be created from a catalog item instance, deleted, or rehydrated (re-evaluated against current policies).


Backend capabilities

  • Secure proxy — all API calls from the browser are routed through the DCM backend, which injects a short-lived SSO bearer token (obtained via client-credentials flow from Red Hat SSO) before forwarding to the configured dcm.apiGatewayUrl. Tokens are cached server-side and refreshed automatically before expiry.
  • Permission integration — access to the plugin is controlled via Backstage's permission framework. A dcm.plugin read permission gates both the UI and the /token endpoint, so users without the required permission cannot interact with the DCM API.
  • Access endpoint — exposes a /access endpoint the frontend uses to determine whether the current user has permission, enabling the UI to show appropriate messaging rather than silent failures.

Configuration

dcm:
  apiGatewayUrl: https://your-dcm-gateway.example.com
  clientId: your-client-id
  clientSecret: your-client-secret
  # optional — defaults to https://sso.redhat.com
  ssoBaseUrl: https://sso.redhat.com
  
  
 ---

## Tests and Coverage
- **Tests** — added tests to cover every tab content (Each API Endpoint)

@rhdh-gh-app
Copy link
Copy Markdown

rhdh-gh-app bot commented Apr 14, 2026

Important

This PR includes changes that affect public-facing API. Please ensure you are adding/updating documentation for new features or behavior.

Missing Changesets

The following package(s) are changed by this PR but do not have a changeset:

  • @red-hat-developer-hub/backstage-plugin-dcm-backend
  • @red-hat-developer-hub/backstage-plugin-dcm-common

See CONTRIBUTING.md for more information about how to add changesets.

Changed Packages

Package Name Package Path Changeset Bump Current Version
@red-hat-developer-hub/backstage-plugin-dcm-backend workspaces/dcm/plugins/dcm-backend none v0.1.0
@red-hat-developer-hub/backstage-plugin-dcm-common workspaces/dcm/plugins/dcm-common none v0.1.0
@red-hat-developer-hub/backstage-plugin-dcm workspaces/dcm/plugins/dcm minor v0.1.0

@rhdh-qodo-merge
Copy link
Copy Markdown

Review Summary by Qodo

Comprehensive DCM plugin refactor with API proxy, shared abstractions, and six new CRUD tabs

✨ Enhancement 🧪 Tests 🐞 Bug fix

Grey Divider

Walkthroughs

Description
• **Comprehensive DCM plugin refactor** across three packages (dcm, dcm-backend, dcm-common)
  with improved code quality, reusability, and test coverage
• **Frontend abstractions**: New useCrudTab hook centralizes CRUD state management;
  createYupValidator factory eliminates validation boilerplate; reusable components
  (DcmCrudTabLayout, DcmFormDialogActions, DcmDeleteDialog, DcmErrorSnackbar) reduce per-tab
  code duplication
• **Shared library improvements**: New DcmBaseClient abstract class eliminates ~60 lines of
  duplicated fetch boilerplate; new DcmClientError custom error class carries HTTP status and
  structured RFC 7807 error data; extractApiError utility moved to dcm-common for shared use
• **Backend hardening**: New secure API proxy route (/api/dcm/proxy/*) with automatic SSO token
  injection; improved tokenUtil error messages and cache alignment; dead code removal
  (GetTokenResponse.ts, setupTests.ts)
• **Four new API clients**: CatalogClient, PolicyManagerClient, ProvidersClient,
  PlacementClient with full CRUD support and proper HTTP methods/headers
• **Six new CRUD tabs**: Providers, Policies, Service Types, Catalog Items, Catalog Item Instances,
  Resources with live API integration replacing mock data
• **Bug fix**: PolicyManagerClient.updatePolicy now sends correct `Content-Type:
  application/merge-patch+json` header
• **Test coverage**: ~88 new tests across 10 test suites covering hooks, components, clients,
  utilities, and backend routes
• **Dead code removal**: Deleted unused ExampleComponent directory and GetTokenResponse.ts
Diagram
flowchart LR
  FE["Frontend Plugin<br/>dcm"]
  BE["Backend Plugin<br/>dcm-backend"]
  COMMON["Shared Library<br/>dcm-common"]
  PROXY["API Proxy Route<br/>/api/dcm/proxy/*"]
  SSO["SSO Token<br/>Acquisition"]
  UPSTREAM["Upstream Services<br/>Catalog, Policy Manager,<br/>Providers, Placement"]
  
  FE -- "useCrudTab hook<br/>DcmCrudTabLayout<br/>Form components" --> COMMON
  FE -- "API refs<br/>CatalogApiRef<br/>PolicyManagerApiRef<br/>ProvidersApiRef<br/>PlacementApiRef" --> COMMON
  COMMON -- "DcmBaseClient<br/>DcmClientError<br/>extractApiError" --> BE
  BE -- "Proxy requests<br/>with bearer token" --> PROXY
  PROXY -- "Inject SSO token" --> SSO
  PROXY -- "Forward requests" --> UPSTREAM
  UPSTREAM -- "JSON responses<br/>RFC 7807 errors" --> PROXY
  PROXY -- "Response/error" --> BE
  BE -- "Typed responses" --> FE
Loading

Grey Divider

File Changes

1. workspaces/dcm/plugins/dcm/src/hooks/useCrudTab.ts ✨ Enhancement +394/-0

New useCrudTab hook for centralized CRUD state management

• Introduces a new useCrudTab hook that centralizes CRUD state management (loading, search,
 pagination, create/edit/delete dialogs)
• Stores all option callbacks in a ref to prevent infinite loops and allow inline definitions
• Provides stable callbacks for list operations, form submission, and error handling
• Integrates extractApiError for consistent error message extraction

workspaces/dcm/plugins/dcm/src/hooks/useCrudTab.ts


2. workspaces/dcm/plugins/dcm/src/hooks/useCrudTab.test.ts 🧪 Tests +305/-0

Test suite for useCrudTab hook with 305 lines

• Comprehensive test suite covering initial load, search/pagination, create/edit/delete flows
• Tests error handling, form validation, and reload functionality
• Validates state transitions and item list mutations

workspaces/dcm/plugins/dcm/src/hooks/useCrudTab.test.ts


3. workspaces/dcm/plugins/dcm-backend/src/routes/proxy.test.ts 🧪 Tests +212/-0

Test suite for DCM API proxy route handler

• Tests the DCM API proxy route handler with various scenarios
• Validates configuration checks, token acquisition failures, upstream errors
• Tests GET/POST/DELETE request proxying and response forwarding
• Covers 204 No Content handling

workspaces/dcm/plugins/dcm-backend/src/routes/proxy.test.ts


View more (77)
4. workspaces/dcm/plugins/dcm-backend/src/util/tokenUtil.test.ts 🧪 Tests +178/-0

Test suite for SSO token utility with caching

• Tests SSO token acquisition and caching logic
• Validates cache reuse when token has >60s remaining
• Tests cache refresh when token is near expiry
• Covers error handling for failed SSO requests

workspaces/dcm/plugins/dcm-backend/src/util/tokenUtil.test.ts


5. workspaces/dcm/plugins/dcm-common/src/clients/DcmBaseClient.test.ts 🧪 Tests +154/-0

Test suite for DcmBaseClient abstract base class

• Tests the abstract DcmBaseClient base class for all API clients
• Validates proxy URL construction, 204 No Content handling
• Tests DcmClientError creation with HTTP status and RFC 7807 error parsing
• Verifies header merging and error message formatting

workspaces/dcm/plugins/dcm-common/src/clients/DcmBaseClient.test.ts


6. workspaces/dcm/plugins/dcm-common/src/clients/PlacementClient.test.ts 🧪 Tests +122/-0

Test suite for PlacementClient API methods

• Tests the PlacementClient API methods for resource management
• Validates query parameter handling in list operations
• Tests create, delete, and rehydrate resource operations

workspaces/dcm/plugins/dcm-common/src/clients/PlacementClient.test.ts


7. workspaces/dcm/plugins/dcm/src/pages/providers/providerFormTypes.test.ts 🧪 Tests +130/-0

Test suite for provider form types and validators

• Tests provider form validation, conversion, and round-trip transformations
• Validates slug-to-title-case conversion for display names
• Tests form-to-provider and provider-to-form conversions

workspaces/dcm/plugins/dcm/src/pages/providers/providerFormTypes.test.ts


8. workspaces/dcm/plugins/dcm-backend/src/routes/proxy.ts ✨ Enhancement +120/-0

New DCM API proxy route handler implementation

• Implements the DCM API proxy route handler that forwards requests to the upstream API gateway
• Injects SSO bearer token automatically via tokenUtil
• Handles query parameter forwarding, request body proxying, and response status/headers
• Returns appropriate error responses for missing configuration or upstream failures

workspaces/dcm/plugins/dcm-backend/src/routes/proxy.ts


9. workspaces/dcm/plugins/dcm-common/src/clients/CatalogClient.ts ✨ Enhancement +121/-0

New CatalogClient API client implementation

• Implements CatalogClient extending DcmBaseClient for Catalog API operations
• Provides methods for service types, catalog items, and catalog item instances
• Supports CRUD operations with proper HTTP methods and content-type headers

workspaces/dcm/plugins/dcm-common/src/clients/CatalogClient.ts


10. workspaces/dcm/plugins/dcm-common/src/clients/ProvidersClient.test.ts 🧪 Tests +111/-0

Test suite for ProvidersClient API methods

• Tests the ProvidersClient API methods for provider management
• Validates list, get, create, apply (PUT), and delete operations
• Verifies correct HTTP methods and request body handling

workspaces/dcm/plugins/dcm-common/src/clients/ProvidersClient.test.ts


11. workspaces/dcm/plugins/dcm/src/pages/catalog-items/catalogItemFormTypes.ts ✨ Enhancement +119/-0

Catalog item form types and validators

• Defines CatalogItemForm type with display name, API version, service type, and fields
• Implements Yup schema validation for catalog item forms
• Provides conversion functions between form and API entity representations

workspaces/dcm/plugins/dcm/src/pages/catalog-items/catalogItemFormTypes.ts


12. workspaces/dcm/plugins/dcm/src/pages/providers/providerFormTypes.ts ✨ Enhancement +107/-0

Provider form types and validators

• Defines ProviderForm type with name, endpoint, service type, schema version, and operations
• Implements Yup schema validation with slug pattern and URL format checks
• Provides conversion functions and display name generation from provider names

workspaces/dcm/plugins/dcm/src/pages/providers/providerFormTypes.ts


13. workspaces/dcm/plugins/dcm/src/utils/createYupValidator.test.ts 🧪 Tests +94/-0

Test suite for createYupValidator factory utility

• Tests the createYupValidator factory utility for Yup schema validation
• Validates error object generation and isValid boolean checks
• Tests optional transform function for form-to-model conversion

workspaces/dcm/plugins/dcm/src/utils/createYupValidator.test.ts


14. workspaces/dcm/plugins/dcm/src/pages/catalog-item-instances/instanceFormTypes.ts ✨ Enhancement +99/-0

Catalog item instance form types and validators

• Defines InstanceForm type for catalog item instance creation with user-supplied values
• Implements Yup schema validation for display name, catalog item selection, and API version
• Provides conversion functions and user value row building from catalog item fields

workspaces/dcm/plugins/dcm/src/pages/catalog-item-instances/instanceFormTypes.ts


15. workspaces/dcm/plugins/dcm-common/src/types/catalog.ts ✨ Enhancement +115/-0

Catalog API type definitions

• Defines TypeScript interfaces for Catalog API types (ServiceType, CatalogItem, FieldConfiguration)
• Includes CatalogItemInstance and UserValue types for provisioned resources
• Provides paginated list response types for all catalog entities

workspaces/dcm/plugins/dcm-common/src/types/catalog.ts


16. workspaces/dcm/plugins/dcm/src/pages/policies/policyFormTypes.test.ts 🧪 Tests +98/-0

Test suite for policy form types and validators

• Tests policy form validation including display name, rego code, and priority range
• Validates policy type enumeration (GLOBAL/USER)
• Tests form-to-policy and policy-to-form round-trip conversions

workspaces/dcm/plugins/dcm/src/pages/policies/policyFormTypes.test.ts


17. workspaces/dcm/plugins/dcm/src/pages/policies/policyFormTypes.ts ✨ Enhancement +92/-0

Policy form types and validators

• Defines PolicyForm type with display name, rego code, priority, and policy type
• Implements Yup schema validation with priority range (1-1000) and policy type enumeration
• Provides conversion functions between form and API policy representations

workspaces/dcm/plugins/dcm/src/pages/policies/policyFormTypes.ts


18. workspaces/dcm/plugins/dcm-backend/src/util/tokenUtil.ts 🐞 Bug fix +20/-18

Enhanced tokenUtil with better error handling and cache alignment

• Improves SSO error messages to include full status code, status text, and response body
• Aligns cache shape to use accessToken field matching TokenResult type
• Downgrades verbose per-request cache diagnostics from info to debug level
• Adds type-safe token result caching with TTL

workspaces/dcm/plugins/dcm-backend/src/util/tokenUtil.ts


19. workspaces/dcm/plugins/dcm/src/pages/resources/resourceFormTypes.test.ts 🧪 Tests +86/-0

Test suite for resource form types and validators

• Tests resource form validation for catalog item instance ID and spec JSON
• Validates optional ID pattern matching (lowercase alphanumeric with hyphens)
• Tests form-to-resource conversion with JSON spec parsing

workspaces/dcm/plugins/dcm/src/pages/resources/resourceFormTypes.test.ts


20. workspaces/dcm/plugins/dcm-common/src/utils/extractApiError.ts ✨ Enhancement +81/-0

Utility for extracting and unwrapping nested API errors

• Implements recursive error unwrapping utility for deeply-nested DCM API error messages
• Extracts human-readable strings from RFC 7807 / AEP-193 JSON error responses
• Handles both structured JSON errors and plain text error messages

workspaces/dcm/plugins/dcm-common/src/utils/extractApiError.ts


21. workspaces/dcm/plugins/dcm-common/src/errors/DcmClientError.ts ✨ Enhancement +76/-0

Custom DcmClientError class for API client error handling

• Defines custom DcmClientError class extending Error with HTTP status and structured API error
• Provides static factory method to parse RFC 7807 / AEP-193 JSON error responses
• Carries both human-readable message and structured apiError object

workspaces/dcm/plugins/dcm-common/src/errors/DcmClientError.ts


22. workspaces/dcm/plugins/dcm/src/utils/extractApiError.test.ts 🧪 Tests +71/-0

Test suite for extractApiError utility (frontend import)

• Tests the extractApiError utility for error message extraction and unwrapping
• Validates handling of plain errors, JSON errors, and nested error chains
• Tests edge cases like empty strings and unparseable JSON

workspaces/dcm/plugins/dcm/src/utils/extractApiError.test.ts


23. workspaces/dcm/plugins/dcm-common/src/clients/PlacementClient.ts ✨ Enhancement +76/-0

New PlacementClient API client implementation

• Implements PlacementClient extending DcmBaseClient for Placement Manager API operations
• Provides methods for listing, creating, deleting, and rehydrating resources
• Supports query parameter options for filtering and pagination

workspaces/dcm/plugins/dcm-common/src/clients/PlacementClient.ts


24. workspaces/dcm/plugins/dcm/src/pages/resources/resourceFormTypes.ts ✨ Enhancement +75/-0

Resource form types and validators

• Defines ResourceForm type with catalog item instance ID, spec JSON, and optional client ID
• Implements Yup schema validation for JSON spec and ID pattern matching
• Provides conversion function from form to Resource with JSON parsing

workspaces/dcm/plugins/dcm/src/pages/resources/resourceFormTypes.ts


25. workspaces/dcm/plugins/dcm/src/plugin.ts ✨ Enhancement +57/-0

Plugin registration of API clients and route references

• Registers four API factories for CatalogClient, PolicyManagerClient, ProvidersClient, and
 PlacementClient
• Adds route references for all CRUD tabs (providers, policies, service types, catalog items,
 instances, resources)
• Integrates API clients with discovery and fetch APIs

workspaces/dcm/plugins/dcm/src/plugin.ts


26. workspaces/dcm/plugins/dcm-common/src/types/index.ts ✨ Enhancement +6/-1

Type exports from dcm-common package

• Exports all type definitions from catalog, common, placement, policy-manager, and providers
 modules
• Replaces unused ExampleComponent export with comprehensive type exports

workspaces/dcm/plugins/dcm-common/src/types/index.ts


27. workspaces/dcm/plugins/dcm-common/src/index.ts ✨ Enhancement +5/-0

Public API exports for dcm-common package

• Exports all types, clients, and error utilities from dcm-common
• Includes DcmClientError and extractApiError for use by frontend and backend
• Provides public API surface for the shared library

workspaces/dcm/plugins/dcm-common/src/index.ts


28. workspaces/dcm/plugins/dcm-common/src/clients/DcmBaseClient.ts ✨ Enhancement +62/-0

Abstract base client for shared API logic

• Introduces abstract base class shared by all DCM API clients
• Centralises fetch<T>() method with request/response handling and error conversion
• Eliminates ~60 lines of duplicated boilerplate across client implementations
• Routes all requests through dcm-backend secure proxy at /api/dcm/proxy/<path>

workspaces/dcm/plugins/dcm-common/src/clients/DcmBaseClient.ts


29. workspaces/dcm/plugins/dcm-common/src/clients/PolicyManagerClient.ts ✨ Enhancement +65/-0

Policy Manager API client implementation

• Implements PolicyManagerApi interface extending DcmBaseClient
• Provides CRUD operations for policies (list, get, create, update, delete)
• Fixes PATCH content-type to application/merge-patch+json for policy updates
• Routes requests through backend proxy to upstream policy manager service

workspaces/dcm/plugins/dcm-common/src/clients/PolicyManagerClient.ts


30. workspaces/dcm/plugins/dcm-common/src/clients/ProvidersClient.ts ✨ Enhancement +61/-0

Providers API client implementation

• Implements ProvidersApi interface extending DcmBaseClient
• Provides CRUD operations for providers (list, get, create, apply/PUT, delete)
• Routes requests through backend proxy to upstream providers service

workspaces/dcm/plugins/dcm-common/src/clients/ProvidersClient.ts


31. workspaces/dcm/plugins/dcm-common/src/clients/CatalogApi.ts ✨ Enhancement +56/-0

Catalog API interface definition

• Replaces unused ExampleComponent with CatalogApi interface definition
• Defines contract for catalog service operations (service types, items, instances)
• Supports CRUD operations across three resource types

workspaces/dcm/plugins/dcm-common/src/clients/CatalogApi.ts


32. workspaces/dcm/plugins/dcm-common/src/clients/PolicyManagerApi.ts ✨ Enhancement +14/-21

Policy Manager API interface definition

• Replaces unused ExampleComponent with PolicyManagerApi interface definition
• Defines contract for policy manager service CRUD operations

workspaces/dcm/plugins/dcm-common/src/clients/PolicyManagerApi.ts


33. workspaces/dcm/plugins/dcm-common/src/clients/PlacementApi.ts ✨ Enhancement +54/-0

Placement Manager API interface definition

• Introduces PlacementApi interface for placement manager operations
• Defines methods for resource CRUD and rehydration operations
• Supports optional filtering and pagination parameters

workspaces/dcm/plugins/dcm-common/src/clients/PlacementApi.ts


34. workspaces/dcm/plugins/dcm-common/src/clients/ProvidersApi.ts ✨ Enhancement +11/-5

Providers API interface definition

• Replaces unused GetTokenResponse with ProvidersApi interface definition
• Defines contract for provider service CRUD operations

workspaces/dcm/plugins/dcm-common/src/clients/ProvidersApi.ts


35. workspaces/dcm/plugins/dcm-common/src/clients/index.ts ✨ Enhancement +26/-0

Clients module barrel export

• Exports all API interfaces and client implementations
• Provides single entry point for importing DCM clients

workspaces/dcm/plugins/dcm-common/src/clients/index.ts


36. workspaces/dcm/plugins/dcm-common/src/types/common.ts ✨ Enhancement +52/-0

Common types for error handling and health checks

• Defines DcmErrorType enum for RFC 7807 / AEP-193 error codes
• Introduces DcmApiError interface for structured error responses
• Adds DcmHealth interface for service health check responses

workspaces/dcm/plugins/dcm-common/src/types/common.ts


37. workspaces/dcm/plugins/dcm-common/src/types/policy-manager.ts ✨ Enhancement +50/-0

Policy Manager API types

• Defines Policy interface with OPA Rego code, labels, and priority fields
• Introduces PolicyType enum (GLOBAL | USER)
• Adds PolicyList interface for paginated policy responses

workspaces/dcm/plugins/dcm-common/src/types/policy-manager.ts


38. workspaces/dcm/plugins/dcm-common/src/types/providers.ts ✨ Enhancement +70/-0

Providers API types

• Defines Provider interface for registered service providers
• Introduces ResourceCapacity and ProviderMetadata for infrastructure metrics
• Adds ProviderStatus type and ProviderList for paginated responses

workspaces/dcm/plugins/dcm-common/src/types/providers.ts


39. workspaces/dcm/plugins/dcm-common/src/types/placement.ts ✨ Enhancement +56/-0

Placement Manager API types

• Defines Resource interface for provisioned resources with approval status
• Introduces ResourceList for paginated resource responses
• Adds RehydrateRequest interface for policy re-evaluation operations

workspaces/dcm/plugins/dcm-common/src/types/placement.ts


40. workspaces/dcm/plugins/dcm-backend/config.d.ts ⚙️ Configuration changes +54/-0

Backend plugin configuration schema

• Introduces configuration schema for DCM backend plugin
• Defines dcm.apiGatewayUrl, dcm.ssoBaseUrl, dcm.clientId, dcm.clientSecret config keys
• Marks sensitive fields (clientId, clientSecret) with visibility annotations

workspaces/dcm/plugins/dcm-backend/config.d.ts


41. workspaces/dcm/plugins/dcm-backend/src/router.ts ✨ Enhancement +3/-0

Backend router proxy endpoint registration

• Adds import for createDcmProxy route handler
• Registers /proxy/* route to forward requests to upstream DCM services

workspaces/dcm/plugins/dcm-backend/src/router.ts


42. workspaces/dcm/plugins/dcm-backend/src/plugin.ts ✨ Enhancement +4/-0

Backend plugin auth policy for proxy

• Adds auth policy for /proxy route with user-cookie allow rule
• Ensures proxy endpoint is protected by user authentication

workspaces/dcm/plugins/dcm-backend/src/plugin.ts


43. workspaces/dcm/plugins/dcm-backend/package.json ⚙️ Configuration changes +1/-1

Backend package configuration schema reference

• Adds configSchema field pointing to config.d.ts
• Enables Backstage configuration validation for the backend plugin

workspaces/dcm/plugins/dcm-backend/package.json


44. workspaces/dcm/plugins/dcm/src/apis.ts ✨ Enhancement +69/-0

Frontend API reference definitions

• Introduces four API refs: catalogApiRef, policyManagerApiRef, providersApiRef,
 placementApiRef
• Each ref provides typed access to corresponding DCM service client
• Enables dependency injection of API clients throughout the frontend plugin

workspaces/dcm/plugins/dcm/src/apis.ts


45. workspaces/dcm/plugins/dcm/src/index.ts ✨ Enhancement +6/-0

Frontend plugin public API exports

• Exports all four API refs for public consumption
• Allows external packages to inject and use DCM service clients

workspaces/dcm/plugins/dcm/src/index.ts


46. workspaces/dcm/plugins/dcm/src/routes.ts ✨ Enhancement +38/-0

Frontend route definitions for CRUD tabs

• Adds six new route refs for API-aligned tabs: providers, policies, service-types, catalog-items,
 catalog-item-instances, resources
• Each route is a sub-route of the root DCM route with descriptive paths

workspaces/dcm/plugins/dcm/src/routes.ts


47. workspaces/dcm/plugins/dcm/src/utils/createYupValidator.ts ✨ Enhancement +58/-0

Yup validator factory utility

• Factory function that wraps Yup schema and returns validate and isValid helpers
• Supports optional transform function for pre-processing form values
• Eliminates per-tab validation boilerplate across CRUD forms

workspaces/dcm/plugins/dcm/src/utils/createYupValidator.ts


48. workspaces/dcm/plugins/dcm/src/utils/extractApiError.ts ✨ Enhancement +3/-1

API error extraction re-export

• Re-exports extractApiError from dcm-common for backward compatibility
• Allows consumers to import from either path

workspaces/dcm/plugins/dcm/src/utils/extractApiError.ts


49. workspaces/dcm/plugins/dcm/src/components/DcmCrudTabLayout.tsx ✨ Enhancement +199/-0

Generic CRUD tab layout component

• Generic layout component for CRUD tab pages with loading, error, empty, and table states
• Handles search, pagination, and primary action button
• Eliminates per-tab layout boilerplate

workspaces/dcm/plugins/dcm/src/components/DcmCrudTabLayout.tsx


50. workspaces/dcm/plugins/dcm/src/pages/policies/PoliciesTabContent.tsx ✨ Enhancement +327/-0

Policies CRUD tab page

• Implements policies CRUD tab using useCrudTab hook and DcmCrudTabLayout
• Provides table with display name, type, priority, enabled status, and description columns
• Includes inline toggle for enable/disable with per-row spinner
• Supports create, edit, and delete operations with form dialogs

workspaces/dcm/plugins/dcm/src/pages/policies/PoliciesTabContent.tsx


51. workspaces/dcm/plugins/dcm/src/pages/providers/ProvidersTabContent.tsx ✨ Enhancement +322/-0

Providers CRUD tab page

• Implements providers CRUD tab using useCrudTab hook and DcmCrudTabLayout
• Displays provider details: display name, name, endpoint, service type, operations, health status
• Includes copy button for endpoint URLs
• Supports create (register), edit, and delete operations

workspaces/dcm/plugins/dcm/src/pages/providers/ProvidersTabContent.tsx


52. workspaces/dcm/plugins/dcm/src/pages/providers/components/ProviderStatus.tsx ✨ Enhancement +92/-0

Provider health status display component

• Maps provider health_status string to Backstage Status components
• Supports ready, degraded, error, running, aborted, and pending states
• Renders as inline chip with status icon

workspaces/dcm/plugins/dcm/src/pages/providers/components/ProviderStatus.tsx


53. workspaces/dcm/plugins/dcm/src/pages/catalog-items/CatalogItemsTabContent.tsx ✨ Enhancement +302/-0

Catalog items CRUD tab page

• Implements catalog items CRUD tab using useCrudTab hook and DcmCrudTabLayout
• Displays items with display name, API version, service type, field count, and creation date
• Supports create, edit, and delete operations with dynamic field management
• Tracks submit attempts to show field-level validation errors

workspaces/dcm/plugins/dcm/src/pages/catalog-items/CatalogItemsTabContent.tsx


54. workspaces/dcm/plugins/dcm/src/pages/catalog-items/components/CatalogItemFormFields.tsx ✨ Enhancement +242/-0

Catalog item form fields component

• Form component for creating/editing catalog items with display name, API version, service type
• Dynamic field management: add/remove fields with path, display name, and editable toggle
• Validates at least one field is present

workspaces/dcm/plugins/dcm/src/pages/catalog-items/components/CatalogItemFormFields.tsx


55. workspaces/dcm/plugins/dcm/src/pages/catalog-item-instances/CatalogItemInstancesTabContent.tsx ✨ Enhancement +230/-0

Catalog item instances CRUD tab page

• Implements catalog item instances CRUD tab using useCrudTab hook and DcmCrudTabLayout
• Displays instances with display name, catalog item reference, resource ID, API version, creation
 date
• Supports create and delete operations

workspaces/dcm/plugins/dcm/src/pages/catalog-item-instances/CatalogItemInstancesTabContent.tsx


56. workspaces/dcm/plugins/dcm/src/pages/catalog-item-instances/components/InstanceFormFields.tsx ✨ Enhancement +192/-0

Catalog item instance form fields component

• Form component for creating catalog item instances with display name, catalog item selection, API
 version
• Dynamically renders editable field inputs based on selected catalog item
• Validates required fields

workspaces/dcm/plugins/dcm/src/pages/catalog-item-instances/components/InstanceFormFields.tsx


57. workspaces/dcm/plugins/dcm/src/pages/resources/ResourcesTabContent.tsx ✨ Enhancement +242/-0

Resources CRUD tab page

• Implements resources CRUD tab using useCrudTab hook and DcmCrudTabLayout
• Displays resources with ID, catalog instance, provider, approval status, creation date
• Supports create and delete operations
• Maps catalog item instances for display names

workspaces/dcm/plugins/dcm/src/pages/resources/ResourcesTabContent.tsx


58. workspaces/dcm/plugins/dcm/src/pages/data-center/EnvironmentsTabContent.tsx ✨ Enhancement +121/-50

Environments tab API integration

• Migrates from mock data to live ProvidersApi calls for environment management
• Adds loading state and error handling with fallback to optimistic updates
• Converts between Provider and Environment domain models
• Supports register, edit, and delete operations via API

workspaces/dcm/plugins/dcm/src/pages/data-center/EnvironmentsTabContent.tsx


59. workspaces/dcm/plugins/dcm/src/pages/service-spec/ServiceSpecsTabContent.tsx ✨ Enhancement +125/-37

Service specs tab API integration

• Migrates from mock data to live CatalogApi calls for service spec management
• Adds loading state and error handling with fallback to optimistic updates
• Converts between CatalogItem and ServiceSpec domain models
• Supports create, edit, and delete operations via API

workspaces/dcm/plugins/dcm/src/pages/service-spec/ServiceSpecsTabContent.tsx


60. workspaces/dcm/package.json Dependencies +3/-0

Workspace dependency for form validation

• Adds yup ^1.7.1 as a workspace dependency
• Enables form validation across CRUD tabs

workspaces/dcm/package.json


61. workspaces/dcm/plugins/dcm/src/pages/resources/components/ResourceFormFields.tsx ✨ Enhancement +177/-0

Resource form fields component with file upload

• New component for rendering resource form fields (catalog item instance selector, JSON spec
 textarea with file upload, optional resource ID)
• Implements form state management with touched tracking and validation via validateResourceForm
• Includes file upload handler that parses JSON and formats it with indentation

workspaces/dcm/plugins/dcm/src/pages/resources/components/ResourceFormFields.tsx


62. workspaces/dcm/plugins/dcm/src/pages/service-types/ServiceTypesTabContent.tsx ✨ Enhancement +184/-0

Service types tab with searchable table display

• New tab content component displaying a searchable, paginated table of service types
• Implements data loading from catalog API with error handling and empty state
• Renders service type details (name, API version, path, creation date) with truncated text and
 tooltips

workspaces/dcm/plugins/dcm/src/pages/service-types/ServiceTypesTabContent.tsx


63. workspaces/dcm/plugins/dcm/src/pages/providers/components/ProviderFormFields.tsx ✨ Enhancement +176/-0

Provider form fields component with validation

• New component for provider form fields (name, endpoint, service type selector, schema version,
 operations multi-select)
• Implements validation via validateProviderForm with touched field tracking
• Provides helper text and error messages for each field

workspaces/dcm/plugins/dcm/src/pages/providers/components/ProviderFormFields.tsx


64. workspaces/dcm/plugins/dcm/src/pages/policies/components/PolicyFormFields.tsx ✨ Enhancement +164/-0

Policy form fields component with Rego editor

• New component for policy form fields (display name, description, policy type, priority, Rego code,
 enabled toggle)
• Implements validation via validatePolicyForm with touched field tracking
• Includes helper text with Rego code placeholder and priority range guidance

workspaces/dcm/plugins/dcm/src/pages/policies/components/PolicyFormFields.tsx


65. workspaces/dcm/plugins/dcm/src/components/DcmFormDialogActions.test.tsx 🧪 Tests +79/-0

Tests for form dialog actions component

• New test suite for DcmFormDialogActions component with 7 test cases
• Tests button rendering, custom labels, click handlers, submitting state, and disabled state

workspaces/dcm/plugins/dcm/src/components/DcmFormDialogActions.test.tsx


66. workspaces/dcm/plugins/dcm/src/components/DcmFormDialog.tsx ✨ Enhancement +14/-0

Form dialog error handling and submitting state

• Added error prop to display error banner between form and actions
• Added submitting prop to disable close button during request
• Integrated Collapse and MuiAlert for error message display

workspaces/dcm/plugins/dcm/src/components/DcmFormDialog.tsx


67. workspaces/dcm/plugins/dcm/src/components/DcmFormDialogActions.tsx ✨ Enhancement +86/-0

Reusable form dialog actions component

• New reusable component for Save/Cancel button row with loading spinner
• Supports custom submit label, submitting state, and form validity disabled state
• Shows "Saving…" text with spinner during submission

workspaces/dcm/plugins/dcm/src/components/DcmFormDialogActions.tsx


68. workspaces/dcm/plugins/dcm/src/components/DcmDeleteDialog.test.tsx 🧪 Tests +61/-0

Tests for delete confirmation dialog

• New test suite for DcmDeleteDialog component with 6 test cases
• Tests resource name rendering, custom labels, button callbacks, and visibility states

workspaces/dcm/plugins/dcm/src/components/DcmDeleteDialog.test.tsx


69. workspaces/dcm/plugins/dcm/src/components/TruncatedText.tsx ✨ Enhancement +73/-0

Text truncation component with tooltip

• New utility component for rendering text with ellipsis truncation and tooltip on hover
• Supports customizable max-width, typography variant, color, and fallback content
• Useful for table cells with unbounded text content

workspaces/dcm/plugins/dcm/src/components/TruncatedText.tsx


70. workspaces/dcm/plugins/dcm/src/pages/data-center/DataCenterPage.tsx ✨ Enhancement +36/-8

Data center page tab restructuring

• Replaced EnvironmentsTabContent and ServiceSpecsTabContent with six new tab routes
• Added tabs for Providers, Policies, Service types, Catalog items, Instances, and Resources
• Updated route references to use new route refs from routes module

workspaces/dcm/plugins/dcm/src/pages/data-center/DataCenterPage.tsx


71. workspaces/dcm/.changeset/slim-lobsters-refactor.md 📝 Documentation +39/-0

Changelog for frontend refactor

• Changelog entry documenting frontend refactor with new utilities, components, and test coverage
• Lists new shared hooks (useCrudTab, createYupValidator), components, and per-feature file
 decomposition
• Documents error handling improvements and dead code removal

workspaces/dcm/.changeset/slim-lobsters-refactor.md


72. workspaces/dcm/plugins/dcm/src/components/dcmTabListHelpers.tsx ✨ Enhancement +17/-7

Add tooltips to action buttons

• Added Tooltip import from Material-UI
• Wrapped Edit and Delete icon buttons with Tooltip components for better UX

workspaces/dcm/plugins/dcm/src/components/dcmTabListHelpers.tsx


73. workspaces/dcm/plugins/dcm/src/components/DcmErrorSnackbar.tsx ✨ Enhancement +55/-0

Error snackbar notification component

• New component for displaying transient error notifications at bottom-center
• Supports customizable auto-hide duration and close callback
• Renders nothing when error is falsy

workspaces/dcm/plugins/dcm/src/components/DcmErrorSnackbar.tsx


74. workspaces/dcm/plugins/dcm/src/components/DcmDeleteDialog.tsx ✨ Enhancement +60/-0

Generic delete confirmation dialog

• New generic delete confirmation dialog component reused across resource tabs
• Customizable resource name and label with confirmation/cancel actions
• Uses DcmFormDialog as base with secondary delete button

workspaces/dcm/plugins/dcm/src/components/DcmDeleteDialog.tsx


75. workspaces/dcm/plugins/dcm/src/pages/providers/components/CopyButton.tsx ✨ Enhancement +49/-0

Copy to clipboard button component

• New utility component for copy-to-clipboard icon button with visual feedback
• Shows checkmark icon for 2 seconds after successful copy
• Includes tooltip and accessibility label

workspaces/dcm/plugins/dcm/src/pages/providers/components/CopyButton.tsx


76. workspaces/dcm/plugins/dcm/src/components/DcmDataCenterTabEmptyState.tsx ✨ Enhancement +11/-5

Optional primary action in empty state

• Made primaryActionLabel and onPrimaryAction props optional
• Added conditional rendering of primary action button only when both props are provided
• Allows empty states without action buttons

workspaces/dcm/plugins/dcm/src/components/DcmDataCenterTabEmptyState.tsx


77. workspaces/dcm/plugins/dcm/package.json Dependencies +2/-1

Add yup validation library dependency

• Added yup dependency version ^1.7.1 for form validation schema support

workspaces/dcm/plugins/dcm/package.json


78. workspaces/dcm/app-config.yaml ⚙️ Configuration changes +9/-0

API Gateway and SSO configuration documentation

• Added commented configuration documentation for DCM API Gateway URL
• Added commented SSO configuration properties (ssoBaseUrl, clientId, clientSecret)
• Provides guidance for local development overrides

workspaces/dcm/app-config.yaml


79. workspaces/dcm/plugins/dcm-common/src/utils/extractApiError.test.ts Additional files +71/-0

...

workspaces/dcm/plugins/dcm-common/src/utils/extractApiError.test.ts


80. workspaces/dcm/plugins/dcm/src/components/ExampleComponent/ExampleComponent.test.tsx Additional files +0/-38

...

workspaces/dcm/plugins/dcm/src/components/ExampleComponent/ExampleComponent.test.tsx


Grey Divider

Qodo Logo

@rhdh-qodo-merge
Copy link
Copy Markdown

rhdh-qodo-merge bot commented Apr 14, 2026

Code Review by Qodo

🐞 Bugs (3)   📘 Rule violations (0)   📎 Requirement gaps (0)
🐞\ ≡ Correctness (1) ⛨ Security (1) ⚙ Maintainability (1)

Grey Divider


Action required

1. Proxy skips permission check 🐞
Description
createDcmProxy forwards requests for any authenticated caller but never runs the DCM permission
authorization used elsewhere (authorize() via permissions.authorize), so users without DCM
permissions can still reach upstream DCM APIs through /proxy. This is an authorization bypass
relative to /token which explicitly denies unauthorized callers with 403.
Code

workspaces/dcm/plugins/dcm-backend/src/routes/proxy.ts[R31-75]

+export function createDcmProxy(options: RouterOptions) {
+  return async (req: Request, res: Response): Promise<void> => {
+    const { logger, config } = options;
+
+    const apiGatewayUrl = config.getOptionalString('dcm.apiGatewayUrl');
+    if (!apiGatewayUrl) {
+      logger.error(
+        'dcm.apiGatewayUrl is not configured — cannot proxy DCM API requests.',
+      );
+      res
+        .status(503)
+        .json({ error: 'DCM API gateway is not configured on the server.' });
+      return;
+    }
+
+    // req.params[0] is the captured wildcard after /proxy/
+    const wildcardPath = (req.params as Record<string, string>)[0] ?? '';
+
+    const targetUrl = new URL(
+      `${API_BASE_PATH}/${wildcardPath}`,
+      apiGatewayUrl,
+    );
+
+    // Forward all query parameters from the original request
+    const incomingParams = new URLSearchParams(
+      req.query as Record<string, string>,
+    );
+    incomingParams.forEach((value, key) => {
+      targetUrl.searchParams.set(key, value);
+    });
+
+    logger.debug(
+      `DCM proxy: ${req.method} ${req.path} → ${targetUrl.toString()}`,
+    );
+
+    let tokenResult;
+    try {
+      tokenResult = await getTokenFromApi(options);
+    } catch (err) {
+      logger.error(`DCM proxy: failed to obtain access token — ${err}`);
+      res
+        .status(502)
+        .json({ error: 'Failed to obtain upstream access token.' });
+      return;
+    }
Evidence
The proxy handler obtains an upstream token and forwards the request without calling authorize();
meanwhile, the /token endpoint explicitly calls authorize() (which uses permissions.authorize over
dcmPluginPermissions) and returns 403 when denied. The backend plugin’s httpRouter auth policy for
/token and /proxy is the same (user-cookie), demonstrating that the proxy still needs an explicit
permission check if DCM permissions are intended to gate access.

workspaces/dcm/plugins/dcm-backend/src/routes/proxy.ts[31-75]
workspaces/dcm/plugins/dcm-backend/src/routes/token.ts[23-35]
workspaces/dcm/plugins/dcm-backend/src/routes/access.ts[22-34]
workspaces/dcm/plugins/dcm-backend/src/plugin.ts[48-63]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The new DCM backend proxy (`/proxy/*`) forwards upstream DCM API requests for any authenticated user, but it does not enforce the DCM permission check used by `/token`.

## Issue Context
`/token` calls `authorize(req, options)` and returns `403` when the user lacks DCM permissions. The proxy should apply the same authorization before minting an upstream token and forwarding requests.

## Fix Focus Areas
- workspaces/dcm/plugins/dcm-backend/src/routes/proxy.ts[31-75]
- workspaces/dcm/plugins/dcm-backend/src/routes/token.ts[23-35]
- workspaces/dcm/plugins/dcm-backend/src/routes/access.ts[22-34]

## Implementation notes
- Import and call `authorize(req, options)` early in the proxy handler.
- If denied, return `403 { error: 'Forbidden' }` (consistent with `/token`) and do not call `getTokenFromApi` or `fetch` upstream.
- Consider adding/adjusting tests in `proxy.test.ts` to verify a denied authorization returns 403.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Query params lose duplicates 🐞
Description
The proxy converts req.query to URLSearchParams and uses searchParams.set(), which overwrites any
duplicate query keys and can change semantics for multi-valued parameters. This can cause upstream
requests like ?a=1&a=2 to arrive as a single value instead of two.
Code

workspaces/dcm/plugins/dcm-backend/src/routes/proxy.ts[R54-60]

+    // Forward all query parameters from the original request
+    const incomingParams = new URLSearchParams(
+      req.query as Record<string, string>,
+    );
+    incomingParams.forEach((value, key) => {
+      targetUrl.searchParams.set(key, value);
+    });
Evidence
The implementation uses URLSearchParams and then calls targetUrl.searchParams.set(key, value) for
each param. URLSearchParams.set replaces existing values for the same key, so repeated query params
cannot be preserved.

workspaces/dcm/plugins/dcm-backend/src/routes/proxy.ts[54-60]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The proxy currently forwards query parameters by iterating `URLSearchParams` and calling `targetUrl.searchParams.set(key, value)`, which overwrites repeated keys.

## Issue Context
Some clients/servers rely on repeated query keys (e.g. `?label=a&label=b`). The proxy should preserve these instead of collapsing them.

## Fix Focus Areas
- workspaces/dcm/plugins/dcm-backend/src/routes/proxy.ts[54-60]

## Implementation notes
- Iterate `req.query` entries directly.
- If a value is an array, `append` each element.
- If a value is a single scalar, `set` it (or `append` consistently).
- Prefer `append` over `set` when forwarding from an already-parsed structure to preserve duplication.
- Add a focused unit test for duplicate query keys (e.g. `/proxy/providers?a=1&a=2`).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Advisory comments

3. Headers merge overridden 🐞
Description
DcmBaseClient.fetch constructs merged headers but then spreads ...init after, which will overwrite
the merged headers object whenever init.headers is provided. This makes the header merging
fragile and can silently drop defaults in future call sites.
Code

workspaces/dcm/plugins/dcm-common/src/clients/DcmBaseClient.ts[R42-48]

+  protected async fetch<T>(path: string, init?: RequestInit): Promise<T> {
+    const baseUrl = await this.discoveryApi.getBaseUrl(PLUGIN_ID);
+    const url = `${baseUrl}/proxy/${path}`;
+    const response = await this.fetchApi.fetch(url, {
+      headers: { 'Content-Type': 'application/json', ...init?.headers },
+      ...init,
+    });
Evidence
The RequestInit object passed to fetch is built with headers: { ... } and then ...init. If
init contains a headers property, it replaces the previously built merged headers object,
negating the merge behavior.

workspaces/dcm/plugins/dcm-common/src/clients/DcmBaseClient.ts[42-48]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`DcmBaseClient.fetch` attempts to merge default headers with `init.headers`, but it spreads `...init` after specifying `headers`, which can overwrite the merged header object.

## Issue Context
This is easy to miss and can cause subtle bugs if future callers provide `init.headers` (e.g., adding `Accept`) and unintentionally drop defaults.

## Fix Focus Areas
- workspaces/dcm/plugins/dcm-common/src/clients/DcmBaseClient.ts[42-48]

## Implementation notes
- Spread `...init` first, then define `headers` last, e.g.:
 - `const response = await fetch(url, { ...init, headers: { 'Content-Type': 'application/json', ...(init?.headers ?? {}) } });`
- Keep the existing override semantics (caller headers should win).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Comment on lines +31 to +75
export function createDcmProxy(options: RouterOptions) {
return async (req: Request, res: Response): Promise<void> => {
const { logger, config } = options;

const apiGatewayUrl = config.getOptionalString('dcm.apiGatewayUrl');
if (!apiGatewayUrl) {
logger.error(
'dcm.apiGatewayUrl is not configured — cannot proxy DCM API requests.',
);
res
.status(503)
.json({ error: 'DCM API gateway is not configured on the server.' });
return;
}

// req.params[0] is the captured wildcard after /proxy/
const wildcardPath = (req.params as Record<string, string>)[0] ?? '';

const targetUrl = new URL(
`${API_BASE_PATH}/${wildcardPath}`,
apiGatewayUrl,
);

// Forward all query parameters from the original request
const incomingParams = new URLSearchParams(
req.query as Record<string, string>,
);
incomingParams.forEach((value, key) => {
targetUrl.searchParams.set(key, value);
});

logger.debug(
`DCM proxy: ${req.method} ${req.path} → ${targetUrl.toString()}`,
);

let tokenResult;
try {
tokenResult = await getTokenFromApi(options);
} catch (err) {
logger.error(`DCM proxy: failed to obtain access token — ${err}`);
res
.status(502)
.json({ error: 'Failed to obtain upstream access token.' });
return;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Proxy skips permission check 🐞 Bug ⛨ Security

createDcmProxy forwards requests for any authenticated caller but never runs the DCM permission
authorization used elsewhere (authorize() via permissions.authorize), so users without DCM
permissions can still reach upstream DCM APIs through /proxy. This is an authorization bypass
relative to /token which explicitly denies unauthorized callers with 403.
Agent Prompt
## Issue description
The new DCM backend proxy (`/proxy/*`) forwards upstream DCM API requests for any authenticated user, but it does not enforce the DCM permission check used by `/token`.

## Issue Context
`/token` calls `authorize(req, options)` and returns `403` when the user lacks DCM permissions. The proxy should apply the same authorization before minting an upstream token and forwarding requests.

## Fix Focus Areas
- workspaces/dcm/plugins/dcm-backend/src/routes/proxy.ts[31-75]
- workspaces/dcm/plugins/dcm-backend/src/routes/token.ts[23-35]
- workspaces/dcm/plugins/dcm-backend/src/routes/access.ts[22-34]

## Implementation notes
- Import and call `authorize(req, options)` early in the proxy handler.
- If denied, return `403 { error: 'Forbidden' }` (consistent with `/token`) and do not call `getTokenFromApi` or `fetch` upstream.
- Consider adding/adjusting tests in `proxy.test.ts` to verify a denied authorization returns 403.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@asmasarw asmasarw force-pushed the feature/api-proxy branch from 9649302 to 4bf2bfc Compare April 14, 2026 15:18
@sonarqubecloud
Copy link
Copy Markdown

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.

1 participant