From 87cec6c9b727f7fb75f2239075b37c5901836d37 Mon Sep 17 00:00:00 2001 From: Thomas Heartman Date: Wed, 19 Jul 2023 17:18:47 +0200 Subject: [PATCH] openapi: update ui-config endpoints (#4280) --- src/lib/openapi/meta-schema-rules.test.ts | 8 --- src/lib/openapi/spec/set-ui-config-schema.ts | 5 ++ src/lib/openapi/spec/ui-config-schema.ts | 63 ++++++++++++++++++++ src/lib/openapi/spec/variant-flag-schema.ts | 12 ++++ src/lib/openapi/spec/version-schema.ts | 22 ++++++- src/lib/openapi/validate.ts | 2 +- src/lib/routes/admin-api/config.ts | 6 ++ src/test/e2e/api/admin/config.e2e.test.ts | 13 +++- 8 files changed, 119 insertions(+), 12 deletions(-) diff --git a/src/lib/openapi/meta-schema-rules.test.ts b/src/lib/openapi/meta-schema-rules.test.ts index 0420e48116..986c24c981 100644 --- a/src/lib/openapi/meta-schema-rules.test.ts +++ b/src/lib/openapi/meta-schema-rules.test.ts @@ -100,15 +100,11 @@ const metaRules: Rule[] = [ 'pushVariantsSchema', 'resetPasswordSchema', 'sdkContextSchema', - 'setUiConfigSchema', 'stateSchema', - 'uiConfigSchema', 'upsertContextFieldSchema', 'upsertStrategySchema', 'usersGroupsBaseSchema', 'validateEdgeTokensSchema', - 'variantFlagSchema', - 'versionSchema', 'projectOverviewSchema', ], }, @@ -136,17 +132,13 @@ const metaRules: Rule[] = [ 'playgroundStrategySchema', 'pushVariantsSchema', 'resetPasswordSchema', - 'setUiConfigSchema', 'sortOrderSchema', - 'uiConfigSchema', 'upsertContextFieldSchema', 'upsertStrategySchema', 'usersGroupsBaseSchema', 'usersSearchSchema', 'validateEdgeTokensSchema', - 'variantFlagSchema', 'variantsSchema', - 'versionSchema', ], }, ]; diff --git a/src/lib/openapi/spec/set-ui-config-schema.ts b/src/lib/openapi/spec/set-ui-config-schema.ts index c566b46f80..7e7dcdc017 100644 --- a/src/lib/openapi/spec/set-ui-config-schema.ts +++ b/src/lib/openapi/spec/set-ui-config-schema.ts @@ -4,13 +4,18 @@ export const setUiConfigSchema = { $id: '#/components/schemas/setUiConfigSchema', type: 'object', additionalProperties: false, + description: 'Unleash configuration settings affect the admin UI.', properties: { frontendSettings: { type: 'object', + description: 'Settings related to the front-end API.', additionalProperties: false, required: ['frontendApiOrigins'], properties: { frontendApiOrigins: { + description: + 'The list of origins that the front-end API should accept requests from.', + example: ['*'], type: 'array', items: { type: 'string' }, }, diff --git a/src/lib/openapi/spec/ui-config-schema.ts b/src/lib/openapi/spec/ui-config-schema.ts index 38f0acc053..bb715fefb4 100644 --- a/src/lib/openapi/spec/ui-config-schema.ts +++ b/src/lib/openapi/spec/ui-config-schema.ts @@ -6,52 +6,96 @@ export const uiConfigSchema = { $id: '#/components/schemas/uiConfigSchema', type: 'object', additionalProperties: false, + description: + 'A collection of properties used to configure the Unleash Admin UI.', required: ['version', 'unleashUrl', 'baseUriPath', 'versionInfo'], properties: { slogan: { type: 'string', + description: 'The slogan to display in the UI footer.', + example: 'The enterprise-ready feature toggle service.', }, name: { type: 'string', + description: + 'The name of this Unleash instance. Used to build the text in the footer.', + example: 'Unleash enterprise', }, version: { type: 'string', + description: 'The current version of Unleash', + example: '5.3.0-main', }, environment: { type: 'string', + description: + 'What kind of Unleash instance it is: Enterprise, Pro, or Open source', + example: 'Enterprise', }, unleashUrl: { type: 'string', + description: 'The URL of the Unleash instance.', + example: 'https://unleash.mycompany.com/enterprise', }, baseUriPath: { type: 'string', + description: + 'The base URI path at which this Unleash instance is listening.', + example: '/enterprise', }, disablePasswordAuth: { type: 'boolean', + description: + 'Whether password authentication should be disabled or not.', + example: false, }, emailEnabled: { type: 'boolean', + description: 'Whether this instance can send out emails or not.', + example: true, }, maintenanceMode: { type: 'boolean', + description: 'Whether maintenance mode is currently active or not.', + example: false, }, segmentValuesLimit: { type: 'number', + description: + 'The maximum number of values that can be used in a single segment.', + example: 1000, }, strategySegmentsLimit: { type: 'number', + description: + 'The maximum number of segments that can be applied to a single strategy.', + example: 5, }, networkViewEnabled: { type: 'boolean', + description: 'Whether to enable the Unleash network view or not.', + example: true, }, frontendApiOrigins: { type: 'array', + description: + 'The list of origins that the front-end API should accept requests from.', + example: ['*'], items: { type: 'string', }, }, flags: { type: 'object', + description: + 'Additional (largely experimental) features that are enabled in this Unleash instance.', + example: { + messageBanner: { + name: 'disabled', + enabled: false, + }, + featuresExportImport: true, + }, additionalProperties: { anyOf: [ { @@ -64,6 +108,22 @@ export const uiConfigSchema = { }, }, links: { + description: 'Relevant links to use in the UI.', + example: [ + { + value: 'Documentation', + icon: 'library_books', + href: 'https://docs.getunleash.io/docs', + title: 'User documentation', + }, + { + value: 'GitHub', + icon: 'c_github', + href: 'https://github.com/Unleash/unleash', + title: 'Source code on GitHub', + }, + ], + 'x-enforcer-exception-skip-codes': 'WSCH006', // allow additional properties in example (openapi enforcer) type: 'array', items: { type: 'object', @@ -71,6 +131,9 @@ export const uiConfigSchema = { }, authenticationType: { type: 'string', + description: + 'The type of authentication enabled for this Unleash instance', + example: 'enterprise', enum: [ 'open-source', 'demo', diff --git a/src/lib/openapi/spec/variant-flag-schema.ts b/src/lib/openapi/spec/variant-flag-schema.ts index ed46d7b9cd..939a0b5100 100644 --- a/src/lib/openapi/spec/variant-flag-schema.ts +++ b/src/lib/openapi/spec/variant-flag-schema.ts @@ -4,22 +4,34 @@ export const variantFlagSchema = { $id: '#/components/schemas/variantFlagSchema', type: 'object', additionalProperties: false, + description: 'A representation of an evaluated Unleash feature variant.', properties: { name: { + description: + 'The name of the variant. Will always be disabled if `enabled` is false.', + example: 'blue', type: 'string', }, enabled: { type: 'boolean', + description: 'Whether the variant is enabled or not.', + example: true, }, payload: { type: 'object', + description: 'Additional data associated with this variant.', additionalProperties: false, properties: { type: { + description: 'The type of data contained.', type: 'string', + enum: ['string', 'json', 'csv'], + example: 'json', }, value: { + description: 'The actual associated data', type: 'string', + example: '{ "starter": "squirtle" }', }, }, }, diff --git a/src/lib/openapi/spec/version-schema.ts b/src/lib/openapi/spec/version-schema.ts index 6d0010a348..d0d80a8b3a 100644 --- a/src/lib/openapi/spec/version-schema.ts +++ b/src/lib/openapi/spec/version-schema.ts @@ -4,16 +4,24 @@ export const versionSchema = { $id: '#/components/schemas/versionSchema', type: 'object', additionalProperties: false, - required: ['current', 'latest', 'isLatest', 'instanceId'], + description: 'Detailed information about an Unleash version', + required: ['current', 'latest', 'isLatest'], properties: { current: { type: 'object', additionalProperties: false, + description: 'The current version of Unleash.', properties: { oss: { + description: + 'The OSS version used when building this Unleash instance, represented as a git revision belonging to the [main Unleash git repo](https://github.com/Unleash/unleash/)', + example: '5.3.0-main', type: 'string', }, enterprise: { + description: + 'The Enterpris version of Unleash used to build this instance, represented as a git revision belonging to the [Unleash Enterprise](https://github.com/ivarconr/unleash-enterprise) repository. Will be an empty string if no enterprise version was used,', + example: '5.3.0-main+2105.45ed03c9', type: 'string', }, }, @@ -21,20 +29,32 @@ export const versionSchema = { latest: { type: 'object', additionalProperties: false, + description: + 'Information about the latest available Unleash releases. Will be an empty object if no data is available.', properties: { oss: { + description: 'The latest available OSS version of Unleash', type: 'string', + example: '5.1.5', }, enterprise: { + description: + 'The latest available Enterprise version of Unleash', type: 'string', + example: '5.1.5', }, }, }, isLatest: { type: 'boolean', + description: + 'Whether the Unleash server is running the latest release (`true`) or if there are updates available (`false`)', + example: true, }, instanceId: { type: 'string', + description: 'The instance identifier of the Unleash instance', + example: '0d652a82-43db-4144-8e02-864b0b030710', }, }, components: {}, diff --git a/src/lib/openapi/validate.ts b/src/lib/openapi/validate.ts index c89a38a7cb..1df2ab0d02 100644 --- a/src/lib/openapi/validate.ts +++ b/src/lib/openapi/validate.ts @@ -13,7 +13,7 @@ const ajv = new Ajv({ ), // example was superseded by examples in openapi 3.1, but we're still on 3.0, so // let's add it back in! - keywords: ['example'], + keywords: ['example', 'x-enforcer-exception-skip-codes'], formats: { 'date-time': true, uri: true, diff --git a/src/lib/routes/admin-api/config.ts b/src/lib/routes/admin-api/config.ts index 6ed55c95ea..8dcc3ea6bd 100644 --- a/src/lib/routes/admin-api/config.ts +++ b/src/lib/routes/admin-api/config.ts @@ -75,6 +75,9 @@ class ConfigController extends Controller { middleware: [ openApiService.validPath({ tags: ['Admin UI'], + summary: 'Get UI configuration', + description: + 'Retrieves the full configuration used to set up the Unleash Admin UI.', operationId: 'getUiConfig', responses: { 200: createResponseSchema('uiConfigSchema'), @@ -91,6 +94,9 @@ class ConfigController extends Controller { middleware: [ openApiService.validPath({ tags: ['Admin UI'], + summary: 'Set UI configuration', + description: + 'Sets the UI configuration for this Unleash instance.', operationId: 'setUiConfig', requestBody: createRequestSchema('setUiConfigSchema'), responses: { 200: emptyResponse }, diff --git a/src/test/e2e/api/admin/config.e2e.test.ts b/src/test/e2e/api/admin/config.e2e.test.ts index c6e30be67e..d2a3a51caa 100644 --- a/src/test/e2e/api/admin/config.e2e.test.ts +++ b/src/test/e2e/api/admin/config.e2e.test.ts @@ -1,5 +1,8 @@ import dbInit, { ITestDb } from '../../helpers/database-init'; -import { IUnleashTest, setupApp } from '../../helpers/test-helper'; +import { + IUnleashTest, + setupAppWithCustomConfig, +} from '../../helpers/test-helper'; import getLogger from '../../../fixtures/no-logger'; import { simpleAuthSettingsKey } from '../../../../lib/types/settings/simple-auth-settings'; import { randomId } from '../../../../lib/util/random-id'; @@ -9,7 +12,13 @@ let app: IUnleashTest; beforeAll(async () => { db = await dbInit('config_api_serial', getLogger); - app = await setupApp(db.stores); + app = await setupAppWithCustomConfig(db.stores, { + experimental: { + flags: { + strictSchemaValidation: true, + }, + }, + }); }); afterAll(async () => {