diff --git a/packages/config-loader/src/index.ts b/packages/config-loader/src/index.ts index 415b6d4022..b08380d8ce 100644 --- a/packages/config-loader/src/index.ts +++ b/packages/config-loader/src/index.ts @@ -15,5 +15,6 @@ */ export { readEnvConfig, loadConfigSchema } from './lib'; +export type { ConfigSchema } from './lib'; export { loadConfig } from './loader'; export type { LoadConfigOptions } from './loader'; diff --git a/packages/config-loader/src/lib/index.ts b/packages/config-loader/src/lib/index.ts index 4da0c97788..ceb7c34222 100644 --- a/packages/config-loader/src/lib/index.ts +++ b/packages/config-loader/src/lib/index.ts @@ -17,4 +17,4 @@ export { readConfigFile } from './reader'; export { readEnvConfig } from './env'; export { readSecret } from './secrets'; -export { loadConfigSchema } from './schema'; +export * from './schema'; diff --git a/packages/config-loader/src/lib/schema/collect.ts b/packages/config-loader/src/lib/schema/collect.ts index 6e3755926c..f7fed94748 100644 --- a/packages/config-loader/src/lib/schema/collect.ts +++ b/packages/config-loader/src/lib/schema/collect.ts @@ -15,7 +15,11 @@ */ import fs from 'fs-extra'; -import { resolve as resolvePath, dirname } from 'path'; +import { + resolve as resolvePath, + relative as relativePath, + dirname, +} from 'path'; import { ConfigSchemaPackageEntry } from './types'; type Item = { @@ -36,8 +40,9 @@ export async function collectConfigSchemas( ): Promise { const visitedPackages = new Set(); const schemas = Array(); + const currentDir = process.cwd(); - async function process({ name, parentPath }: Item) { + async function processItem({ name, parentPath }: Item) { // Ensures that we only process each package once. We don't bother with // loading in schemas from duplicates of different versions, as that's not // supported by Backstage right now anyway. We may want to change that in @@ -81,22 +86,22 @@ export async function collectConfigSchemas( ); schemas.push({ value, - path: pkgPath, + path: relativePath(currentDir, pkgPath), }); } else { schemas.push({ value: pkg.configSchema, - path: pkgPath, + path: relativePath(currentDir, pkgPath), }); } } await Promise.all( - depNames.map(name => process({ name, parentPath: pkgPath })), + depNames.map(name => processItem({ name, parentPath: pkgPath })), ); } - await Promise.all(packageNames.map(name => process({ name }))); + await Promise.all(packageNames.map(name => processItem({ name }))); return schemas; } diff --git a/packages/config-loader/src/lib/schema/index.ts b/packages/config-loader/src/lib/schema/index.ts index 990c0dc182..bc3c7d8ebc 100644 --- a/packages/config-loader/src/lib/schema/index.ts +++ b/packages/config-loader/src/lib/schema/index.ts @@ -15,3 +15,4 @@ */ export { loadConfigSchema } from './load'; +export type { ConfigSchema } from './types'; diff --git a/packages/config-loader/src/lib/schema/load.ts b/packages/config-loader/src/lib/schema/load.ts index 6352da65af..cdfc8f9549 100644 --- a/packages/config-loader/src/lib/schema/load.ts +++ b/packages/config-loader/src/lib/schema/load.ts @@ -14,15 +14,19 @@ * limitations under the License. */ -import { AppConfig } from '@backstage/config'; +import { AppConfig, JsonObject } from '@backstage/config'; import { compileConfigSchemas } from './compile'; import { collectConfigSchemas } from './collect'; import { filterByVisibility } from './filtering'; -import { ConfigSchema } from './types'; +import { ConfigSchema, ConfigSchemaPackageEntry } from './types'; -type Options = { - dependencies: string[]; -}; +type Options = + | { + dependencies: string[]; + } + | { + serialized: JsonObject; + }; /** * Loads config schema for a Backstage instance. @@ -30,7 +34,19 @@ type Options = { export async function loadConfigSchema( options: Options, ): Promise { - const schemas = await collectConfigSchemas(options.dependencies); + let schemas: ConfigSchemaPackageEntry[]; + + if ('dependencies' in options) { + schemas = await collectConfigSchemas(options.dependencies); + } else { + const { serialized } = options; + if (serialized?.backstageConfigSchemaVersion !== 1) { + throw new Error( + 'Serialized configuration schema is invalid or has an invalid version number', + ); + } + schemas = serialized.schemas as ConfigSchemaPackageEntry[]; + } const validate = compileConfigSchemas(schemas); @@ -54,5 +70,11 @@ export async function loadConfigSchema( return processedConfigs; }, + serialize(): JsonObject { + return { + schemas, + backstageConfigSchemaVersion: 1, + }; + }, }; } diff --git a/packages/config-loader/src/lib/schema/types.ts b/packages/config-loader/src/lib/schema/types.ts index 7e831a7a1c..cec2da0160 100644 --- a/packages/config-loader/src/lib/schema/types.ts +++ b/packages/config-loader/src/lib/schema/types.ts @@ -14,8 +14,7 @@ * limitations under the License. */ -import { AppConfig } from '@backstage/config'; -import { JSONSchema7 as JSONSchema } from 'json-schema'; +import { AppConfig, JsonObject } from '@backstage/config'; /** * An sub-set of configuration schema. @@ -24,7 +23,7 @@ export type ConfigSchemaPackageEntry = { /** * The configuration schema itself, as JSONSchema draft-07 */ - value: JSONSchema; + value: JsonObject; /** * The path that the configuration schema was discovered at. */ @@ -91,4 +90,6 @@ export type ConfigSchema = { appConfigs: AppConfig[], options?: ConfigProcessingOptions, ): AppConfig[]; + + serialize(): JsonObject; };