diff --git a/.prettierignore b/.prettierignore index dd536776..d27bbf0b 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,4 +1,3 @@ coverage/ dist/ flake.lock -packages/openapi-generator/test/ diff --git a/packages/openapi-generator/test/codec.test.ts b/packages/openapi-generator/test/codec.test.ts index 94ea09e2..6ad2e224 100644 --- a/packages/openapi-generator/test/codec.test.ts +++ b/packages/openapi-generator/test/codec.test.ts @@ -220,7 +220,10 @@ export const FOO = t.union([t.string, t.number]); testCase('union type is parsed', UNION, { FOO: { type: 'union', - schemas: [{ type: 'string', primitive: true }, { type: 'number', primitive: true }], + schemas: [ + { type: 'string', primitive: true }, + { type: 'number', primitive: true }, + ], }, }); @@ -235,7 +238,10 @@ export const FOO = t.union([...common, t.number]); testCase('union type with spread is parsed', UNION_SPREAD, { FOO: { type: 'union', - schemas: [{ type: 'string', primitive: true }, { type: 'number', primitive: true }], + schemas: [ + { type: 'string', primitive: true }, + { type: 'number', primitive: true }, + ], }, common: { type: 'tuple', @@ -252,7 +258,10 @@ export const FOO = t.union([...[t.string], t.number]); testCase('union type with inline spread is parsed', UNION_INLINE_SPREAD, { FOO: { type: 'union', - schemas: [{ type: 'string', primitive: true }, { type: 'number', primitive: true }], + schemas: [ + { type: 'string', primitive: true }, + { type: 'number', primitive: true }, + ], }, }); @@ -285,7 +294,11 @@ export const FOO = t.record(t.string, t.number); `; testCase('record type is parsed', RECORD, { - FOO: { type: 'record', domain: {type: 'string', primitive: true}, codomain: { type: 'number', primitive: true } }, + FOO: { + type: 'record', + domain: { type: 'string', primitive: true }, + codomain: { type: 'number', primitive: true }, + }, }); const ENUM = ` diff --git a/packages/openapi-generator/test/externalModule.test.ts b/packages/openapi-generator/test/externalModule.test.ts index b951377d..86805b92 100644 --- a/packages/openapi-generator/test/externalModule.test.ts +++ b/packages/openapi-generator/test/externalModule.test.ts @@ -28,7 +28,7 @@ async function testCase( for (const path of Object.keys(expected)) { const resolvedPath = p.resolve(path); const sourceFile = project.get(resolvedPath); - + if (sourceFile === undefined) { throw new Error(`Source file ${path} not found`); } diff --git a/packages/openapi-generator/test/externalModuleApiSpec.test.ts b/packages/openapi-generator/test/externalModuleApiSpec.test.ts index eb623f7c..08837ced 100644 --- a/packages/openapi-generator/test/externalModuleApiSpec.test.ts +++ b/packages/openapi-generator/test/externalModuleApiSpec.test.ts @@ -103,7 +103,7 @@ async function testCase( const codecE = parseCodecInitializer(project, newSourceFile, init); if (E.isLeft(codecE)) { errors.push( - `Could not parse codec '${ref.name}' in '${ref.location}': ${codecE.left}`, + `Could not parse codec '${ref.name}' in '${ref.location}': ${codecE.left}`, ); break; } @@ -117,7 +117,7 @@ async function testCase( const openapi = convertRoutesToOpenAPI( { title: name, - version: "1.0.0", + version: '1.0.0', description, }, [], @@ -125,7 +125,7 @@ async function testCase( components, ); - assert.deepStrictEqual(errors, expectedErrors); + assert.deepStrictEqual(errors, expectedErrors); assert.deepStrictEqual(openapi, expected); }); } @@ -275,91 +275,96 @@ testCase( 'simple api spec with exported union type', 'test/sample-types/apiSpecWithUnion.ts', { - openapi: "3.0.3", + openapi: '3.0.3', info: { - title: "simple api spec with exported union type", - version: "1.0.0", - description: "simple api spec with exported union type" + title: 'simple api spec with exported union type', + version: '1.0.0', + description: 'simple api spec with exported union type', }, paths: { - "/test": { + '/test': { get: { parameters: [], responses: { 200: { - description: "OK", + description: 'OK', content: { 'application/json': { schema: { - $ref: "#/components/schemas/SampleUnion" - } - } - } - } - } - } - } + $ref: '#/components/schemas/SampleUnion', + }, + }, + }, + }, + }, + }, + }, }, components: { schemas: { SampleUnion: { - title: "SampleUnion", + title: 'SampleUnion', oneOf: [ { - type: "string" + type: 'string', }, { - type: "number" - } - ] - } - } - } + type: 'number', + }, + ], + }, + }, + }, }, - [] -) + [], +); -testCase("simple api spec with custom codec", "test/sample-types/apiSpecWithCustomCodec.ts", { - openapi: "3.0.3", - info: { - title: "simple api spec with custom codec", - version: "1.0.0", - description: "simple api spec with custom codec" - }, - paths: { - "/test": { - get: { - parameters: [], - responses: { - 200: { - description: "OK", - content: { - 'application/json': { - schema: { - type: 'string', - description: 'Sample custom codec', - example: 'sample', - format: 'sample' - } - } - } +testCase( + 'simple api spec with custom codec', + 'test/sample-types/apiSpecWithCustomCodec.ts', + { + openapi: '3.0.3', + info: { + title: 'simple api spec with custom codec', + version: '1.0.0', + description: 'simple api spec with custom codec', + }, + paths: { + '/test': { + get: { + parameters: [], + responses: { + 200: { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'string', + description: 'Sample custom codec', + example: 'sample', + format: 'sample', + }, + }, + }, + }, + 201: { + description: 'Created', + content: { + 'application/json': { + schema: { + type: 'number', + description: 'Another sample codec', + }, + }, + }, + }, }, - 201: { - description: 'Created', - content: { - 'application/json': { - schema: { - type: 'number', - description: 'Another sample codec', - } - } - } - } - } - } - } + }, + }, + }, + components: { + schemas: {}, + }, }, - components: { - schemas: {} - } -}, []); + [], +); diff --git a/packages/openapi-generator/test/openapi/base.test.ts b/packages/openapi-generator/test/openapi/base.test.ts index 6617df93..db7409c1 100644 --- a/packages/openapi-generator/test/openapi/base.test.ts +++ b/packages/openapi-generator/test/openapi/base.test.ts @@ -1,5 +1,4 @@ -import { testCase } from "./testHarness"; - +import { testCase } from './testHarness'; const SIMPLE = ` import * as t from 'io-ts'; @@ -534,9 +533,6 @@ testCase('optional parameter', OPTIONAL_PARAM, { }, }); - - - const ROUTE_WITH_ARRAY_QUERY_PARAM = ` import * as t from 'io-ts'; import * as h from '@api-ts/io-ts-http'; @@ -568,64 +564,63 @@ export const route = h.httpRoute({ }); `; -testCase('route with optional array query parameter and documentation', ROUTE_WITH_ARRAY_QUERY_PARAM, { - openapi: '3.0.3', - info: { - title: 'Test', - version: '1.0.0' - }, - paths: { - '/foo': { - get: { - summary: 'A simple route with type descriptions for references', - operationId: 'api.v1.test', - tags: [ - 'Test Routes' - ], - parameters: [ - { - description: 'This is a foo description.', - in: 'query', - name: 'foo', - schema: { - items: { - description: 'This is a foo description.', - example: 'abc', - type: 'string', - pattern: '^[a-z]+$' +testCase( + 'route with optional array query parameter and documentation', + ROUTE_WITH_ARRAY_QUERY_PARAM, + { + openapi: '3.0.3', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + get: { + summary: 'A simple route with type descriptions for references', + operationId: 'api.v1.test', + tags: ['Test Routes'], + parameters: [ + { + description: 'This is a foo description.', + in: 'query', + name: 'foo', + schema: { + items: { + description: 'This is a foo description.', + example: 'abc', + type: 'string', + pattern: '^[a-z]+$', + }, + type: 'array', }, - type: 'array' - } - } - ], - responses: { - '200': { - description: 'OK', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - test: { - type: 'string' - } + }, + ], + responses: { + '200': { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + test: { + type: 'string', + }, + }, + required: ['test'], }, - required: [ - 'test' - ] - } - } - } - } - } - } - } + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: {}, + }, }, - components: { - schemas: {} - } -}); - +); const ROUTE_WITH_ARRAY_UNION_NULL_UNDEFINED_QUERY_PARAM = ` import * as t from 'io-ts'; @@ -658,63 +653,63 @@ export const route = h.httpRoute({ }); `; -testCase('route with array union of null and undefined', ROUTE_WITH_ARRAY_UNION_NULL_UNDEFINED_QUERY_PARAM, { - openapi: '3.0.3', - info: { - title: 'Test', - version: '1.0.0' - }, - paths: { - '/foo': { - get: { - summary: 'A simple route with type descriptions for references', - operationId: 'api.v1.test', - tags: [ - 'Test Routes' - ], - parameters: [ - { - description: 'This is a foo description.', - in: 'query', - name: 'ipRestrict', - schema: { - items: { - description: 'This is a foo description.', - example: 'abc', - type: 'string', - pattern: '^[a-z]+$' +testCase( + 'route with array union of null and undefined', + ROUTE_WITH_ARRAY_UNION_NULL_UNDEFINED_QUERY_PARAM, + { + openapi: '3.0.3', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + get: { + summary: 'A simple route with type descriptions for references', + operationId: 'api.v1.test', + tags: ['Test Routes'], + parameters: [ + { + description: 'This is a foo description.', + in: 'query', + name: 'ipRestrict', + schema: { + items: { + description: 'This is a foo description.', + example: 'abc', + type: 'string', + pattern: '^[a-z]+$', + }, + type: 'array', }, - type: 'array' - } - } - ], - responses: { - '200': { - description: 'OK', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - test: { - type: 'string' - } + }, + ], + responses: { + '200': { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + test: { + type: 'string', + }, + }, + required: ['test'], }, - required: [ - 'test' - ] - } - } - } - } - } - } - } + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: {}, + }, }, - components: { - schemas: {} - } -}); +); const MULTIPLE_ROUTES = ` import * as t from 'io-ts'; diff --git a/packages/openapi-generator/test/openapi/comments.test.ts b/packages/openapi-generator/test/openapi/comments.test.ts index fc8930e6..10bd3342 100644 --- a/packages/openapi-generator/test/openapi/comments.test.ts +++ b/packages/openapi-generator/test/openapi/comments.test.ts @@ -1,4 +1,4 @@ -import { testCase } from "./testHarness"; +import { testCase } from './testHarness'; const ROUTE_WITH_TYPE_DESCRIPTIONS = ` import * as t from 'io-ts'; @@ -56,9 +56,9 @@ testCase('route with type descriptions', ROUTE_WITH_TYPE_DESCRIPTIONS, { name: 'bar', required: true, schema: { - type: 'string' - } - } + type: 'string', + }, + }, ], requestBody: { content: { @@ -67,34 +67,28 @@ testCase('route with type descriptions', ROUTE_WITH_TYPE_DESCRIPTIONS, { properties: { bar: { description: 'bar description', - type: 'number' + type: 'number', }, child: { properties: { child: { description: 'child description', - type: 'string' - } + type: 'string', + }, }, - required: [ - 'child' - ], - type: 'object' + required: ['child'], + type: 'object', }, foo: { description: 'foo description', - type: 'string' - } + type: 'string', + }, }, - required: [ - 'foo', - 'bar', - 'child' - ], - type: 'object' - } - } - } + required: ['foo', 'bar', 'child'], + type: 'object', + }, + }, + }, }, responses: { 200: { @@ -122,7 +116,6 @@ testCase('route with type descriptions', ROUTE_WITH_TYPE_DESCRIPTIONS, { }, }); - const ROUTE_WITH_TYPE_DESCRIPTIONS_OPTIONAL = ` import * as t from 'io-ts'; import * as h from '@api-ts/io-ts-http'; @@ -160,74 +153,75 @@ export const route = h.httpRoute({ }); `; - -testCase('route with type descriptions with optional fields', ROUTE_WITH_TYPE_DESCRIPTIONS_OPTIONAL, { - openapi: '3.0.3', - info: { - title: 'Test', - version: '1.0.0', - }, - paths: { - '/foo': { - get: { - summary: 'A simple route with type descriptions', - operationId: 'api.v1.test', - tags: ['Test Routes'], - parameters: [ - { - description: 'bar param', - in: 'query', - name: 'bar', - required: true, - schema: { - type: 'string' - } - } - ], - requestBody: { - content: { - 'application/json': { +testCase( + 'route with type descriptions with optional fields', + ROUTE_WITH_TYPE_DESCRIPTIONS_OPTIONAL, + { + openapi: '3.0.3', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + get: { + summary: 'A simple route with type descriptions', + operationId: 'api.v1.test', + tags: ['Test Routes'], + parameters: [ + { + description: 'bar param', + in: 'query', + name: 'bar', + required: true, schema: { - properties: { - bar: { - description: 'bar description', - type: 'number' - }, - child: { - properties: { - child: { - description: 'child description', - type: 'string' - } - }, - type: 'object' - }, - foo: { - description: 'foo description', - type: 'string' - } - }, - required: [ - 'child' - ], - type: 'object' - } - } - } - }, - responses: { - 200: { - description: 'OK', + type: 'string', + }, + }, + ], + requestBody: { content: { 'application/json': { schema: { - type: 'object', properties: { - test: { + bar: { + description: 'bar description', + type: 'number', + }, + child: { + properties: { + child: { + description: 'child description', + type: 'string', + }, + }, + type: 'object', + }, + foo: { + description: 'foo description', type: 'string', }, }, - required: ['test'], + required: ['child'], + type: 'object', + }, + }, + }, + }, + responses: { + 200: { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + test: { + type: 'string', + }, + }, + required: ['test'], + }, }, }, }, @@ -235,11 +229,11 @@ testCase('route with type descriptions with optional fields', ROUTE_WITH_TYPE_DE }, }, }, + components: { + schemas: {}, + }, }, - components: { - schemas: {}, - }, -}); +); const ROUTE_WITH_MIXED_TYPES_AND_DESCRIPTIONS = ` import * as t from 'io-ts'; @@ -285,143 +279,132 @@ export const route = h.httpRoute({ }); `; -testCase('route with mixed types and descriptions', ROUTE_WITH_MIXED_TYPES_AND_DESCRIPTIONS, +testCase( + 'route with mixed types and descriptions', + ROUTE_WITH_MIXED_TYPES_AND_DESCRIPTIONS, { - openapi: "3.0.3", + openapi: '3.0.3', info: { - title: "Test", - version: "1.0.0" + title: 'Test', + version: '1.0.0', }, paths: { '/foo': { get: { - summary: "A simple route with type descriptions", - operationId: "api.v1.test", - tags: [ - "Test Routes" - ], + summary: 'A simple route with type descriptions', + operationId: 'api.v1.test', + tags: ['Test Routes'], parameters: [ { - name: "bar", - description: "bar param", - in: "query", + name: 'bar', + description: 'bar param', + in: 'query', required: true, schema: { - type: "string" - } - } + type: 'string', + }, + }, ], requestBody: { content: { 'application/json': { schema: { - type: "object", + type: 'object', properties: { foo: { - type: "string", - description: "description to describe an optional string" + type: 'string', + description: 'description to describe an optional string', }, bar: { oneOf: [ { - type: "number" + type: 'number', }, { - type: "string" - } + type: 'string', + }, ], - description: "description to describe an optional union of number and string" + description: + 'description to describe an optional union of number and string', }, child: { - type: "object", - description: "description to describe an object", + type: 'object', + description: 'description to describe an object', properties: { child: { - type: "object", - description: "dsecription to describe an intersection of a type and a partial", + type: 'object', + description: + 'dsecription to describe an intersection of a type and a partial', properties: { foo: { - type: "string" + type: 'string', }, bar: { - type: "number" - } + type: 'number', + }, }, - required: [ - "foo" - ] - } + required: ['foo'], + }, }, - required: [ - "child" - ] + required: ['child'], }, error: { - type: "object", - description: "description to describe a t.type", + type: 'object', + description: 'description to describe a t.type', properties: { error: { - type: "string" - } + type: 'string', + }, }, - required: [ - "error" - ] + required: ['error'], }, obj: { - type: "object", - description: "description to describe an optional t.object", - properties: {} + type: 'object', + description: 'description to describe an optional t.object', + properties: {}, }, exact: { - type: "object", - description: "description to describe a t.exact", + type: 'object', + description: 'description to describe a t.exact', properties: { foo: { - type: "string" - } + type: 'string', + }, }, - required: [ - "foo" - ] - } + required: ['foo'], + }, }, - required: [ - "child", - "error", - "exact" - ] - } - } - } + required: ['child', 'error', 'exact'], + }, + }, + }, }, responses: { 200: { - description: "OK", + description: 'OK', content: { 'application/json': { schema: { - type: "object", + type: 'object', properties: { test: { - type: "string" - } + type: 'string', + }, }, - required: [ - "test" - ] - } - } - } - } - } - } - } + required: ['test'], + }, + }, + }, + }, + }, + }, + }, }, components: { - schemas: {} - } - }); + schemas: {}, + }, + }, +); const ROUTE_WITH_ARRAY_TYPES_AND_DESCRIPTIONS = ` import * as t from 'io-ts'; @@ -460,110 +443,104 @@ export const route = h.httpRoute({ }); `; -testCase('route with array types and descriptions', ROUTE_WITH_ARRAY_TYPES_AND_DESCRIPTIONS, { - openapi: '3.0.3', - info: { - title: 'Test', - version: '1.0.0' - }, - paths: { - '/foo': { - get: { - summary: 'A simple route with type descriptions', - operationId: 'api.v1.test', - tags: [ - 'Test Routes' - ], - parameters: [ - { - name: 'bar', - description: 'bar param', - in: 'query', - required: true, - schema: { - type: 'string' - } - } - ], - requestBody: { - content: { - 'application/json': { +testCase( + 'route with array types and descriptions', + ROUTE_WITH_ARRAY_TYPES_AND_DESCRIPTIONS, + { + openapi: '3.0.3', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + get: { + summary: 'A simple route with type descriptions', + operationId: 'api.v1.test', + tags: ['Test Routes'], + parameters: [ + { + name: 'bar', + description: 'bar param', + in: 'query', + required: true, schema: { - type: 'object', - properties: { - foo: { - type: 'array', - items: { - type: 'string', - description: 'foo description' + type: 'string', + }, + }, + ], + requestBody: { + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + foo: { + type: 'array', + items: { + type: 'string', + description: 'foo description', + }, }, - }, - bar: { - type: 'array', - items: { - type: 'number', - description: 'bar description' + bar: { + type: 'array', + items: { + type: 'number', + description: 'bar description', + }, + }, + child: { + type: 'object', + properties: { + child: { + type: 'array', + items: { + oneOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + ], + description: 'child description', + }, + }, + }, + required: ['child'], }, }, - child: { + required: ['foo', 'bar', 'child'], + }, + }, + }, + }, + responses: { + '200': { + description: 'OK', + content: { + 'application/json': { + schema: { type: 'object', properties: { - child: { - type: 'array', - items: { - oneOf: [ - { - type: 'string' - }, - { - type: 'number' - } - ], - description: 'child description' - }, - } + test: { + type: 'string', + }, }, - required: [ - 'child' - ] - } + required: ['test'], + }, }, - required: [ - 'foo', - 'bar', - 'child' - ] - } - } - } + }, + }, + }, }, - responses: { - '200': { - description: 'OK', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - test: { - type: 'string' - } - }, - required: [ - 'test' - ] - } - } - } - } - } - } - } + }, + }, + components: { + schemas: {}, + }, }, - components: { - schemas: {} - } -}); +); const ROUTE_WITH_RECORD_TYPES_AND_DESCRIPTIONS = ` import * as t from 'io-ts'; @@ -600,108 +577,103 @@ export const route = h.httpRoute({ }); `; -testCase('route with record types and descriptions', ROUTE_WITH_RECORD_TYPES_AND_DESCRIPTIONS, { - openapi: '3.0.3', - info: { - title: 'Test', - version: '1.0.0' - }, - paths: { - '/foo': { - get: { - summary: 'A simple route with type descriptions', - operationId: 'api.v1.test', - tags: [ - 'Test Routes' - ], - parameters: [ - { - name: 'bar', - description: 'bar param', - in: 'query', - required: true, - schema: { - type: 'object', - additionalProperties: { - type: 'string' - } - } - } - ], - requestBody: { - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - foo: { - type: 'object', - additionalProperties: { - type: 'number' - }, - description: 'foo description' - }, - child: { - type: 'object', - properties: { - child: { - type: 'object', - additionalProperties: { - type: 'array', - items: { - oneOf: [ - { - type: 'string' - }, - { - type: 'number' - } - ] - } - }, - description: 'child description' - } - }, - required: [ - 'child' - ] - } +testCase( + 'route with record types and descriptions', + ROUTE_WITH_RECORD_TYPES_AND_DESCRIPTIONS, + { + openapi: '3.0.3', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + get: { + summary: 'A simple route with type descriptions', + operationId: 'api.v1.test', + tags: ['Test Routes'], + parameters: [ + { + name: 'bar', + description: 'bar param', + in: 'query', + required: true, + schema: { + type: 'object', + additionalProperties: { + type: 'string', }, - required: [ - 'foo', - 'child' - ] - } - } - } - }, - responses: { - '200': { - description: 'OK', + }, + }, + ], + requestBody: { content: { 'application/json': { schema: { type: 'object', properties: { - test: { - type: 'string' - } + foo: { + type: 'object', + additionalProperties: { + type: 'number', + }, + description: 'foo description', + }, + child: { + type: 'object', + properties: { + child: { + type: 'object', + additionalProperties: { + type: 'array', + items: { + oneOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + ], + }, + }, + description: 'child description', + }, + }, + required: ['child'], + }, }, - required: [ - 'test' - ] - } - } - } - } - } - } - } + required: ['foo', 'child'], + }, + }, + }, + }, + responses: { + '200': { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + test: { + type: 'string', + }, + }, + required: ['test'], + }, + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: {}, + }, }, - components: { - schemas: {} - } -}); +); const ROUTE_WITH_DESCRIPTIONS_PATTERNS_EXAMPLES = ` import * as t from 'io-ts'; @@ -747,107 +719,102 @@ export const route = h.httpRoute({ }); `; -testCase('route with descriptions, patterns, and examples', ROUTE_WITH_DESCRIPTIONS_PATTERNS_EXAMPLES, { - openapi: '3.0.3', - info: { - title: 'Test', - version: '1.0.0' - }, - paths: { - '/foo': { - get: { - summary: 'A simple route with type descriptions', - operationId: 'api.v1.test', - tags: [ - 'Test Routes' - ], - parameters: [ - { - name: 'bar', - description: 'This is a bar param.', - in: 'query', - required: true, - schema: { - type: 'object', - example: { - foo: 'bar' - }, - additionalProperties: { - type: 'string' - } - } - } - ], - requestBody: { - content: { - 'application/json': { +testCase( + 'route with descriptions, patterns, and examples', + ROUTE_WITH_DESCRIPTIONS_PATTERNS_EXAMPLES, + { + openapi: '3.0.3', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + get: { + summary: 'A simple route with type descriptions', + operationId: 'api.v1.test', + tags: ['Test Routes'], + parameters: [ + { + name: 'bar', + description: 'This is a bar param.', + in: 'query', + required: true, schema: { type: 'object', - properties: { - foo: { - type: 'number', - description: 'foo description', - example: 12345, - pattern: '^[1-9][0-9]{4}$' - }, - child: { - type: 'object', - properties: { - child: { - type: 'array', - items: { - description: 'child description', - oneOf: [ - { - type: 'string' - }, - { - type: 'number' - } - ] - }, - } - }, - required: [ - 'child' - ] - } + example: { + foo: 'bar', }, - required: [ - 'foo', - 'child' - ] - } - } - } - }, - responses: { - '200': { - description: 'OK', + additionalProperties: { + type: 'string', + }, + }, + }, + ], + requestBody: { content: { 'application/json': { schema: { type: 'object', properties: { - test: { - type: 'string' - } + foo: { + type: 'number', + description: 'foo description', + example: 12345, + pattern: '^[1-9][0-9]{4}$', + }, + child: { + type: 'object', + properties: { + child: { + type: 'array', + items: { + description: 'child description', + oneOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + ], + }, + }, + }, + required: ['child'], + }, }, - required: [ - 'test' - ] - } - } - } - } - } - } - } + required: ['foo', 'child'], + }, + }, + }, + }, + responses: { + '200': { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + test: { + type: 'string', + }, + }, + required: ['test'], + }, + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: {}, + }, }, - components: { - schemas: {} - } -}); +); const ROUTE_WITH_DESCRIPTIONS_FOR_REFERENCES = ` import * as t from 'io-ts'; @@ -886,114 +853,107 @@ export const route = h.httpRoute({ }); `; -testCase('route with descriptions for references', ROUTE_WITH_DESCRIPTIONS_FOR_REFERENCES, { - openapi: '3.0.3', - info: { - title: 'Test', - version: '1.0.0' - }, - paths: { - '/foo': { - get: { - summary: 'A simple route with type descriptions for references', - operationId: 'api.v1.test', - tags: [ - 'Test Routes' - ], - parameters: [ - { - name: 'bar', - in: 'query', - required: true, - schema: { - type: 'array', - items: { - type: 'string' - } - } - } - ], - requestBody: { - content: { - 'application/json': { +testCase( + 'route with descriptions for references', + ROUTE_WITH_DESCRIPTIONS_FOR_REFERENCES, + { + openapi: '3.0.3', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + get: { + summary: 'A simple route with type descriptions for references', + operationId: 'api.v1.test', + tags: ['Test Routes'], + parameters: [ + { + name: 'bar', + in: 'query', + required: true, schema: { - type: 'object', - properties: { - // needs to be wrapped in an allOf to preserve the description - foo: { - allOf: [ - { - $ref: '#/components/schemas/Foo' - } - ], - description: 'This is a foo description.', - example: 'BitGo Inc' - }, - // should not need to be wrapped in an allOf - bar: { - $ref: '#/components/schemas/Bar' - } + type: 'array', + items: { + type: 'string', }, - required: [ - 'foo', - 'bar' - ] - } - } - } - }, - responses: { - '200': { - description: 'OK', + }, + }, + ], + requestBody: { content: { 'application/json': { schema: { type: 'object', properties: { - test: { - type: 'string' - } + // needs to be wrapped in an allOf to preserve the description + foo: { + allOf: [ + { + $ref: '#/components/schemas/Foo', + }, + ], + description: 'This is a foo description.', + example: 'BitGo Inc', + }, + // should not need to be wrapped in an allOf + bar: { + $ref: '#/components/schemas/Bar', + }, }, - required: [ - 'test' - ] - } - } - } - } - } - } - } - }, - components: { - schemas: { - Foo: { - title: 'Foo', - type: 'object', - properties: { - foo: { - type: 'string' - } + required: ['foo', 'bar'], + }, + }, + }, + }, + responses: { + '200': { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + test: { + type: 'string', + }, + }, + required: ['test'], + }, + }, + }, + }, + }, }, - required: [ - 'foo' - ] }, - Bar: { - title: 'Bar', - type: 'object', - properties: { - bar: { - type: 'number' - } + }, + components: { + schemas: { + Foo: { + title: 'Foo', + type: 'object', + properties: { + foo: { + type: 'string', + }, + }, + required: ['foo'], }, - required: [ - 'bar' - ] - } - } - } -}); + Bar: { + title: 'Bar', + type: 'object', + properties: { + bar: { + type: 'number', + }, + }, + required: ['bar'], + }, + }, + }, + }, +); const ROUTE_WITH_MIN_AND_MAX_VALUES_FOR_STRINGS_AND_DEFAULT = ` import * as t from 'io-ts'; @@ -1031,85 +991,80 @@ export const route = h.httpRoute({ }); `; -testCase('route with min and max values for strings and default value', ROUTE_WITH_MIN_AND_MAX_VALUES_FOR_STRINGS_AND_DEFAULT, { - openapi: '3.0.3', - info: { - title: 'Test', - version: '1.0.0' - }, - paths: { - '/foo': { - get: { - summary: 'A simple route with type descriptions for references', - operationId: 'api.v1.test', - tags: [ - 'Test Routes' - ], - parameters: [ - { - name: 'bar', - in: 'query', - required: true, - schema: { - type: 'array', - items: { - type: 'string' - } - } - } - ], - requestBody: { - content: { - 'application/json': { +testCase( + 'route with min and max values for strings and default value', + ROUTE_WITH_MIN_AND_MAX_VALUES_FOR_STRINGS_AND_DEFAULT, + { + openapi: '3.0.3', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + get: { + summary: 'A simple route with type descriptions for references', + operationId: 'api.v1.test', + tags: ['Test Routes'], + parameters: [ + { + name: 'bar', + in: 'query', + required: true, schema: { - type: 'object', - properties: { - foo: { - type: 'string', - description: 'This is a foo description.', - example: 'SomeInc', - default: 'BitgoInc', - minLength: 5, - maxLength: 10 - } + type: 'array', + items: { + type: 'string', }, - required: [ - 'foo' - ] - } - } - } - }, - responses: { - '200': { - description: 'OK', + }, + }, + ], + requestBody: { content: { 'application/json': { schema: { type: 'object', properties: { - test: { - type: 'string' - } + foo: { + type: 'string', + description: 'This is a foo description.', + example: 'SomeInc', + default: 'BitgoInc', + minLength: 5, + maxLength: 10, + }, }, - required: [ - 'test' - ] - } - } - } - } - } - } - } + required: ['foo'], + }, + }, + }, + }, + responses: { + '200': { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + test: { + type: 'string', + }, + }, + required: ['test'], + }, + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: {}, + }, }, - components: { - schemas: {} - } -}); - - - +); const ROUTE_WITH_OVERRIDING_COMMENTS = ` import * as t from 'io-ts'; @@ -1138,70 +1093,68 @@ export const route = h.httpRoute({ }); `; -testCase("route with overriding comments", ROUTE_WITH_OVERRIDING_COMMENTS, { - openapi: "3.0.3", +testCase('route with overriding comments', ROUTE_WITH_OVERRIDING_COMMENTS, { + openapi: '3.0.3', info: { - title: "Test", - version: "1.0.0" + title: 'Test', + version: '1.0.0', }, paths: { - "/foo": { + '/foo': { post: { parameters: [], requestBody: { content: { - "application/json": { + 'application/json': { schema: { - type: "object", + type: 'object', properties: { target: { - type: "string", - description: "This description should show with the example", - example: "abc" - } - } - } - } - } + type: 'string', + description: 'This description should show with the example', + example: 'abc', + }, + }, + }, + }, + }, }, responses: { 200: { - description: "OK", + description: 'OK', content: { - "application/json": { + 'application/json': { schema: { - type: "string", - enum: [ - "OK" - ] - } - } - } - } - } - } - } + type: 'string', + enum: ['OK'], + }, + }, + }, + }, + }, + }, + }, }, components: { schemas: { TargetSchema: { - title: "TargetSchema", - type: "string", - example: "abc" + title: 'TargetSchema', + type: 'string', + example: 'abc', }, ParentSchema: { - title: "ParentSchema", - type: "object", + title: 'ParentSchema', + type: 'object', properties: { target: { - type: "string", - description: "This description should show with the example", - example: "abc" - } - } - } - } - } + type: 'string', + description: 'This description should show with the example', + example: 'abc', + }, + }, + }, + }, + }, }); const ROUTE_WITH_NESTED_OVERRIDEN_COMMENTS = ` @@ -1236,91 +1189,93 @@ export const route = h.httpRoute({ }); `; - -testCase("route with nested overriding comments", ROUTE_WITH_NESTED_OVERRIDEN_COMMENTS, { - openapi: "3.0.3", - info: { - title: "Test", - version: "1.0.0" - }, - paths: { - "/foo": { - post: { - parameters: [], - requestBody: { - content: { - "application/json": { - schema: { - type: "object", - properties: { - parent: { - allOf: [ - { - '$ref': '#/components/schemas/ParentSchema' - } - ], - description: 'This description should override the previous description', +testCase( + 'route with nested overriding comments', + ROUTE_WITH_NESTED_OVERRIDEN_COMMENTS, + { + openapi: '3.0.3', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + post: { + parameters: [], + requestBody: { + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + parent: { + allOf: [ + { + $ref: '#/components/schemas/ParentSchema', + }, + ], + description: + 'This description should override the previous description', + }, + }, + required: ['parent'], + }, + }, + }, + }, + responses: { + 200: { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'string', + enum: ['OK'], }, }, - required: ['parent'] - } - } - } + }, + }, + }, }, - responses: { - 200: { - description: "OK", - content: { - "application/json": { - schema: { - type: "string", - enum: [ - "OK" - ] - } - } - } - } - } - } - } - }, - components: { - schemas: { - TargetSchema: { - title: "TargetSchema", - type: "string", - example: "abc" }, - ParentSchema: { - title: "ParentSchema", - type: "object", - properties: { - target: { - type: "string", - description: "This description should show with the example", - example: "abc" - } - } - }, - GrandParentSchema: { - title: "GrandParentSchema", - type: "object", - properties: { - parent: { - allOf: [ - { - '$ref': '#/components/schemas/ParentSchema' - } - ], - description: 'This description should override the previous description' - } + }, + components: { + schemas: { + TargetSchema: { + title: 'TargetSchema', + type: 'string', + example: 'abc', }, - required: ['parent'] - } - } - } -}); + ParentSchema: { + title: 'ParentSchema', + type: 'object', + properties: { + target: { + type: 'string', + description: 'This description should show with the example', + example: 'abc', + }, + }, + }, + GrandParentSchema: { + title: 'GrandParentSchema', + type: 'object', + properties: { + parent: { + allOf: [ + { + $ref: '#/components/schemas/ParentSchema', + }, + ], + description: 'This description should override the previous description', + }, + }, + required: ['parent'], + }, + }, + }, + }, +); const ROUTE_WITH_OVERRIDEN_COMMENTS_IN_UNION = ` import * as t from 'io-ts'; @@ -1371,121 +1326,117 @@ export const route = h.httpRoute({ }); `; -testCase("route with overriden comments in union", ROUTE_WITH_OVERRIDEN_COMMENTS_IN_UNION, { - openapi: "3.0.3", - info: { - title: "Test", - version: "1.0.0" - }, - paths: { - "/foo": { - post: { - parameters: [], - requestBody: { - content: { - "application/json": { - schema: { - title: "Grand Parent Schema", - description: 'This is grandparent schema description', - type: "object", - properties: { - parent: { - "$ref": "#/components/schemas/ParentSchema" - }, - secondaryParent: { - "$ref": "#/components/schemas/SecondaryParentSchema" - } - }, - required: [ - "parent", - "secondaryParent" - ] - } - } - } - }, - responses: { - 200: { - description: "OK", +testCase( + 'route with overriden comments in union', + ROUTE_WITH_OVERRIDEN_COMMENTS_IN_UNION, + { + openapi: '3.0.3', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + post: { + parameters: [], + requestBody: { content: { - "application/json": { + 'application/json': { schema: { - type: "string", - enum: [ - "OK" - ] - } - } - } - } - } - } - } - }, - components: { - schemas: { - TargetSchema: { - title: "TargetSchema", - type: "string", - example: "abc" - }, - TargetSchema2: { - title: "TargetSchema2", - type: "string", - example: "def" - }, - ParentSchema: { - title: "ParentSchema", - type: "object", - properties: { - target: { - oneOf: [ - { - "$ref": "#/components/schemas/TargetSchema" + title: 'Grand Parent Schema', + description: 'This is grandparent schema description', + type: 'object', + properties: { + parent: { + $ref: '#/components/schemas/ParentSchema', + }, + secondaryParent: { + $ref: '#/components/schemas/SecondaryParentSchema', + }, + }, + required: ['parent', 'secondaryParent'], + }, }, - { - "$ref": "#/components/schemas/TargetSchema2" - } - ], - description: "This description should show with the example" - } - } - }, - SecondaryParentSchema: { - title: "SecondaryParentSchema", - type: "object", - properties: { - target: { - oneOf: [ - { - "$ref": "#/components/schemas/TargetSchema" + }, + }, + responses: { + 200: { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'string', + enum: ['OK'], + }, + }, }, - { - "$ref": "#/components/schemas/TargetSchema2" - } - ], - description: "This description should show with the overriden example", - example: "\"overridden example\"" - } - } + }, + }, + }, }, - GrandParentSchema: { - title: "Grand Parent Schema", - description: 'This is grandparent schema description', - type: "object", - properties: { - parent: { - "$ref": "#/components/schemas/ParentSchema" + }, + components: { + schemas: { + TargetSchema: { + title: 'TargetSchema', + type: 'string', + example: 'abc', + }, + TargetSchema2: { + title: 'TargetSchema2', + type: 'string', + example: 'def', + }, + ParentSchema: { + title: 'ParentSchema', + type: 'object', + properties: { + target: { + oneOf: [ + { + $ref: '#/components/schemas/TargetSchema', + }, + { + $ref: '#/components/schemas/TargetSchema2', + }, + ], + description: 'This description should show with the example', + }, }, - secondaryParent: { - "$ref": "#/components/schemas/SecondaryParentSchema" - } }, - required: [ - "parent", - "secondaryParent" - ] - } - } - } -}); \ No newline at end of file + SecondaryParentSchema: { + title: 'SecondaryParentSchema', + type: 'object', + properties: { + target: { + oneOf: [ + { + $ref: '#/components/schemas/TargetSchema', + }, + { + $ref: '#/components/schemas/TargetSchema2', + }, + ], + description: 'This description should show with the overriden example', + example: '"overridden example"', + }, + }, + }, + GrandParentSchema: { + title: 'Grand Parent Schema', + description: 'This is grandparent schema description', + type: 'object', + properties: { + parent: { + $ref: '#/components/schemas/ParentSchema', + }, + secondaryParent: { + $ref: '#/components/schemas/SecondaryParentSchema', + }, + }, + required: ['parent', 'secondaryParent'], + }, + }, + }, + }, +); diff --git a/packages/openapi-generator/test/openapi/jsdoc.test.ts b/packages/openapi-generator/test/openapi/jsdoc.test.ts index 7262f6f6..ccdd5e34 100644 --- a/packages/openapi-generator/test/openapi/jsdoc.test.ts +++ b/packages/openapi-generator/test/openapi/jsdoc.test.ts @@ -1,5 +1,4 @@ -import { testCase } from "./testHarness"; - +import { testCase } from './testHarness'; const TITLE_TAG = ` import * as t from 'io-ts'; @@ -162,11 +161,11 @@ export const route = h.httpRoute({ }); `; -testCase("route with private headers", ROUTE_WITH_PRIVATE_HEADERS, { +testCase('route with private headers', ROUTE_WITH_PRIVATE_HEADERS, { openapi: '3.0.3', info: { title: 'Test', - version: '1.0.0' + version: '1.0.0', }, paths: { '/foo': { @@ -179,17 +178,17 @@ testCase("route with private headers", ROUTE_WITH_PRIVATE_HEADERS, { name: 'x-private-header', required: true, schema: { - type: 'string' - } + type: 'string', + }, }, { in: 'header', name: 'public-header', required: true, schema: { - type: 'string' - } - } + type: 'string', + }, + }, ], responses: { '200': { @@ -197,21 +196,20 @@ testCase("route with private headers", ROUTE_WITH_PRIVATE_HEADERS, { content: { 'application/json': { schema: { - type: 'string' - } - } - } - } - } - } - } + type: 'string', + }, + }, + }, + }, + }, + }, + }, }, components: { - schemas: {} - } + schemas: {}, + }, }); - const ROUTE_WITH_RESPONSE_EXAMPLE_STRING = ` import * as t from 'io-ts'; import * as h from '@api-ts/io-ts-http'; @@ -535,9 +533,6 @@ testCase('route with multiple unknown tags', ROUTE_WITH_MULTIPLE_UNKNOWN_TAGS, { }, }); - - - const ROUTE_WITH_DEPRECATED_TAG = ` import * as t from 'io-ts'; import * as h from '@api-ts/io-ts-http'; @@ -572,7 +567,7 @@ testCase('route with deprecated tag', ROUTE_WITH_DEPRECATED_TAG, { openapi: '3.0.3', info: { title: 'Test', - version: '1.0.0' + version: '1.0.0', }, paths: { '/foo': { @@ -580,9 +575,7 @@ testCase('route with deprecated tag', ROUTE_WITH_DEPRECATED_TAG, { summary: 'A simple route with type descriptions for references', operationId: 'api.v1.test', parameters: [], - tags: [ - 'Test Routes' - ], + tags: ['Test Routes'], requestBody: { content: { 'application/json': { @@ -592,15 +585,13 @@ testCase('route with deprecated tag', ROUTE_WITH_DEPRECATED_TAG, { foo: { type: 'string', description: 'This is a foo description.', - deprecated: true - } + deprecated: true, + }, }, - required: [ - 'foo' - ] - } - } - } + required: ['foo'], + }, + }, + }, }, responses: { '200': { @@ -611,27 +602,23 @@ testCase('route with deprecated tag', ROUTE_WITH_DEPRECATED_TAG, { type: 'object', properties: { test: { - type: 'string' - } + type: 'string', + }, }, - required: [ - 'test' - ] - } - } - } - } - } - } - } + required: ['test'], + }, + }, + }, + }, + }, + }, + }, }, components: { - schemas: {} - } + schemas: {}, + }, }); - - const ROUTE_WITH_MIN_MAX_AND_OTHER_TAGS = ` import * as t from 'io-ts'; import * as h from '@api-ts/io-ts-http'; @@ -677,7 +664,7 @@ testCase('route with min and max tags', ROUTE_WITH_MIN_MAX_AND_OTHER_TAGS, { openapi: '3.0.3', info: { title: 'Test', - version: '1.0.0' + version: '1.0.0', }, paths: { '/foo': { @@ -685,9 +672,7 @@ testCase('route with min and max tags', ROUTE_WITH_MIN_MAX_AND_OTHER_TAGS, { summary: 'A simple route with type descriptions for references', operationId: 'api.v1.test', parameters: [], - tags: [ - 'Test Routes' - ], + tags: ['Test Routes'], requestBody: { content: { 'application/json': { @@ -708,15 +693,13 @@ testCase('route with min and max tags', ROUTE_WITH_MIN_MAX_AND_OTHER_TAGS, { exclusiveMaximum: true, uniqueItems: true, readOnly: true, - writeOnly: true - } + writeOnly: true, + }, }, - required: [ - 'foo' - ] - } - } - } + required: ['foo'], + }, + }, + }, }, responses: { '200': { @@ -727,26 +710,23 @@ testCase('route with min and max tags', ROUTE_WITH_MIN_MAX_AND_OTHER_TAGS, { type: 'object', properties: { test: { - type: 'string' - } + type: 'string', + }, }, - required: [ - 'test' - ] - } - } - } - } - } - } - } + required: ['test'], + }, + }, + }, + }, + }, + }, + }, }, components: { - schemas: {} - } + schemas: {}, + }, }); - const SCHEMA_WITH_TITLES_IN_REQUEST_BODIES = ` import * as t from 'io-ts'; import * as h from '@api-ts/io-ts-http'; @@ -777,11 +757,11 @@ export const route = h.httpRoute({ }); `; -testCase("route with titles in request bodies", SCHEMA_WITH_TITLES_IN_REQUEST_BODIES, { +testCase('route with titles in request bodies', SCHEMA_WITH_TITLES_IN_REQUEST_BODIES, { openapi: '3.0.3', info: { title: 'Test', - version: '1.0.0' + version: '1.0.0', }, paths: { '/foo': { @@ -795,11 +775,11 @@ testCase("route with titles in request bodies", SCHEMA_WITH_TITLES_IN_REQUEST_BO properties: { params: { type: 'object', - title: "Some Readable ParamsFoo Title", + title: 'Some Readable ParamsFoo Title', properties: { - someId: { type: 'string' } + someId: { type: 'string' }, }, - required: ['someId'] + required: ['someId'], }, body: { type: 'object', @@ -807,16 +787,16 @@ testCase("route with titles in request bodies", SCHEMA_WITH_TITLES_IN_REQUEST_BO properties: { foo: { type: 'string', - description: 'a foo description' - } + description: 'a foo description', + }, }, - required: ['foo'] - } + required: ['foo'], + }, }, - required: ['params', 'body'] - } - } - } + required: ['params', 'body'], + }, + }, + }, }, responses: { '200': { @@ -825,14 +805,14 @@ testCase("route with titles in request bodies", SCHEMA_WITH_TITLES_IN_REQUEST_BO 'application/json': { schema: { type: 'string', - enum: ['OK'] - } - } - } - } - } - } - } + enum: ['OK'], + }, + }, + }, + }, + }, + }, + }, }, components: { schemas: { @@ -840,7 +820,7 @@ testCase("route with titles in request bodies", SCHEMA_WITH_TITLES_IN_REQUEST_BO title: 'Some Readable ParamsFoo Title', type: 'object', properties: { someId: { type: 'string' } }, - required: ['someId'] + required: ['someId'], }, BodyFoo: { title: 'Some Readable BodyFoo Title', @@ -848,16 +828,15 @@ testCase("route with titles in request bodies", SCHEMA_WITH_TITLES_IN_REQUEST_BO properties: { foo: { type: 'string', - description: 'a foo description' - } + description: 'a foo description', + }, }, - required: ['foo'] - } - } - } + required: ['foo'], + }, + }, + }, }); - const ROUTE_WITH_ARRAY_EXAMPLE = ` import * as t from 'io-ts'; import * as h from '@api-ts/io-ts-http'; @@ -899,11 +878,11 @@ export const route = h.httpRoute({ }, });`; -testCase("route with array examples", ROUTE_WITH_ARRAY_EXAMPLE, { +testCase('route with array examples', ROUTE_WITH_ARRAY_EXAMPLE, { openapi: '3.0.3', info: { title: 'Test', - version: '1.0.0' + version: '1.0.0', }, paths: { '/foo': { @@ -919,7 +898,7 @@ testCase("route with array examples", ROUTE_WITH_ARRAY_EXAMPLE, { type: 'array', items: { type: 'string', - example: '"btc"' + example: '"btc"', }, }, array2: { @@ -927,41 +906,36 @@ testCase("route with array examples", ROUTE_WITH_ARRAY_EXAMPLE, { example: ['btc', 'eth'], items: { type: 'string', - example: 'btc' + example: 'btc', }, }, array3: { items: { - type: 'number' + type: 'number', }, maxItems: 5, minItems: 1, - type: 'array' + type: 'array', }, objectWithArray: { properties: { nestedArray: { - example: [ - 'btc', - 'eth' - ], + example: ['btc', 'eth'], items: { example: 'btc', - type: 'string' + type: 'string', }, - type: 'array' - } + type: 'array', + }, }, - required: [ - 'nestedArray' - ], - type: 'object' + required: ['nestedArray'], + type: 'object', }, }, required: ['array1', 'array2', 'array3', 'objectWithArray'], }, - } - } + }, + }, }, responses: { '200': { @@ -970,27 +944,26 @@ testCase("route with array examples", ROUTE_WITH_ARRAY_EXAMPLE, { 'application/json': { schema: { type: 'string', - enum: ['OK'] - } - } - } - } - } - } - } + enum: ['OK'], + }, + }, + }, + }, + }, + }, + }, }, components: { schemas: { innerItems: { - title: "innerItems", - type: "string", - example: 'btc' - } - } - } + title: 'innerItems', + type: 'string', + example: 'btc', + }, + }, + }, }); - const ROUTE_WITH_NESTED_ARRAY_EXAMPLES = ` import * as t from 'io-ts'; import * as h from '@api-ts/io-ts-http'; @@ -1025,91 +998,96 @@ export const route = h.httpRoute({ }); `; -testCase("route with nested array examples", ROUTE_WITH_NESTED_ARRAY_EXAMPLES, { - openapi: "3.0.3", +testCase('route with nested array examples', ROUTE_WITH_NESTED_ARRAY_EXAMPLES, { + openapi: '3.0.3', info: { - title: "Test", - version: "1.0.0" + title: 'Test', + version: '1.0.0', }, paths: { - "/foo": { + '/foo': { post: { parameters: [], requestBody: { content: { - "application/json": { + 'application/json': { schema: { - type: "object", + type: 'object', properties: { nested: { - "$ref": "#/components/schemas/thirdLevel" - } + $ref: '#/components/schemas/thirdLevel', + }, }, - required: [ - "nested" - ] - } - } - } + required: ['nested'], + }, + }, + }, }, responses: { 200: { - description: "OK", + description: 'OK', content: { - "application/json": { + 'application/json': { schema: { - type: "string", - enum: [ - "OK" - ] - } - } - } - } - } - } - } + type: 'string', + enum: ['OK'], + }, + }, + }, + }, + }, + }, + }, }, components: { schemas: { firstLevel: { - title: "firstLevel", - type: "array", - example: [ "a", "b" ], + title: 'firstLevel', + type: 'array', + example: ['a', 'b'], items: { - type: "string" - } + type: 'string', + }, }, secondLevel: { - title: "secondLevel", - type: "array", - example: [ [ "a", "b" ], [ "c", "d" ] ], + title: 'secondLevel', + type: 'array', + example: [ + ['a', 'b'], + ['c', 'd'], + ], items: { - type: "array", - example: [ "a", "b" ], + type: 'array', + example: ['a', 'b'], items: { - type: "string" - } - } + type: 'string', + }, + }, }, thirdLevel: { - title: "thirdLevel", - type: "array", - example: [[["a"],["b"]],[["c"],["d"]]], + title: 'thirdLevel', + type: 'array', + example: [ + [['a'], ['b']], + [['c'], ['d']], + ], items: { - type: "array", - example: [["a","b"],["c","d"]], + type: 'array', + example: [ + ['a', 'b'], + ['c', 'd'], + ], items: { - type: "array", - example: ["a","b"], + type: 'array', + example: ['a', 'b'], items: { - type: "string" - } - } - } - } - } - } + type: 'string', + }, + }, + }, + }, + }, + }, }); const ROUTE_WITH_PRIVATE_PROPERTIES = ` @@ -1146,119 +1124,111 @@ export const route = h.httpRoute({ }); `; -testCase("route with private properties in request query, params, body, and response", ROUTE_WITH_PRIVATE_PROPERTIES, { - openapi: "3.0.3", - info: { - title: "Test", - version: "1.0.0" - }, - paths: { - '/foo': { - get: { - parameters: [ - { - 'x-internal': true, - description: '', - in: 'query', - name: 'query', - required: true, - schema: { - type: 'string' - } - }, - { - 'x-internal': true, - description: '', - in: 'path', - name: 'path', - required: true, - schema: { - type: 'string' - } - } - ], - requestBody: { - content: { - 'application/json': { +testCase( + 'route with private properties in request query, params, body, and response', + ROUTE_WITH_PRIVATE_PROPERTIES, + { + openapi: '3.0.3', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + get: { + parameters: [ + { + 'x-internal': true, + description: '', + in: 'query', + name: 'query', + required: true, schema: { - properties: { - bar: { - 'x-internal': true, - type: 'string' - }, - foo: { - type: 'string' - }, - privateObject: { - 'x-internal': true, - properties: { - privateFieldInObject: { - type: 'boolean' - } - }, - required: [ - 'privateFieldInObject' - ], - type: 'object' - } - }, - required: [ - 'foo', - 'bar', - 'privateObject' - ], - type: 'object' - } - } - }, - }, - responses: { - '200': { + type: 'string', + }, + }, + { + 'x-internal': true, + description: '', + in: 'path', + name: 'path', + required: true, + schema: { + type: 'string', + }, + }, + ], + requestBody: { content: { 'application/json': { schema: { - '$ref': '#/components/schemas/SampleType' - } - } + properties: { + bar: { + 'x-internal': true, + type: 'string', + }, + foo: { + type: 'string', + }, + privateObject: { + 'x-internal': true, + properties: { + privateFieldInObject: { + type: 'boolean', + }, + }, + required: ['privateFieldInObject'], + type: 'object', + }, + }, + required: ['foo', 'bar', 'privateObject'], + type: 'object', + }, + }, }, - description: 'OK' - } - } - } - }, - }, - components: { - schemas: { - SampleType: { - properties: { - bar: { - 'x-internal': true, - type: 'string' }, - foo: { - type: 'string' + responses: { + '200': { + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/SampleType', + }, + }, + }, + description: 'OK', + }, }, - privateObject: { - 'x-internal': true, - properties: { - privateFieldInObject: { - type: 'boolean' - } + }, + }, + }, + components: { + schemas: { + SampleType: { + properties: { + bar: { + 'x-internal': true, + type: 'string', + }, + foo: { + type: 'string', }, - required: [ - 'privateFieldInObject' - ], - type: 'object' - } + privateObject: { + 'x-internal': true, + properties: { + privateFieldInObject: { + type: 'boolean', + }, + }, + required: ['privateFieldInObject'], + type: 'object', + }, + }, + required: ['foo', 'bar', 'privateObject'], + title: 'SampleType', + type: 'object', }, - required: [ - 'foo', - 'bar', - 'privateObject' - ], - title: 'SampleType', - type: 'object' - } - } + }, + }, }, -}); +); diff --git a/packages/openapi-generator/test/openapi/knownImports.test.ts b/packages/openapi-generator/test/openapi/knownImports.test.ts index f0ec846b..f71af85b 100644 --- a/packages/openapi-generator/test/openapi/knownImports.test.ts +++ b/packages/openapi-generator/test/openapi/knownImports.test.ts @@ -1,4 +1,4 @@ -import { testCase } from "./testHarness"; +import { testCase } from './testHarness'; const ROUTE_WITH_SCHEMA_WITH_DEFAULT_METADATA = ` import * as t from 'io-ts'; @@ -21,55 +21,57 @@ export const route = h.httpRoute({ }); `; -testCase('route with schema with default metadata', ROUTE_WITH_SCHEMA_WITH_DEFAULT_METADATA, { - openapi: '3.0.3', - info: { - title: 'Test', - version: '1.0.0' - }, - paths: { - '/foo': { - get: { - parameters: [ - { - in: 'query', - name: 'ipRestrict', - required: true, - schema: { - type: 'boolean', - } - } - ], - responses: { - '200': { - description: 'OK', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - test: { - type: 'number', - format: 'number', - title: 'Unix Time (milliseconds)', - description: 'Number of milliseconds since the Unix epoch', - } +testCase( + 'route with schema with default metadata', + ROUTE_WITH_SCHEMA_WITH_DEFAULT_METADATA, + { + openapi: '3.0.3', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + get: { + parameters: [ + { + in: 'query', + name: 'ipRestrict', + required: true, + schema: { + type: 'boolean', + }, + }, + ], + responses: { + '200': { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + test: { + type: 'number', + format: 'number', + title: 'Unix Time (milliseconds)', + description: 'Number of milliseconds since the Unix epoch', + }, + }, + required: ['test'], }, - required: [ - 'test' - ] - } - } - } - } - } - } - } + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: {}, + }, }, - components: { - schemas: {} - } -}); +); const ROUTE_WITH_OVERIDDEN_METADATA = ` import * as t from 'io-ts'; @@ -100,7 +102,7 @@ testCase('route with schema with default metadata', ROUTE_WITH_OVERIDDEN_METADAT openapi: '3.0.3', info: { title: 'Test', - version: '1.0.0' + version: '1.0.0', }, paths: { '/foo': { @@ -112,8 +114,8 @@ testCase('route with schema with default metadata', ROUTE_WITH_OVERIDDEN_METADAT required: true, schema: { type: 'boolean', - } - } + }, + }, ], responses: { '200': { @@ -128,20 +130,18 @@ testCase('route with schema with default metadata', ROUTE_WITH_OVERIDDEN_METADAT format: 'string', title: 'Unix Time (milliseconds)', description: 'Testing overridden metadata', - } + }, }, - required: [ - 'test' - ] - } - } - } - } - } - } - } + required: ['test'], + }, + }, + }, + }, + }, + }, + }, }, components: { - schemas: {} - } + schemas: {}, + }, }); diff --git a/packages/openapi-generator/test/openapi/misc.test.ts b/packages/openapi-generator/test/openapi/misc.test.ts index dba14e38..801e5190 100644 --- a/packages/openapi-generator/test/openapi/misc.test.ts +++ b/packages/openapi-generator/test/openapi/misc.test.ts @@ -1,4 +1,4 @@ -import { testCase } from "./testHarness"; +import { testCase } from './testHarness'; const HEADER_COMMENT = ` /* @@ -69,12 +69,6 @@ testCase('source file with a header comment', HEADER_COMMENT, { }, }); - - - - - - const SCHEMA_WITH_MANY_RESPONSE_TYPES = ` import * as t from 'io-ts'; import * as h from '@api-ts/io-ts-http'; @@ -96,66 +90,62 @@ export const route = h.httpRoute({ }) `; -testCase('route with many response codes uses default status code descriptions', SCHEMA_WITH_MANY_RESPONSE_TYPES, { - openapi: '3.0.3', - info: { - title: 'Test', - version: '1.0.0' - }, - paths: { - '/foo': { - get: { - parameters: [], - responses: { - '200': { - description: 'OK', - content: { - 'application/json': { - schema: { - description: 'string response type', - type: 'string' - } - } - } +testCase( + 'route with many response codes uses default status code descriptions', + SCHEMA_WITH_MANY_RESPONSE_TYPES, + { + openapi: '3.0.3', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + get: { + parameters: [], + responses: { + '200': { + description: 'OK', + content: { + 'application/json': { + schema: { + description: 'string response type', + type: 'string', + }, + }, + }, + }, + '400': { + description: 'Bad Request', + content: { + 'application/json': { + schema: { + $ref: '#/components/schemas/ApiError', + }, + }, + }, + }, }, - '400': { - description: 'Bad Request', - content: { - 'application/json': { - schema: { - '$ref': '#/components/schemas/ApiError' - } - } - } - } - } - } - } - }, - components: { - schemas: { - ApiError: { - properties: { - error: { - type: 'string', - description: 'error message', - } }, - required: [ - 'error' - ], - type: 'object', - title: 'ApiError' }, - } - } -}); - - - - - - + }, + components: { + schemas: { + ApiError: { + properties: { + error: { + type: 'string', + description: 'error message', + }, + }, + required: ['error'], + type: 'object', + title: 'ApiError', + }, + }, + }, + }, +); const ROUTE_WITH_RECORD_TYPES = ` import * as t from 'io-ts'; @@ -181,11 +171,11 @@ export const route = h.httpRoute({ }); `; -testCase("route with record types", ROUTE_WITH_RECORD_TYPES, { +testCase('route with record types', ROUTE_WITH_RECORD_TYPES, { openapi: '3.0.3', info: { title: 'Test', - version: '1.0.0' + version: '1.0.0', }, paths: { '/foo': { @@ -196,9 +186,9 @@ testCase("route with record types", ROUTE_WITH_RECORD_TYPES, { in: 'query', required: true, schema: { - type: 'string' - } - } + type: 'string', + }, + }, ], responses: { '200': { @@ -214,9 +204,9 @@ testCase("route with record types", ROUTE_WITH_RECORD_TYPES, { properties: { name: { type: 'string' }, age: { type: 'string' }, - address: { type: 'string' } + address: { type: 'string' }, }, - required: [ 'name', 'age', 'address' ] + required: ['name', 'age', 'address'], }, // becomes t.type() anotherPerson: { @@ -226,33 +216,33 @@ testCase("route with record types", ROUTE_WITH_RECORD_TYPES, { type: 'object', properties: { bigName: { type: 'string' }, - bigAge: { type: 'number' } + bigAge: { type: 'number' }, }, - required: [ 'bigName', 'bigAge' ] + required: ['bigName', 'bigAge'], }, age: { type: 'object', properties: { bigName: { type: 'string' }, - bigAge: { type: 'number' } + bigAge: { type: 'number' }, }, - required: [ 'bigName', 'bigAge' ] + required: ['bigName', 'bigAge'], }, address: { type: 'object', properties: { bigName: { type: 'string' }, - bigAge: { type: 'number' } + bigAge: { type: 'number' }, }, - required: [ 'bigName', 'bigAge' ] - } + required: ['bigName', 'bigAge'], + }, }, - required: [ 'name', 'age', 'address' ] + required: ['name', 'age', 'address'], }, bigPerson: { // stays as t.record() type: 'object', - additionalProperties: { type: 'string' } + additionalProperties: { type: 'string' }, }, anotherBigPerson: { // stays as t.record() @@ -261,34 +251,39 @@ testCase("route with record types", ROUTE_WITH_RECORD_TYPES, { type: 'object', properties: { bigName: { type: 'string' }, - bigAge: { type: 'number' } + bigAge: { type: 'number' }, }, - required: [ 'bigName', 'bigAge' ] - } - } + required: ['bigName', 'bigAge'], + }, + }, }, - required: [ 'person', 'anotherPerson', 'bigPerson', 'anotherBigPerson' ] - } - } - } - } - } - } - } + required: [ + 'person', + 'anotherPerson', + 'bigPerson', + 'anotherBigPerson', + ], + }, + }, + }, + }, + }, + }, + }, }, components: { schemas: { ValidKeys: { title: 'ValidKeys', type: 'string', - enum: [ 'name', 'age', 'address' ] + enum: ['name', 'age', 'address'], }, PersonObject: { title: 'PersonObject', type: 'object', properties: { bigName: { type: 'string' }, bigAge: { type: 'number' } }, - required: [ 'bigName', 'bigAge' ] - } - } - } + required: ['bigName', 'bigAge'], + }, + }, + }, }); diff --git a/packages/openapi-generator/test/openapi/ref.test.ts b/packages/openapi-generator/test/openapi/ref.test.ts index 585c1d98..c52d5369 100644 --- a/packages/openapi-generator/test/openapi/ref.test.ts +++ b/packages/openapi-generator/test/openapi/ref.test.ts @@ -1,4 +1,4 @@ -import { testCase } from "./testHarness"; +import { testCase } from './testHarness'; const SCHEMA_REF = ` import * as t from 'io-ts'; @@ -100,59 +100,59 @@ const Foo = t.string; `; testCase('request body ref with comments', SCHEMA_REF_WITH_COMMENT_AT_DECLARATION, { - openapi: "3.0.3", + openapi: '3.0.3', info: { - title: "Test", - version: "1.0.0" + title: 'Test', + version: '1.0.0', }, paths: { - "/foo": { + '/foo': { get: { parameters: [ { - name: "body", - in: "path", + name: 'body', + in: 'path', required: true, schema: { - type: "string" - } + type: 'string', + }, }, { - name: "size", - description: "Size of the body", - in: "path", + name: 'size', + description: 'Size of the body', + in: 'path', required: true, schema: { - type: "number", - example: 10 - } - } + type: 'number', + example: 10, + }, + }, ], responses: { - "200": { - description: "OK", + '200': { + description: 'OK', content: { - "application/json": { + 'application/json': { schema: { - $ref: "#/components/schemas/Foo" - } - } - } - } - } - } - } + $ref: '#/components/schemas/Foo', + }, + }, + }, + }, + }, + }, + }, }, components: { schemas: { Foo: { - title: "Foo", - type: "string", + title: 'Foo', + type: 'string', description: "a Foo of type 'string'", - example: "foo" - } - } - } + example: 'foo', + }, + }, + }, }); const SCHEMA_DOUBLE_REF = ` @@ -305,7 +305,6 @@ testCase('request body nullable ref', SCHEMA_NULLABLE_REF, { }, }); - const ROUTE_WITH_SCHEMA_WITH_COMMENT = ` import * as t from 'io-ts'; import * as h from '@api-ts/io-ts-http'; @@ -352,16 +351,14 @@ testCase('route with api error schema', ROUTE_WITH_SCHEMA_WITH_COMMENT, { openapi: '3.0.3', info: { title: 'Test', - version: '1.0.0' + version: '1.0.0', }, paths: { '/foo': { get: { summary: 'A simple route with type descriptions for references', operationId: 'api.v1.test', - tags: [ - 'Test Routes' - ], + tags: ['Test Routes'], parameters: [], responses: { '200': { @@ -369,33 +366,33 @@ testCase('route with api error schema', ROUTE_WITH_SCHEMA_WITH_COMMENT, { content: { 'application/json': { schema: { - '$ref': '#/components/schemas/SimpleRouteResponse' - } - } - } + $ref: '#/components/schemas/SimpleRouteResponse', + }, + }, + }, }, '400': { content: { 'application/json': { schema: { - '$ref': '#/components/schemas/ApiError' - } - } + $ref: '#/components/schemas/ApiError', + }, + }, }, - description: 'Bad Request' + description: 'Bad Request', }, '401': { description: 'Unauthorized', content: { 'application/json': { schema: { - $ref: '#/components/schemas/InvalidError' - } - } - } - } - } - } + $ref: '#/components/schemas/InvalidError', + }, + }, + }, + }, + }, + }, }, }, components: { @@ -403,26 +400,22 @@ testCase('route with api error schema', ROUTE_WITH_SCHEMA_WITH_COMMENT, { ApiError: { properties: { error: { - type: 'string' - } + type: 'string', + }, }, - required: [ - 'error' - ], + required: ['error'], title: 'Human Readable Api Error Schema', description: 'Human readable description of the ApiError schema', - type: 'object' + type: 'object', }, SimpleRouteResponse: { description: 'Human readable description of the Simple Route Response', properties: { test: { - type: 'string' - } + type: 'string', + }, }, - required: [ - 'test' - ], + required: ['test'], title: 'Human Readable Simple Route Response', type: 'object', }, @@ -435,20 +428,16 @@ testCase('route with api error schema', ROUTE_WITH_SCHEMA_WITH_COMMENT, { properties: { error: { type: 'string', - enum: [ - 'invalid' - ] - } + enum: ['invalid'], + }, }, - required: [ - 'error' - ] + required: ['error'], }, { - $ref: '#/components/schemas/ApiError' - } + $ref: '#/components/schemas/ApiError', + }, ], }, - } - } -}); \ No newline at end of file + }, + }, +}); diff --git a/packages/openapi-generator/test/openapi/testHarness.ts b/packages/openapi-generator/test/openapi/testHarness.ts index 61fd7bd2..34a9b140 100644 --- a/packages/openapi-generator/test/openapi/testHarness.ts +++ b/packages/openapi-generator/test/openapi/testHarness.ts @@ -2,59 +2,59 @@ import * as E from 'fp-ts/lib/Either'; import assert from 'node:assert/strict'; import test from 'node:test'; import { - convertRoutesToOpenAPI, - parsePlainInitializer, - parseSource, - parseRoute, - Project, - type Route, - type Schema, - } from '../../src'; + convertRoutesToOpenAPI, + parsePlainInitializer, + parseSource, + parseRoute, + Project, + type Route, + type Schema, +} from '../../src'; import { SourceFile } from '../../src/sourceFile'; export async function testCase( - description: string, - src: string, - expected: any, - expectedErrors: string[] = [], - ) { - test(description, async () => { - const sourceFile = await parseSource('./index.ts', src); - if (sourceFile === undefined) { - throw new Error('Failed to parse source file'); - } - const files: Record = { './index.ts': sourceFile }; - const project = new Project(files); - const routes: Route[] = []; - const schemas: Record = {}; - const errors: string[] = []; - for (const symbol of sourceFile.symbols.declarations) { - if (symbol.init !== undefined) { - const routeSchemaE = parsePlainInitializer(project, sourceFile, symbol.init); - if (E.isLeft(routeSchemaE)) { - errors.push(routeSchemaE.left); - continue; - } - if (symbol.comment !== undefined) { - routeSchemaE.right.comment = symbol.comment; - } - const result = parseRoute(project, routeSchemaE.right); - if (E.isLeft(result)) { - schemas[symbol.name] = routeSchemaE.right; - } else { - routes.push(result.right); - } + description: string, + src: string, + expected: any, + expectedErrors: string[] = [], +) { + test(description, async () => { + const sourceFile = await parseSource('./index.ts', src); + if (sourceFile === undefined) { + throw new Error('Failed to parse source file'); + } + const files: Record = { './index.ts': sourceFile }; + const project = new Project(files); + const routes: Route[] = []; + const schemas: Record = {}; + const errors: string[] = []; + for (const symbol of sourceFile.symbols.declarations) { + if (symbol.init !== undefined) { + const routeSchemaE = parsePlainInitializer(project, sourceFile, symbol.init); + if (E.isLeft(routeSchemaE)) { + errors.push(routeSchemaE.left); + continue; + } + if (symbol.comment !== undefined) { + routeSchemaE.right.comment = symbol.comment; + } + const result = parseRoute(project, routeSchemaE.right); + if (E.isLeft(result)) { + schemas[symbol.name] = routeSchemaE.right; + } else { + routes.push(result.right); } } - - const actual = convertRoutesToOpenAPI( - { title: 'Test', version: '1.0.0' }, - [], - routes, - schemas, - ); - - assert.deepEqual(errors, expectedErrors); - assert.deepEqual(actual, expected); - }); - } \ No newline at end of file + } + + const actual = convertRoutesToOpenAPI( + { title: 'Test', version: '1.0.0' }, + [], + routes, + schemas, + ); + + assert.deepEqual(errors, expectedErrors); + assert.deepEqual(actual, expected); + }); +} diff --git a/packages/openapi-generator/test/openapi/union.test.ts b/packages/openapi-generator/test/openapi/union.test.ts index ec715f3c..492a7633 100644 --- a/packages/openapi-generator/test/openapi/union.test.ts +++ b/packages/openapi-generator/test/openapi/union.test.ts @@ -1,4 +1,4 @@ -import { testCase } from "./testHarness"; +import { testCase } from './testHarness'; const SCHEMA_WITH_REDUNDANT_UNIONS = ` import * as t from 'io-ts'; @@ -35,7 +35,7 @@ testCase('route with reduntant response schemas', SCHEMA_WITH_REDUNDANT_UNIONS, openapi: '3.0.3', info: { title: 'Test', - version: '1.0.0' + version: '1.0.0', }, paths: { '/foo': { @@ -46,29 +46,25 @@ testCase('route with reduntant response schemas', SCHEMA_WITH_REDUNDANT_UNIONS, name: 'foo', required: true, schema: { - type: 'string' - } + type: 'string', + }, }, { in: 'query', name: 'bar', required: true, schema: { - type: 'number' - } + type: 'number', + }, }, { in: 'query', name: 'bucket', required: true, schema: { - oneOf: [ - { type: 'string' }, - { type: 'number' }, - { type: 'boolean' } - ] - } - } + oneOf: [{ type: 'string' }, { type: 'number' }, { type: 'boolean' }], + }, + }, ], requestBody: { content: { @@ -80,48 +76,37 @@ testCase('route with reduntant response schemas', SCHEMA_WITH_REDUNDANT_UNIONS, nested: { properties: { bar: { - type: 'number' + type: 'number', }, foo: { - type: 'string' - } + type: 'string', + }, }, - required: [ - 'bar', - 'foo' - ], - type: 'object' - } + required: ['bar', 'foo'], + type: 'object', + }, }, - required: [ - 'nested' - ], - type: 'object' + required: ['nested'], + type: 'object', }, typeUnion: { properties: { bar: { - type: 'number' + type: 'number', }, foo: { - type: 'string' - } + type: 'string', + }, }, - required: [ - 'bar', - 'foo' - ], - type: 'object' - } + required: ['bar', 'foo'], + type: 'object', + }, }, - required: [ - 'typeUnion', - 'nestedTypeUnion' - ], - type: 'object' - } - } - } + required: ['typeUnion', 'nestedTypeUnion'], + type: 'object', + }, + }, + }, }, responses: { '200': { @@ -129,35 +114,37 @@ testCase('route with reduntant response schemas', SCHEMA_WITH_REDUNDANT_UNIONS, content: { 'application/json': { schema: { - oneOf: [{ - type: 'string' - }, { - type: 'number' - }] - } - } - } + oneOf: [ + { + type: 'string', + }, + { + type: 'number', + }, + ], + }, + }, + }, }, '400': { description: 'Bad Request', content: { 'application/json': { schema: { - type: 'boolean' - } - } - } - } - } - } - } + type: 'boolean', + }, + }, + }, + }, + }, + }, + }, }, components: { - schemas: {} - } + schemas: {}, + }, }); - const ROUTE_WITH_CONSOLIDATABLE_UNION_SCHEMAS = ` import * as t from 'io-ts'; import * as h from '@api-ts/io-ts-http'; @@ -188,93 +175,88 @@ export const route = h.httpRoute({ }); `; -testCase("route with consolidatable union schemas", ROUTE_WITH_CONSOLIDATABLE_UNION_SCHEMAS, { - openapi: '3.0.3', - info: { - title: 'Test', - version: '1.0.0' - }, - paths: { - '/foo': { - get: { - parameters: [ - { - name: 'firstUnion', - in: 'query', - required: true, - schema: { - oneOf: [ - { type: 'string' }, - { type: 'number' } - ] - } - }, - { - name: 'secondUnion', - in: 'query', - required: true, - schema: { - oneOf: [ - { type: 'boolean' }, - { type: 'string', format: 'number' } - ] - } - }, - { - name: 'thirdUnion', - in: 'query', - required: true, - schema: { - oneOf: [ - { type: 'string' }, - { type: 'boolean' } - ] - } - }, - { - name: 'firstNonUnion', - in: 'query', - required: true, - schema: { type: 'boolean' } - }, - { - name: 'secondNonUnion', - in: 'query', - required: true, - schema: { type: 'string', format: 'number' } - }, - { - name: 'thirdNonUnion', - in: 'query', - required: true, - schema: { type: 'string' } - } - ], - responses: { - '200': { - description: 'OK', - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - fourthUnion: { type: 'boolean' }, - fifthUnion: { type: 'boolean' }, - sixthUnion: { type: 'number' } +testCase( + 'route with consolidatable union schemas', + ROUTE_WITH_CONSOLIDATABLE_UNION_SCHEMAS, + { + openapi: '3.0.3', + info: { + title: 'Test', + version: '1.0.0', + }, + paths: { + '/foo': { + get: { + parameters: [ + { + name: 'firstUnion', + in: 'query', + required: true, + schema: { + oneOf: [{ type: 'string' }, { type: 'number' }], + }, + }, + { + name: 'secondUnion', + in: 'query', + required: true, + schema: { + oneOf: [{ type: 'boolean' }, { type: 'string', format: 'number' }], + }, + }, + { + name: 'thirdUnion', + in: 'query', + required: true, + schema: { + oneOf: [{ type: 'string' }, { type: 'boolean' }], + }, + }, + { + name: 'firstNonUnion', + in: 'query', + required: true, + schema: { type: 'boolean' }, + }, + { + name: 'secondNonUnion', + in: 'query', + required: true, + schema: { type: 'string', format: 'number' }, + }, + { + name: 'thirdNonUnion', + in: 'query', + required: true, + schema: { type: 'string' }, + }, + ], + responses: { + '200': { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'object', + properties: { + fourthUnion: { type: 'boolean' }, + fifthUnion: { type: 'boolean' }, + sixthUnion: { type: 'number' }, + }, + required: ['fourthUnion', 'sixthUnion'], }, - required: ['fourthUnion', 'sixthUnion'] - } - } - } - } - } - } - } + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: {}, + }, }, - components: { - schemas: {} - } -}); +); const ROUTE_WITH_UNKNOWN_UNIONS = ` import * as t from 'io-ts'; @@ -299,10 +281,10 @@ export const route = h.httpRoute({ }); `; -testCase("route with unknown unions", ROUTE_WITH_UNKNOWN_UNIONS, { +testCase('route with unknown unions', ROUTE_WITH_UNKNOWN_UNIONS, { info: { title: 'Test', - version: '1.0.0' + version: '1.0.0', }, openapi: '3.0.3', paths: { @@ -316,62 +298,58 @@ testCase("route with unknown unions", ROUTE_WITH_UNKNOWN_UNIONS, { schema: { properties: { nested: { - '$ref': '#/components/schemas/NestedUnknownUnion' + $ref: '#/components/schemas/NestedUnknownUnion', }, single: { - '$ref': '#/components/schemas/SingleUnknownUnion' + $ref: '#/components/schemas/SingleUnknownUnion', }, unknown: { - '$ref': '#/components/schemas/UnknownUnion' - } + $ref: '#/components/schemas/UnknownUnion', + }, }, - required: [ - 'single', - 'unknown', - 'nested' - ], - type: 'object' - } - } + required: ['single', 'unknown', 'nested'], + type: 'object', + }, + }, }, - description: 'OK' - } - } - } - } + description: 'OK', + }, + }, + }, + }, }, components: { schemas: { NestedUnknownUnion: { oneOf: [ { - type: 'string' + type: 'string', }, { - type: 'boolean' - } + type: 'boolean', + }, ], - title: 'NestedUnknownUnion' + title: 'NestedUnknownUnion', }, SingleUnknownUnion: { title: 'SingleUnknownUnion', - type: 'string' + type: 'string', }, UnknownUnion: { oneOf: [ { - type: 'string' + type: 'string', }, { - type: 'number' + type: 'number', }, { - type: 'boolean' - } + type: 'boolean', + }, ], - title: 'UnknownUnion' - } - } + title: 'UnknownUnion', + }, + }, }, }); @@ -402,39 +380,48 @@ export const route = h.httpRoute({ }); `; -testCase("route with duplicate headers in request union", ROUTE_WITH_DUPLICATE_HEADERS, { - info: { - title: 'Test', - version: '1.0.0' - }, - openapi: '3.0.3', - paths: { - '/foo': { - get: { - parameters: [ - { in: 'header', name: 'x-foo', required: true, schema: { type: 'string' } }, - { in: 'header', name: 'x-common', required: true, schema: { type: 'string' } }, - { in: 'header', name: 'x-bar', required: true, schema: { type: 'number' } }, - ], - responses: { - '200': { - description: 'OK', - content: { - 'application/json': { - schema: { - type: 'string' - } - } - } - } - } - } - } +testCase( + 'route with duplicate headers in request union', + ROUTE_WITH_DUPLICATE_HEADERS, + { + info: { + title: 'Test', + version: '1.0.0', + }, + openapi: '3.0.3', + paths: { + '/foo': { + get: { + parameters: [ + { in: 'header', name: 'x-foo', required: true, schema: { type: 'string' } }, + { + in: 'header', + name: 'x-common', + required: true, + schema: { type: 'string' }, + }, + { in: 'header', name: 'x-bar', required: true, schema: { type: 'number' } }, + ], + responses: { + '200': { + description: 'OK', + content: { + 'application/json': { + schema: { + type: 'string', + }, + }, + }, + }, + }, + }, + }, + }, + components: { + schemas: {}, + }, }, - components: { - schemas: {} - } -}); +); const ROUTE_WITH_REQUEST_UNION = ` import * as t from 'io-ts'; @@ -458,10 +445,10 @@ export const route = h.httpRoute({ }); `; -testCase("route with request union", ROUTE_WITH_REQUEST_UNION, { +testCase('route with request union', ROUTE_WITH_REQUEST_UNION, { info: { title: 'Test', - version: '1.0.0' + version: '1.0.0', }, openapi: '3.0.3', paths: { @@ -476,16 +463,16 @@ testCase("route with request union", ROUTE_WITH_REQUEST_UNION, { content: { 'application/json': { schema: { - type: 'string' - } - } - } - } - } - } - } + type: 'string', + }, + }, + }, + }, + }, + }, + }, }, components: { - schemas: {} - } + schemas: {}, + }, }); diff --git a/packages/openapi-generator/test/optimize.test.ts b/packages/openapi-generator/test/optimize.test.ts index eaa58af7..d37b44ba 100644 --- a/packages/openapi-generator/test/optimize.test.ts +++ b/packages/openapi-generator/test/optimize.test.ts @@ -155,7 +155,7 @@ test('consolidatable unions are consolidated to single primitive type', () => { required: [], }; - const expected: Schema = { type: 'boolean', }; + const expected: Schema = { type: 'boolean' }; assert.deepEqual(optimize(input), expected); }); @@ -174,8 +174,8 @@ test('non-consolidatable unions are not consolidated', () => { type: 'union', schemas: [ { type: 'string', primitive: true }, - { type: 'string', enum: [ 'true', 'false' ] }, - ] + { type: 'string', enum: ['true', 'false'] }, + ], }; assert.deepEqual(optimize(input), expected); diff --git a/packages/openapi-generator/test/resolve.test.ts b/packages/openapi-generator/test/resolve.test.ts index afe0fdd7..03790306 100644 --- a/packages/openapi-generator/test/resolve.test.ts +++ b/packages/openapi-generator/test/resolve.test.ts @@ -86,7 +86,10 @@ testCase( { FOO: { type: 'union', - schemas: [{ type: 'string', primitive: true }, { type: 'number', primitive: true }], + schemas: [ + { type: 'string', primitive: true }, + { type: 'number', primitive: true }, + ], }, }, ['Unimplemented initializer type ArrayExpression'], diff --git a/packages/openapi-generator/test/sample-types/apiSpecWithCustomCodec.ts b/packages/openapi-generator/test/sample-types/apiSpecWithCustomCodec.ts index d708457d..b7b46abe 100644 --- a/packages/openapi-generator/test/sample-types/apiSpecWithCustomCodec.ts +++ b/packages/openapi-generator/test/sample-types/apiSpecWithCustomCodec.ts @@ -2,15 +2,15 @@ import { SampleCustomCodec, AnotherSampleCodec } from '@bitgo/custom-codecs'; import * as h from '@api-ts/io-ts-http'; export const apiSpec = h.apiSpec({ - 'api.get.test': { - get: h.httpRoute({ - path: '/test', - method: 'GET', - request: h.httpRequest({}), - response: { - 200: SampleCustomCodec, - 201: AnotherSampleCodec, - }, - }), - }, -}) \ No newline at end of file + 'api.get.test': { + get: h.httpRoute({ + path: '/test', + method: 'GET', + request: h.httpRequest({}), + response: { + 200: SampleCustomCodec, + 201: AnotherSampleCodec, + }, + }), + }, +});