From 11b001cf4dad381d3d6717482112dacafbdbf729 Mon Sep 17 00:00:00 2001 From: Matt Benson Date: Fri, 18 Oct 2024 14:09:51 -0500 Subject: [PATCH] Add proxyEndpointsExtensionPoint Signed-off-by: Matt Benson --- .changeset/four-bananas-sneeze.md | 6 +++ docs/plugins/proxying.md | 39 ++++++++++++++ plugins/proxy-backend/dev/index.ts | 28 ++++++++++ plugins/proxy-backend/package.json | 1 + plugins/proxy-backend/report.api.md | 3 ++ plugins/proxy-backend/src/plugin.ts | 10 ++++ plugins/proxy-backend/src/service/router.ts | 6 ++- plugins/proxy-node/.eslintrc.js | 1 + plugins/proxy-node/README.md | 5 ++ plugins/proxy-node/catalog-info.yaml | 10 ++++ plugins/proxy-node/package.json | 59 +++++++++++++++++++++ plugins/proxy-node/report-alpha.api.md | 19 +++++++ plugins/proxy-node/src/alpha/index.ts | 36 +++++++++++++ yarn.lock | 13 +++++ 14 files changed, 235 insertions(+), 1 deletion(-) create mode 100644 .changeset/four-bananas-sneeze.md create mode 100644 plugins/proxy-node/.eslintrc.js create mode 100644 plugins/proxy-node/README.md create mode 100644 plugins/proxy-node/catalog-info.yaml create mode 100644 plugins/proxy-node/package.json create mode 100644 plugins/proxy-node/report-alpha.api.md create mode 100644 plugins/proxy-node/src/alpha/index.ts diff --git a/.changeset/four-bananas-sneeze.md b/.changeset/four-bananas-sneeze.md new file mode 100644 index 0000000000..025dae518a --- /dev/null +++ b/.changeset/four-bananas-sneeze.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-proxy-backend': minor +'@backstage/plugin-proxy-node': minor +--- + +Add proxyEndpointsExtensionPoint diff --git a/docs/plugins/proxying.md b/docs/plugins/proxying.md index 09afa3f519..e4aff4b399 100644 --- a/docs/plugins/proxying.md +++ b/docs/plugins/proxying.md @@ -120,3 +120,42 @@ third-parties. The same logic applies to headers that are sent from the target back to the frontend. + +### New backend extension + +The proxy plugin additionally supports a `proxyExtensionEndpoint` which a proxy +plugin module can utilize in order to programmatically register additional +endpoints, whose payloads are specified exactly as in app-config (described +above). Note that endpoints configured in app-config will always override those +registered in this manner. + +Example: + +```ts +backend.add( + createBackendModule({ + pluginId: 'proxy', + moduleId: 'demo-additional-endpoints', + register: reg => { + reg.registerInit({ + deps: { + proxyEndpoints: proxyEndpointsExtensionPoint, + }, + init: async ({ proxyEndpoints }) => { + let largerExampleAuth: string = /* exercise for the reader */; + proxyEndpoints.addProxyEndpoints({ + "/simple-example": "http://simple.example.com:8080", + "/larger-example/v1": { + target: "http://larger.example.com:8080/svc.v1", + credentials: "require", + headers: { + Authorization: largerExampleAuth + }, + }, + }); + }, + }); + }, + }), +); +``` diff --git a/plugins/proxy-backend/dev/index.ts b/plugins/proxy-backend/dev/index.ts index 43c75d24df..4e4f84056a 100644 --- a/plugins/proxy-backend/dev/index.ts +++ b/plugins/proxy-backend/dev/index.ts @@ -15,7 +15,35 @@ */ import { createBackend } from '@backstage/backend-defaults'; +import { createBackendModule } from '@backstage/backend-plugin-api'; +import { proxyEndpointsExtensionPoint } from '@backstage/plugin-proxy-node/alpha'; const backend = createBackend(); backend.add(import('../src/alpha')); + +backend.add( + createBackendModule({ + pluginId: 'proxy', + moduleId: 'demo-additional-endpoints', + register: reg => { + reg.registerInit({ + deps: { + proxyEndpoints: proxyEndpointsExtensionPoint, + }, + init: async ({ proxyEndpoints }) => { + proxyEndpoints.addProxyEndpoints({ + ...Object.fromEntries( + ['foo', 'bar', 'baz'].map(msv => [ + `/${msv}`, + { target: `http://${msv}.org` }, + ]), + ), + '/gocd': 'http://cannot-override-config.no', + }); + }, + }); + }, + }), +); + backend.start(); diff --git a/plugins/proxy-backend/package.json b/plugins/proxy-backend/package.json index 97c14a271e..b297981013 100644 --- a/plugins/proxy-backend/package.json +++ b/plugins/proxy-backend/package.json @@ -56,6 +56,7 @@ "@backstage/backend-common": "^0.25.0", "@backstage/backend-plugin-api": "workspace:^", "@backstage/config": "workspace:^", + "@backstage/plugin-proxy-node": "workspace:^", "@backstage/types": "workspace:^", "@types/express": "^4.17.6", "express": "^4.17.1", diff --git a/plugins/proxy-backend/report.api.md b/plugins/proxy-backend/report.api.md index c0601eac30..ef0fe37c96 100644 --- a/plugins/proxy-backend/report.api.md +++ b/plugins/proxy-backend/report.api.md @@ -6,6 +6,7 @@ import { BackendFeature } from '@backstage/backend-plugin-api'; import { DiscoveryService } from '@backstage/backend-plugin-api'; import express from 'express'; +import { JsonObject } from '@backstage/types'; import { Logger } from 'winston'; import { RootConfigService } from '@backstage/backend-plugin-api'; @@ -18,6 +19,8 @@ export default proxyPlugin; // @public @deprecated (undocumented) export interface RouterOptions { + // (undocumented) + additionalEndpoints?: JsonObject; // (undocumented) config: RootConfigService; // (undocumented) diff --git a/plugins/proxy-backend/src/plugin.ts b/plugins/proxy-backend/src/plugin.ts index 1039a6a3e8..2e310860d5 100644 --- a/plugins/proxy-backend/src/plugin.ts +++ b/plugins/proxy-backend/src/plugin.ts @@ -20,6 +20,8 @@ import { coreServices, } from '@backstage/backend-plugin-api'; import { createRouterInternal } from './service/router'; +import { proxyEndpointsExtensionPoint } from '@backstage/plugin-proxy-node/alpha'; +import { JsonObject } from '@backstage/types'; /** * The proxy backend plugin. @@ -29,6 +31,13 @@ import { createRouterInternal } from './service/router'; export const proxyPlugin = createBackendPlugin({ pluginId: 'proxy', register(env) { + const additionalEndpoints = {}; + + env.registerExtensionPoint(proxyEndpointsExtensionPoint, { + addProxyEndpoints(endpoints: JsonObject) { + Object.assign(additionalEndpoints, endpoints); + }, + }); env.registerInit({ deps: { config: coreServices.rootConfig, @@ -42,6 +51,7 @@ export const proxyPlugin = createBackendPlugin({ discovery, logger: loggerToWinstonLogger(logger), httpRouterService: httpRouter, + additionalEndpoints, }); }, }); diff --git a/plugins/proxy-backend/src/service/router.ts b/plugins/proxy-backend/src/service/router.ts index 9ea064ea55..b0c7c95589 100644 --- a/plugins/proxy-backend/src/service/router.ts +++ b/plugins/proxy-backend/src/service/router.ts @@ -63,6 +63,7 @@ export interface RouterOptions { discovery: DiscoveryService; skipInvalidProxies?: boolean; reviveConsumedRequestBodies?: boolean; + additionalEndpoints?: JsonObject; } export interface ProxyConfig extends Options { @@ -315,7 +316,10 @@ export async function createRouterInternal( const externalUrl = await options.discovery.getExternalBaseUrl('proxy'); const { pathname: pathPrefix } = new URL(externalUrl); - const proxyConfig = readProxyConfig(options.config, options.logger); + const proxyConfig = { + ...(options.additionalEndpoints ?? {}), + ...readProxyConfig(options.config, options.logger), + }; configureMiddlewares( proxyOptions, currentRouter, diff --git a/plugins/proxy-node/.eslintrc.js b/plugins/proxy-node/.eslintrc.js new file mode 100644 index 0000000000..e2a53a6ad2 --- /dev/null +++ b/plugins/proxy-node/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/proxy-node/README.md b/plugins/proxy-node/README.md new file mode 100644 index 0000000000..7f48a17cf6 --- /dev/null +++ b/plugins/proxy-node/README.md @@ -0,0 +1,5 @@ +# backstage-plugin-proxy-node-node + +Welcome to the Node.js library package for the proxy-node plugin! + +_This plugin was created through the Backstage CLI_ diff --git a/plugins/proxy-node/catalog-info.yaml b/plugins/proxy-node/catalog-info.yaml new file mode 100644 index 0000000000..94dad42af2 --- /dev/null +++ b/plugins/proxy-node/catalog-info.yaml @@ -0,0 +1,10 @@ +apiVersion: backstage.io/v1alpha1 +kind: Component +metadata: + name: backstage-plugin-proxy-node + title: '@backstage/plugin-proxy-node' + description: The plugin-proxy-node module for @backstage/plugin-proxy-backend +spec: + lifecycle: experimental + type: backstage-node-library + owner: maintainers diff --git a/plugins/proxy-node/package.json b/plugins/proxy-node/package.json new file mode 100644 index 0000000000..8293ae0c98 --- /dev/null +++ b/plugins/proxy-node/package.json @@ -0,0 +1,59 @@ +{ + "name": "@backstage/plugin-proxy-node", + "version": "0.0.0", + "description": "The plugin-proxy-node module for @backstage/plugin-proxy-backend", + "backstage": { + "role": "node-library", + "pluginId": "proxy-backend", + "pluginPackages": [ + "@backstage/plugin-proxy-node" + ] + }, + "publishConfig": { + "access": "public" + }, + "homepage": "https://backstage.io", + "repository": { + "type": "git", + "url": "https://github.com/backstage/backstage", + "directory": "plugins/proxy-node" + }, + "license": "Apache-2.0", + "exports": { + "./alpha": "./src/alpha/index.ts", + "./package.json": "./package.json" + }, + "main": "src/alpha/index.ts", + "types": "src/alpha/index.ts", + "typesVersions": { + "*": { + "alpha": [ + "src/alpha/index.ts" + ], + "package.json": [ + "package.json" + ] + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "backstage-cli package build", + "clean": "backstage-cli package clean", + "lint": "backstage-cli package lint", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack", + "start": "backstage-cli package start", + "test": "backstage-cli package test" + }, + "dependencies": { + "@backstage/backend-plugin-api": "workspace:^", + "@backstage/types": "workspace:^" + }, + "devDependencies": { + "@backstage/backend-test-utils": "workspace:^", + "@backstage/cli": "workspace:^", + "@backstage/config": "workspace:^" + } +} diff --git a/plugins/proxy-node/report-alpha.api.md b/plugins/proxy-node/report-alpha.api.md new file mode 100644 index 0000000000..c64e5b773a --- /dev/null +++ b/plugins/proxy-node/report-alpha.api.md @@ -0,0 +1,19 @@ +## API Report File for "@backstage/plugin-proxy-node" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +import { ExtensionPoint } from '@backstage/backend-plugin-api'; +import { JsonObject } from '@backstage/types'; + +// @alpha +export interface ProxyEndpointsExtensionPoint { + // (undocumented) + addProxyEndpoints(endpoints: JsonObject): void; +} + +// @alpha +export const proxyEndpointsExtensionPoint: ExtensionPoint; + +// (No @packageDocumentation comment for this package) +``` diff --git a/plugins/proxy-node/src/alpha/index.ts b/plugins/proxy-node/src/alpha/index.ts new file mode 100644 index 0000000000..215ec93d17 --- /dev/null +++ b/plugins/proxy-node/src/alpha/index.ts @@ -0,0 +1,36 @@ +/* + * Copyright 2024 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { createExtensionPoint } from '@backstage/backend-plugin-api'; +import { JsonObject } from '@backstage/types'; + +/** + * Extension point interface for managing proxy endpoints. + * + * @alpha + */ +export interface ProxyEndpointsExtensionPoint { + addProxyEndpoints(endpoints: JsonObject): void; +} + +/** + * Extension point for managing proxy endpoints. + * + * @alpha + */ +export const proxyEndpointsExtensionPoint = + createExtensionPoint({ + id: 'proxy.endpoints', + }); diff --git a/yarn.lock b/yarn.lock index b48c24a935..665257942b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7120,6 +7120,7 @@ __metadata: "@backstage/config": "workspace:^" "@backstage/config-loader": "workspace:^" "@backstage/errors": "workspace:^" + "@backstage/plugin-proxy-node": "workspace:^" "@backstage/types": "workspace:^" "@types/express": ^4.17.6 "@types/http-proxy-middleware": ^1.0.0 @@ -7137,6 +7138,18 @@ __metadata: languageName: unknown linkType: soft +"@backstage/plugin-proxy-node@workspace:^, @backstage/plugin-proxy-node@workspace:plugins/proxy-node": + version: 0.0.0-use.local + resolution: "@backstage/plugin-proxy-node@workspace:plugins/proxy-node" + dependencies: + "@backstage/backend-plugin-api": "workspace:^" + "@backstage/backend-test-utils": "workspace:^" + "@backstage/cli": "workspace:^" + "@backstage/config": "workspace:^" + "@backstage/types": "workspace:^" + languageName: unknown + linkType: soft + "@backstage/plugin-scaffolder-backend-module-azure@workspace:^, @backstage/plugin-scaffolder-backend-module-azure@workspace:plugins/scaffolder-backend-module-azure": version: 0.0.0-use.local resolution: "@backstage/plugin-scaffolder-backend-module-azure@workspace:plugins/scaffolder-backend-module-azure"