From 87fe576be7ebe32835ff7b45d8f0c2d145316e85 Mon Sep 17 00:00:00 2001 From: Nicolas Froidure Date: Wed, 29 May 2024 11:22:31 +0200 Subject: [PATCH] feat(openapi): move OpenAPI support to 3.1 --- .../src/commands/testHTTPLambda.ts | 4 +- packages/whook-aws-lambda/src/index.ts | 4 +- .../src/services/_autoload.ts | 14 +- .../src/wrappers/awsConsumerLambda.ts | 10 +- .../src/wrappers/awsCronLambda.ts | 10 +- .../src/wrappers/awsHTTPLambda.ts | 16 +- .../src/wrappers/awsKafkaConsumerLambda.ts | 10 +- .../src/wrappers/awsLogSubscriberLambda.ts | 10 +- .../src/wrappers/awsS3Lambda.ts | 10 +- .../src/wrappers/awsTransformerLambda.ts | 12 +- .../src/__snapshots__/index.test.ts.snap | 2 +- packages/whook-cors/src/index.test.ts | 8 +- packages/whook-cors/src/index.ts | 185 ++++++------- packages/whook-example/src/index.test.ts | 152 +++++------ packages/whook-example/src/openAPISchema.d.ts | 8 +- .../whook-example/src/services/API.test.ts | 3 +- packages/whook-example/src/services/API.ts | 18 +- .../services/__snapshots__/API.test.ts.snap | 2 +- .../src/commands/testHTTPFunction.ts | 10 +- packages/whook-gcp-functions/src/index.ts | 4 +- .../src/services/_autoload.ts | 14 +- .../wrapHandlerForGoogleHTTPFunction.ts | 15 +- packages/whook-graphiql/src/index.test.ts | 6 +- packages/whook-graphql/src/index.test.ts | 6 +- packages/whook-http-router/src/index.test.ts | 28 +- packages/whook-http-router/src/index.ts | 10 +- packages/whook-http-router/src/libs/body.ts | 6 +- .../src/libs/openAPIUtils.ts | 95 ++++--- packages/whook-http-router/src/libs/utils.ts | 12 +- .../src/libs/validation.test.ts | 20 +- .../whook-http-router/src/libs/validation.ts | 25 +- .../src/services/errorHandler.ts | 4 +- packages/whook-http-transaction/src/index.ts | 16 +- .../whook-method-override/src/index.test.ts | 6 +- packages/whook-oauth2/README.md | 14 +- .../src/handlers/postOAuth2Token.ts | 4 +- packages/whook-oauth2/src/index.test.ts | 6 +- .../__snapshots__/getOpenAPI.test.ts.snap | 8 +- .../src/handlers/getOpenAPI.test.ts | 10 +- .../src/handlers/getOpenAPI.ts | 55 ++-- packages/whook-swagger-ui/src/index.test.ts | 6 +- .../src/__snapshots__/index.test.ts.snap | 2 +- packages/whook-versions/src/index.test.ts | 2 +- packages/whook-versions/src/index.ts | 27 +- packages/whook/README.md | 4 +- packages/whook/src/commands/create.test.ts | 6 +- packages/whook/src/commands/create.ts | 4 +- .../commands/generateOpenAPISchema.test.ts | 64 ++--- .../src/commands/generateOpenAPITypes.test.ts | 6 +- .../whook/src/handlers/getOpenAPI.test.ts | 218 ++++++++-------- packages/whook/src/handlers/getOpenAPI.ts | 70 ++--- packages/whook/src/index.test.ts | 2 +- packages/whook/src/libs/openapi.test.ts | 242 +++++++++--------- packages/whook/src/libs/openapi.ts | 12 +- .../whook/src/services/API_DEFINITIONS.ts | 42 +-- packages/whook/src/services/_autoload.test.ts | 4 +- 56 files changed, 792 insertions(+), 771 deletions(-) diff --git a/packages/whook-aws-lambda/src/commands/testHTTPLambda.ts b/packages/whook-aws-lambda/src/commands/testHTTPLambda.ts index 5e1c0d86..01248b35 100644 --- a/packages/whook-aws-lambda/src/commands/testHTTPLambda.ts +++ b/packages/whook-aws-lambda/src/commands/testHTTPLambda.ts @@ -17,7 +17,7 @@ import type { } from '@whook/whook'; import type { AppEnvVars } from 'application-services'; import type { LogService, TimeService } from 'common-services'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { WhookAPIOperationAWSLambdaConfig } from '../index.js'; import type { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda'; @@ -69,7 +69,7 @@ async function initTestHTTPLambdaCommand({ APP_ENV: string; PROJECT_DIR: string; COMPILER_OPTIONS?: WhookCompilerOptions; - API: OpenAPIV3.Document; + API: OpenAPIV3_1.Document; time: TimeService; log: LogService; args: WhookCommandArgs; diff --git a/packages/whook-aws-lambda/src/index.ts b/packages/whook-aws-lambda/src/index.ts index 2144d6eb..94ac276a 100644 --- a/packages/whook-aws-lambda/src/index.ts +++ b/packages/whook-aws-lambda/src/index.ts @@ -28,7 +28,7 @@ import type { WhookCompilerOptions, WhookCompilerService, } from '@whook/whook'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { LogService } from 'common-services'; import type { CprOptions } from 'cpr'; @@ -171,7 +171,7 @@ export async function runBuild( compiler: WhookCompilerService; log: LogService; $autoload: Autoloader>; - API: OpenAPIV3.Document; + API: OpenAPIV3_1.Document; buildInitializer: BuildInitializer; } = await $.run([ 'APP_ENV', diff --git a/packages/whook-aws-lambda/src/services/_autoload.ts b/packages/whook-aws-lambda/src/services/_autoload.ts index 40ffa3a8..8eda8fd4 100644 --- a/packages/whook-aws-lambda/src/services/_autoload.ts +++ b/packages/whook-aws-lambda/src/services/_autoload.ts @@ -32,7 +32,7 @@ import type { ServiceInitializerWrapper, } from 'knifecycle'; import type { LogService } from 'common-services'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { WhookAPIOperationAWSLambdaConfig, WhookAWSLambdaConfiguration, @@ -99,12 +99,12 @@ const initializerWrapper: ServiceInitializerWrapper< path: string; }> > => { - let API: OpenAPIV3.Document; + let API: OpenAPIV3_1.Document; let OPERATION_APIS: WhookRawOperation[]; const getAPIOperation: ( serviceName: string, ) => Promise< - [WhookAWSLambdaConfiguration['type'], string, OpenAPIV3.Document] + [WhookAWSLambdaConfiguration['type'], string, OpenAPIV3_1.Document] > = (() => { return async (serviceName) => { const cleanedName = serviceName.split('_').pop(); @@ -131,11 +131,11 @@ const initializerWrapper: ServiceInitializerWrapper< } // eslint-disable-next-line - const OPERATION_API: OpenAPIV3.Document = cleanupOpenAPI({ + const OPERATION_API: OpenAPIV3_1.Document = cleanupOpenAPI({ ...API, paths: { [OPERATION.path]: { - [OPERATION.method]: API.paths[OPERATION.path]?.[OPERATION.method], + [OPERATION.method]: API.paths?.[OPERATION.path]?.[OPERATION.method], }, }, }); @@ -152,7 +152,9 @@ const initializerWrapper: ServiceInitializerWrapper< { path: OPERATION.path, method: OPERATION.method, - ...OPERATION_API.paths[OPERATION.path]?.[OPERATION.method], + ...OPERATION_API.paths?.[OPERATION.path]?.[ + OPERATION.method + ], parameters: OPERATION.parameters, }, ]) diff --git a/packages/whook-aws-lambda/src/wrappers/awsConsumerLambda.ts b/packages/whook-aws-lambda/src/wrappers/awsConsumerLambda.ts index 02c35051..33792eec 100644 --- a/packages/whook-aws-lambda/src/wrappers/awsConsumerLambda.ts +++ b/packages/whook-aws-lambda/src/wrappers/awsConsumerLambda.ts @@ -11,7 +11,7 @@ import type { WhookWrapper, } from '@whook/whook'; import type { TimeService, LogService } from 'common-services'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { KinesisStreamEvent, SQSEvent, @@ -47,7 +47,7 @@ export type LambdaConsumerOutput = WhookResponse; export type WhookWrapConsumerLambdaDependencies = { ENV: AppEnvVars; - OPERATION_API: OpenAPIV3.Document; + OPERATION_API: OpenAPIV3_1.Document; apm: WhookAPMService; time?: TimeService; log?: LogService; @@ -111,12 +111,12 @@ async function handleForAWSConsumerLambda( context: Context, callback: (err: Error) => void, ) { - const path = Object.keys(OPERATION_API.paths)[0]; - const method = Object.keys(OPERATION_API.paths[path] || {})[0]; + const path = Object.keys(OPERATION_API.paths || {})?.[0]; + const method = Object.keys(OPERATION_API.paths?.[path] || {})[0]; const OPERATION: WhookOperation = { path, method, - ...OPERATION_API.paths[path]?.[method], + ...OPERATION_API.paths?.[path]?.[method], }; const startTime = time(); const parameters: LambdaConsumerInput = { diff --git a/packages/whook-aws-lambda/src/wrappers/awsCronLambda.ts b/packages/whook-aws-lambda/src/wrappers/awsCronLambda.ts index b062d819..ce341bcc 100644 --- a/packages/whook-aws-lambda/src/wrappers/awsCronLambda.ts +++ b/packages/whook-aws-lambda/src/wrappers/awsCronLambda.ts @@ -11,7 +11,7 @@ import type { WhookWrapper, } from '@whook/whook'; import type { LogService, TimeService } from 'common-services'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { ScheduledEvent, Context } from 'aws-lambda'; import type { JsonObject } from 'type-fest'; import type { AppEnvVars } from 'application-services'; @@ -24,7 +24,7 @@ export type LambdaCronOutput = WhookResponse; export type WhookWrapCronLambdaDependencies = { ENV: AppEnvVars; - OPERATION_API: OpenAPIV3.Document; + OPERATION_API: OpenAPIV3_1.Document; apm: WhookAPMService; time?: TimeService; log?: LogService; @@ -83,12 +83,12 @@ async function handleForAWSCronLambda( context: Context, callback: (err: Error) => void, ) { - const path = Object.keys(OPERATION_API.paths)[0]; - const method = Object.keys(OPERATION_API.paths[path] || {})[0]; + const path = Object.keys(OPERATION_API.paths || {})[0]; + const method = Object.keys(OPERATION_API.paths?.[path] || {})[0]; const OPERATION: WhookOperation = { path, method, - ...OPERATION_API.paths[path]?.[method], + ...OPERATION_API.paths?.[path]?.[method], }; const startTime = time(); const parameters: LambdaCronInput = { diff --git a/packages/whook-aws-lambda/src/wrappers/awsHTTPLambda.ts b/packages/whook-aws-lambda/src/wrappers/awsHTTPLambda.ts index caa46ffd..00235932 100644 --- a/packages/whook-aws-lambda/src/wrappers/awsHTTPLambda.ts +++ b/packages/whook-aws-lambda/src/wrappers/awsHTTPLambda.ts @@ -43,10 +43,10 @@ import type { WhookWrapper, } from '@whook/whook'; import type { TimeService, LogService } from 'common-services'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { Readable } from 'stream'; import { - DereferencedParameterObject, + type DereferencedParameterObject, pickAllHeaderValues, } from '@whook/http-transaction'; import type { @@ -65,7 +65,7 @@ const uuidPattern = '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$'; export type WhookWrapConsumerLambdaDependencies = { - OPERATION_API: OpenAPIV3.Document; + OPERATION_API: OpenAPIV3_1.Document; ENV: AppEnvVars; DEBUG_NODE_ENVS?: string[]; DECODERS?: typeof DEFAULT_DECODERS; @@ -135,8 +135,8 @@ async function initWrapHandlerForConsumerLambda({ }: WhookWrapConsumerLambdaDependencies): Promise> { log('debug', '📥 - Initializing the AWS LAmbda consumer wrapper.'); - const path = Object.keys(OPERATION_API.paths)[0]; - const pathObject = OPERATION_API.paths[path]; + const path = Object.keys(OPERATION_API.paths || {})[0]; + const pathObject = OPERATION_API.paths?.[path]; if (typeof pathObject === 'undefined' || '$ref' in pathObject) { throw new YError('E_BAD_OPERATION', 'pathObject', pathObject); @@ -175,7 +175,7 @@ async function initWrapHandlerForConsumerLambda({ const validators = prepareParametersValidators( ajv, operation.operationId, - ((operation.parameters || []) as OpenAPIV3.ParameterObject[]).concat( + ((operation.parameters || []) as DereferencedParameterObject[]).concat( ammendedParameters, ), ); @@ -371,13 +371,13 @@ async function handleForAWSHTTPLambda( // specified and it is not a binary one const responseObject = operation.responses && - (operation.responses[response.status] as OpenAPIV3.ResponseObject); + (operation.responses[response.status] as OpenAPIV3_1.ResponseObject); const responseSchema = responseObject && responseObject.content && responseObject.content[response.headers['content-type']] && (responseObject.content[response.headers['content-type']] - .schema as OpenAPIV3.SchemaObject); + .schema as OpenAPIV3_1.SchemaObject); const responseHasSchema = responseSchema && (responseSchema.type !== 'string' || responseSchema.format !== 'binary'); diff --git a/packages/whook-aws-lambda/src/wrappers/awsKafkaConsumerLambda.ts b/packages/whook-aws-lambda/src/wrappers/awsKafkaConsumerLambda.ts index cc72dded..56cf14e5 100644 --- a/packages/whook-aws-lambda/src/wrappers/awsKafkaConsumerLambda.ts +++ b/packages/whook-aws-lambda/src/wrappers/awsKafkaConsumerLambda.ts @@ -11,7 +11,7 @@ import type { WhookWrapper, } from '@whook/whook'; import type { TimeService, LogService } from 'common-services'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { MSKEvent, Context } from 'aws-lambda'; import type { AppEnvVars } from 'application-services'; @@ -24,7 +24,7 @@ export type LambdaKafkaConsumerOutput = WhookResponse< export type WhookWrapKafkaLambdaDependencies = { ENV: AppEnvVars; - OPERATION_API: OpenAPIV3.Document; + OPERATION_API: OpenAPIV3_1.Document; apm: WhookAPMService; time?: TimeService; log?: LogService; @@ -83,12 +83,12 @@ async function handleForAWSKafkaConsumerLambda( context: Context, callback: (err: Error) => void, ) { - const path = Object.keys(OPERATION_API.paths)[0]; - const method = Object.keys(OPERATION_API.paths[path] || {})[0]; + const path = Object.keys(OPERATION_API.paths || {})[0]; + const method = Object.keys(OPERATION_API.paths?.[path] || {})[0]; const OPERATION: WhookOperation = { path, method, - ...OPERATION_API.paths[path]?.[method], + ...OPERATION_API.paths?.[path]?.[method], }; const startTime = time(); const parameters: LambdaKafkaConsumerInput = { diff --git a/packages/whook-aws-lambda/src/wrappers/awsLogSubscriberLambda.ts b/packages/whook-aws-lambda/src/wrappers/awsLogSubscriberLambda.ts index 58e2a8d8..3794745b 100644 --- a/packages/whook-aws-lambda/src/wrappers/awsLogSubscriberLambda.ts +++ b/packages/whook-aws-lambda/src/wrappers/awsLogSubscriberLambda.ts @@ -12,7 +12,7 @@ import type { WhookWrapper, } from '@whook/whook'; import type { TimeService, LogService } from 'common-services'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { JsonValue } from 'type-fest'; import type { CloudWatchLogsEvent, @@ -33,7 +33,7 @@ export type LambdaLogSubscriberOutput = WhookResponse< export type WhookWrapLogSubscriberLambdaDependencies = { ENV: AppEnvVars; - OPERATION_API: OpenAPIV3.Document; + OPERATION_API: OpenAPIV3_1.Document; apm: WhookAPMService; time?: TimeService; log?: LogService; @@ -97,12 +97,12 @@ async function handleForAWSLogSubscriberLambda< context: Context, callback: (err: Error) => void, ) { - const path = Object.keys(OPERATION_API.paths)[0]; - const method = Object.keys(OPERATION_API.paths[path] || {})[0]; + const path = Object.keys(OPERATION_API.paths || {})[0]; + const method = Object.keys(OPERATION_API.paths?.[path] || {})[0]; const OPERATION: WhookOperation = { path, method, - ...OPERATION_API.paths[path]?.[method], + ...OPERATION_API.paths?.[path]?.[method], }; const startTime = time(); const parameters = { diff --git a/packages/whook-aws-lambda/src/wrappers/awsS3Lambda.ts b/packages/whook-aws-lambda/src/wrappers/awsS3Lambda.ts index df14623f..56531928 100644 --- a/packages/whook-aws-lambda/src/wrappers/awsS3Lambda.ts +++ b/packages/whook-aws-lambda/src/wrappers/awsS3Lambda.ts @@ -11,7 +11,7 @@ import type { WhookWrapper, } from '@whook/whook'; import type { LogService, TimeService } from 'common-services'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { S3Event, Context } from 'aws-lambda'; import type { AppEnvVars } from 'application-services'; @@ -20,7 +20,7 @@ export type LambdaS3Output = WhookResponse; export type WhookWrapS3LambdaDependencies = { ENV: AppEnvVars; - OPERATION_API: OpenAPIV3.Document; + OPERATION_API: OpenAPIV3_1.Document; apm: WhookAPMService; time?: TimeService; log?: LogService; @@ -79,12 +79,12 @@ async function handleForAWSS3Lambda( context: Context, callback: (err: Error) => void, ) { - const path = Object.keys(OPERATION_API.paths)[0]; - const method = Object.keys(OPERATION_API.paths[path] || {})[0]; + const path = Object.keys(OPERATION_API.paths || {})[0]; + const method = Object.keys(OPERATION_API.paths?.[path] || {})[0]; const OPERATION: WhookOperation = { path, method, - ...OPERATION_API.paths[path]?.[method], + ...OPERATION_API.paths?.[path]?.[method], }; const startTime = time(); const parameters = { diff --git a/packages/whook-aws-lambda/src/wrappers/awsTransformerLambda.ts b/packages/whook-aws-lambda/src/wrappers/awsTransformerLambda.ts index 689d2ecb..7e77e16e 100644 --- a/packages/whook-aws-lambda/src/wrappers/awsTransformerLambda.ts +++ b/packages/whook-aws-lambda/src/wrappers/awsTransformerLambda.ts @@ -11,7 +11,7 @@ import type { WhookWrapper, } from '@whook/whook'; import type { TimeService, LogService } from 'common-services'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { FirehoseTransformationEvent, FirehoseTransformationEventRecord, @@ -23,7 +23,7 @@ import type { AppEnvVars } from 'application-services'; type TransformerWrapperDependencies = { ENV: AppEnvVars; - OPERATION_API: OpenAPIV3.Document; + OPERATION_API: OpenAPIV3_1.Document; apm: WhookAPMService; time?: TimeService; log?: LogService; @@ -57,7 +57,7 @@ export type LambdaTransformerOutput = WhookResponse< export type WhookWrapConsumerLambdaDependencies = { ENV: AppEnvVars; - OPERATION_API: OpenAPIV3.Document; + OPERATION_API: OpenAPIV3_1.Document; apm: WhookAPMService; time?: TimeService; log?: LogService; @@ -119,12 +119,12 @@ async function handleForAWSTransformerLambda( context: Context, callback: (err: Error | null, result?: FirehoseTransformationResult) => void, ) { - const path = Object.keys(OPERATION_API.paths)[0]; - const method = Object.keys(OPERATION_API.paths[path] || {})[0]; + const path = Object.keys(OPERATION_API.paths || {})[0]; + const method = Object.keys(OPERATION_API.paths?.[path] || {})[0]; const OPERATION: WhookOperation = { path, method, - ...OPERATION_API.paths[path]?.[method], + ...OPERATION_API.paths?.[path]?.[method], }; const startTime = time(); const parameters = { diff --git a/packages/whook-cors/src/__snapshots__/index.test.ts.snap b/packages/whook-cors/src/__snapshots__/index.test.ts.snap index 8c08c216..f129405a 100644 --- a/packages/whook-cors/src/__snapshots__/index.test.ts.snap +++ b/packages/whook-cors/src/__snapshots__/index.test.ts.snap @@ -47,7 +47,7 @@ exports[`augmentAPIWithCORS() should work 1`] = ` "title": "Sample OpenAPI", "version": "1.0.0", }, - "openapi": "3.0.2", + "openapi": "3.1.0", "paths": { "/crons/tokens": { "post": { diff --git a/packages/whook-cors/src/index.test.ts b/packages/whook-cors/src/index.test.ts index 4f8f079d..6a787072 100644 --- a/packages/whook-cors/src/index.test.ts +++ b/packages/whook-cors/src/index.test.ts @@ -8,7 +8,7 @@ import { import { handler } from 'knifecycle'; import { YHTTPError } from 'yhttperror'; import type { CORSConfig } from './index.js'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { WhookOperation } from '@whook/whook'; import type { LogService } from 'common-services'; @@ -216,7 +216,7 @@ describe('augmentAPIWithCORS()', () => { it('should work', async () => { expect( await augmentAPIWithCORS({ - openapi: '3.0.2', + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample OpenAPI', @@ -354,8 +354,8 @@ describe('augmentAPIWithCORS()', () => { description: 'Pong', }, }, - } as OpenAPIV3.OperationObject, - }, + }, + } as OpenAPIV3_1.PathItemObject, }, }), ).toMatchSnapshot(); diff --git a/packages/whook-cors/src/index.ts b/packages/whook-cors/src/index.ts index 83d80873..cceb7bee 100644 --- a/packages/whook-cors/src/index.ts +++ b/packages/whook-cors/src/index.ts @@ -6,7 +6,7 @@ import initErrorHandlerWithCORS, { } from './services/errorHandler.js'; import initWrapHandlerWithCORS from './wrappers/wrapHandlerWithCORS.js'; import type { WhookOperation } from '@whook/whook'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; export type { CORSConfig, @@ -31,104 +31,107 @@ const METHOD_CORS_PRIORITY = ['head', 'get', 'post', 'put', 'delete', 'patch']; * @returns {Promise} The augmented OpenAPI object */ export async function augmentAPIWithCORS( - API: OpenAPIV3.Document, -): Promise { - // Temporar type fix due to version mismatch of OpenAPIV3 + API: OpenAPIV3_1.Document, +): Promise { + // Temporar type fix due to version mismatch of OpenAPIV3_1 // between Whook and SwaggerParser // eslint-disable-next-line @typescript-eslint/no-explicit-any const $refs = await SwaggerParser.resolve(API as any); - return Object.keys(API.paths).reduce((newAPI, path) => { - const existingOperation = newAPI.paths[path]?.options; - - if (existingOperation) { - return newAPI; - } - - const canonicalOperationMethod = [ - ...new Set([...METHOD_CORS_PRIORITY]), - ...Object.keys(newAPI.paths[path] || {}), - ].find((method) => newAPI.paths[path]?.[method]) as string; - const canonicalOperation: OpenAPIV3.OperationObject = - newAPI.paths[path]?.[canonicalOperationMethod]; - - const whookConfig = { - type: 'http', - ...(canonicalOperation['x-whook'] || {}), - suffix: 'CORS', - sourceOperationId: canonicalOperation.operationId, - private: true, - }; - - if (whookConfig.type !== 'http') { - return newAPI; - } - - newAPI.paths[path] = { - ...(newAPI.paths[path] || {}), - options: { - operationId: 'optionsWithCORS', - summary: 'Enable OPTIONS for CORS', - tags: ['CORS'], - parameters: (canonicalOperation.parameters || []) - .concat( - extractOperationSecurityParameters( - API, - canonicalOperation as WhookOperation, - ), - ) - .filter((parameter) => { - const dereferencedParameter = ( - parameter as OpenAPIV3.ReferenceObject - ).$ref - ? ($refs.get( - (parameter as OpenAPIV3.ReferenceObject).$ref, - ) as OpenAPIV3.ParameterObject) - : (parameter as OpenAPIV3.ParameterObject); - - return ( - 'path' === dereferencedParameter.in || - 'query' === dereferencedParameter.in - ); - }) - .map((parameter) => { - const dereferencedParameter = ( - parameter as OpenAPIV3.ReferenceObject - ).$ref - ? ($refs.get( - (parameter as OpenAPIV3.ReferenceObject).$ref, - ) as OpenAPIV3.ParameterObject) - : (parameter as OpenAPIV3.ParameterObject); - - if ( - dereferencedParameter.in === 'path' || - !dereferencedParameter.required - ) { - return parameter; - } - - // Avoid to require parameters for CORS - return { - ...dereferencedParameter, - required: false, - }; - }), - responses: { - 200: { - description: 'CORS sent.', + return { + ...API, + paths: Object.keys(API?.paths || {}).reduce((newPaths, path) => { + const existingOperation = newPaths?.[path]?.options; + + if (existingOperation) { + return newPaths; + } + + const canonicalOperationMethod = [ + ...new Set([...METHOD_CORS_PRIORITY]), + ...Object.keys(newPaths[path] || {}), + ].find((method) => newPaths[path]?.[method]) as string; + const canonicalOperation: OpenAPIV3_1.OperationObject = + newPaths[path]?.[canonicalOperationMethod]; + + const whookConfig = { + type: 'http', + ...(canonicalOperation['x-whook'] || {}), + suffix: 'CORS', + sourceOperationId: canonicalOperation.operationId, + private: true, + }; + + if (whookConfig.type !== 'http') { + return newPaths; + } + + newPaths[path] = { + ...(newPaths[path] || {}), + options: { + operationId: 'optionsWithCORS', + summary: 'Enable OPTIONS for CORS', + tags: ['CORS'], + parameters: (canonicalOperation.parameters || []) + .concat( + extractOperationSecurityParameters( + API, + canonicalOperation as WhookOperation, + ) as OpenAPIV3_1.ParameterObject[], + ) + .filter((parameter) => { + const dereferencedParameter = ( + parameter as OpenAPIV3_1.ReferenceObject + ).$ref + ? ($refs.get( + (parameter as OpenAPIV3_1.ReferenceObject).$ref, + ) as OpenAPIV3_1.ParameterObject) + : (parameter as OpenAPIV3_1.ParameterObject); + + return ( + 'path' === dereferencedParameter.in || + 'query' === dereferencedParameter.in + ); + }) + .map((parameter) => { + const dereferencedParameter = ( + parameter as OpenAPIV3_1.ReferenceObject + ).$ref + ? ($refs.get( + (parameter as OpenAPIV3_1.ReferenceObject).$ref, + ) as OpenAPIV3_1.ParameterObject) + : (parameter as OpenAPIV3_1.ParameterObject); + + if ( + dereferencedParameter.in === 'path' || + !dereferencedParameter.required + ) { + return parameter; + } + + // Avoid to require parameters for CORS + return { + ...dereferencedParameter, + required: false, + }; + }), + responses: { + 200: { + description: 'CORS sent.', + }, }, }, - }, - }; + }; - // Must be set separately since not supported by OAS3 types atm - // eslint-disable-next-line @typescript-eslint/no-explicit-any - ((newAPI.paths[path] as any).options as any)['x-whook'] = { - ...whookConfig, - }; + // Must be set separately since not supported by OAS3 types atm + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ((newPaths[path] as any).options as any)['x-whook'] = { + ...whookConfig, + }; - return newAPI; - }, API); + return newPaths; + }, API?.paths || {}), + }; } export { initOptionsWithCORS }; diff --git a/packages/whook-example/src/index.test.ts b/packages/whook-example/src/index.test.ts index 774a04c3..0a1e40be 100644 --- a/packages/whook-example/src/index.test.ts +++ b/packages/whook-example/src/index.test.ts @@ -105,232 +105,232 @@ describe('runServer', () => { { "debugCalls": [ [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/handlers/optionsWithCORS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/BUFFER_LIMIT.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/DECODERS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/DEFAULT_ERROR_CODE.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/ENCODERS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/HTTP_SERVER_OPTIONS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/IGNORED_FILES_PREFIXES.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/IGNORED_FILES_SUFFIXES.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/JWT_SECRET_ENV_NAME.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/MAX_CLEAR_RATIO.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/PARSERS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/PROCESS_NAME.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/REDUCED_FILES_SUFFIXES.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/SHIELD_CHAR.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/SIGNALS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/STRINGIFYERS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/TIMEOUT.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/services/uniqueId.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/wrappers/wrapHandlerWithAuthorization.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/dist/wrappers/wrapHandlerWithCORS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/BUFFER_LIMIT.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/DECODERS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/DEFAULT_ERROR_CODE.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/ENCODERS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/HTTP_SERVER_OPTIONS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/IGNORED_FILES_PREFIXES.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/IGNORED_FILES_SUFFIXES.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/JWT_SECRET_ENV_NAME.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/MAX_CLEAR_RATIO.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/PARSERS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/PROCESS_NAME.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/REDUCED_FILES_SUFFIXES.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/SHIELD_CHAR.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/SIGNALS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/STRINGIFYERS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/TIMEOUT.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-authorization/dist/services/uniqueId.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/BUFFER_LIMIT.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/DECODERS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/DEFAULT_ERROR_CODE.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/ENCODERS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/HTTP_SERVER_OPTIONS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/IGNORED_FILES_PREFIXES.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/IGNORED_FILES_SUFFIXES.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/JWT_SECRET_ENV_NAME.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/MAX_CLEAR_RATIO.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/PARSERS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/PROCESS_NAME.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/REDUCED_FILES_SUFFIXES.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/SHIELD_CHAR.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/SIGNALS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/STRINGIFYERS.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/TIMEOUT.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/services/uniqueId.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-cors/dist/wrappers/wrapHandlerWithAuthorization.js'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/handlers/getPing.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/handlers/optionsWithCORS.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/BUFFER_LIMIT.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/DECODERS.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/DEFAULT_ERROR_CODE.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/ENCODERS.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/HTTP_SERVER_OPTIONS.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/IGNORED_FILES_PREFIXES.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/IGNORED_FILES_SUFFIXES.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/JWT_SECRET_ENV_NAME.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/MAX_CLEAR_RATIO.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/PARSERS.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/PROCESS_NAME.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/REDUCED_FILES_SUFFIXES.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/SHIELD_CHAR.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/SIGNALS.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/STRINGIFYERS.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/TIMEOUT.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/services/uniqueId.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/wrappers/wrapHandlerWithAuthorization.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ - "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/wrappers/wrapHandlerWithCORS.ts'", + "Error: ENOENT: no such file or directory, access 'file:///home/whoiam/projects/whook/packages/whook-example/src/index.test.ts:78:59)", ], [ "⌛ - Delay service initialized.", diff --git a/packages/whook-example/src/openAPISchema.d.ts b/packages/whook-example/src/openAPISchema.d.ts index df7c05f3..c7184a49 100644 --- a/packages/whook-example/src/openAPISchema.d.ts +++ b/packages/whook-example/src/openAPISchema.d.ts @@ -13,15 +13,10 @@ declare namespace API { } export namespace GetDiagnostic { export type Output = Responses.$200; - export type Input = { - readonly test?: Parameters.Test; - }; + export type Input = {}; export namespace Responses { export type $200 = Components.Responses.Diagnostic<200>; } - export namespace Parameters { - export type Test = Components.Parameters.GetDiagnostic0; - } } export namespace GetOpenAPI { export type Output = Responses.$200; @@ -83,7 +78,6 @@ declare namespace Components { export type Duration = NonNullable; export type PathParam1 = NonNullable; export type PathParam2 = NonNullable; - export type GetDiagnostic0 = NonNullable; export type GetParameters3 = NonNullable; export type QueryParam = NonNullable[]>; export type GetParameters4 = NonNullable[]>; diff --git a/packages/whook-example/src/services/API.test.ts b/packages/whook-example/src/services/API.test.ts index ac95e70a..c24903b4 100644 --- a/packages/whook-example/src/services/API.test.ts +++ b/packages/whook-example/src/services/API.test.ts @@ -110,7 +110,8 @@ describe('API', () => { log, }); - await SwaggerParser.validate(API); + // Not pure function... so deep cloning the dirtiest way + await SwaggerParser.validate(JSON.parse(JSON.stringify(API))); }); describe('should always have the same amount of', () => { diff --git a/packages/whook-example/src/services/API.ts b/packages/whook-example/src/services/API.ts index f56e70d2..c65b3638 100644 --- a/packages/whook-example/src/services/API.ts +++ b/packages/whook-example/src/services/API.ts @@ -3,7 +3,7 @@ import { augmentAPIWithCORS } from '@whook/cors'; import { noop } from '@whook/whook'; import type { WhookConfig, WhookAPIDefinitions } from '@whook/whook'; import type { LogService } from 'common-services'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; export type APIEnv = { DEV_MODE?: string; @@ -41,8 +41,8 @@ async function initAPI({ }: APIDependencies) { log('debug', '🦄 - Initializing the API service!'); - const API: OpenAPIV3.Document = { - openapi: '3.0.3', + const API: OpenAPIV3_1.Document = { + openapi: '3.1.0', info: { version: API_VERSION, title: CONFIG.name, @@ -97,23 +97,23 @@ The API definition is a JSON serializable object, you */ async function augmentAPIWithFakeAuth( { ENV }: { ENV: APIEnv }, - API: OpenAPIV3.Document, -): Promise { + API: OpenAPIV3_1.Document, +): Promise { if (!ENV.DEV_MODE) { return API; } return { ...API, - paths: Object.keys(API.paths).reduce( + paths: Object.keys(API.paths || {}).reduce( (newPathsObject, path) => ({ ...newPathsObject, - [path]: Object.keys(API.paths[path] || {}).reduce( + [path]: Object.keys(API.paths?.[path] || {}).reduce( (newPathItem, method) => ({ ...newPathItem, [method]: { - ...API.paths[path]?.[method], - ...(API.paths[path]?.[method].security + ...API.paths?.[path]?.[method], + ...(API.paths?.[path]?.[method].security ? { security: [ ...(API.paths[path]?.[method]?.security || {}), diff --git a/packages/whook-example/src/services/__snapshots__/API.test.ts.snap b/packages/whook-example/src/services/__snapshots__/API.test.ts.snap index 16aca39e..de1a0977 100644 --- a/packages/whook-example/src/services/__snapshots__/API.test.ts.snap +++ b/packages/whook-example/src/services/__snapshots__/API.test.ts.snap @@ -182,7 +182,7 @@ exports[`API should work 1`] = ` "title": "@whook/example", "version": "1.1.0", }, - "openapi": "3.0.3", + "openapi": "3.1.0", "paths": { "/delay": { "get": { diff --git a/packages/whook-gcp-functions/src/commands/testHTTPFunction.ts b/packages/whook-gcp-functions/src/commands/testHTTPFunction.ts index 9fda6fba..3d5e66d0 100644 --- a/packages/whook-gcp-functions/src/commands/testHTTPFunction.ts +++ b/packages/whook-gcp-functions/src/commands/testHTTPFunction.ts @@ -14,7 +14,7 @@ import type { WhookCompilerOptions, } from '@whook/whook'; import type { LogService } from 'common-services'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; const SEARCH_SEPARATOR = '?'; const PATH_SEPARATOR = '/'; @@ -64,7 +64,7 @@ async function initTestHTTPFunctionCommand({ APP_ENV: string; PROJECT_DIR: string; COMPILER_OPTIONS?: WhookCompilerOptions; - API: OpenAPIV3.Document; + API: OpenAPIV3_1.Document; log: LogService; args: WhookCommandArgs; }) { @@ -94,7 +94,9 @@ async function initTestHTTPFunctionCommand({ const hasBody = !!OPERATION.requestBody; const parameters = JSON.parse(rawParameters); - const search = ((OPERATION.parameters || []) as OpenAPIV3.ParameterObject[]) + const search = ( + (OPERATION.parameters || []) as OpenAPIV3_1.ParameterObject[] + ) .filter((p) => p.in === 'query') .reduce((accSearch, p) => { if (null != parameters[p.name]) { @@ -124,7 +126,7 @@ async function initTestHTTPFunctionCommand({ const gcpfRequest = { method: OPERATION.method, originalUrl: path + (search ? SEARCH_SEPARATOR + search : ''), - headers: ((OPERATION.parameters || []) as OpenAPIV3.ParameterObject[]) + headers: ((OPERATION.parameters || []) as OpenAPIV3_1.ParameterObject[]) .filter((p) => p.in === 'header') .reduce((headerParameters, p) => { headerParameters[p.name] = parameters[camelCase(p.name)]; diff --git a/packages/whook-gcp-functions/src/index.ts b/packages/whook-gcp-functions/src/index.ts index 3414bba5..72944680 100644 --- a/packages/whook-gcp-functions/src/index.ts +++ b/packages/whook-gcp-functions/src/index.ts @@ -28,7 +28,7 @@ import type { WhookCompilerOptions, WhookCompilerService, } from '@whook/whook'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { LogService } from 'common-services'; import type { CprOptions } from 'cpr'; @@ -91,7 +91,7 @@ export async function runBuild( compiler: WhookCompilerService; log: LogService; $autoload: Autoloader>; - API: OpenAPIV3.Document; + API: OpenAPIV3_1.Document; buildInitializer: BuildInitializer; } = await $.run([ 'APP_ENV', diff --git a/packages/whook-gcp-functions/src/services/_autoload.ts b/packages/whook-gcp-functions/src/services/_autoload.ts index 17f79b82..29c6bee3 100644 --- a/packages/whook-gcp-functions/src/services/_autoload.ts +++ b/packages/whook-gcp-functions/src/services/_autoload.ts @@ -26,7 +26,7 @@ import type { import type { WhookBuildConstantsService } from '@whook/whook'; import type { WhookRawOperation } from '@whook/http-router'; import type { LogService } from 'common-services'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { WhookAPIOperationGCPFunctionConfig } from '../index.js'; export type WhookGoogleFunctionsAutoloadDependencies = { @@ -66,7 +66,7 @@ const initializerWrapper: ServiceInitializerWrapper< path: string; }> > => { - let API: OpenAPIV3.Document; + let API: OpenAPIV3_1.Document; let OPERATION_APIS: WhookRawOperation[]; const getAPIOperation: ( serviceName: string, @@ -74,7 +74,7 @@ const initializerWrapper: ServiceInitializerWrapper< [ Required['type'], string, - OpenAPIV3.Document, + OpenAPIV3_1.Document, ] > = (() => { return async (serviceName) => { @@ -101,11 +101,11 @@ const initializerWrapper: ServiceInitializerWrapper< } // eslint-disable-next-line - const OPERATION_API: OpenAPIV3.Document = cleanupOpenAPI({ + const OPERATION_API: OpenAPIV3_1.Document = cleanupOpenAPI({ ...API, paths: { [OPERATION.path]: { - [OPERATION.method]: API.paths[OPERATION.path]?.[OPERATION.method], + [OPERATION.method]: API.paths?.[OPERATION.path]?.[OPERATION.method], }, }, }); @@ -122,7 +122,9 @@ const initializerWrapper: ServiceInitializerWrapper< { path: OPERATION.path, method: OPERATION.method, - ...OPERATION_API.paths[OPERATION.path]?.[OPERATION.method], + ...OPERATION_API.paths?.[OPERATION.path]?.[ + OPERATION.method + ], parameters: OPERATION.parameters, }, ]) diff --git a/packages/whook-gcp-functions/src/wrappers/wrapHandlerForGoogleHTTPFunction.ts b/packages/whook-gcp-functions/src/wrappers/wrapHandlerForGoogleHTTPFunction.ts index e6da3d25..beb5f3df 100644 --- a/packages/whook-gcp-functions/src/wrappers/wrapHandlerForGoogleHTTPFunction.ts +++ b/packages/whook-gcp-functions/src/wrappers/wrapHandlerForGoogleHTTPFunction.ts @@ -43,15 +43,16 @@ import type { WhookErrorHandler, } from '@whook/whook'; import type { LogService } from 'common-services'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { Readable } from 'stream'; import type { AppEnvVars } from 'application-services'; +import type { DereferencedParameterObject } from '@whook/http-transaction'; const SEARCH_SEPARATOR = '?'; const PATH_SEPARATOR = '/'; export type WhookWrapHTTPFunctionDependencies = { - OPERATION_API: OpenAPIV3.Document; + OPERATION_API: OpenAPIV3_1.Document; ENV: AppEnvVars; DEBUG_NODE_ENVS?: string[]; DECODERS?: typeof DEFAULT_DECODERS; @@ -113,8 +114,8 @@ async function initWrapHandlerForGoogleHTTPFunction({ }: WhookWrapHTTPFunctionDependencies): Promise> { log('debug', '📥 - Initializing the AWS Lambda cron wrapper.'); - const path = Object.keys(OPERATION_API.paths)[0]; - const pathObject = OPERATION_API.paths[path]; + const path = Object.keys(OPERATION_API.paths || {})[0]; + const pathObject = OPERATION_API.paths?.[path]; if (typeof pathObject === 'undefined' || '$ref' in pathObject) { throw new YError('E_BAD_OPERATION', 'pathObject', pathObject); @@ -153,7 +154,7 @@ async function initWrapHandlerForGoogleHTTPFunction({ const validators = prepareParametersValidators( ajv, operation.operationId, - ((operation.parameters || []) as OpenAPIV3.ParameterObject[]).concat( + ((operation.parameters || []) as DereferencedParameterObject[]).concat( ammendedParameters, ), ); @@ -359,13 +360,13 @@ async function handleForAWSHTTPFunction( // specified and it is not a binary one const responseObject = operation.responses && - (operation.responses[response.status] as OpenAPIV3.ResponseObject); + (operation.responses[response.status] as OpenAPIV3_1.ResponseObject); const responseSchema = responseObject && responseObject.content && responseObject.content[response.headers['content-type']] && (responseObject.content[response.headers['content-type']] - .schema as OpenAPIV3.SchemaObject); + .schema as OpenAPIV3_1.SchemaObject); const responseHasSchema = responseSchema && (responseSchema.type !== 'string' || responseSchema.format !== 'binary'); diff --git a/packages/whook-graphiql/src/index.test.ts b/packages/whook-graphiql/src/index.test.ts index 70f44063..4f1331e1 100644 --- a/packages/whook-graphiql/src/index.test.ts +++ b/packages/whook-graphiql/src/index.test.ts @@ -10,15 +10,15 @@ import initHTTPRouter from '@whook/http-router'; import { YError } from 'yerror'; import wrapHTTPRouterWithGraphIQL from './index.js'; import type { WhookGraphIQLOptions } from './index.js'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { Logger } from 'common-services'; describe('wrapHTTPRouterWithGraphIQL', () => { const HOST = 'localhost'; const PORT = 11111; const BASE_PATH = '/v1'; - const API: OpenAPIV3.Document = { - openapi: '3.0.2', + const API: OpenAPIV3_1.Document = { + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample OpenAPI', diff --git a/packages/whook-graphql/src/index.test.ts b/packages/whook-graphql/src/index.test.ts index 55741e1d..3a1bb862 100644 --- a/packages/whook-graphql/src/index.test.ts +++ b/packages/whook-graphql/src/index.test.ts @@ -35,7 +35,7 @@ import { import { mapSchema, getDirective, MapperKind } from '@graphql-tools/utils'; import type { WhookGraphQLFragmentService } from './index.js'; import type { Knifecycle, Autoloader } from 'knifecycle'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { AuthenticationService } from '@whook/authorization'; import type { WhookGraphQLContextFunction } from './index.js'; import type { Logger, TimeService } from 'common-services'; @@ -55,8 +55,8 @@ describe('GraphQL server', () => { async (baseContext) => baseContext, ); - const API: OpenAPIV3.Document = { - openapi: '3.0.2', + const API: OpenAPIV3_1.Document = { + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample OpenAPI', diff --git a/packages/whook-http-router/src/index.test.ts b/packages/whook-http-router/src/index.test.ts index 8e08b19e..ecf4e64a 100644 --- a/packages/whook-http-router/src/index.test.ts +++ b/packages/whook-http-router/src/index.test.ts @@ -7,7 +7,8 @@ import { YError } from 'yerror'; import initHTTPRouter from './index.js'; import { NodeEnv } from 'application-services'; import initErrorHandler from './services/errorHandler.js'; -import type { OpenAPIV3 } from 'openapi-types'; +import SwaggerParser from '@apidevtools/swagger-parser'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { WhookHTTPTransactionService, WhookHandler, @@ -92,8 +93,8 @@ describe('initHTTPRouter', () => { const ENV: AppEnvVars = { NODE_ENV: NodeEnv.Test }; const DEBUG_NODE_ENVS = ['test']; const BASE_PATH = '/v1'; - const API: OpenAPIV3.Document = { - openapi: '3.0.2', + const API: OpenAPIV3_1.Document = { + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample Swagger', @@ -514,15 +515,10 @@ describe('initHTTPRouter', () => { log.mockReset(); }); - // test('should test a valid swagger file', async () => { - // const result = OpenAPISchemaValidator({ version: 3 }).validate( - // API as OpenAPIV3.Document, - // ); - - // assert.deepEqual(result, { - // errors: [], - // }); - // }); + test('should test a valid swagger file', async () => { + // Not pure function... so deep cloning the dirtiest way + await SwaggerParser.validate(JSON.parse(JSON.stringify(API))); + }); test('should work', async () => { const { httpTransaction, HANDLERS } = prepareTransaction(); @@ -570,7 +566,7 @@ describe('initHTTPRouter', () => { get: {}, }, }, - } as unknown as OpenAPIV3.Document, + } as unknown as OpenAPIV3_1.Document, log, BASE_PATH, httpTransaction, @@ -742,7 +738,7 @@ describe('initHTTPRouter', () => { schema: { type: 'string', }, - } as unknown as OpenAPIV3.ParameterObject, + } as unknown as OpenAPIV3_1.ParameterObject, ], } as never, }, @@ -784,7 +780,7 @@ describe('initHTTPRouter', () => { { name: 'lol', in: 'query', - } as unknown as OpenAPIV3.ParameterObject, + } as unknown as OpenAPIV3_1.ParameterObject, ], } as never, }, @@ -828,7 +824,7 @@ describe('initHTTPRouter', () => { schema: { type: 'string', }, - } as unknown as OpenAPIV3.ParameterObject, + } as unknown as OpenAPIV3_1.ParameterObject, ], } as never, }, diff --git a/packages/whook-http-router/src/index.ts b/packages/whook-http-router/src/index.ts index 7812722e..67207927 100644 --- a/packages/whook-http-router/src/index.ts +++ b/packages/whook-http-router/src/index.ts @@ -63,7 +63,7 @@ import type { DereferencedParameterObject, WhookResponse, } from '@whook/http-transaction'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { LogService } from 'common-services'; import type { IncomingMessage, ServerResponse } from 'node:http'; import type { @@ -167,7 +167,7 @@ export type WhookHTTPRouterDependencies = WhookHTTPRouterConfig & { ENV: AppEnvVars; HANDLERS: WhookHandlersService; - API: OpenAPIV3.Document; + API: OpenAPIV3_1.Document; PARSERS?: WhookParsers; STRINGIFYERS?: WhookStringifyers; DECODERS?: WhookEncoders; @@ -576,7 +576,7 @@ async function _createRouters({ ajv, log, }: { - API: OpenAPIV3.Document; + API: OpenAPIV3_1.Document; HANDLERS: WhookHandlersService; BASE_PATH?: string; ajv: Ajv.default; @@ -606,7 +606,7 @@ async function _createRouters({ // TODO: create a new major version of Siso to handle OpenAPI // path params mode widely - const pathParameters = ((parameters || []) as OpenAPIV3.ParameterObject[]) + const pathParameters = ((parameters || []) as OpenAPIV3_1.ParameterObject[]) .filter((p) => 'path' === p.in) .map((p) => { if (p.style) { @@ -617,7 +617,7 @@ async function _createRouters({ ...p, // TODO: Remove when this issue is tackled: // https://github.com/nfroidure/siso/issues/45 - ...((p.schema as OpenAPIV3.SchemaObject)?.enum + ...((p.schema as OpenAPIV3_1.SchemaObject)?.enum ? {} : { pattern: '^.*$', diff --git a/packages/whook-http-router/src/libs/body.ts b/packages/whook-http-router/src/libs/body.ts index b9f96807..39c7f447 100644 --- a/packages/whook-http-router/src/libs/body.ts +++ b/packages/whook-http-router/src/libs/body.ts @@ -5,7 +5,7 @@ import { YError } from 'yerror'; import { pickFirstHeaderValue } from '@whook/http-transaction'; import type { WhookOperation, WhookResponse } from '@whook/http-transaction'; import type { WhookBodySpec } from './utils.js'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { JsonValue } from 'type-fest'; import type { WhookEncoders, @@ -46,7 +46,7 @@ export async function getBody< ): Promise { const bodyIsEmpty = !(bodySpec.contentType && bodySpec.contentLength); const requestBody = operation.requestBody - ? (operation.requestBody as OpenAPIV3.RequestBodyObject) + ? (operation.requestBody as OpenAPIV3_1.RequestBodyObject) : undefined; const schemaObject = requestBody && @@ -54,7 +54,7 @@ export async function getBody< requestBody.content[bodySpec.contentType] && requestBody.content[bodySpec.contentType].schema && (requestBody.content[bodySpec.contentType] - .schema as OpenAPIV3.NonArraySchemaObject); + .schema as OpenAPIV3_1.NonArraySchemaObject); const bodyIsParseable = schemaObject && (schemaObject.type !== 'string' || schemaObject.format !== 'binary'); diff --git a/packages/whook-http-router/src/libs/openAPIUtils.ts b/packages/whook-http-router/src/libs/openAPIUtils.ts index fb5fb95c..f60d336f 100644 --- a/packages/whook-http-router/src/libs/openAPIUtils.ts +++ b/packages/whook-http-router/src/libs/openAPIUtils.ts @@ -1,7 +1,7 @@ import SwaggerParser from '@apidevtools/swagger-parser'; import type { $Refs } from '@apidevtools/swagger-parser'; import { YError } from 'yerror'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { DereferencedRequestBodyObject, DereferencedResponseObject, @@ -20,15 +20,15 @@ export const OPEN_API_METHODS = [ ]; export type WhookRawOperation> = - OpenAPIV3.OperationObject & { + OpenAPIV3_1.OperationObject<{ path: string; method: string; 'x-whook'?: T; - }; + }>; export type SupportedSecurityScheme = - | OpenAPIV3.HttpSecurityScheme - | OpenAPIV3.ApiKeySecurityScheme - | OpenAPIV3.OAuth2SecurityScheme; + | OpenAPIV3_1.HttpSecurityScheme + | OpenAPIV3_1.ApiKeySecurityScheme + | OpenAPIV3_1.OAuth2SecurityScheme; /** * Return a OpenAPI operation in a more @@ -47,9 +47,9 @@ export type SupportedSecurityScheme = * }); */ export function getOpenAPIOperations>( - API: OpenAPIV3.Document, + API: OpenAPIV3_1.Document, ): WhookRawOperation[] { - return Object.keys(API.paths).reduce[]>( + return Object.keys(API?.paths || {}).reduce[]>( (operations, path) => Object.keys(API.paths?.[path] || {}) .filter((key) => OPEN_API_METHODS.includes(key)) @@ -58,8 +58,8 @@ export function getOpenAPIOperations>( path, method, ...API.paths?.[path]?.[method], - parameters: (API.paths[path]?.[method].parameters || []).concat( - API.paths[path]?.parameters || [], + parameters: (API?.paths?.[path]?.[method].parameters || []).concat( + API?.paths?.[path]?.parameters || [], ), }; @@ -80,7 +80,7 @@ export function getOpenAPIOperations>( * The dereferenced OpenAPI operations */ export async function dereferenceOpenAPIOperations>( - API: OpenAPIV3.Document, + API: OpenAPIV3_1.Document, operations: WhookRawOperation[], ): Promise[]> { let $refs: $Refs; @@ -94,11 +94,11 @@ export async function dereferenceOpenAPIOperations>( return operations.map((operation) => { const parameters = (operation.parameters || []) .map((parameter) => - (parameter as OpenAPIV3.ReferenceObject).$ref + (parameter as OpenAPIV3_1.ReferenceObject).$ref ? ($refs.get( - (parameter as OpenAPIV3.ReferenceObject).$ref, - ) as OpenAPIV3.ParameterObject) - : (parameter as OpenAPIV3.ParameterObject), + (parameter as OpenAPIV3_1.ReferenceObject).$ref, + ) as OpenAPIV3_1.ParameterObject) + : (parameter as OpenAPIV3_1.ParameterObject), ) .map((parameter) => { // Currently supporting only schema based @@ -108,11 +108,11 @@ export async function dereferenceOpenAPIOperations>( } return { ...parameter, - schema: (parameter.schema as OpenAPIV3.ReferenceObject).$ref + schema: (parameter.schema as OpenAPIV3_1.ReferenceObject).$ref ? ($refs.get( - (parameter.schema as OpenAPIV3.ReferenceObject).$ref, - ) as OpenAPIV3.SchemaObject) - : (parameter.schema as OpenAPIV3.SchemaObject), + (parameter.schema as OpenAPIV3_1.ReferenceObject).$ref, + ) as OpenAPIV3_1.SchemaObject) + : (parameter.schema as OpenAPIV3_1.SchemaObject), }; }) .map((parameter) => @@ -121,23 +121,23 @@ export async function dereferenceOpenAPIOperations>( ...parameter, schema: { ...parameter.schema, - items: (parameter.schema.items as OpenAPIV3.ReferenceObject) + items: (parameter.schema.items as OpenAPIV3_1.ReferenceObject) .$ref ? ($refs.get( - (parameter.schema.items as OpenAPIV3.ReferenceObject) + (parameter.schema.items as OpenAPIV3_1.ReferenceObject) .$ref, - ) as OpenAPIV3.SchemaObject) - : (parameter.schema.items as OpenAPIV3.SchemaObject), + ) as OpenAPIV3_1.SchemaObject) + : (parameter.schema.items as OpenAPIV3_1.SchemaObject), }, } : parameter, ); const baseRequestBody = operation.requestBody - ? (operation.requestBody as OpenAPIV3.ReferenceObject).$ref + ? (operation.requestBody as OpenAPIV3_1.ReferenceObject).$ref ? ($refs.get( - (operation.requestBody as OpenAPIV3.ReferenceObject).$ref, - ) as OpenAPIV3.ParameterObject) - : (operation.requestBody as OpenAPIV3.RequestBodyObject) + (operation.requestBody as OpenAPIV3_1.ReferenceObject).$ref, + ) as OpenAPIV3_1.ParameterObject) + : (operation.requestBody as OpenAPIV3_1.RequestBodyObject) : undefined; const requestBody: DereferencedRequestBodyObject | undefined = @@ -150,16 +150,16 @@ export async function dereferenceOpenAPIOperations>( .schema ? ( baseRequestBody.content[mediaType] - .schema as OpenAPIV3.ReferenceObject + .schema as OpenAPIV3_1.ReferenceObject ).$ref ? ($refs.get( ( baseRequestBody.content[mediaType] - .schema as OpenAPIV3.ReferenceObject + .schema as OpenAPIV3_1.ReferenceObject ).$ref, - ) as OpenAPIV3.SchemaObject) + ) as OpenAPIV3_1.SchemaObject) : (baseRequestBody.content[mediaType] - .schema as OpenAPIV3.SchemaObject) + .schema as OpenAPIV3_1.SchemaObject) : undefined; return { @@ -168,7 +168,7 @@ export async function dereferenceOpenAPIOperations>( ...baseRequestBody.content?.[mediaType], schema: buildJSONSchemaFromAPISchema( API, - mediaTypeSchema as OpenAPIV3.SchemaObject, + mediaTypeSchema as OpenAPIV3_1.SchemaObject, ), }, }; @@ -180,13 +180,12 @@ export async function dereferenceOpenAPIOperations>( const responses: Record = Object.keys( operation.responses || {}, ).reduce((allResponses, status) => { - const responseObject = ( - operation.responses[status] as OpenAPIV3.ReferenceObject - ).$ref - ? ($refs.get( - (operation.responses[status] as OpenAPIV3.ReferenceObject).$ref, - ) as OpenAPIV3.ResponseObject) - : (operation.responses[status] as OpenAPIV3.ResponseObject); + const responseObject = + operation.responses && '$ref' in operation.responses[status] + ? ($refs.get( + (operation.responses[status] as OpenAPIV3_1.ReferenceObject).$ref, + ) as OpenAPIV3_1.ResponseObject) + : (operation?.responses?.[status] as OpenAPIV3_1.ResponseObject); const finalResponseObject: DereferencedResponseObject = { ...responseObject, content: responseObject.content @@ -196,16 +195,16 @@ export async function dereferenceOpenAPIOperations>( .schema ? ( responseObject.content[mediaType] - .schema as OpenAPIV3.ReferenceObject + .schema as OpenAPIV3_1.ReferenceObject ).$ref ? ($refs.get( ( responseObject.content[mediaType] - .schema as OpenAPIV3.ReferenceObject + .schema as OpenAPIV3_1.ReferenceObject ).$ref, - ) as OpenAPIV3.SchemaObject) + ) as OpenAPIV3_1.SchemaObject) : (responseObject.content[mediaType] - .schema as OpenAPIV3.SchemaObject) + .schema as OpenAPIV3_1.SchemaObject) : undefined; return { @@ -214,7 +213,7 @@ export async function dereferenceOpenAPIOperations>( ...responseObject.content?.[mediaType], schema: buildJSONSchemaFromAPISchema( API, - mediaTypeSchema as OpenAPIV3.SchemaObject, + mediaTypeSchema as OpenAPIV3_1.SchemaObject, ), }, }; @@ -240,7 +239,7 @@ export async function dereferenceOpenAPIOperations>( } export function pickupOperationSecuritySchemes( - openAPI: OpenAPIV3.Document, + openAPI: OpenAPIV3_1.Document, operation: WhookOperation, ): { [name: string]: SupportedSecurityScheme } { const securitySchemes = @@ -271,9 +270,9 @@ export function pickupOperationSecuritySchemes( } function buildJSONSchemaFromAPISchema( - API: OpenAPIV3.Document, - baseSchema: OpenAPIV3.SchemaObject, -): OpenAPIV3.SchemaObject { + API: OpenAPIV3_1.Document, + baseSchema: OpenAPIV3_1.SchemaObject, +): OpenAPIV3_1.SchemaObject { return JSON.parse( JSON.stringify({ ...baseSchema, diff --git a/packages/whook-http-router/src/libs/utils.ts b/packages/whook-http-router/src/libs/utils.ts index b7f7c0ce..67070d00 100644 --- a/packages/whook-http-router/src/libs/utils.ts +++ b/packages/whook-http-router/src/libs/utils.ts @@ -7,7 +7,7 @@ import { pickFirstHeaderValue, pickAllHeaderValues, } from '@whook/http-transaction'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { WhookRequest, WhookHandler, @@ -30,7 +30,7 @@ export type WhookResponseSpec = { }; export function extractConsumableMediaTypes( - operation: OpenAPIV3.OperationObject, + operation: WhookOperation, ): string[] { if (!operation.requestBody) { return []; @@ -41,12 +41,12 @@ export function extractConsumableMediaTypes( // https://swagger.io/specification/#requestBodyObject return Object.keys( - (operation.requestBody as OpenAPIV3.RequestBodyObject).content, + (operation.requestBody as OpenAPIV3_1.RequestBodyObject).content, ); } export function extractProduceableMediaTypes( - operation: OpenAPIV3.OperationObject, + operation: WhookOperation, ): string[] { if (!operation.responses) { return []; @@ -58,7 +58,7 @@ export function extractProduceableMediaTypes( (produceableMediaTypes, status) => { const response = operation.responses[ status - ] as OpenAPIV3.ResponseObject; + ] as OpenAPIV3_1.ResponseObject; if (!response.content) { return produceableMediaTypes; @@ -179,7 +179,7 @@ export function checkBodyMediaType( } export function extractResponseSpec( - operation: OpenAPIV3.OperationObject, + operation: WhookOperation, request: WhookRequest, supportedMediaTypes: string[], supportedCharsets: string[], diff --git a/packages/whook-http-router/src/libs/validation.test.ts b/packages/whook-http-router/src/libs/validation.test.ts index a7033511..13c9d0cd 100644 --- a/packages/whook-http-router/src/libs/validation.test.ts +++ b/packages/whook-http-router/src/libs/validation.test.ts @@ -5,7 +5,7 @@ import { extractOperationSecurityParameters, } from './validation.js'; import { YError } from 'yerror'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; describe('extractParametersFromSecuritySchemes', () => { describe('should work', () => { @@ -178,8 +178,8 @@ describe('extractOperationSecurityParameters', () => { parameters: [], responses: {}, }; - const API: OpenAPIV3.Document = { - openapi: '3.0.2', + const API: OpenAPIV3_1.Document = { + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample OpenAPI', @@ -223,8 +223,8 @@ describe('extractOperationSecurityParameters', () => { parameters: [], responses: {}, }; - const API: OpenAPIV3.Document = { - openapi: '3.0.2', + const API: OpenAPIV3_1.Document = { + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample OpenAPI', @@ -285,8 +285,8 @@ describe('extractOperationSecurityParameters', () => { parameters: [], responses: {}, }; - const API: OpenAPIV3.Document = { - openapi: '3.0.2', + const API: OpenAPIV3_1.Document = { + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample OpenAPI', @@ -339,12 +339,12 @@ describe('extractOperationSecurityParameters', () => { { basicAuth: ['user:delegate'], }, - ] as OpenAPIV3.OperationObject['security'], + ] as OpenAPIV3_1.OperationObject['security'], parameters: [], responses: {}, }; - const API: OpenAPIV3.Document = { - openapi: '3.0.2', + const API: OpenAPIV3_1.Document = { + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample OpenAPI', diff --git a/packages/whook-http-router/src/libs/validation.ts b/packages/whook-http-router/src/libs/validation.ts index ba3aa007..0498007f 100644 --- a/packages/whook-http-router/src/libs/validation.ts +++ b/packages/whook-http-router/src/libs/validation.ts @@ -11,7 +11,7 @@ import Ajv from 'ajv'; import { parseReentrantNumber, parseBoolean } from 'strict-qs'; import type { ValidateFunction } from 'ajv'; import type { SupportedSecurityScheme } from './openAPIUtils.js'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { DereferencedParameterObject, WhookOperation, @@ -44,7 +44,7 @@ export function applyValidators( validators: { [name: string]: ValidateFunction }, parameters: JsonValue[], ): void { - ((operation.parameters || []) as OpenAPIV3.ParameterObject[]).forEach( + ((operation.parameters || []) as OpenAPIV3_1.ParameterObject[]).forEach( ({ name, in: isIn }) => { if ('header' === isIn) { return validators[name](parameters[camelCase(name)]); @@ -121,7 +121,7 @@ function _validateRequestBody( value: unknown, ): void { if ( - (operation.requestBody as OpenAPIV3.RequestBodyObject).required && + (operation.requestBody as OpenAPIV3_1.RequestBodyObject).required && 'undefined' === typeof value ) { throw new YHTTPError( @@ -169,7 +169,7 @@ function _rejectAnyRequestBody( const SUPPORTED_HTTP_SCHEMES = ['basic', 'bearer', 'digest']; export function extractOperationSecurityParameters( - openAPI: OpenAPIV3.Document, + openAPI: OpenAPIV3_1.Document, operation: WhookOperation, ): DereferencedParameterObject[] { const operationSecuritySchemes = pickupOperationSecuritySchemes( @@ -184,7 +184,10 @@ export function extractOperationSecurityParameters( } export function extractParametersFromSecuritySchemes( - securitySchemes: (SupportedSecurityScheme | OpenAPIV3.OpenIdSecurityScheme)[], + securitySchemes: ( + | SupportedSecurityScheme + | OpenAPIV3_1.OpenIdSecurityScheme + )[], ): DereferencedParameterObject[] { const hasOAuth = securitySchemes.some((securityScheme) => ['oauth2', 'openIdConnect'].includes(securityScheme.type), @@ -196,15 +199,15 @@ export function extractParametersFromSecuritySchemes( .map((securityScheme) => { if ( !SUPPORTED_HTTP_SCHEMES.includes( - (securityScheme as OpenAPIV3.HttpSecurityScheme).scheme, + (securityScheme as OpenAPIV3_1.HttpSecurityScheme).scheme, ) ) { throw new YError( 'E_UNSUPPORTED_HTTP_SCHEME', - (securityScheme as OpenAPIV3.HttpSecurityScheme).scheme, + (securityScheme as OpenAPIV3_1.HttpSecurityScheme).scheme, ); } - return (securityScheme as OpenAPIV3.HttpSecurityScheme).scheme; + return (securityScheme as OpenAPIV3_1.HttpSecurityScheme).scheme; }), ...(hasOAuth ? ['bearer'] : []), ]), @@ -215,7 +218,7 @@ export function extractParametersFromSecuritySchemes( const securityParameters: DereferencedParameterObject[] = securitySchemes .filter( - (securityScheme): securityScheme is OpenAPIV3.ApiKeySecurityScheme => + (securityScheme): securityScheme is OpenAPIV3_1.ApiKeySecurityScheme => securityScheme.type === 'apiKey', ) .map((securityScheme) => { @@ -292,7 +295,7 @@ export function extractParametersFromSecuritySchemes( export function prepareParametersValidators( ajv: Ajv.default, operationId: string, - parameters: OpenAPIV3.ParameterObject[], + parameters: DereferencedParameterObject[], ): { [name: string]: ValidateFunction } { return parameters.reduce((validators, parameter, index) => { if ('string' !== typeof parameter.name) { @@ -360,7 +363,7 @@ export function prepareParametersValidators( } export function _validateParameter( - parameter: OpenAPIV3.ParameterObject, + parameter: DereferencedParameterObject, validator: ValidateFunction, value: unknown, ): void { diff --git a/packages/whook-http-router/src/services/errorHandler.ts b/packages/whook-http-router/src/services/errorHandler.ts index 6947b924..7334ad84 100644 --- a/packages/whook-http-router/src/services/errorHandler.ts +++ b/packages/whook-http-router/src/services/errorHandler.ts @@ -9,7 +9,7 @@ import type { WhookStringifyers } from '../index.js'; import type { WhookResponseSpec } from '../libs/utils.js'; import type { WhookResponse } from '@whook/http-transaction'; import type { YHTTPError } from 'yhttperror'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { AppEnvVars } from 'application-services'; /* Architecture Note #2: Error handler @@ -340,7 +340,7 @@ export type WhookErrorResponse = WhookResponse< } >; -export const WhookErrorSchema: OpenAPIV3.SchemaObject = { +export const WhookErrorSchema: OpenAPIV3_1.SchemaObject = { type: 'object', additionalProperties: false, properties: { diff --git a/packages/whook-http-transaction/src/index.ts b/packages/whook-http-transaction/src/index.ts index 01db7f33..1274d32c 100644 --- a/packages/whook-http-transaction/src/index.ts +++ b/packages/whook-http-transaction/src/index.ts @@ -10,7 +10,7 @@ import { printStackTrace, YError } from 'yerror'; import type { Parameters, HandlerFunction, Dependencies } from 'knifecycle'; import type { LogService, TimeService, DelayService } from 'common-services'; import type { IncomingMessage, ServerResponse } from 'node:http'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { JsonValue } from 'type-fest'; import type { Readable } from 'stream'; export type { @@ -24,13 +24,13 @@ export type { WhookAPMDependencies, WhookAPMService } from './services/apm.js'; export { initObfuscatorService, initAPMService }; export type DereferencedMediaTypeObject = Omit< - OpenAPIV3.MediaTypeObject, + OpenAPIV3_1.MediaTypeObject, 'schema' > & { - schema: OpenAPIV3.SchemaObject; + schema: OpenAPIV3_1.SchemaObject; }; export type DereferencedResponseObject = Omit< - OpenAPIV3.ResponseObject, + OpenAPIV3_1.ResponseObject, 'content' > & { content?: { @@ -38,7 +38,7 @@ export type DereferencedResponseObject = Omit< }; }; export type DereferencedRequestBodyObject = Omit< - OpenAPIV3.RequestBodyObject, + OpenAPIV3_1.RequestBodyObject, 'content' > & { content: { @@ -46,14 +46,14 @@ export type DereferencedRequestBodyObject = Omit< }; }; export type DereferencedParameterObject = Omit< - OpenAPIV3.ParameterObject, + OpenAPIV3_1.ParameterObject, 'schema' > & { - schema: OpenAPIV3.SchemaObject; + schema: OpenAPIV3_1.SchemaObject; }; export type DereferencedOperationObject = Omit< - OpenAPIV3.OperationObject, + OpenAPIV3_1.OperationObject, 'parameters' | 'requestBody' | 'responses' > & { parameters: DereferencedParameterObject[]; diff --git a/packages/whook-method-override/src/index.test.ts b/packages/whook-method-override/src/index.test.ts index a9c64ba6..37a9778c 100644 --- a/packages/whook-method-override/src/index.test.ts +++ b/packages/whook-method-override/src/index.test.ts @@ -19,15 +19,15 @@ import { constant, initializer } from 'knifecycle'; import axios from 'axios'; import { YError } from 'yerror'; import type { Knifecycle } from 'knifecycle'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { Logger } from 'common-services'; describe('wrapHTTPTransactionWithMethodOverride', () => { const BASE_PATH = '/v1'; const PORT = 6666; const HOST = 'localhost'; - const API: OpenAPIV3.Document = { - openapi: '3.0.2', + const API: OpenAPIV3_1.Document = { + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample OpenAPI', diff --git a/packages/whook-oauth2/README.md b/packages/whook-oauth2/README.md index 06fc11f9..bf203810 100644 --- a/packages/whook-oauth2/README.md +++ b/packages/whook-oauth2/README.md @@ -280,7 +280,7 @@ import { initPostOAuth2Acknowledge, postOAuth2AcknowledgeDefinition, } from '@whook/oauth2'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { WhookAPIHandlerDefinition } from '@whook/whook'; export default initPostOAuth2Acknowledge; @@ -304,26 +304,26 @@ export const definition: WhookAPIHandlerDefinition = { schema: { ...(( postOAuth2AcknowledgeDefinition.operation - .requestBody as OpenAPIV3.RequestBodyObject + .requestBody as OpenAPIV3_1.RequestBodyObject ).content['application/json'] - .schema as OpenAPIV3.NonArraySchemaObject), + .schema as OpenAPIV3_1.NonArraySchemaObject), required: [ 'userId', ...( ( postOAuth2AcknowledgeDefinition.operation - .requestBody as OpenAPIV3.RequestBodyObject + .requestBody as OpenAPIV3_1.RequestBodyObject ).content['application/json'] - .schema as OpenAPIV3.NonArraySchemaObject + .schema as OpenAPIV3_1.NonArraySchemaObject ).required, ], properties: { ...( ( postOAuth2AcknowledgeDefinition.operation - .requestBody as OpenAPIV3.RequestBodyObject + .requestBody as OpenAPIV3_1.RequestBodyObject ).content['application/json'] - .schema as OpenAPIV3.NonArraySchemaObject + .schema as OpenAPIV3_1.NonArraySchemaObject ).properties, userId: { type: 'string', diff --git a/packages/whook-oauth2/src/handlers/postOAuth2Token.ts b/packages/whook-oauth2/src/handlers/postOAuth2Token.ts index 015bf347..5c68fab3 100644 --- a/packages/whook-oauth2/src/handlers/postOAuth2Token.ts +++ b/packages/whook-oauth2/src/handlers/postOAuth2Token.ts @@ -3,7 +3,7 @@ import { camelCaseObjectProperties } from './getOAuth2Authorize.js'; import { noop } from '@whook/whook'; import { YError, printStackTrace } from 'yerror'; import { refersTo } from '@whook/whook'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { LogService, TimeService } from 'common-services'; import type { WhookAPISchemaDefinition, @@ -127,7 +127,7 @@ export const tokenBodySchema: WhookAPISchemaDefinition = { refersTo(clientCredentialsTokenRequestBodySchema), refersTo(refreshTokenRequestBodySchema), ], - } as OpenAPIV3.SchemaObject, + } as OpenAPIV3_1.SchemaObject, }; export const definition: WhookAPIHandlerDefinition = { diff --git a/packages/whook-oauth2/src/index.test.ts b/packages/whook-oauth2/src/index.test.ts index e821f1f6..5e1529e7 100644 --- a/packages/whook-oauth2/src/index.test.ts +++ b/packages/whook-oauth2/src/index.test.ts @@ -61,7 +61,7 @@ import type { OAuth2AccessTokenService, } from './index.js'; import type { Knifecycle } from 'knifecycle'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { Logger } from 'common-services'; import type { AuthenticationService, @@ -84,8 +84,8 @@ describe('OAuth2 server', () => { const time = jest.fn(); const $autoload = jest.fn(); - const API: OpenAPIV3.Document = { - openapi: '3.0.2', + const API: OpenAPIV3_1.Document = { + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample OpenAPI', diff --git a/packages/whook-swagger-ui/src/handlers/__snapshots__/getOpenAPI.test.ts.snap b/packages/whook-swagger-ui/src/handlers/__snapshots__/getOpenAPI.test.ts.snap index 3d35d62e..b735e3c7 100644 --- a/packages/whook-swagger-ui/src/handlers/__snapshots__/getOpenAPI.test.ts.snap +++ b/packages/whook-swagger-ui/src/handlers/__snapshots__/getOpenAPI.test.ts.snap @@ -8,7 +8,7 @@ exports[`getOpenAPI should show every endpoints when authenticated 1`] = ` "title": "test", "version": "", }, - "openapi": "3.0.0", + "openapi": "3.1.0", "paths": { "/time": { "delete": { @@ -61,7 +61,7 @@ exports[`getOpenAPI should work 1`] = ` "title": "test", "version": "", }, - "openapi": "3.0.0", + "openapi": "3.1.0", "paths": { "/time": { "get": { @@ -99,7 +99,7 @@ exports[`getOpenAPI should work with muted parameter 1`] = ` "title": "test", "version": "", }, - "openapi": "3.0.0", + "openapi": "3.1.0", "paths": { "/time": { "get": { @@ -135,7 +135,7 @@ exports[`getOpenAPI should work with muted tags 1`] = ` "title": "test", "version": "", }, - "openapi": "3.0.0", + "openapi": "3.1.0", "paths": { "/time": { "delete": { diff --git a/packages/whook-swagger-ui/src/handlers/getOpenAPI.test.ts b/packages/whook-swagger-ui/src/handlers/getOpenAPI.test.ts index d728edde..8169d762 100644 --- a/packages/whook-swagger-ui/src/handlers/getOpenAPI.test.ts +++ b/packages/whook-swagger-ui/src/handlers/getOpenAPI.test.ts @@ -1,10 +1,10 @@ import { describe, it, expect } from '@jest/globals'; import initGetOpenAPI from './getOpenAPI.js'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; describe('getOpenAPI', () => { const API = { - openapi: '3.0.0', + openapi: '3.1.0', info: { title: 'test', version: '1', @@ -30,10 +30,10 @@ describe('getOpenAPI', () => { }, }, tags: [{ name: 'public' }, { name: 'private' }], - } as unknown as OpenAPIV3.Document; + } as unknown as OpenAPIV3_1.Document; const APIWithParameters = { - openapi: '3.0.0', + openapi: '3.1.0', info: { title: 'test', version: '1', @@ -74,7 +74,7 @@ describe('getOpenAPI', () => { }, }, tags: [{ name: 'public' }, { name: 'private' }], - } as unknown as OpenAPIV3.Document; + } as unknown as OpenAPIV3_1.Document; it('should work', async () => { const getOpenAPI = await initGetOpenAPI({ diff --git a/packages/whook-swagger-ui/src/handlers/getOpenAPI.ts b/packages/whook-swagger-ui/src/handlers/getOpenAPI.ts index 8c477b71..c0dfed8a 100644 --- a/packages/whook-swagger-ui/src/handlers/getOpenAPI.ts +++ b/packages/whook-swagger-ui/src/handlers/getOpenAPI.ts @@ -2,7 +2,7 @@ import { autoHandler } from 'knifecycle'; import { getOpenAPIOperations } from '@whook/http-router'; import SwaggerParser from '@apidevtools/swagger-parser'; import type { WhookAPIHandlerDefinition, WhookResponse } from '@whook/whook'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; export type WhookAPIOperationSwaggerConfig = { private?: boolean; @@ -10,32 +10,31 @@ export type WhookAPIOperationSwaggerConfig = { export default autoHandler(getOpenAPI); -export const definition: WhookAPIHandlerDefinition = - { - path: '/openAPI', - method: 'get', - operation: { - operationId: 'getOpenAPI', - summary: 'Get API documentation.', - tags: ['system'], - 'x-whook': { private: false }, - responses: { - '200': { - description: 'Provides the private Open API documentation', - content: { - 'application/json': { - schema: { - type: 'object', - }, +export const definition: WhookAPIHandlerDefinition = { + path: '/openAPI', + method: 'get', + operation: { + operationId: 'getOpenAPI', + summary: 'Get API documentation.', + tags: ['system'], + 'x-whook': { private: false }, + responses: { + '200': { + description: 'Provides the private Open API documentation', + content: { + 'application/json': { + schema: { + type: 'object', }, }, }, }, }, - } as WhookAPIHandlerDefinition; + }, +} as WhookAPIHandlerDefinition; async function getOpenAPI( - { API }: { API: OpenAPIV3.Document }, + { API }: { API: OpenAPIV3_1.Document }, { authenticated = false, mutedMethods = ['options'], @@ -47,7 +46,7 @@ async function getOpenAPI( mutedParameters?: string[]; mutedTags?: string[]; }, -): Promise> { +): Promise> { const operations = getOpenAPIOperations(API); const $refs = await SwaggerParser.resolve(API); @@ -78,7 +77,7 @@ async function getOpenAPI( paths[operation.path] = { ...paths[operation.path], [operation.method]: { - ...(API.paths[operation.path]?.[operation.method] || {}), + ...(API?.paths?.[operation.path]?.[operation.method] || {}), ...(operation.parameters && operation.parameters.length && { parameters: removeMutedParameters( @@ -108,18 +107,18 @@ async function getOpenAPI( } function removeMutedParameters( - parameters: Array, + parameters: Array, mutedParameters: string[], $refs: SwaggerParser.$Refs, ) { return parameters.reduce( (acc, parameter) => { - const dereferencedParameter = (parameter as OpenAPIV3.ReferenceObject) + const dereferencedParameter = (parameter as OpenAPIV3_1.ReferenceObject) .$ref ? ($refs.get( - (parameter as OpenAPIV3.ReferenceObject).$ref, - ) as OpenAPIV3.ParameterObject) - : (parameter as OpenAPIV3.ParameterObject); + (parameter as OpenAPIV3_1.ReferenceObject).$ref, + ) as OpenAPIV3_1.ParameterObject) + : (parameter as OpenAPIV3_1.ParameterObject); if (mutedParameters.includes(dereferencedParameter.name)) { return acc; @@ -127,6 +126,6 @@ function removeMutedParameters( return acc.concat(parameter); }, - [] as (OpenAPIV3.ReferenceObject | OpenAPIV3.ParameterObject)[], + [] as (OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.ParameterObject)[], ); } diff --git a/packages/whook-swagger-ui/src/index.test.ts b/packages/whook-swagger-ui/src/index.test.ts index 226644d1..f6d92b0b 100644 --- a/packages/whook-swagger-ui/src/index.test.ts +++ b/packages/whook-swagger-ui/src/index.test.ts @@ -9,15 +9,15 @@ import { import initHTTPRouter from '@whook/http-router'; import wrapHTTPRouterWithSwaggerUI from './index.js'; import { YError } from 'yerror'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { Logger } from 'common-services'; describe('wrapHTTPRouterWithSwaggerUI', () => { const HOST = 'localhost'; const PORT = 22222; const BASE_PATH = '/v1'; - const API: OpenAPIV3.Document = { - openapi: '3.0.2', + const API: OpenAPIV3_1.Document = { + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample OpenAPI', diff --git a/packages/whook-versions/src/__snapshots__/index.test.ts.snap b/packages/whook-versions/src/__snapshots__/index.test.ts.snap index c51d21a2..635d6905 100644 --- a/packages/whook-versions/src/__snapshots__/index.test.ts.snap +++ b/packages/whook-versions/src/__snapshots__/index.test.ts.snap @@ -41,7 +41,7 @@ exports[`augmentAPIWithVersionsHeaders() should work 1`] = ` "title": "Sample Swagger", "version": "1.0.0", }, - "openapi": "3.0.2", + "openapi": "3.1.0", "paths": { "/ping": { "get": { diff --git a/packages/whook-versions/src/index.test.ts b/packages/whook-versions/src/index.test.ts index 3f8c79d3..7fdf58ff 100644 --- a/packages/whook-versions/src/index.test.ts +++ b/packages/whook-versions/src/index.test.ts @@ -32,7 +32,7 @@ describe('augmentAPIWithVersionsHeaders()', () => { expect( await augmentAPIWithVersionsHeaders( { - openapi: '3.0.2', + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample Swagger', diff --git a/packages/whook-versions/src/index.ts b/packages/whook-versions/src/index.ts index b1b75f23..ed99ac30 100644 --- a/packages/whook-versions/src/index.ts +++ b/packages/whook-versions/src/index.ts @@ -2,7 +2,7 @@ import camelCase from 'camelcase'; import { DEFAULT_ERROR_URI, DEFAULT_HELP_URI } from '@whook/whook'; import initWrapHandlerWithVersionChecker from './wrappers/wrapHandlerWithVersionChecker.js'; import type { WhookErrorsDescriptors } from '@whook/whook'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { VersionDescriptor } from './wrappers/wrapHandlerWithVersionChecker.js'; export const VERSIONS_ERRORS_DESCRIPTORS: WhookErrorsDescriptors = { @@ -35,16 +35,16 @@ export type { * @returns {Promise} The augmented OpenAPI object */ export async function augmentAPIWithVersionsHeaders( - API: OpenAPIV3.Document, + API: OpenAPIV3_1.Document, VERSIONS: VersionDescriptor[], -): Promise { +): Promise { return { ...API, components: { ...(API.components || {}), parameters: { ...((API.components || {}).parameters || {}), - ...VERSIONS.reduce<{ [key: string]: OpenAPIV3.ParameterObject }>( + ...VERSIONS.reduce<{ [key: string]: OpenAPIV3_1.ParameterObject }>( (versionsParameters, version) => ({ ...versionsParameters, [camelCase(version.header)]: { @@ -63,28 +63,31 @@ export async function augmentAPIWithVersionsHeaders( ), }, }, - paths: Object.keys(API.paths).reduce( + paths: Object.keys(API.paths || {}).reduce( reducePaths, - API.paths, + API.paths || {}, ), }; function reducePaths( - pathsObject: OpenAPIV3.PathsObject, + pathsObject: OpenAPIV3_1.PathsObject, path: string, - ): OpenAPIV3.PathsObject { + ): OpenAPIV3_1.PathsObject { return { ...pathsObject, [path]: Object.keys( - API.paths[path] || {}, - ).reduce(reduceMethods, API.paths[path] || {}), + API.paths?.[path] || {}, + ).reduce( + reduceMethods, + API.paths?.[path] || {}, + ), }; } function reduceMethods( - pathItemObject: OpenAPIV3.PathItemObject, + pathItemObject: OpenAPIV3_1.PathItemObject, method: string, - ): OpenAPIV3.PathItemObject { + ): OpenAPIV3_1.PathItemObject { return { ...pathItemObject, [method]: { diff --git a/packages/whook/README.md b/packages/whook/README.md index c0f05a91..f6114965 100644 --- a/packages/whook/README.md +++ b/packages/whook/README.md @@ -15,8 +15,8 @@ `whook` allows you to create REST web services by providing its ingredients and the recipe to make it work altogether. -The recipe is your Open API (OAS3) definition while ingredients are handlers, -wrappers, services, commands and their configuration. +The recipe is your Open API (version 3.1) definition while ingredients are +handlers, wrappers, services, commands and their configuration. ## Quick start diff --git a/packages/whook/src/commands/create.test.ts b/packages/whook/src/commands/create.test.ts index 2189621e..957a9757 100644 --- a/packages/whook/src/commands/create.test.ts +++ b/packages/whook/src/commands/create.test.ts @@ -3,14 +3,14 @@ import { describe, it, beforeEach, jest, expect } from '@jest/globals'; import _inquirer from 'inquirer'; import initCreateCommand from './create.js'; import { definition as initGetPingDefinition } from '../handlers/getPing.js'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { LogService } from 'common-services'; import type { WhookPromptArgs } from '../services/promptArgs.js'; describe('createCommand', () => { const PROJECT_DIR = '/hom/whoiam/project'; - const API: OpenAPIV3.Document = { - openapi: '3.0.2', + const API: OpenAPIV3_1.Document = { + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample OpenAPI', diff --git a/packages/whook/src/commands/create.ts b/packages/whook/src/commands/create.ts index cbf23aa5..042ea9ef 100644 --- a/packages/whook/src/commands/create.ts +++ b/packages/whook/src/commands/create.ts @@ -15,7 +15,7 @@ import type { WhookPromptArgs, } from '../services/promptArgs.js'; import type { LogService } from 'common-services'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; const { writeFile: _writeFile, @@ -97,7 +97,7 @@ async function initCreateCommand({ log = noop, }: { PROJECT_DIR: string; - API: OpenAPIV3.Document; + API: OpenAPIV3_1.Document; inquirer: typeof _inquirer; promptArgs: WhookPromptArgs; writeFile: typeof _writeFile; diff --git a/packages/whook/src/commands/generateOpenAPISchema.test.ts b/packages/whook/src/commands/generateOpenAPISchema.test.ts index 521b849a..69537438 100644 --- a/packages/whook/src/commands/generateOpenAPISchema.test.ts +++ b/packages/whook/src/commands/generateOpenAPISchema.test.ts @@ -22,7 +22,7 @@ describe('generateOpenAPISchema', () => { getOpenAPI.mockResolvedValueOnce({ status: 200, body: { - openapi: '3.0.2', + openapi: '3.1.0', info: { version: '0.0.0', title: 'api', @@ -62,39 +62,39 @@ describe('generateOpenAPISchema', () => { }).toMatchInlineSnapshot( {}, ` +{ + "getOpenAPICalls": [ + [ { - "getOpenAPICalls": [ - [ - { - "authenticated": true, - "mutedMethods": [ - "options", - ], - "mutedParameters": [], - }, - ], + "authenticated": true, + "mutedMethods": [ + "options", ], - "logCalls": [ - [ - "warning", - "📥 - Retrieving schema...", - ], - [ - "warning", - "📇 - Writing Open API schema...", - ], - ], - "output": "{ - "openapi": "3.0.2", - "info": { - "version": "0.0.0", - "title": "api", - "description": "The API" - } - }", - "result": undefined, - } - `, + "mutedParameters": [], + }, + ], + ], + "logCalls": [ + [ + "warning", + "📥 - Retrieving schema...", + ], + [ + "warning", + "📇 - Writing Open API schema...", + ], + ], + "output": "{ + "openapi": "3.1.0", + "info": { + "version": "0.0.0", + "title": "api", + "description": "The API" + } +}", + "result": undefined, +} +`, ); }); }); diff --git a/packages/whook/src/commands/generateOpenAPITypes.test.ts b/packages/whook/src/commands/generateOpenAPITypes.test.ts index 5822378f..a83292ca 100644 --- a/packages/whook/src/commands/generateOpenAPITypes.test.ts +++ b/packages/whook/src/commands/generateOpenAPITypes.test.ts @@ -3,7 +3,7 @@ import initGenerateOpenAPITypes from './generateOpenAPITypes.js'; import { PassThrough } from 'stream'; import { definition as initGetPingDefinition } from '../handlers/getPing.js'; import type { OpenAPITypesGenerationOptions } from 'schema2dts'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { LogService } from 'common-services'; describe('generateOpenAPITypes', () => { @@ -17,8 +17,8 @@ describe('generateOpenAPITypes', () => { brandedTypes: [], tuplesFromFixedArraysLengthLimit: 5, }; - const API: OpenAPIV3.Document = { - openapi: '3.0.2', + const API: OpenAPIV3_1.Document = { + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample OpenAPI', diff --git a/packages/whook/src/handlers/getOpenAPI.test.ts b/packages/whook/src/handlers/getOpenAPI.test.ts index 34772f0a..84069abd 100644 --- a/packages/whook/src/handlers/getOpenAPI.test.ts +++ b/packages/whook/src/handlers/getOpenAPI.test.ts @@ -1,10 +1,10 @@ import { describe, it, expect } from '@jest/globals'; import initGetOpenAPI from './getOpenAPI.js'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; describe('getOpenAPI', () => { const API = { - openapi: '3.0.0', + openapi: '3.1.0', info: { title: 'test', version: '1', @@ -26,10 +26,10 @@ describe('getOpenAPI', () => { }, }, tags: [{ name: 'public' }, { name: 'private' }], - } as unknown as OpenAPIV3.Document; + } as unknown as OpenAPIV3_1.Document; const APIWithParameters = { - openapi: '3.0.0', + openapi: '3.1.0', info: { title: 'test', version: '1', @@ -70,7 +70,7 @@ describe('getOpenAPI', () => { }, }, tags: [{ name: 'public' }, { name: 'private' }], - } as unknown as OpenAPIV3.Document; + } as unknown as OpenAPIV3_1.Document; it('should work', async () => { const getOpenAPI = await initGetOpenAPI({ @@ -90,37 +90,37 @@ describe('getOpenAPI', () => { }, }, }).toMatchInlineSnapshot(` - { - "response": { - "body": { - "info": { - "title": "test", - "version": "", - }, - "openapi": "3.0.0", - "paths": { - "/time": { - "get": { - "tags": [ - "public", - ], - "x-whook": undefined, - }, - }, - }, +{ + "response": { + "body": { + "info": { + "title": "test", + "version": "", + }, + "openapi": "3.1.0", + "paths": { + "/time": { + "get": { "tags": [ - { - "name": "public", - }, - { - "name": "private", - }, + "public", ], + "x-whook": undefined, }, - "status": 200, }, - } - `); + }, + "tags": [ + { + "name": "public", + }, + { + "name": "private", + }, + ], + }, + "status": 200, + }, +} +`); }); it('should show every endpoints when authenticated', async () => { @@ -143,48 +143,48 @@ describe('getOpenAPI', () => { }, }, }).toMatchInlineSnapshot(` - { - "response": { - "body": { - "info": { - "title": "test", - "version": "", - }, - "openapi": "3.0.0", - "paths": { - "/time": { - "get": { - "tags": [ - "public", - ], - "x-whook": { - "memx": 2, - "tx": 18, - }, - }, - "put": { - "tags": [ - "private", - ], - "x-whook": { - "private": true, - }, - }, - }, +{ + "response": { + "body": { + "info": { + "title": "test", + "version": "", + }, + "openapi": "3.1.0", + "paths": { + "/time": { + "get": { + "tags": [ + "public", + ], + "x-whook": { + "memx": 2, + "tx": 18, }, + }, + "put": { "tags": [ - { - "name": "public", - }, - { - "name": "private", - }, + "private", ], + "x-whook": { + "private": true, + }, }, - "status": 200, }, - } - `); + }, + "tags": [ + { + "name": "public", + }, + { + "name": "private", + }, + ], + }, + "status": 200, + }, +} +`); }); it('should work with muted paramerter', async () => { @@ -207,50 +207,50 @@ describe('getOpenAPI', () => { }, }, }).toMatchInlineSnapshot(` - { - "response": { - "body": { - "components": { - "parameters": { - "xRefToRemove": { - "in": "header", - "name": "X-Ref-To-Remove", - }, - }, - }, - "info": { - "title": "test", - "version": "", - }, - "openapi": "3.0.0", - "paths": { - "/time": { - "get": { - "parameters": [ - { - "in": "query", - "name": "queryParam", - }, - ], - "tags": [ - "public", - ], - "x-whook": undefined, - }, - }, - }, - "tags": [ - { - "name": "public", - }, +{ + "response": { + "body": { + "components": { + "parameters": { + "xRefToRemove": { + "in": "header", + "name": "X-Ref-To-Remove", + }, + }, + }, + "info": { + "title": "test", + "version": "", + }, + "openapi": "3.1.0", + "paths": { + "/time": { + "get": { + "parameters": [ { - "name": "private", + "in": "query", + "name": "queryParam", }, ], + "tags": [ + "public", + ], + "x-whook": undefined, }, - "status": 200, }, - } - `); + }, + "tags": [ + { + "name": "public", + }, + { + "name": "private", + }, + ], + }, + "status": 200, + }, +} +`); }); }); diff --git a/packages/whook/src/handlers/getOpenAPI.ts b/packages/whook/src/handlers/getOpenAPI.ts index 233e59d2..303d70f1 100644 --- a/packages/whook/src/handlers/getOpenAPI.ts +++ b/packages/whook/src/handlers/getOpenAPI.ts @@ -6,7 +6,7 @@ import type { WhookAPIOperationConfig, } from '../services/API_DEFINITIONS.js'; import type { WhookResponse } from '@whook/http-transaction'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; export default autoHandler(getOpenAPI); @@ -15,7 +15,7 @@ export const definition: WhookAPIHandlerDefinition = { method: 'get', operation: { operationId: 'getOpenAPI', - summary: 'Get API documentation.', + summary: 'Get the API documentation.', tags: ['system'], 'x-whook': { private: false }, responses: { @@ -31,44 +31,22 @@ export const definition: WhookAPIHandlerDefinition = { }, }, }, -} as WhookAPIHandlerDefinition; - -function removeMutedParameters( - parameters: Array, - mutedParameters: string[], - $refs: SwaggerParser.$Refs, -) { - return parameters.reduce( - (acc, parameter) => { - const dereferencedParameter = (parameter as OpenAPIV3.ReferenceObject) - .$ref - ? ($refs.get( - (parameter as OpenAPIV3.ReferenceObject).$ref, - ) as OpenAPIV3.ParameterObject) - : (parameter as OpenAPIV3.ParameterObject); - - if (mutedParameters.includes(dereferencedParameter.name)) { - return acc; - } - - return acc.concat(parameter); - }, - [] as (OpenAPIV3.ReferenceObject | OpenAPIV3.ParameterObject)[], - ); -} +}; async function getOpenAPI( - { API }: { API: OpenAPIV3.Document }, + { API }: { API: OpenAPIV3_1.Document }, { authenticated = false, mutedMethods = ['options'], mutedParameters = [], + mutedTags = [], }: { authenticated?: boolean; mutedMethods?: string[]; mutedParameters?: string[]; + mutedTags?: string[]; }, -): Promise> { +): Promise> { const operations = getOpenAPIOperations(API); const $refs = await SwaggerParser.resolve(API); @@ -91,11 +69,19 @@ async function getOpenAPI( if (mutedMethods.includes(operation.method)) { return paths; } + if (operation.tags?.every((tag) => mutedTags.includes(tag))) { + return paths; + } + if (operation.tags) { + operation.tags.forEach((tag) => { + tagIsPresent[tag] = !mutedTags.includes(tag); + }); + } paths[operation.path] = { ...paths[operation.path], [operation.method]: { - ...(API.paths[operation.path]?.[operation.method] || {}), + ...(API?.paths?.[operation.path]?.[operation.method] || {}), ...(operation.parameters && operation.parameters.length && { parameters: removeMutedParameters( @@ -122,3 +108,27 @@ async function getOpenAPI( body: CLEANED_API, }; } + +function removeMutedParameters( + parameters: Array, + mutedParameters: string[], + $refs: SwaggerParser.$Refs, +) { + return parameters.reduce( + (acc, parameter) => { + const dereferencedParameter = (parameter as OpenAPIV3_1.ReferenceObject) + .$ref + ? ($refs.get( + (parameter as OpenAPIV3_1.ReferenceObject).$ref, + ) as OpenAPIV3_1.ParameterObject) + : (parameter as OpenAPIV3_1.ParameterObject); + + if (mutedParameters.includes(dereferencedParameter.name)) { + return acc; + } + + return acc.concat(parameter); + }, + [] as (OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.ParameterObject)[], + ); +} diff --git a/packages/whook/src/index.test.ts b/packages/whook/src/index.test.ts index 10880c60..c9f4fb0c 100644 --- a/packages/whook/src/index.test.ts +++ b/packages/whook/src/index.test.ts @@ -13,7 +13,7 @@ describe('runServer', () => { const HOST = 'localhost'; const BASE_PATH = '/v1'; const API = { - openapi: '3.0.2', + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample OpenAPI', diff --git a/packages/whook/src/libs/openapi.test.ts b/packages/whook/src/libs/openapi.test.ts index fb174884..28be341b 100644 --- a/packages/whook/src/libs/openapi.test.ts +++ b/packages/whook/src/libs/openapi.test.ts @@ -1,10 +1,10 @@ import { describe, it, expect } from '@jest/globals'; import { collectRefs, cleanupOpenAPI } from './openapi.js'; import type { JsonObject, JsonValue } from 'type-fest'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; -const sampleAPI: OpenAPIV3.Document = { - openapi: '3.0.2', +const sampleAPI: OpenAPIV3_1.Document = { + openapi: '3.1.0', info: { version: '8.2.0', title: '@whook/example', @@ -142,140 +142,140 @@ describe('collectRefs', () => { describe('cleanupOpenAPI', () => { it('should remove unused refs in an OpenAPI document', () => { expect(cleanupOpenAPI(sampleAPI)).toMatchInlineSnapshot(` - { - "components": { - "parameters": { - "duration": { - "description": "Duration in milliseconds", - "in": "query", - "name": "duration", - "required": true, - "schema": { - "type": "number", - }, - }, +{ + "components": { + "parameters": { + "duration": { + "description": "Duration in milliseconds", + "in": "query", + "name": "duration", + "required": true, + "schema": { + "type": "number", + }, + }, + }, + "schemas": { + "AString": { + "type": "string", + }, + "Echo": { + "additionalProperties": false, + "properties": { + "echo": { + "$ref": "#/components/schemas/AString", }, - "schemas": { - "AString": { - "type": "string", - }, - "Echo": { - "additionalProperties": false, - "properties": { - "echo": { - "$ref": "#/components/schemas/AString", - }, - }, - "required": [ - "echo", - ], - "type": "object", - }, - "Recursive": { - "additionalProperties": false, - "properties": { - "child": { + }, + "required": [ + "echo", + ], + "type": "object", + }, + "Recursive": { + "additionalProperties": false, + "properties": { + "child": { + "$ref": "#/components/schemas/Recursive", + }, + }, + "required": [], + "type": "object", + }, + }, + "securitySchemes": { + "bearerAuth": { + "description": "Bearer authentication with a user API token", + "scheme": "bearer", + "type": "http", + }, + "fakeAuth": { + "description": "A fake authentication for development purpose.", + "in": "header", + "name": "Authorization", + "type": "apiKey", + }, + }, + }, + "info": { + "description": "A basic Whook server", + "title": "@whook/example", + "version": "8.2.0", + }, + "openapi": "3.1.0", + "paths": { + "/delay": { + "get": { + "operationId": "getDelay", + "parameters": [ + { + "$ref": "#/components/parameters/duration", + }, + ], + "responses": { + "204": { + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/Recursive", }, }, - "required": [], - "type": "object", }, + "description": "Delay expired", }, - "securitySchemes": { - "bearerAuth": { - "description": "Bearer authentication with a user API token", - "scheme": "bearer", - "type": "http", - }, - "fakeAuth": { - "description": "A fake authentication for development purpose.", - "in": "header", - "name": "Authorization", - "type": "apiKey", - }, - }, - }, - "info": { - "description": "A basic Whook server", - "title": "@whook/example", - "version": "8.2.0", }, - "openapi": "3.0.2", - "paths": { - "/delay": { - "get": { - "operationId": "getDelay", - "parameters": [ - { - "$ref": "#/components/parameters/duration", - }, - ], - "responses": { - "204": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Recursive", - }, - }, - }, - "description": "Delay expired", - }, + "summary": "Answer after a given delay.", + "tags": [ + "example", + ], + }, + }, + "/echo": { + "put": { + "operationId": "putEcho", + "requestBody": { + "content": { + "application/json": { + "example": { + "echo": "Repeat this!", + }, + "schema": { + "$ref": "#/components/schemas/Echo", }, - "summary": "Answer after a given delay.", - "tags": [ - "example", - ], }, }, - "/echo": { - "put": { - "operationId": "putEcho", - "requestBody": { - "content": { - "application/json": { - "example": { - "echo": "Repeat this!", - }, - "schema": { - "$ref": "#/components/schemas/Echo", - }, - }, - }, - "description": "The input sentence", - "required": true, - }, - "responses": { - "200": { - "content": { - "application/json": { - "schema": { - "$ref": "#/components/schemas/Echo", - }, - }, - }, - "description": "The actual echo", + "description": "The input sentence", + "required": true, + }, + "responses": { + "200": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Echo", }, }, - "summary": "Echoes what it takes.", - "tags": [ - "example", - ], }, + "description": "The actual echo", }, }, - "servers": [ - { - "url": "http://localhost:8001/v8", - }, - ], + "summary": "Echoes what it takes.", "tags": [ - { - "name": "system", - }, + "example", ], - } - `); + }, + }, + }, + "servers": [ + { + "url": "http://localhost:8001/v8", + }, + ], + "tags": [ + { + "name": "system", + }, + ], +} +`); }); }); diff --git a/packages/whook/src/libs/openapi.ts b/packages/whook/src/libs/openapi.ts index 40146bdc..b2003485 100644 --- a/packages/whook/src/libs/openapi.ts +++ b/packages/whook/src/libs/openapi.ts @@ -1,4 +1,4 @@ -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { WhookAPISchemaDefinition, WhookAPIParameterDefinition, @@ -9,7 +9,7 @@ import type { } from '../services/API_DEFINITIONS.js'; import type { JsonObject, JsonValue } from 'type-fest'; -type ComponentType = keyof NonNullable; +type ComponentType = keyof NonNullable; export const COMPONENTS_TYPES: ComponentType[] = [ 'schemas', @@ -20,7 +20,9 @@ export const COMPONENTS_TYPES: ComponentType[] = [ 'headers', ]; -export function cleanupOpenAPI(api: OpenAPIV3.Document): OpenAPIV3.Document { +export function cleanupOpenAPI( + api: OpenAPIV3_1.Document, +): OpenAPIV3_1.Document { const seenRefs = [ ...new Set( collectRefs( @@ -107,12 +109,12 @@ export function refersTo( | WhookAPISchemaDefinition | WhookAPIParameterDefinition | WhookAPIExampleDefinition< - T extends JsonValue | OpenAPIV3.ReferenceObject ? T : never + T extends JsonValue | OpenAPIV3_1.ReferenceObject ? T : never > | WhookAPIHeaderDefinition | WhookAPIResponseDefinition | WhookAPIRequestBodyDefinition, -): OpenAPIV3.ReferenceObject { +): OpenAPIV3_1.ReferenceObject { return { $ref: `#/components/${ (resource as WhookAPISchemaDefinition).schema diff --git a/packages/whook/src/services/API_DEFINITIONS.ts b/packages/whook/src/services/API_DEFINITIONS.ts index 9b89e49e..69a365d2 100644 --- a/packages/whook/src/services/API_DEFINITIONS.ts +++ b/packages/whook/src/services/API_DEFINITIONS.ts @@ -3,7 +3,7 @@ import { readdir as _readDir } from 'node:fs/promises'; import { extname, join as pathJoin } from 'node:path'; import { noop } from '../libs/utils.js'; import type { ImporterService, LogService } from 'common-services'; -import type { OpenAPIV3 } from 'openapi-types'; +import type { OpenAPIV3_1 } from 'openapi-types'; import type { JsonValue } from 'type-fest'; import { WHOOK_DEFAULT_PLUGINS, @@ -46,8 +46,8 @@ export type WhookAPIDefinitionsDependencies = WhookAPIDefinitionsConfig & { }; export type WhookAPIDefinitions = { - paths: OpenAPIV3.PathsObject; - components: OpenAPIV3.ComponentsObject; + paths: OpenAPIV3_1.PathsObject; + components: OpenAPIV3_1.ComponentsObject; }; export interface WhookAPIOperationConfig { @@ -58,13 +58,13 @@ export interface WhookAPIOperationConfig { export interface WhookAPIOperationAddition< T extends Record = Record, > { - operationId: OpenAPIV3.OperationObject['operationId']; + operationId: OpenAPIV3_1.OperationObject['operationId']; 'x-whook'?: WhookAPIOperationConfig & T; } export type WhookAPIOperation< T extends Record = Record, -> = OpenAPIV3.OperationObject & WhookAPIOperationAddition; +> = OpenAPIV3_1.OperationObject & WhookAPIOperationAddition; export interface WhookBaseAPIHandlerDefinition< T extends Record = Record, @@ -90,25 +90,25 @@ export interface WhookAPIHandlerDefinition< > extends WhookBaseAPIHandlerDefinition {} export interface WhookAPISchemaDefinition< - T extends JsonValue | OpenAPIV3.ReferenceObject | void | unknown = unknown, + T extends JsonValue | OpenAPIV3_1.ReferenceObject | void | unknown = unknown, > { name: string; - schema: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject; + schema: OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.SchemaObject; example?: T; examples?: Record; } export interface WhookAPIParameterDefinition< - T extends JsonValue | OpenAPIV3.ReferenceObject | void | unknown = unknown, + T extends JsonValue | OpenAPIV3_1.ReferenceObject | void | unknown = unknown, > { name: string; - parameter: OpenAPIV3.ParameterObject; + parameter: OpenAPIV3_1.ParameterObject; example?: T; examples?: Record; } export interface WhookAPIExampleDefinition< - T extends JsonValue | OpenAPIV3.ReferenceObject, + T extends JsonValue | OpenAPIV3_1.ReferenceObject, > { name: string; value: T; @@ -116,17 +116,17 @@ export interface WhookAPIExampleDefinition< export interface WhookAPIHeaderDefinition { name: string; - header: OpenAPIV3.HeaderObject | OpenAPIV3.ReferenceObject; + header: OpenAPIV3_1.HeaderObject | OpenAPIV3_1.ReferenceObject; } export interface WhookAPIResponseDefinition { name: string; - response: OpenAPIV3.ResponseObject | OpenAPIV3.ReferenceObject; + response: OpenAPIV3_1.ResponseObject | OpenAPIV3_1.ReferenceObject; } export interface WhookAPIRequestBodyDefinition { name: string; - requestBody: OpenAPIV3.RequestBodyObject | OpenAPIV3.ReferenceObject; + requestBody: OpenAPIV3_1.RequestBodyObject | OpenAPIV3_1.ReferenceObject; } export interface WhookAPIDefinitionFilter< @@ -243,7 +243,7 @@ async function initAPIDefinitions({ ); const API_DEFINITIONS = { - paths: handlersModules.reduce( + paths: handlersModules.reduce( ( paths, { file, module }: { file: string; module: WhookAPIHandlerModule }, @@ -297,7 +297,7 @@ async function initAPIDefinitions({ ), components: { schemas: handlersModules.reduce<{ - [key: string]: OpenAPIV3.ReferenceObject | OpenAPIV3.SchemaObject; + [key: string]: OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.SchemaObject; }>( (schemas, { module }) => ({ ...schemas, @@ -319,7 +319,9 @@ async function initAPIDefinitions({ {}, ), parameters: handlersModules.reduce<{ - [key: string]: OpenAPIV3.ReferenceObject | OpenAPIV3.ParameterObject; + [key: string]: + | OpenAPIV3_1.ReferenceObject + | OpenAPIV3_1.ParameterObject; }>( (parameters, { module }) => ({ ...parameters, @@ -346,7 +348,7 @@ async function initAPIDefinitions({ {}, ), headers: handlersModules.reduce<{ - [key: string]: OpenAPIV3.ReferenceObject | OpenAPIV3.HeaderObject; + [key: string]: OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.HeaderObject; }>( (headers, { module }) => ({ ...headers, @@ -371,7 +373,9 @@ async function initAPIDefinitions({ {}, ), requestBodies: handlersModules.reduce<{ - [key: string]: OpenAPIV3.ReferenceObject | OpenAPIV3.RequestBodyObject; + [key: string]: + | OpenAPIV3_1.ReferenceObject + | OpenAPIV3_1.RequestBodyObject; }>( (requestBodies, { module }) => ({ ...requestBodies, @@ -396,7 +400,7 @@ async function initAPIDefinitions({ {}, ), responses: handlersModules.reduce<{ - [key: string]: OpenAPIV3.ReferenceObject | OpenAPIV3.ResponseObject; + [key: string]: OpenAPIV3_1.ReferenceObject | OpenAPIV3_1.ResponseObject; }>( (responses, { module }) => ({ ...responses, diff --git a/packages/whook/src/services/_autoload.test.ts b/packages/whook/src/services/_autoload.test.ts index d67f4daa..0e3f324a 100644 --- a/packages/whook/src/services/_autoload.test.ts +++ b/packages/whook/src/services/_autoload.test.ts @@ -280,7 +280,7 @@ describe('$autoload', () => { it('for handlers hash', async () => { $injector.mockResolvedValueOnce({ API: { - openapi: '3.0.2', + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample OpenAPI', @@ -372,7 +372,7 @@ describe('$autoload', () => { it('for wrappers hash', async () => { $injector.mockResolvedValueOnce({ API: { - openapi: '3.0.2', + openapi: '3.1.0', info: { version: '1.0.0', title: 'Sample OpenAPI',