From 8ee9b75e4854812d114500c7bded40b733257ee1 Mon Sep 17 00:00:00 2001 From: Jaanus Sellin Date: Thu, 9 Feb 2023 15:21:03 +0200 Subject: [PATCH] Add option to run unleash in strict schema validation (#3073) feat: strict schema validation --- src/lib/__snapshots__/create-config.test.ts.snap | 2 ++ src/lib/openapi/spec/feature-schema.ts | 1 + src/lib/openapi/spec/group-schema.ts | 1 + src/lib/routes/admin-api/project/features.ts | 11 +++++++++-- src/lib/services/openapi-service.ts | 7 +++++++ src/lib/types/experimental.ts | 4 ++++ .../openapi/__snapshots__/openapi.e2e.test.ts.snap | 2 ++ 7 files changed, 26 insertions(+), 2 deletions(-) diff --git a/src/lib/__snapshots__/create-config.test.ts.snap b/src/lib/__snapshots__/create-config.test.ts.snap index bb08afe436..518bd4a336 100644 --- a/src/lib/__snapshots__/create-config.test.ts.snap +++ b/src/lib/__snapshots__/create-config.test.ts.snap @@ -83,6 +83,7 @@ exports[`should create default config 1`] = ` "proxyReturnAllToggles": false, "responseTimeWithAppName": false, "showProjectApiAccess": false, + "strictSchemaValidation": false, "variantsPerEnvironment": false, }, }, @@ -104,6 +105,7 @@ exports[`should create default config 1`] = ` "proxyReturnAllToggles": false, "responseTimeWithAppName": false, "showProjectApiAccess": false, + "strictSchemaValidation": false, "variantsPerEnvironment": false, }, "externalResolver": { diff --git a/src/lib/openapi/spec/feature-schema.ts b/src/lib/openapi/spec/feature-schema.ts index d7dba80d1d..154b79fbe6 100644 --- a/src/lib/openapi/spec/feature-schema.ts +++ b/src/lib/openapi/spec/feature-schema.ts @@ -21,6 +21,7 @@ export const featureSchema = { }, description: { type: 'string', + nullable: true, }, archived: { type: 'boolean', diff --git a/src/lib/openapi/spec/group-schema.ts b/src/lib/openapi/spec/group-schema.ts index 78d507eff8..d8f2ccf565 100644 --- a/src/lib/openapi/spec/group-schema.ts +++ b/src/lib/openapi/spec/group-schema.ts @@ -16,6 +16,7 @@ export const groupSchema = { }, description: { type: 'string', + nullable: true, }, mappingsSSO: { type: 'array', diff --git a/src/lib/routes/admin-api/project/features.ts b/src/lib/routes/admin-api/project/features.ts index 67d29e2261..57da03ea0a 100644 --- a/src/lib/routes/admin-api/project/features.ts +++ b/src/lib/routes/admin-api/project/features.ts @@ -24,6 +24,7 @@ import { createRequestSchema, createResponseSchema, emptyResponse, + featureEnvironmentSchema, FeatureEnvironmentSchema, featureSchema, FeatureSchema, @@ -590,7 +591,13 @@ export default class ProjectFeaturesController extends Controller { const result = { ...environmentInfo, strategies: environmentInfo.strategies.map((strategy) => { - const { strategyName, ...rest } = strategy; + const { + strategyName, + projectId: project, + environment: environmentId, + createdAt, + ...rest + } = strategy; return { ...rest, name: strategyName }; }), }; @@ -598,7 +605,7 @@ export default class ProjectFeaturesController extends Controller { this.openApiService.respondWithValidation( 200, res, - featureSchema.$id, + featureEnvironmentSchema.$id, serializeDates(result), ); } diff --git a/src/lib/services/openapi-service.ts b/src/lib/services/openapi-service.ts index 89b6f5f016..dfee790164 100644 --- a/src/lib/services/openapi-service.ts +++ b/src/lib/services/openapi-service.ts @@ -10,6 +10,7 @@ import { import { ApiOperation } from '../openapi/util/api-operation'; import { Logger } from '../logger'; import { validateSchema } from '../openapi/validate'; +import { IFlagResolver } from '../types'; export class OpenApiService { private readonly config: IUnleashConfig; @@ -18,8 +19,11 @@ export class OpenApiService { private readonly api: IExpressOpenApi; + private flagResolver: IFlagResolver; + constructor(config: IUnleashConfig) { this.config = config; + this.flagResolver = config.flagResolver; this.logger = config.getLogger('openapi-service.ts'); this.api = openapi( @@ -78,6 +82,9 @@ export class OpenApiService { 'Invalid response:', JSON.stringify(errors, null, 4), ); + if (this.flagResolver.isEnabled('strictSchemaValidation')) { + throw new Error(JSON.stringify(errors, null, 4)); + } } Object.entries(headers).forEach(([header, value]) => diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index 12d59ec021..ea13f6836c 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -66,6 +66,10 @@ const flags = { process.env.UNLEASH_EXPERIMENTAL_PROJECT_API_ACCESS, false, ), + strictSchemaValidation: parseEnvVarBoolean( + process.env.UNLEASH_STRICT_SCHEMA_VALIDTION, + false, + ), }; export const defaultExperimentalOptions: IExperimentalOptions = { diff --git a/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap b/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap index 5844f82e87..72bc086a42 100644 --- a/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap +++ b/src/test/e2e/api/openapi/__snapshots__/openapi.e2e.test.ts.snap @@ -1245,6 +1245,7 @@ exports[`should serve the OpenAPI spec 1`] = ` "type": "string", }, "description": { + "nullable": true, "type": "string", }, "enabled": { @@ -1524,6 +1525,7 @@ exports[`should serve the OpenAPI spec 1`] = ` "type": "string", }, "description": { + "nullable": true, "type": "string", }, "id": {