From 4860c2ebd9354c7c6bec12bcaeb1043ecfe674a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 6 Oct 2024 10:39:28 +0200 Subject: [PATCH 1/3] Export all componentSchemas --- src/compileComponentSchemas.ts | 40 +++++++++ src/compiler.ts | 13 +-- .../compileValueSchema.test.ts.snap | 84 +++++++++++++++++++ src/tests/__snapshots__/compiler.test.ts.snap | 14 ++++ src/tests/compiler.test.ts | 2 - tests/gitbook.test.ts | 25 +++++- 6 files changed, 163 insertions(+), 15 deletions(-) create mode 100644 src/compileComponentSchemas.ts diff --git a/src/compileComponentSchemas.ts b/src/compileComponentSchemas.ts new file mode 100644 index 0000000..c50713a --- /dev/null +++ b/src/compileComponentSchemas.ts @@ -0,0 +1,40 @@ +import { builders } from "ast-types"; +import { Compiler } from "./compiler"; +import { compileValueSchema } from "./compileValueSchema"; +import { OpenAPIValueSchema } from "./types"; +import { annotateWithJSDocComment } from "./comments"; + +const COMMENT = ` +Map of all components defined in the spec to their validation functions. +`; + +/** + * Compile all component schemas to be expoerted as `components['Name']`. + */ +export function compileComponentSchemas(compiler: Compiler, schemas: { + [key: string]: OpenAPIValueSchema; +}) { + const properties = Object.entries(schemas).map(([name]) => { + return builders.property( + 'init', + builders.literal(name), + compileValueSchema(compiler, schemas[name]), + ); + }); + + return [ + annotateWithJSDocComment( + builders.exportNamedDeclaration( + builders.variableDeclaration('const', [ + builders.variableDeclarator( + builders.identifier('componentSchemas'), + builders.objectExpression( + properties, + ), + ) + ]), + ), + COMMENT, + ), + ]; +} \ No newline at end of file diff --git a/src/compiler.ts b/src/compiler.ts index 9227fc2..d5c2ccd 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -5,6 +5,7 @@ import { OpenAPIRef, OpenAPISpec } from './types'; import { compileValueSchema } from './compileValueSchema'; import { hash } from './hash'; import { compileValidateRequest } from './compileValidateRequest'; +import { compileComponentSchemas } from './compileComponentSchemas'; /** * Compiler for OpenAPI specs. @@ -117,23 +118,13 @@ export class Compiler { }); } - /** - * Build the AST from the entire spec. - */ - public indexAllComponents() { - // Index all the schema components. - const schemas = this.input.components?.schemas ?? {}; - Object.values(schemas).forEach((schema) => { - compileValueSchema(this, schema); - }); - } - /** * Return the AST for the program. */ public ast() { return builders.program([ ...compileValidateRequest(this, this.input), + ...compileComponentSchemas(this, this.input.components?.schemas ?? {}), ...this.globalDeclarations, ]); } diff --git a/src/tests/__snapshots__/compileValueSchema.test.ts.snap b/src/tests/__snapshots__/compileValueSchema.test.ts.snap index 584bfdf..de7d400 100644 --- a/src/tests/__snapshots__/compileValueSchema.test.ts.snap +++ b/src/tests/__snapshots__/compileValueSchema.test.ts.snap @@ -10,6 +10,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -49,6 +53,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -91,6 +99,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -133,6 +145,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -175,6 +191,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -217,6 +237,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -256,6 +280,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -298,6 +326,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -334,6 +366,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -374,6 +410,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -414,6 +454,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -492,6 +536,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -552,6 +600,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -589,6 +641,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -649,6 +705,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -725,6 +785,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -768,6 +832,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -826,6 +894,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -883,6 +955,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -939,6 +1015,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -1002,6 +1082,10 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = {}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ diff --git a/src/tests/__snapshots__/compiler.test.ts.snap b/src/tests/__snapshots__/compiler.test.ts.snap index f0ec8e4..76b91ca 100644 --- a/src/tests/__snapshots__/compiler.test.ts.snap +++ b/src/tests/__snapshots__/compiler.test.ts.snap @@ -10,6 +10,13 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = { + 'A': obj0, + 'B': obj1 +}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ @@ -86,6 +93,13 @@ Validate a request against the OpenAPI spec export function validateRequest(request, context) { return new RequestError(404, 'no operation match path'); } +/** +Map of all components defined in the spec to their validation functions. +*/ +export const componentSchemas = { + 'A': obj0, + 'B': obj1 +}; export class RequestError extends Error { /** @param {number} code HTTP code for the error @param {string} message The error message*/ diff --git a/src/tests/compiler.test.ts b/src/tests/compiler.test.ts index e274597..3ec0b78 100644 --- a/src/tests/compiler.test.ts +++ b/src/tests/compiler.test.ts @@ -22,7 +22,6 @@ test('components ref', () => { }, }, }); - compiler.indexAllComponents(); expect(compiler.compile()).toMatchSnapshot(); }); @@ -50,6 +49,5 @@ test('recursive refs', () => { }, }, }); - compiler.indexAllComponents(); expect(compiler.compile()).toMatchSnapshot(); }); diff --git a/tests/gitbook.test.ts b/tests/gitbook.test.ts index 6b5cecf..a875a5c 100644 --- a/tests/gitbook.test.ts +++ b/tests/gitbook.test.ts @@ -1,5 +1,5 @@ -import { expect, test } from 'bun:test'; -import { validateRequest, ValidationError } from './gitbook.validate'; +import { describe, expect, test } from 'bun:test'; +import { validateRequest, ValidationError, componentSchemas } from './gitbook.validate'; test('POST orgs/appleId/custom-fields', () => { const result = validateRequest({ @@ -287,3 +287,24 @@ test('/orgs/xxx/synced-blocks?ids[]=foo allow string parameter as array too', () }); expect(result.query).toEqual({ ids: ['foo'] }); }); + +describe('componentSchemas', () => { + test('should export a function to validate a component', () => { + const validate = componentSchemas['ApiInformation']; + expect(validate).toBeInstanceOf(Function); + expect(validate([], { + version: '1.0.0', + build: '123', + })).toEqual({ + version: '1.0.0', + build: '123', + }); + + const error = validate([], { + version: '1.0.0', + // Missing property + }) + expect(error instanceof ValidationError ? error.path : null).toEqual([]); + expect(error instanceof ValidationError ? error.message : null).toEqual('expected "build" to be defined'); + }); +}); From 3dc7ea3edfae740fbb44bd5305d73d63a55933cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 6 Oct 2024 10:39:43 +0200 Subject: [PATCH 2/3] Format --- src/compileComponentSchemas.ts | 27 ++++++++++++++------------- tests/gitbook.test.ts | 16 ++++++++++------ 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/compileComponentSchemas.ts b/src/compileComponentSchemas.ts index c50713a..1762e1d 100644 --- a/src/compileComponentSchemas.ts +++ b/src/compileComponentSchemas.ts @@ -1,8 +1,8 @@ -import { builders } from "ast-types"; -import { Compiler } from "./compiler"; -import { compileValueSchema } from "./compileValueSchema"; -import { OpenAPIValueSchema } from "./types"; -import { annotateWithJSDocComment } from "./comments"; +import { builders } from 'ast-types'; +import { Compiler } from './compiler'; +import { compileValueSchema } from './compileValueSchema'; +import { OpenAPIValueSchema } from './types'; +import { annotateWithJSDocComment } from './comments'; const COMMENT = ` Map of all components defined in the spec to their validation functions. @@ -11,9 +11,12 @@ Map of all components defined in the spec to their validation functions. /** * Compile all component schemas to be expoerted as `components['Name']`. */ -export function compileComponentSchemas(compiler: Compiler, schemas: { - [key: string]: OpenAPIValueSchema; -}) { +export function compileComponentSchemas( + compiler: Compiler, + schemas: { + [key: string]: OpenAPIValueSchema; + }, +) { const properties = Object.entries(schemas).map(([name]) => { return builders.property( 'init', @@ -28,13 +31,11 @@ export function compileComponentSchemas(compiler: Compiler, schemas: { builders.variableDeclaration('const', [ builders.variableDeclarator( builders.identifier('componentSchemas'), - builders.objectExpression( - properties, - ), - ) + builders.objectExpression(properties), + ), ]), ), COMMENT, ), ]; -} \ No newline at end of file +} diff --git a/tests/gitbook.test.ts b/tests/gitbook.test.ts index a875a5c..76b6f4c 100644 --- a/tests/gitbook.test.ts +++ b/tests/gitbook.test.ts @@ -292,10 +292,12 @@ describe('componentSchemas', () => { test('should export a function to validate a component', () => { const validate = componentSchemas['ApiInformation']; expect(validate).toBeInstanceOf(Function); - expect(validate([], { - version: '1.0.0', - build: '123', - })).toEqual({ + expect( + validate([], { + version: '1.0.0', + build: '123', + }), + ).toEqual({ version: '1.0.0', build: '123', }); @@ -303,8 +305,10 @@ describe('componentSchemas', () => { const error = validate([], { version: '1.0.0', // Missing property - }) + }); expect(error instanceof ValidationError ? error.path : null).toEqual([]); - expect(error instanceof ValidationError ? error.message : null).toEqual('expected "build" to be defined'); + expect(error instanceof ValidationError ? error.message : null).toEqual( + 'expected "build" to be defined', + ); }); }); From 619c233297a17a1cb155683c329f9f6f1e02ff82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samy=20Pess=C3=A9?= Date: Sun, 6 Oct 2024 10:44:21 +0200 Subject: [PATCH 3/3] Improve type --- src/compileComponentSchemas.ts | 1 + .../compileValueSchema.test.ts.snap | 21 +++++++++++++++++++ src/tests/__snapshots__/compiler.test.ts.snap | 2 ++ 3 files changed, 24 insertions(+) diff --git a/src/compileComponentSchemas.ts b/src/compileComponentSchemas.ts index 1762e1d..2d57f69 100644 --- a/src/compileComponentSchemas.ts +++ b/src/compileComponentSchemas.ts @@ -6,6 +6,7 @@ import { annotateWithJSDocComment } from './comments'; const COMMENT = ` Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} `; /** diff --git a/src/tests/__snapshots__/compileValueSchema.test.ts.snap b/src/tests/__snapshots__/compileValueSchema.test.ts.snap index de7d400..5d72bdb 100644 --- a/src/tests/__snapshots__/compileValueSchema.test.ts.snap +++ b/src/tests/__snapshots__/compileValueSchema.test.ts.snap @@ -12,6 +12,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -55,6 +56,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -101,6 +103,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -147,6 +150,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -193,6 +197,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -239,6 +244,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -282,6 +288,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -328,6 +335,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -368,6 +376,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -412,6 +421,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -456,6 +466,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -538,6 +549,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -602,6 +614,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -643,6 +656,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -707,6 +721,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -787,6 +802,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -834,6 +850,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -896,6 +913,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -957,6 +975,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -1017,6 +1036,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { @@ -1084,6 +1104,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = {}; export class RequestError extends Error { diff --git a/src/tests/__snapshots__/compiler.test.ts.snap b/src/tests/__snapshots__/compiler.test.ts.snap index 76b91ca..b30687f 100644 --- a/src/tests/__snapshots__/compiler.test.ts.snap +++ b/src/tests/__snapshots__/compiler.test.ts.snap @@ -12,6 +12,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = { 'A': obj0, @@ -95,6 +96,7 @@ export function validateRequest(request, context) { } /** Map of all components defined in the spec to their validation functions. +{Object.(path: string[], value: T, context: any) => (T | ValidationError)>} */ export const componentSchemas = { 'A': obj0,