Merge pull request #21632 from davidfestal/support-additional-config-schemas

Support external config schemas in the backend
This commit is contained in:
Patrik Oldsberg
2024-02-13 12:04:04 +01:00
committed by GitHub
23 changed files with 540 additions and 13 deletions
+2
View File
@@ -4,6 +4,7 @@
```ts
import { Config } from '@backstage/config';
import { ConfigSchema } from '@backstage/config-loader';
import express from 'express';
import { Logger } from 'winston';
import { PluginDatabaseManager } from '@backstage/backend-common';
@@ -20,6 +21,7 @@ export interface RouterOptions {
disableConfigInjection?: boolean;
// (undocumented)
logger: Logger;
schema?: ConfigSchema;
staticFallbackHandler?: express.Handler;
}
```
+11 -2
View File
@@ -19,7 +19,11 @@ import { resolve as resolvePath } from 'path';
import { Logger } from 'winston';
import { AppConfig, Config } from '@backstage/config';
import { JsonObject } from '@backstage/types';
import { loadConfigSchema, readEnvConfig } from '@backstage/config-loader';
import {
ConfigSchema,
loadConfigSchema,
readEnvConfig,
} from '@backstage/config-loader';
type InjectOptions = {
appConfigs: AppConfig[];
@@ -74,6 +78,7 @@ type ReadOptions = {
env: { [name: string]: string | undefined };
appDistDir: string;
config: Config;
schema?: ConfigSchema;
};
/**
@@ -90,7 +95,11 @@ export async function readConfigs(options: ReadOptions): Promise<AppConfig[]> {
const serializedSchema = await fs.readJson(schemaPath);
try {
const schema = await loadConfigSchema({ serialized: serializedSchema });
const schema =
options.schema ||
(await loadConfigSchema({
serialized: serializedSchema,
}));
const frontendConfigs = await schema.process(
[{ data: config.get() as JsonObject, context: 'app' }],
+19 -1
View File
@@ -21,7 +21,11 @@ import {
} from '@backstage/backend-plugin-api';
import { createRouter } from './router';
import { loggerToWinstonLogger } from '@backstage/backend-common';
import { staticFallbackHandlerExtensionPoint } from '@backstage/plugin-app-node';
import {
configSchemaExtensionPoint,
staticFallbackHandlerExtensionPoint,
} from '@backstage/plugin-app-node';
import { ConfigSchema } from '@backstage/config-loader';
/**
* The App plugin is responsible for serving the frontend app bundle and static assets.
@@ -31,6 +35,7 @@ export const appPlugin = createBackendPlugin({
pluginId: 'app',
register(env) {
let staticFallbackHandler: express.Handler | undefined;
let schema: ConfigSchema | undefined;
env.registerExtensionPoint(staticFallbackHandlerExtensionPoint, {
setStaticFallbackHandler(handler) {
@@ -43,6 +48,17 @@ export const appPlugin = createBackendPlugin({
},
});
env.registerExtensionPoint(configSchemaExtensionPoint, {
setConfigSchema(configSchema) {
if (schema) {
throw new Error(
'Attempted to set config schema for the app-backend twice',
);
}
schema = configSchema;
},
});
env.registerInit({
deps: {
logger: coreServices.logger,
@@ -53,6 +69,7 @@ export const appPlugin = createBackendPlugin({
async init({ logger, config, database, httpRouter }) {
const appPackageName =
config.getOptionalString('app.packageName') ?? 'app';
const winstonLogger = loggerToWinstonLogger(logger);
const router = await createRouter({
@@ -61,6 +78,7 @@ export const appPlugin = createBackendPlugin({
database,
appPackageName,
staticFallbackHandler,
schema,
});
httpRouter.use(router);
},
@@ -37,6 +37,7 @@ import {
CACHE_CONTROL_NO_CACHE,
CACHE_CONTROL_REVALIDATE_CACHE,
} from '../lib/headers';
import { ConfigSchema } from '@backstage/config-loader';
// express uses mime v1 while we only have types for mime v2
type Mime = { lookup(arg0: string): string };
@@ -85,6 +86,13 @@ export interface RouterOptions {
* This also disables configuration injection though `APP_CONFIG_` environment variables.
*/
disableConfigInjection?: boolean;
/**
*
* Provides a ConfigSchema.
*
*/
schema?: ConfigSchema;
}
/** @public */
@@ -121,6 +129,7 @@ export async function createRouter(
config,
appDistDir,
env: process.env,
schema: options.schema,
});
injectedConfigPath = await injectConfig({ appConfigs, logger, staticDir });
+14
View File
@@ -3,9 +3,23 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
import { ConfigSchema } from '@backstage/config-loader';
import { ExtensionPoint } from '@backstage/backend-plugin-api';
import { Handler } from 'express';
// @public
export interface ConfigSchemaExtensionPoint {
setConfigSchema(configSchema: ConfigSchema): void;
}
// @public
export const configSchemaExtensionPoint: ExtensionPoint<ConfigSchemaExtensionPoint>;
// @public
export function loadCompiledConfigSchema(
appDistDir: string,
): Promise<ConfigSchema | undefined>;
// @public
export interface StaticFallbackHandlerExtensionPoint {
setStaticFallbackHandler(handler: Handler): void;
+3 -1
View File
@@ -34,7 +34,9 @@
],
"dependencies": {
"@backstage/backend-plugin-api": "workspace:^",
"@backstage/config-loader": "workspace:^",
"@types/express": "^4.17.6",
"express": "^4.17.1"
"express": "^4.17.1",
"fs-extra": "10.1.0"
}
}
+23
View File
@@ -15,6 +15,7 @@
*/
import { createExtensionPoint } from '@backstage/backend-plugin-api';
import { ConfigSchema } from '@backstage/config-loader';
import { Handler } from 'express';
/**
@@ -50,3 +51,25 @@ export const staticFallbackHandlerExtensionPoint =
createExtensionPoint<StaticFallbackHandlerExtensionPoint>({
id: 'app.staticFallbackHandler',
});
/**
* The interface for {@link configSchemaExtensionPoint}.
*
* @public
*/
export interface ConfigSchemaExtensionPoint {
/**
* Sets the config schema. This can only be done once.
*/
setConfigSchema(configSchema: ConfigSchema): void;
}
/**
* An extension point the exposes the ability to override the config schema used by the frontend application.
*
* @public
*/
export const configSchemaExtensionPoint =
createExtensionPoint<ConfigSchemaExtensionPoint>({
id: 'app.configSchema',
});
+4
View File
@@ -23,4 +23,8 @@
export {
staticFallbackHandlerExtensionPoint,
type StaticFallbackHandlerExtensionPoint,
configSchemaExtensionPoint,
type ConfigSchemaExtensionPoint,
} from './extensions';
export { loadCompiledConfigSchema } from './schema';
+39
View File
@@ -0,0 +1,39 @@
/*
* 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 fs from 'fs-extra';
import { resolve as resolvePath } from 'path';
import { ConfigSchema, loadConfigSchema } from '@backstage/config-loader';
/**
* Loads the config schema that is embedded in the frontend build.
*
* @public
*/
export async function loadCompiledConfigSchema(
appDistDir: string,
): Promise<ConfigSchema | undefined> {
const schemaPath = resolvePath(appDistDir, '.config-schema.json');
if (await fs.pathExists(schemaPath)) {
const serializedSchema = await fs.readJson(schemaPath);
return await loadConfigSchema({
serialized: serializedSchema,
});
}
return undefined;
}