diff --git a/.changeset/shaggy-lies-know.md b/.changeset/shaggy-lies-know.md deleted file mode 100644 index cc016ed226..0000000000 --- a/.changeset/shaggy-lies-know.md +++ /dev/null @@ -1,14 +0,0 @@ ---- -'@backstage/plugin-proxy-backend': minor ---- - -The proxy-backend plugin now supports reviving request bodies that have previously been consumed by an express middleware (e.g. `express.json()`). This is done by setting `reviveRequestBody: true` on the proxy configuration. In order to preserve the current behavior, the proxy will **not** revive request bodies by default. - -The following is an example of a proxy configuration that revives request bodies: - -```diff -proxy: - '/target': - target: 'http://proxy-route' -+ reviveRequestBody: true -``` diff --git a/.changeset/ten-tigers-marry.md b/.changeset/ten-tigers-marry.md new file mode 100644 index 0000000000..f90f7b3ea2 --- /dev/null +++ b/.changeset/ten-tigers-marry.md @@ -0,0 +1,16 @@ +--- +'@backstage/plugin-proxy-backend': patch +--- + +The proxy-backend plugin now supports reviving request bodies that have previously been consumed by an express middleware (e.g. `express.json()`). This is done by setting `reviveConsumedRequestBodies: true` on the proxy `RouterOptions`. In order to preserve the current behavior, the proxy will **not** revive request bodies by default. + +The following is an example of a proxy `createRouter` invocation that revives request bodies: + +```diff +const router = await createRouter({ + config, + logger, + discovery, ++ reviveConsumedRequestBodies: true, +}); +``` diff --git a/plugins/proxy-backend/api-report.md b/plugins/proxy-backend/api-report.md index 741f7c3b54..12d799bdf7 100644 --- a/plugins/proxy-backend/api-report.md +++ b/plugins/proxy-backend/api-report.md @@ -17,6 +17,7 @@ export const proxyPlugin: ( options?: | { skipInvalidProxies?: boolean | undefined; + reviveConsumedRequestBodies?: boolean | undefined; } | undefined, ) => BackendFeature; @@ -30,6 +31,8 @@ export interface RouterOptions { // (undocumented) logger: Logger; // (undocumented) + reviveConsumedRequestBodies?: boolean; + // (undocumented) skipInvalidProxies?: boolean; } ``` diff --git a/plugins/proxy-backend/config.d.ts b/plugins/proxy-backend/config.d.ts index d248845df1..59fc0dc5a6 100644 --- a/plugins/proxy-backend/config.d.ts +++ b/plugins/proxy-backend/config.d.ts @@ -50,11 +50,6 @@ export interface Config { * and headers that are set by the proxy will be forwarded. */ allowedHeaders?: string[]; - /** - * Revive request body if it was consumed by a middleware before reaching the proxy. By default, the request - * body is not revived. - */ - reviveRequestBody?: boolean; }; }; } diff --git a/plugins/proxy-backend/src/plugin.ts b/plugins/proxy-backend/src/plugin.ts index d095bee527..66eeb4a7b2 100644 --- a/plugins/proxy-backend/src/plugin.ts +++ b/plugins/proxy-backend/src/plugin.ts @@ -27,7 +27,10 @@ import { createRouter } from './service/router'; * @alpha */ export const proxyPlugin = createBackendPlugin( - (options?: { skipInvalidProxies?: boolean }) => ({ + (options?: { + skipInvalidProxies?: boolean; + reviveConsumedRequestBodies?: boolean; + }) => ({ pluginId: 'proxy', register(env) { env.registerInit({ @@ -44,6 +47,7 @@ export const proxyPlugin = createBackendPlugin( discovery, logger: loggerToWinstonLogger(logger), skipInvalidProxies: options?.skipInvalidProxies, + reviveConsumedRequestBodies: options?.reviveConsumedRequestBodies, }), ); }, diff --git a/plugins/proxy-backend/src/service/router.test.ts b/plugins/proxy-backend/src/service/router.test.ts index aa51fe5a08..9e8c909d25 100644 --- a/plugins/proxy-backend/src/service/router.test.ts +++ b/plugins/proxy-backend/src/service/router.test.ts @@ -36,25 +36,30 @@ const mockCreateProxyMiddleware = createProxyMiddleware as jest.MockedFunction< describe('createRouter', () => { describe('where all proxy config are valid', () => { + const logger = getVoidLogger(); + const config = new ConfigReader({ + backend: { + baseUrl: 'https://example.com:7007', + listen: { + port: 7007, + }, + }, + proxy: { + '/test': { + target: 'https://example.com', + headers: { + Authorization: 'Bearer supersecret', + }, + }, + }, + }); + const discovery = SingleHostDiscovery.fromConfig(config); + + beforeEach(() => { + mockCreateProxyMiddleware.mockClear(); + }); + it('works', async () => { - const logger = getVoidLogger(); - const config = new ConfigReader({ - backend: { - baseUrl: 'https://example.com:7007', - listen: { - port: 7007, - }, - }, - proxy: { - '/test': { - target: 'https://example.com', - headers: { - Authorization: 'Bearer supersecret', - }, - }, - }, - }); - const discovery = SingleHostDiscovery.fromConfig(config); const router = await createRouter({ config, logger, @@ -62,6 +67,36 @@ describe('createRouter', () => { }); expect(router).toBeDefined(); }); + + it('revives request bodies when set', async () => { + const router = await createRouter({ + config, + logger, + discovery, + reviveConsumedRequestBodies: true, + }); + expect(router).toBeDefined(); + + expect( + mockCreateProxyMiddleware.mock.calls[0][1]?.onProxyReq, + ).toBeDefined(); + expect(mockCreateProxyMiddleware.mock.calls[0][1]?.onProxyReq).toEqual( + fixRequestBody, + ); + }); + + it('does not revive request bodies when not set', async () => { + const router = await createRouter({ + config, + logger, + discovery, + }); + expect(router).toBeDefined(); + + expect( + mockCreateProxyMiddleware.mock.calls[0][1]?.onProxyReq, + ).not.toBeDefined(); + }); }); describe('where buildMiddleware would fail', () => { @@ -395,10 +430,15 @@ describe('buildMiddleware', () => { }); it('revives request body when configured', async () => { - buildMiddleware('/proxy', logger, '/test', { - target: 'http://mocked', - reviveRequestBody: true, - }); + buildMiddleware( + '/proxy', + logger, + '/test', + { + target: 'http://mocked', + }, + true, + ); expect(createProxyMiddleware).toHaveBeenCalledTimes(1); diff --git a/plugins/proxy-backend/src/service/router.ts b/plugins/proxy-backend/src/service/router.ts index 1f66bc7f6b..b669e82f3f 100644 --- a/plugins/proxy-backend/src/service/router.ts +++ b/plugins/proxy-backend/src/service/router.ts @@ -54,6 +54,7 @@ export interface RouterOptions { config: Config; discovery: PluginEndpointDiscovery; skipInvalidProxies?: boolean; + reviveConsumedRequestBodies?: boolean; } export interface ProxyConfig extends Options { @@ -69,6 +70,7 @@ export function buildMiddleware( logger: Logger, route: string, config: string | ProxyConfig, + reviveConsumedRequestBodies?: boolean, ): RequestHandler { const fullConfig = typeof config === 'string' ? { target: config } : { ...config }; @@ -177,7 +179,7 @@ export function buildMiddleware( }); }; - if (fullConfig.reviveRequestBody) { + if (reviveConsumedRequestBodies) { fullConfig.onProxyReq = fixRequestBody; } @@ -248,7 +250,13 @@ function configureMiddlewares( try { router.use( route, - buildMiddleware(pathPrefix, options.logger, route, proxyRouteConfig), + buildMiddleware( + pathPrefix, + options.logger, + route, + proxyRouteConfig, + options.reviveConsumedRequestBodies, + ), ); } catch (e) { if (options.skipInvalidProxies) {