diff --git a/.env.example b/.env.example index 2fd3e140..83ac2ddb 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,3 @@ TEST_FILE_NAME=github-swagger.ts -TEST_SCHEMA_VERSION=v3 +TEST_SCHEMA_VERSION=v3.0#v2.0|v3.0 TEST_WITH_DEBUG=true \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 4032ecd3..83cde646 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # next release +feat: `const` keyword OpenAPI 3.0 draft +fix: problem with using `anyOf` +feat: `--extract-responses` (nodejs: `extractResponses`) option to extract all schemas from `/components/responses` +fix: discriminator and mapping with invalid discriminator property name (#551) +fix: problem with incorrect resolving type name of discriminator mapping types data contracts + ## 13.0.0 BREAKING_CHANGE: disable support NodeJS 14.x diff --git a/README.md b/README.md index 12994d4f..379ebd7c 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,8 @@ Options: --debug additional information about processes inside this tool (default: false) --another-array-type generate array types as Array (by default Type[]) (default: false) --sort-types sort fields and types (default: false) + --sort-routes sort routes in alphabetical order (default: false) + --custom-config custom config: primitiveTypeConstructs, hooks, ... (default: "") --extract-enums extract all enums from inline interface\type content to typescript enum construction (default: false) -h, --help display help for command @@ -149,6 +151,8 @@ generateApi({ enumKeyPrefix: '', enumKeySuffix: '', addReadonly: false, + sortTypes: false, + sortRouters: false, extractingOptions: { requestBodySuffix: ["Payload", "Body", "Input"], requestParamsSuffix: ["Params"], diff --git a/index.js b/index.js index 5f382e89..9302d910 100644 --- a/index.js +++ b/index.js @@ -114,6 +114,12 @@ const program = cli({ default: codeGenBaseConfig.extractResponseError, internal: { formatter: Boolean }, }, + { + flags: '--extract-responses', + description: 'extract all responses described in /components/responses', + default: codeGenBaseConfig.extractResponses, + internal: { formatter: Boolean }, + }, { flags: '--modular', description: diff --git a/package.json b/package.json index 965fd513..27ecda64 100644 --- a/package.json +++ b/package.json @@ -40,6 +40,7 @@ "test:--extract-response-error": "node tests/spec/extractResponseError/test.js", "test:--enum-names-as-values": "node tests/spec/enumNamesAsValues/test.js", "test:--default-response": "node tests/spec/defaultResponse/test.js", + "test:const-keyword": "node tests/spec/const-keyword/test.js", "test:--js": "node tests/spec/js/test.js", "test:jsSingleHttpClientModular": "node tests/spec/jsSingleHttpClientModular/test.js", "test:--js--axios": "node tests/spec/jsAxios/test.js", diff --git a/src/code-gen-process.js b/src/code-gen-process.js index a8e0a7b6..22d12b49 100644 --- a/src/code-gen-process.js +++ b/src/code-gen-process.js @@ -116,9 +116,14 @@ class CodeGenProcess { }), ); - const schemaComponents = this.schemaComponentsMap.filter('schemas'); + /** + * @type {SchemaComponent[]} + */ + const componentsToParse = this.schemaComponentsMap.filter( + _.compact(['schemas', this.config.extractResponses && 'responses']), + ); - const parsedSchemas = schemaComponents.map((schemaComponent) => { + const parsedSchemas = componentsToParse.map((schemaComponent) => { const parsed = this.schemaParserFabric.parseSchema( schemaComponent.rawTypeData, schemaComponent.typeName, @@ -234,8 +239,13 @@ class CodeGenProcess { const components = this.schemaComponentsMap.getComponents(); let modelTypes = []; + const modelTypeComponents = _.compact([ + 'schemas', + this.config.extractResponses && 'responses', + ]); + const getSchemaComponentsCount = () => - components.filter((c) => c.componentName === 'schemas').length; + this.schemaComponentsMap.filter(...modelTypeComponents).length; let schemaComponentsCount = getSchemaComponentsCount(); let processedCount = 0; @@ -244,7 +254,7 @@ class CodeGenProcess { modelTypes = []; processedCount = 0; for (const component of components) { - if (component.componentName === 'schemas') { + if (modelTypeComponents.includes(component.componentName)) { const modelType = this.prepareModelType(component); if (modelType) { modelTypes.push(modelType); diff --git a/src/configuration.js b/src/configuration.js index 44bf5735..10e52905 100644 --- a/src/configuration.js +++ b/src/configuration.js @@ -74,6 +74,7 @@ class CodeGenConfig { extractRequestBody = false; extractResponseBody = false; extractResponseError = false; + extractResponses = false; extractEnums = false; fileNames = { dataContracts: 'data-contracts', @@ -240,7 +241,7 @@ class CodeGenConfig { /** * $A */ - NullValue: (content) => content, + NullValue: (content) => `null`, /** * $A1 | $A2 */ @@ -276,6 +277,9 @@ class CodeGenConfig { */ InterfaceDynamicField: (key, value) => `[key: ${key}]: ${value}`, + /** + * EnumName.EnumKey + */ EnumUsageKey: (enumStruct, key) => `${enumStruct}.${key}`, /** * $A1 = $A2 diff --git a/src/schema-components-map.js b/src/schema-components-map.js index fee41793..7062788d 100644 --- a/src/schema-components-map.js +++ b/src/schema-components-map.js @@ -57,12 +57,14 @@ class SchemaComponentsMap { } /** - * @param componentName {string} + * @params {...string[]} componentNames * @returns {SchemaComponent[]} */ - filter(componentName) { - return _.filter(this._data, (v) => - _.startsWith(v.$ref, `#/components/${componentName}`), + filter(...componentNames) { + return _.filter(this._data, (it) => + componentNames.some((componentName) => + _.startsWith(it.$ref, `#/components/${componentName}`), + ), ); } diff --git a/src/schema-parser/base-schema-parsers/discriminator.js b/src/schema-parser/base-schema-parsers/discriminator.js index d6c247d9..e644983d 100644 --- a/src/schema-parser/base-schema-parsers/discriminator.js +++ b/src/schema-parser/base-schema-parsers/discriminator.js @@ -4,6 +4,7 @@ const { MonoSchemaParser } = require('../mono-schema-parser'); class DiscriminatorSchemaParser extends MonoSchemaParser { parse() { + const ts = this.config.Ts; const { discriminator, ...noDiscriminatorSchema } = this.schema; if (!discriminator.mapping) { @@ -27,7 +28,7 @@ class DiscriminatorSchemaParser extends MonoSchemaParser { abstractSchemaStruct, }); - const schemaContent = this.config.Ts.IntersectionType( + const schemaContent = ts.IntersectionType( [ abstractSchemaStruct?.content, discriminatorSchemaStruct?.content, @@ -40,7 +41,7 @@ class DiscriminatorSchemaParser extends MonoSchemaParser { $parsedSchema: true, schemaType: SCHEMA_TYPES.COMPLEX, type: SCHEMA_TYPES.PRIMITIVE, - typeIdentifier: this.config.Ts.Keyword.Type, + typeIdentifier: ts.Keyword.Type, name: this.typeName, description: this.schemaFormatters.formatDescription( this.schema.description, @@ -50,6 +51,8 @@ class DiscriminatorSchemaParser extends MonoSchemaParser { } createDiscriminatorSchema = ({ skipMappingType, abstractSchemaStruct }) => { + const ts = this.config.Ts; + const refPath = this.schemaComponentsMap.createRef([ 'components', 'schemas', @@ -71,26 +74,26 @@ class DiscriminatorSchemaParser extends MonoSchemaParser { }); if (ableToCreateMappingType) { - mappingTypeName = this.schemaUtils.resolveTypeName( - `${abstractSchemaStruct.typeName} ${discriminator.propertyName}`, - { - suffixes: this.config.extractingOptions.discriminatorMappingSuffix, - resolver: - this.config.extractingOptions.discriminatorMappingNameResolver, - }, - ); - this.schemaParserFabric.createSchema({ - linkedComponent: this.schemaComponentsMap.createComponent( - this.schemaComponentsMap.createRef([ - 'components', - 'schemas', - mappingTypeName, - ]), + const mappingTypeNameRef = this.schemaComponentsMap.createRef([ + 'components', + 'schemas', + this.schemaUtils.resolveTypeName( + `${abstractSchemaStruct.typeName} ${discriminator.propertyName}`, + { + suffixes: this.config.extractingOptions.discriminatorMappingSuffix, + resolver: + this.config.extractingOptions.discriminatorMappingNameResolver, + }, ), - content: this.config.Ts.IntersectionType([ - this.config.Ts.ObjectWrapper( - this.config.Ts.TypeField({ - key: discriminator.propertyName, + ]); + const mappingTypeNameComponent = + this.schemaComponentsMap.createComponent(mappingTypeNameRef); + const mappingTypeNameSchema = this.schemaParserFabric.createSchema({ + linkedComponent: mappingTypeNameComponent, + content: ts.IntersectionType([ + ts.ObjectWrapper( + ts.TypeField({ + key: ts.StringValue(discriminator.propertyName), value: 'Key', }), ), @@ -99,6 +102,8 @@ class DiscriminatorSchemaParser extends MonoSchemaParser { genericArgs: [{ name: 'Key' }, { name: 'Type' }], internal: true, }); + + mappingTypeName = mappingTypeNameSchema.typeData.name; } /** returns (GenericType<"mapping_key", MappingType>) or ({ discriminatorProperty: "mapping_key" } & MappingType) */ @@ -112,18 +117,15 @@ class DiscriminatorSchemaParser extends MonoSchemaParser { const mappingUsageKey = mappingPropertySchemaEnumKeysMap[mappingKey] || - this.config.Ts.StringValue(mappingKey); + ts.StringValue(mappingKey); if (ableToCreateMappingType) { - return this.config.Ts.TypeWithGeneric(mappingTypeName, [ - mappingUsageKey, - content, - ]); + return ts.TypeWithGeneric(mappingTypeName, [mappingUsageKey, content]); } else { - return this.config.Ts.ExpressionGroup( - this.config.Ts.IntersectionType([ - this.config.Ts.ObjectWrapper( - this.config.Ts.TypeField({ + return ts.ExpressionGroup( + ts.IntersectionType([ + ts.ObjectWrapper( + ts.TypeField({ key: discriminator.propertyName, value: mappingUsageKey, }), @@ -151,9 +153,7 @@ class DiscriminatorSchemaParser extends MonoSchemaParser { if (skipMappingType) return null; - const content = this.config.Ts.ExpressionGroup( - this.config.Ts.UnionType(mappingContents), - ); + const content = ts.ExpressionGroup(ts.UnionType(mappingContents)); return { content, @@ -164,6 +164,8 @@ class DiscriminatorSchemaParser extends MonoSchemaParser { abstractSchemaStruct, discPropertyName, }) => { + const ts = this.config.Ts; + let mappingPropertySchemaEnumKeysMap = {}; let mappingPropertySchema = _.get( abstractSchemaStruct?.component?.rawTypeData, @@ -183,7 +185,7 @@ class DiscriminatorSchemaParser extends MonoSchemaParser { (acc, key, index) => { const enumKey = mappingPropertySchema.rawTypeData.$parsed.content[index].key; - acc[key] = this.config.Ts.EnumUsageKey( + acc[key] = ts.EnumUsageKey( mappingPropertySchema.rawTypeData.$parsed.typeName, enumKey, ); @@ -284,12 +286,13 @@ class DiscriminatorSchemaParser extends MonoSchemaParser { }; createComplexSchemaStruct = () => { + const ts = this.config.Ts; const complexType = this.schemaUtils.getComplexType(this.schema); if (complexType === SCHEMA_TYPES.COMPLEX_UNKNOWN) return null; return { - content: this.config.Ts.ExpressionGroup( + content: ts.ExpressionGroup( this.schemaParser._complexSchemaParsers[complexType](this.schema), ), }; diff --git a/src/schema-parser/complex-schema-parsers/any-of.js b/src/schema-parser/complex-schema-parsers/any-of.js index 6a684274..4d81b3da 100644 --- a/src/schema-parser/complex-schema-parsers/any-of.js +++ b/src/schema-parser/complex-schema-parsers/any-of.js @@ -1,7 +1,7 @@ const { MonoSchemaParser } = require('../mono-schema-parser'); const _ = require('lodash'); -// T1 | T2 | (T1 & T2) +// T1 | T2 class AnyOfSchemaParser extends MonoSchemaParser { parse() { const ignoreTypes = [this.config.Ts.Keyword.Any]; @@ -12,20 +12,13 @@ class AnyOfSchemaParser extends MonoSchemaParser { this.schemaPath, ), ); + const filtered = this.schemaUtils.filterSchemaContents( combined, (content) => !ignoreTypes.includes(content), ); - const type = this.config.Ts.UnionType( - _.compact([ - ...filtered, - filtered.length > 1 && - this.config.Ts.ExpressionGroup( - this.config.Ts.IntersectionType(filtered), - ), - ]), - ); + const type = this.config.Ts.UnionType(filtered); return this.schemaUtils.safeAddNullToType(this.schema, type); } diff --git a/src/schema-parser/schema-parser.js b/src/schema-parser/schema-parser.js index d27d310b..53e4a969 100644 --- a/src/schema-parser/schema-parser.js +++ b/src/schema-parser/schema-parser.js @@ -193,10 +193,9 @@ class SchemaParser { this.typeName = this.schemaUtils.getSchemaType(this.schema); } - /** - * swagger schemas fixes - * ----> - */ + //#region swagger schemas fixes + + // schema has items but don't have array type if ( this.schema.items && !Array.isArray(this.schema.items) && @@ -204,6 +203,7 @@ class SchemaParser { ) { this.schema.type = SCHEMA_TYPES.ARRAY; } + // schema is enum with one null value if ( Array.isArray(this.schema.enum) && this.schema.enum.length === 1 && @@ -212,9 +212,22 @@ class SchemaParser { this.logger.debug('invalid enum schema', this.schema); this.schema = { type: this.config.Ts.Keyword.Null }; } - /** - * <---- - */ + // schema is response schema + if ( + 'content' in this.schema && + typeof this.schema['content'] === 'object' + ) { + const schema = this.extractSchemaFromResponseStruct(this.schema); + const schemaParser = this.schemaParserFabric.createSchemaParser({ + schema, + typeName: this.typeName, + schemaPath: this.schemaPath, + }); + this.schema.$parsed = schemaParser.parseSchema(); + return this.schema.$parsed; + } + + //#endregion schemaType = this.schemaUtils.getInternalSchemaType(this.schema); @@ -268,6 +281,21 @@ class SchemaParser { ); return formattedSchema.content; }; + + extractSchemaFromResponseStruct = (responseStruct) => { + const { content, ...extras } = responseStruct; + + const firstResponse = _.first(_.values(content)); + const firstSchema = _.get(firstResponse, 'schema'); + + if (!firstSchema) return; + + return { + ...extras, + ..._.omit(firstResponse, 'schema'), + ...firstSchema, + }; + }; } module.exports = { diff --git a/src/schema-parser/schema-utils.js b/src/schema-parser/schema-utils.js index c5c02643..79133645 100644 --- a/src/schema-parser/schema-utils.js +++ b/src/schema-parser/schema-utils.js @@ -248,24 +248,32 @@ class SchemaUtils { ); } - const primitiveType = this.getSchemaPrimitiveType(schema); - - if (primitiveType == null) return this.config.Ts.Keyword.Any; - let resultType; - const typeAlias = - _.get(this.config.primitiveTypes, [primitiveType, schema.format]) || - _.get(this.config.primitiveTypes, [primitiveType, '$default']) || - this.config.primitiveTypes[primitiveType]; - - if (_.isFunction(typeAlias)) { - resultType = typeAlias(schema, this); + if (this.isConstantSchema(schema)) { + resultType = this.formatJsValue(schema.const); } else { - resultType = typeAlias || primitiveType; + const primitiveType = this.getSchemaPrimitiveType(schema); + + if (primitiveType == null) { + return this.config.Ts.Keyword.Any; + } + + const typeAlias = + _.get(this.config.primitiveTypes, [primitiveType, schema.format]) || + _.get(this.config.primitiveTypes, [primitiveType, '$default']) || + this.config.primitiveTypes[primitiveType]; + + if (_.isFunction(typeAlias)) { + resultType = typeAlias(schema, this); + } else { + resultType = typeAlias || primitiveType; + } } - if (!resultType) return this.config.Ts.Keyword.Any; + if (!resultType) { + return this.config.Ts.Keyword.Any; + } return this.checkAndAddRequiredKeys( schema, @@ -284,6 +292,31 @@ class SchemaUtils { ), ); }; + + isConstantSchema(schema) { + return 'const' in schema; + } + + formatJsValue = (value) => { + switch (typeof value) { + case 'string': { + return this.config.Ts.StringValue(value); + } + case 'boolean': { + return this.config.Ts.BooleanValue(value); + } + case 'number': { + return this.config.Ts.NumberValue(value); + } + default: { + if (value === null) { + return this.config.Ts.NullValue(value); + } + + return this.config.Ts.Keyword.Any; + } + } + }; } module.exports = { diff --git a/src/schema-routes/schema-routes.js b/src/schema-routes/schema-routes.js index ba857e36..365f2328 100644 --- a/src/schema-routes/schema-routes.js +++ b/src/schema-routes/schema-routes.js @@ -1201,22 +1201,19 @@ class SchemaRoutes { routeGroups.outOfModule = this.sortRoutes(routeGroups.outOfModule); } if (routeGroups.combined) { - routeGroups.combined = this.sortRoutes(routeGroups.combined); + _.each(routeGroups.combined, (routeGroup) => { + routeGroup.routes = this.sortRoutes(routeGroup.routes); + }); } } return routeGroups; }; - sortRoutes = (routeInfo) => { - if (routeInfo) { - routeInfo.forEach((routeInfo) => { - routeInfo.routes.sort((routeA, routeB) => - routeA.routeName.usage.localeCompare(routeB.routeName.usage), - ); - }); - } - return routeInfo; + sortRoutes = (routes) => { + return _.slice(routes).sort((routeA, routeB) => + routeA.routeName.usage.localeCompare(routeB.routeName.usage), + ); }; } diff --git a/src/type-name-formatter.js b/src/type-name-formatter.js index 49bc1aee..c3d23525 100644 --- a/src/type-name-formatter.js +++ b/src/type-name-formatter.js @@ -41,7 +41,13 @@ class TypeNameFormatter { ? this.config.enumKeySuffix : this.config.typeSuffix; - const hashKey = `${typePrefix}_${name}_${typeSuffix}`; + const existedFormattedEntry = Array.from( + this.formattedModelNamesMap.entries(), + ).find((entry) => entry[1] === name); + + const hashKey = existedFormattedEntry + ? existedFormattedEntry[0] + : `${typePrefix}_${name}_${typeSuffix}`; if (typeof name !== 'string') { this.logger.warn('wrong name of the model name', name); diff --git a/tests/allSchemas.js b/tests/allSchemas.js index 52229edc..fdeb31cb 100644 --- a/tests/allSchemas.js +++ b/tests/allSchemas.js @@ -16,13 +16,17 @@ let allSchemas = [ ]; if (process.env.TEST_FILE_NAME) { - console.warn("TEST ONLY", process.env.TEST_FILE_NAME); allSchemas = allSchemas.filter((schema) => schema.apiFileName === process.env.TEST_FILE_NAME); + console.warn("TEST ONLY", process.env.TEST_FILE_NAME, 'found:', allSchemas.map(it => it.apiFileName).join(', ') || ''); } if (process.env.TEST_SCHEMA_VERSION) { - console.warn("TEST ONLY", process.env.TEST_SCHEMA_VERSION); allSchemas = allSchemas.filter((schema) => schema.outputPath.endsWith(process.env.TEST_SCHEMA_VERSION)); + console.warn("TEST ONLY", process.env.TEST_SCHEMA_VERSION, 'found:', allSchemas.map(it => it.apiFileName).join(', ') || ''); +} + +if (!allSchemas.length) { + console.warn("TEST SCHEMES NOT FOUND") } module.exports = allSchemas; diff --git a/tests/generate-extended.js b/tests/generate-extended.js index b6b9a774..1b98ce12 100644 --- a/tests/generate-extended.js +++ b/tests/generate-extended.js @@ -23,6 +23,9 @@ allSchemas.forEach(async ({ absolutePath, apiFileName, outputPath }) => { const input = absolutePath; const output = outputPath; + const typePrefix = 'IMySuperPrefix' + const typeSuffix = 'MySuperSuffix' + await generateApiForTest({ name: name, input: input, @@ -36,25 +39,41 @@ allSchemas.forEach(async ({ absolutePath, apiFileName, outputPath }) => { extractEnums: true, extractRequestParams: true, extractResponseError: true, - typePrefix: "IMySuperPrefix", - typeSuffix: "MySuperSuffix", + extractResponses: true, + typePrefix: typePrefix, + typeSuffix: typeSuffix, sortTypes: true, + sortRoutes: true, debugExtras: ["generate-extended", apiFileName], }).then((result) => { result.configuration.modelTypes.forEach((modelType) => { if (modelType.name) { - if (modelType.name.startsWith("IMySuperPrefixIMySuperPrefix")) { + if (modelType.name.startsWith(`${typePrefix}${typePrefix}`)) { + throw new GenerateExtendedError( + `[${outputPath}][${apiFileName}] modelType has prefix/suffix duplicates - ${modelType.name}`, + output, + name, + ); + } + if (!modelType.name.startsWith(typePrefix)) { + throw new GenerateExtendedError( + `[${outputPath}][${apiFileName}] modelType has not prefix/suffix - ${modelType.name}`, + output, + name, + ); + } + if (modelType.name.endsWith(`${typeSuffix}${typeSuffix}`)) { throw new GenerateExtendedError( - `[${outputPath}][${apiFileName}] modelType has prefix/suffix duplicates - ${modelType.name}`, - output, - name, + `[${outputPath}][${apiFileName}] modelType has prefix/suffix duplicates - ${modelType.name}`, + output, + name, ); } - if (!modelType.name.startsWith("IMySuperPrefix")) { + if (!modelType.name.endsWith(typeSuffix)) { throw new GenerateExtendedError( - `[${outputPath}][${apiFileName}] modelType has not prefix/suffix - ${modelType.name}`, - output, - name, + `[${outputPath}][${apiFileName}] modelType has not prefix/suffix - ${modelType.name}`, + output, + name, ); } } diff --git a/tests/generated/v3.0/additional-properties2.ts b/tests/generated/v3.0/additional-properties2.ts index ad285a96..a891006a 100644 --- a/tests/generated/v3.0/additional-properties2.ts +++ b/tests/generated/v3.0/additional-properties2.ts @@ -9,7 +9,7 @@ * --------------------------------------------------------------- */ -export type Primitive = string | number | boolean | null | (string & number & boolean & null); +export type Primitive = string | number | boolean | null; export type PrimitiveMap = Record; diff --git a/tests/generated/v3.0/anyof-example.ts b/tests/generated/v3.0/anyof-example.ts index 5bb06a41..30f52d5e 100644 --- a/tests/generated/v3.0/anyof-example.ts +++ b/tests/generated/v3.0/anyof-example.ts @@ -241,7 +241,7 @@ export class Api extends HttpClient + petsPartialUpdate: (data: PetByAge | PetByType, params: RequestParams = {}) => this.request({ path: `/pets`, method: "PATCH", diff --git a/tests/generated/v3.0/full-swagger-scheme.ts b/tests/generated/v3.0/full-swagger-scheme.ts index ae1543fd..744e78f9 100644 --- a/tests/generated/v3.0/full-swagger-scheme.ts +++ b/tests/generated/v3.0/full-swagger-scheme.ts @@ -3021,7 +3021,7 @@ export interface Installation { * @example "https://api.github.com/installations/1/access_tokens" */ access_tokens_url: string; - account: SimpleUser | Enterprise | (SimpleUser & Enterprise) | null; + account: SimpleUser | Enterprise | null; /** @example 1 */ app_id: number; /** @example "github-actions" */ @@ -17831,13 +17831,6 @@ export class Api extends HttpClient extends HttpClient extends HttpClient diff --git a/tests/generated/v3.0/personal-api-example.ts b/tests/generated/v3.0/personal-api-example.ts index b570ce9e..7f4cd844 100644 --- a/tests/generated/v3.0/personal-api-example.ts +++ b/tests/generated/v3.0/personal-api-example.ts @@ -186,7 +186,7 @@ export type TestAllOfDc2 = FooBarBaz & { prop?: string; }; -export type TestAnyOfDc = (FooBarBaz | FooBar | (FooBarBaz & FooBar)) & { +export type TestAnyOfDc = (FooBarBaz | FooBar) & { prop?: string; }; diff --git a/tests/spec/const-keyword/expected.ts b/tests/spec/const-keyword/expected.ts new file mode 100644 index 00000000..d22e7c78 --- /dev/null +++ b/tests/spec/const-keyword/expected.ts @@ -0,0 +1,91 @@ +/* eslint-disable */ +/* tslint:disable */ +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +export interface ObjTest { + /** + * title + * description + * @default "main" + */ + page_type?: "main"; + /** + * title + * description + */ + page_type_nullable?: "main" | null; +} + +/** + * title + * description + * @default "string" + */ +export type TestString = "string"; + +/** + * title + * description + */ +export type TestStringNullable = "string" | null; + +/** + * title + * description + */ +export type TestBooleanNullable = false | null; + +/** + * title + * description + * @default false + */ +export type TestBooleanFalse = false; + +/** + * title + * description + * @default true + */ +export type TestBooleanTrue = true; + +/** + * title + * description + * @default 5 + */ +export type TestNumber5 = 5; + +/** + * title + * description + */ +export type TestNumberNullable = 666 | null; + +/** + * title + * description + * @default 0 + */ +export type TestNumber0 = 0; + +/** + * title + * description + * @default 10 + */ +export type TestNumber10 = 10; + +/** + * title + * description + * @default null + */ +export type TestNull = null; diff --git a/tests/spec/const-keyword/schema.json b/tests/spec/const-keyword/schema.json new file mode 100644 index 00000000..a68d5140 --- /dev/null +++ b/tests/spec/const-keyword/schema.json @@ -0,0 +1,88 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "Nullable Refs Example", + "version": "1.0.0" + }, + "components": { + "schemas": { + "obj-test": { + "type": "object", + "properties": { + "page_type": { + "const": "main", + "default": "main", + "description": "description", + "title": "title" + }, + "page_type_nullable": { + "const": "main", + "description": "description", + "title": "title", + "nullable": true + } + } + }, + "test-string": { + "const": "string", + "default": "string", + "description": "description", + "title": "title" + }, + "test-string-nullable": { + "const": "string", + "description": "description", + "title": "title", + "nullable": true + }, + "test-boolean-nullable": { + "const": false, + "description": "description", + "title": "title", + "nullable": true + }, + "test-boolean-false": { + "const": false, + "default": false, + "description": "description", + "title": "title" + }, + "test-boolean-true": { + "const": true, + "default": true, + "description": "description", + "title": "title" + }, + "test-number-5": { + "const": 5, + "default": 5, + "description": "description", + "title": "title" + }, + "test-number-nullable": { + "const": 666, + "description": "description", + "title": "title", + "nullable": true, + }, + "test-number-0": { + "const": 0, + "default": 0, + "description": "description", + "title": "title" + }, + "test-number-10": { + "const": 10, + "default": 10, + "description": "description", + "title": "title" + }, + "test-null": { + "const": null, + "default": null, + "description": "description", + "title": "title" + } + } + } +} \ No newline at end of file diff --git a/tests/spec/const-keyword/schema.ts b/tests/spec/const-keyword/schema.ts new file mode 100644 index 00000000..d22e7c78 --- /dev/null +++ b/tests/spec/const-keyword/schema.ts @@ -0,0 +1,91 @@ +/* eslint-disable */ +/* tslint:disable */ +/* + * --------------------------------------------------------------- + * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## + * ## ## + * ## AUTHOR: acacode ## + * ## SOURCE: https://github.com/acacode/swagger-typescript-api ## + * --------------------------------------------------------------- + */ + +export interface ObjTest { + /** + * title + * description + * @default "main" + */ + page_type?: "main"; + /** + * title + * description + */ + page_type_nullable?: "main" | null; +} + +/** + * title + * description + * @default "string" + */ +export type TestString = "string"; + +/** + * title + * description + */ +export type TestStringNullable = "string" | null; + +/** + * title + * description + */ +export type TestBooleanNullable = false | null; + +/** + * title + * description + * @default false + */ +export type TestBooleanFalse = false; + +/** + * title + * description + * @default true + */ +export type TestBooleanTrue = true; + +/** + * title + * description + * @default 5 + */ +export type TestNumber5 = 5; + +/** + * title + * description + */ +export type TestNumberNullable = 666 | null; + +/** + * title + * description + * @default 0 + */ +export type TestNumber0 = 0; + +/** + * title + * description + * @default 10 + */ +export type TestNumber10 = 10; + +/** + * title + * description + * @default null + */ +export type TestNull = null; diff --git a/tests/spec/const-keyword/test.js b/tests/spec/const-keyword/test.js new file mode 100644 index 00000000..ff7785e3 --- /dev/null +++ b/tests/spec/const-keyword/test.js @@ -0,0 +1,22 @@ +const { generateApiForTest } = require("../../helpers/generateApiForTest"); +const { resolve } = require("path"); +const validateGeneratedModule = require("../../helpers/validateGeneratedModule"); +const assertGeneratedModule = require("../../helpers/assertGeneratedModule"); +const createSchemaInfos = require("../../helpers/createSchemaInfos"); + +const schemas = createSchemaInfos({ absolutePathToSchemas: resolve(__dirname, "./") }); + +schemas.forEach(({ absolutePath, apiFileName }) => { + generateApiForTest({ + testName: "const-keyword test", + silent: true, + name: apiFileName, + input: absolutePath, + output: resolve(__dirname, "./"), + addReadonly: true, + generateClient: false, + }).then(() => { + validateGeneratedModule(resolve(__dirname, `./${apiFileName}`)); + assertGeneratedModule(resolve(__dirname, `./${apiFileName}`), resolve(__dirname, `./expected.ts`)); + }); +}); diff --git a/tests/spec/discriminator/expected.ts b/tests/spec/discriminator/expected.ts index 4d63f0ad..637ccd67 100644 --- a/tests/spec/discriminator/expected.ts +++ b/tests/spec/discriminator/expected.ts @@ -107,6 +107,12 @@ export type LizardWithEnum = BasePetWithEnum & { lovesRocks?: boolean; }; +export type InvalidDiscriminatorPropertyName = BaseInvalidDiscriminatorPropertyName & + ( + | BaseInvalidDiscriminatorPropertyNameTypeMapping<"num", number> + | BaseInvalidDiscriminatorPropertyNameTypeMapping<"str", string> + ); + interface BaseBlockDtoWithEnum { title: string; type: BlockDTOEnum; @@ -139,3 +145,9 @@ interface BasePetWithEnum { type BasePetWithEnumPetTypeMapping = { pet_type: Key; } & Type; + +type BaseInvalidDiscriminatorPropertyName = object; + +type BaseInvalidDiscriminatorPropertyNameTypeMapping = { + "@type": Key; +} & Type; diff --git a/tests/spec/discriminator/schema.json b/tests/spec/discriminator/schema.json index 3de9823b..d35c3c04 100644 --- a/tests/spec/discriminator/schema.json +++ b/tests/spec/discriminator/schema.json @@ -363,6 +363,20 @@ } } ] + }, + "InvalidDiscriminatorPropertyName": { + "type": "object", + "discriminator": { + "propertyName": "@type", + "mapping": { + "num": { + "type": "number" + }, + "str": { + "type": "string" + } + } + } } } } diff --git a/tests/spec/discriminator/schema.ts b/tests/spec/discriminator/schema.ts index 4d63f0ad..637ccd67 100644 --- a/tests/spec/discriminator/schema.ts +++ b/tests/spec/discriminator/schema.ts @@ -107,6 +107,12 @@ export type LizardWithEnum = BasePetWithEnum & { lovesRocks?: boolean; }; +export type InvalidDiscriminatorPropertyName = BaseInvalidDiscriminatorPropertyName & + ( + | BaseInvalidDiscriminatorPropertyNameTypeMapping<"num", number> + | BaseInvalidDiscriminatorPropertyNameTypeMapping<"str", string> + ); + interface BaseBlockDtoWithEnum { title: string; type: BlockDTOEnum; @@ -139,3 +145,9 @@ interface BasePetWithEnum { type BasePetWithEnumPetTypeMapping = { pet_type: Key; } & Type; + +type BaseInvalidDiscriminatorPropertyName = object; + +type BaseInvalidDiscriminatorPropertyNameTypeMapping = { + "@type": Key; +} & Type; diff --git a/tests/spec/enumNamesAsValues/expected.ts b/tests/spec/enumNamesAsValues/expected.ts index 8e5dcb1b..ceceaaf0 100644 --- a/tests/spec/enumNamesAsValues/expected.ts +++ b/tests/spec/enumNamesAsValues/expected.ts @@ -17,7 +17,7 @@ export type TestAllOfDc2 = FooBarBaz & { prop?: string; }; -export type TestAnyOfDc = (FooBarBaz | FooBar | (FooBarBaz & FooBar)) & { +export type TestAnyOfDc = (FooBarBaz | FooBar) & { prop?: string; }; diff --git a/tests/spec/enumNamesAsValues/schema.ts b/tests/spec/enumNamesAsValues/schema.ts index 8e5dcb1b..ceceaaf0 100644 --- a/tests/spec/enumNamesAsValues/schema.ts +++ b/tests/spec/enumNamesAsValues/schema.ts @@ -17,7 +17,7 @@ export type TestAllOfDc2 = FooBarBaz & { prop?: string; }; -export type TestAnyOfDc = (FooBarBaz | FooBar | (FooBarBaz & FooBar)) & { +export type TestAnyOfDc = (FooBarBaz | FooBar) & { prop?: string; }; diff --git a/tests/spec/nullable-3.0/expected.ts b/tests/spec/nullable-3.0/expected.ts index 53b5543f..2f34b79d 100644 --- a/tests/spec/nullable-3.0/expected.ts +++ b/tests/spec/nullable-3.0/expected.ts @@ -23,7 +23,7 @@ export interface TestObject { otherObjectMaybeNullA: OtherObject | null; otherObjectMaybeNullB: OtherObject | null; otherObjectMaybeNullC: OtherObject | null; - otherObjectMaybeNullD: OtherObject | null | (OtherObject & null); + otherObjectMaybeNullD: OtherObject | null; } export type OtherObject = object; diff --git a/tests/spec/nullable-3.0/schema.ts b/tests/spec/nullable-3.0/schema.ts index 53b5543f..2f34b79d 100644 --- a/tests/spec/nullable-3.0/schema.ts +++ b/tests/spec/nullable-3.0/schema.ts @@ -23,7 +23,7 @@ export interface TestObject { otherObjectMaybeNullA: OtherObject | null; otherObjectMaybeNullB: OtherObject | null; otherObjectMaybeNullC: OtherObject | null; - otherObjectMaybeNullD: OtherObject | null | (OtherObject & null); + otherObjectMaybeNullD: OtherObject | null; } export type OtherObject = object; diff --git a/tests/spec/object-types/expected.ts b/tests/spec/object-types/expected.ts index 88a023bf..82bb8b9f 100644 --- a/tests/spec/object-types/expected.ts +++ b/tests/spec/object-types/expected.ts @@ -11,6 +11,13 @@ type UtilRequiredKeys = Omit & Required>; +/** + * title + * description + * @example "https://ya.ru/a.png" + */ +export type AnyOfWithNullable = string | null; + /** https://github.com/acacode/swagger-typescript-api/issues/445 */ export interface SpecificEnum1 { myEnum?: ["foo", "bar", "baz"]; diff --git a/tests/spec/object-types/schema.json b/tests/spec/object-types/schema.json index 001ca9f7..f9c9dd05 100644 --- a/tests/spec/object-types/schema.json +++ b/tests/spec/object-types/schema.json @@ -19,6 +19,20 @@ "produces": ["application/json"], "paths": {}, "definitions": { + "AnyOfWithNullable": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "null" + } + ], + "description": "description", + "example": "https://ya.ru/a.png", + "nullable": true, + "title": "title" + }, "SpecificEnum1": { "type": "object", "description": "https://github.com/acacode/swagger-typescript-api/issues/445", diff --git a/tests/spec/object-types/schema.ts b/tests/spec/object-types/schema.ts index 88a023bf..82bb8b9f 100644 --- a/tests/spec/object-types/schema.ts +++ b/tests/spec/object-types/schema.ts @@ -11,6 +11,13 @@ type UtilRequiredKeys = Omit & Required>; +/** + * title + * description + * @example "https://ya.ru/a.png" + */ +export type AnyOfWithNullable = string | null; + /** https://github.com/acacode/swagger-typescript-api/issues/445 */ export interface SpecificEnum1 { myEnum?: ["foo", "bar", "baz"];