Skip to content

Commit e01563c

Browse files
authored
feat(dcm-backend): read dcm.apiUrl with legacy fallback (#3603)
* feat(dcm-backend): read `dcm.apiUrl` with legacy fallback Assisted-By: Claude (Anthropic) Signed-off-by: Gloria Ciavarrini <gciavarrini@redhat.com> * chore(dcm): wire `DCM_API_URL` and document legacy env var Assisted-By: Claude (Anthropic) Signed-off-by: Gloria Ciavarrini <gciavarrini@redhat.com> --------- Signed-off-by: Gloria Ciavarrini <gciavarrini@redhat.com>
1 parent b9f42ce commit e01563c

12 files changed

Lines changed: 52 additions & 31 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@red-hat-developer-hub/backstage-plugin-dcm-backend': patch
3+
---
4+
5+
Rename DCM_API_GATEWAY_URL to DCM_API_URL. Legacy env var and
6+
dcm.apiGatewayUrl config remain supported during migration.

workspaces/dcm/app-config.production.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ backend:
1111
connection: ':memory:'
1212

1313
dcm:
14+
apiUrl: ${DCM_API_URL:-}
15+
# Legacy env var; kept until deploy configs switch to DCM_API_URL.
1416
apiGatewayUrl: ${DCM_API_GATEWAY_URL:-}
1517
ssoBaseUrl: ${DCM_SSO_BASE_URL:-}
1618
clientId: ${DCM_CLIENT_ID:-}

workspaces/dcm/app-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ dcm:
6565
- security-baseline
6666
- compliance-pci
6767
- audit-logging
68-
# Base URL of the DCM API Gateway. All three API services (catalog,
68+
# Base URL of the DCM control plane. All three API services (catalog,
6969
# policy-manager, providers) are routed through this single endpoint.
7070
# Override in app-config.local.yaml for local development.
71-
# apiGatewayUrl: https://your-api-gateway.example.com
71+
# apiUrl: https://your-control-plane.example.com
7272
#
7373
# SSO credentials for the backend to obtain a bearer token:
7474
# ssoBaseUrl: https://sso.redhat.com

workspaces/dcm/plugins/dcm-backend/app-config.dynamic.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
dcm:
2-
# Base URL of the DCM API Gateway (required).
2+
# Base URL of the DCM control plane (required).
3+
apiUrl: ${DCM_API_URL}
4+
# Legacy env var; kept until deploy configs switch to DCM_API_URL.
35
apiGatewayUrl: ${DCM_API_GATEWAY_URL}
46

57
# SSO configuration for the backend to obtain bearer tokens via

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,24 @@
1717
export interface Config {
1818
dcm: {
1919
/**
20-
* Base URL of the DCM API Gateway.
20+
* Base URL of the DCM control plane.
2121
*
2222
* All API services (catalog, policy-manager, providers) are routed
23-
* through this single gateway. The backend appends `/api/v1alpha1/<path>`
23+
* through this endpoint. The backend appends `/api/v1alpha1/<path>`
2424
* to construct the upstream URL.
2525
*
26-
* @example "http://localhost:9080"
26+
* @example "http://localhost:8080"
27+
* @visibility backend
28+
*/
29+
apiUrl?: string;
30+
31+
/**
32+
* Base URL of the DCM API Gateway.
33+
*
34+
* @deprecated Use `apiUrl` instead. This key remains supported for
35+
* backward compatibility during migration.
36+
*
37+
* @example "http://localhost:9080" (legacy API gateway port)
2738
* @visibility backend
2839
*/
2940
apiGatewayUrl?: string;

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ describe('createDcmProxy', () => {
5959
fetchSpy?.mockRestore();
6060
});
6161

62-
it('returns 503 when dcm.apiGatewayUrl is not configured', async () => {
62+
it('returns 503 when dcm.apiUrl is not configured', async () => {
6363
const app = makeApp({ dcm: { clientId: 'id', clientSecret: 'secret' } });
6464

6565
const res = await request(app).get('/proxy/providers');
@@ -77,7 +77,7 @@ describe('createDcmProxy', () => {
7777

7878
const app = makeApp({
7979
dcm: {
80-
apiGatewayUrl: 'https://gateway.example.com',
80+
apiUrl: 'https://control-plane.example.com',
8181
clientId: 'id',
8282
clientSecret: 'secret',
8383
},
@@ -101,7 +101,7 @@ describe('createDcmProxy', () => {
101101

102102
const app = makeApp({
103103
dcm: {
104-
apiGatewayUrl: 'https://gateway.example.com',
104+
apiUrl: 'https://control-plane.example.com',
105105
clientId: 'id',
106106
clientSecret: 'secret',
107107
},
@@ -111,7 +111,7 @@ describe('createDcmProxy', () => {
111111

112112
expect(res.status).toBe(502);
113113
expect(res.body).toMatchObject({
114-
error: expect.stringContaining('DCM API gateway'),
114+
error: expect.stringContaining('DCM API'),
115115
});
116116
});
117117

@@ -134,7 +134,7 @@ describe('createDcmProxy', () => {
134134

135135
const app = makeApp({
136136
dcm: {
137-
apiGatewayUrl: 'https://gateway.example.com',
137+
apiUrl: 'https://control-plane.example.com',
138138
clientId: 'id',
139139
clientSecret: 'secret',
140140
},
@@ -171,7 +171,7 @@ describe('createDcmProxy', () => {
171171

172172
const app = makeApp({
173173
dcm: {
174-
apiGatewayUrl: 'https://gateway.example.com',
174+
apiUrl: 'https://control-plane.example.com',
175175
clientId: 'id',
176176
clientSecret: 'secret',
177177
},
@@ -206,7 +206,7 @@ describe('createDcmProxy', () => {
206206

207207
const app = makeApp({
208208
dcm: {
209-
apiGatewayUrl: 'https://gateway.example.com',
209+
apiUrl: 'https://control-plane.example.com',
210210
clientId: 'id',
211211
clientSecret: 'secret',
212212
},
@@ -227,7 +227,7 @@ describe('createDcmProxy', () => {
227227
expect(JSON.parse(upstreamCall[1].body)).toEqual(patch);
228228
});
229229

230-
it('handles 204 No Content upstream responses without a body', async () => {
230+
it('falls back to legacy dcm.apiGatewayUrl and handles 204 No Content', async () => {
231231
fetchSpy = jest
232232
.spyOn(globalThis, 'fetch')
233233
.mockResolvedValueOnce(TOKEN_RESPONSE)

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,35 +21,35 @@ import { getTokenFromApi } from '../util/tokenUtil';
2121
const API_BASE_PATH = '/api/v1alpha1';
2222

2323
/**
24-
* Proxies all `ALL /proxy/*` requests to the DCM API Gateway.
24+
* Proxies all `ALL /proxy/*` requests to the DCM control plane.
2525
*
2626
* The wildcard path segment is appended to:
27-
* `{dcm.apiGatewayUrl}/api/v1alpha1/<wildcardPath>`
27+
* `{dcm.apiUrl}/api/v1alpha1/<wildcardPath>`
2828
*
2929
* An SSO bearer token is injected automatically via `tokenUtil`.
3030
*/
3131
export function createDcmProxy(options: RouterOptions) {
3232
return async (req: Request, res: Response): Promise<void> => {
3333
const { logger, config } = options;
3434

35-
const apiGatewayUrl = config.getOptionalString('dcm.apiGatewayUrl');
36-
if (!apiGatewayUrl) {
35+
// Prefer dcm.apiUrl (DCM_API_URL); legacy apiGatewayUrl eases migration.
36+
const apiUrl =
37+
config.getOptionalString('dcm.apiUrl') ??
38+
config.getOptionalString('dcm.apiGatewayUrl');
39+
if (!apiUrl) {
3740
logger.error(
38-
'dcm.apiGatewayUrl is not configured — cannot proxy DCM API requests.',
41+
'dcm.apiUrl is not configured — cannot proxy DCM API requests.',
3942
);
4043
res
4144
.status(503)
42-
.json({ error: 'DCM API gateway is not configured on the server.' });
45+
.json({ error: 'DCM API is not configured on the server.' });
4346
return;
4447
}
4548

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

49-
const targetUrl = new URL(
50-
`${API_BASE_PATH}/${wildcardPath}`,
51-
apiGatewayUrl,
52-
);
52+
const targetUrl = new URL(`${API_BASE_PATH}/${wildcardPath}`, apiUrl);
5353

5454
// Forward all query parameters from the original request
5555
const incomingParams = new URLSearchParams(
@@ -80,7 +80,7 @@ export function createDcmProxy(options: RouterOptions) {
8080

8181
// Only attach the Authorization header when an SSO token was obtained.
8282
// When clientId/clientSecret are not configured the token is empty and
83-
// the request is forwarded without auth (open/unauthenticated gateway).
83+
// the request is forwarded without auth (open/unauthenticated API).
8484
if (tokenResult.accessToken) {
8585
requestHeaders.Authorization = `Bearer ${tokenResult.accessToken}`;
8686
}
@@ -104,7 +104,7 @@ export function createDcmProxy(options: RouterOptions) {
104104
});
105105
} catch (err) {
106106
logger.error(`DCM proxy: upstream fetch failed — ${err}`);
107-
res.status(502).json({ error: 'Failed to reach the DCM API gateway.' });
107+
res.status(502).json({ error: 'Failed to reach the DCM API.' });
108108
return;
109109
}
110110

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ import { DcmBaseClient } from './DcmBaseClient';
3030
*
3131
* All requests are sent to `/api/dcm/proxy/<path>` where the backend
3232
* strips the `/proxy` prefix and forwards to:
33-
* `{dcm.apiGatewayUrl}/api/v1alpha1/<path>`
33+
* `{dcm.apiUrl}/api/v1alpha1/<path>`
3434
*
3535
* @public
3636
*/

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const PLUGIN_ID = 'dcm';
2323
* Base class shared by all DCM API clients.
2424
*
2525
* Routes every call through the dcm-backend secure proxy:
26-
* `GET /api/dcm/proxy/<path>` → `{dcm.apiGatewayUrl}/api/v1alpha1/<path>`
26+
* `GET /api/dcm/proxy/<path>` → `{dcm.apiUrl}/api/v1alpha1/<path>`
2727
*
2828
* @public
2929
*/

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { DcmBaseClient } from './DcmBaseClient';
2323
*
2424
* All requests are sent to `/api/dcm/proxy/<path>` where the backend
2525
* strips the `/proxy` prefix and forwards to:
26-
* `{dcm.apiGatewayUrl}/api/v1alpha1/<path>`
26+
* `{dcm.apiUrl}/api/v1alpha1/<path>`
2727
*
2828
* @public
2929
*/

0 commit comments

Comments
 (0)