From 614aac28717acc1f4cdafd149770ccb4b5f69f9b Mon Sep 17 00:00:00 2001 From: js2me Date: Wed, 30 Nov 2022 14:23:52 +0600 Subject: [PATCH] fix: try to fix problem with extracting enums --- src/code-gen-process.js | 92 +++------ src/data-contracts.js | 139 ++++++++++++++ .../base-schema-parsers/discriminator.js | 13 ++ src/schema-parser/base-schema-parsers/enum.js | 31 +-- .../base-schema-parsers/object.js | 10 +- src/schema-parser/mono-schema-parser.js | 3 + src/schema-parser/schema-parser-fabric.js | 26 ++- src/schema-parser/schema-parser.js | 3 + src/schema-parser/schema-utils.js | 61 ++++-- src/schema-routes/schema-routes.js | 180 +++++++++--------- src/type-name-formatter.js | 14 +- tests/spec/extract-enums/schema.ts | 74 +++---- 12 files changed, 408 insertions(+), 238 deletions(-) create mode 100644 src/data-contracts.js diff --git a/src/code-gen-process.js b/src/code-gen-process.js index a6fa5b29..d6c75333 100644 --- a/src/code-gen-process.js +++ b/src/code-gen-process.js @@ -16,6 +16,7 @@ const { CodeFormatter } = require("./code-formatter"); const { pascalCase } = require("./util/pascal-case"); const { internalCase } = require("./util/internal-case"); const { sortByProperty } = require("./util/sort-by-property"); +const { DataContracts } = require("./data-contracts"); const PATCHABLE_INSTANCES = [ "schemaWalker", @@ -52,6 +53,8 @@ class CodeGenProcess { templatesWorker; /** @type {SchemaWalker} */ schemaWalker; + /** @type {DataContracts} */ + dataContracts; /** @type {JavascriptTranslator} */ javascriptTranslator; @@ -70,9 +73,13 @@ class CodeGenProcess { this.templatesWorker = new TemplatesWorker(this); this.codeFormatter = new CodeFormatter(this); this.schemaParserFabric = new SchemaParserFabric(this); + this.dataContracts = new DataContracts(this); this.schemaRoutes = new SchemaRoutes(this); this.javascriptTranslator = new JavascriptTranslator(this); + this.config.componentTypeNameResolver.logger = this.logger; + this.schemaParserFabric.dataContracts = this.dataContracts; + this.schemaParserFabric.schemaUtils.dataContracts = this.dataContracts; } async start() { @@ -99,24 +106,16 @@ class CodeGenProcess { _.each(swagger.usageSchema.components, (component, componentName) => _.each(component, (rawTypeData, typeName) => { - this.schemaComponentsMap.createComponent( - this.schemaComponentsMap.createRef(["components", componentName, typeName]), - rawTypeData, - ); + this.dataContracts.add({ + schema: rawTypeData, + name: typeName, + contractType: componentName, + }); }), ); - const schemaComponents = this.schemaComponentsMap.filter("schemas"); - - const parsedSchemas = schemaComponents.map((schemaComponent) => { - const parsed = this.schemaParserFabric.parseSchema(schemaComponent.rawTypeData, schemaComponent.typeName); - schemaComponent.typeData = parsed; - return parsed; - }); - this.schemaRoutes.attachSchema({ usageSchema: swagger.usageSchema, - parsedSchemas, }); const rawConfiguration = { @@ -206,65 +205,22 @@ class CodeGenProcess { }; collectModelTypes = () => { - const components = this.schemaComponentsMap.getComponents(); - let modelTypes = []; - - const getSchemaComponentsCount = () => components.filter((c) => c.componentName === "schemas").length; - - let schemaComponentsCount = getSchemaComponentsCount(); - let processedCount = 0; - - while (processedCount < schemaComponentsCount) { - modelTypes = []; - processedCount = 0; - for (const component of components) { - if (component.componentName === "schemas") { - const modelType = this.prepareModelType(component); - if (modelType) { - modelTypes.push(modelType); - } - processedCount++; - } - } - schemaComponentsCount = getSchemaComponentsCount(); - } + let dataContracts = [...this.dataContracts.values]; if (this.config.sortTypes) { - return modelTypes.sort(sortByProperty("name")); + return dataContracts.sort(sortByProperty("name")); } - return modelTypes; - }; - - prepareModelType = (typeInfo) => { - if (typeInfo.$prepared) return typeInfo.$prepared; - - if (!typeInfo.typeData) { - typeInfo.typeData = this.schemaParserFabric.parseSchema(typeInfo.rawTypeData, typeInfo.typeName); - } - const rawTypeData = typeInfo.typeData; - const typeData = this.schemaParserFabric.schemaFormatters.base[rawTypeData.type] - ? this.schemaParserFabric.schemaFormatters.base[rawTypeData.type](rawTypeData) - : rawTypeData; - let { typeIdentifier, name: originalName, content, description } = typeData; - const name = this.typeNameFormatter.format(originalName); - - if (name === null) return null; - - const preparedModelType = { - ...typeData, - typeIdentifier, - name, - description, - $content: rawTypeData.content, - rawContent: rawTypeData.content, - content: content, - typeData, - }; - - typeInfo.$prepared = preparedModelType; - - return preparedModelType; + return dataContracts.map((dataContract) => { + return { + ...dataContract.schemas.parsed, + name: dataContract.name, + $content: dataContract.schemas.parsed.content, + rawContent: dataContract.schemas.parsed.content, + content: dataContract.content, + typeData: dataContract.schemas.parsed, + }; + }); }; /** diff --git a/src/data-contracts.js b/src/data-contracts.js new file mode 100644 index 00000000..99622838 --- /dev/null +++ b/src/data-contracts.js @@ -0,0 +1,139 @@ +const _ = require("lodash"); + +/** + * @typedef {{ + * $ref: string, + * schemas: { original: Record, parsed: Record, ref: Record }, + * content: Record | string, + * inlineContent: string, + * refContent: string, + * name: string, + * typeName: string, + * linkers: string[], + * }} DataContract + */ + +class DataContracts { + /** @type {CodeGenConfig} */ + config; + /** @type {Logger} */ + logger; + /** @type {SchemaParserFabric} */ + schemaParserFabric; + /** @type {TypeNameFormatter} */ + typeNameFormatter; + /** @type {SchemaUtils} */ + schemaUtils; + /** @type {SchemaComponentsMap} */ + schemaComponentsMap; + + /** @type {DataContract[]} */ + values; + + constructor({ config, logger, schemaParserFabric }) { + this.values = []; + this.config = config; + this.logger = logger; + this.schemaParserFabric = schemaParserFabric; + this.schemaUtils = this.schemaParserFabric.schemaUtils; + this.typeNameFormatter = this.schemaParserFabric.typeNameFormatter; + this.schemaComponentsMap = this.schemaParserFabric.schemaComponentsMap; + } + + _createDataContractNames = (name, builder) => { + if (!builder) { + const formatted = this.typeNameFormatter.format(name); + const usageName = this.config.componentTypeNameResolver.resolve([formatted]); + + return { formatted, usageName, originalName: name }; + } + + const { usageName, original, formatted } = builder(); + + return { formatted: formatted || usageName, usageName, originalName: original || null }; + }; + + /** + * + * @param schema + * @param name + * @param $ref + * @param {() => ({ usageName: string, formatted: string, original: string })} [nameBuilder] + * @returns {DataContract} + */ + add = ({ schema, name, nameBuilder, contractType = "schemas", schemaPath }) => { + const { formatted, usageName, originalName } = this._createDataContractNames(name, nameBuilder); + const parser = this.schemaParserFabric.createSchemaParser({ + schema, + typeName: originalName, + schemaPath, + }); + const parsed = parser.parseSchema(); + const $ref = this.schemaComponentsMap.createRef(["components", contractType, usageName]); + + const refContent = usageName; + + const linkers = _.uniq( + _.compact([ + originalName, + usageName, + formatted, + $ref, + originalName && this.typeNameFormatter.format(originalName, { onlyFormat: true }), + originalName && this.schemaComponentsMap.createRef(["components", "schemas", originalName]), + this.schemaComponentsMap.createRef(["components", "schemas", formatted]), + refContent, + ]), + ); + + /** + * @type {DataContract} + */ + const dataContract = { + $ref: $ref, + schemas: { + original: schema, + parsed, + }, + refContent, + content: parser.getParseContent(), + inlineContent: parser.getInlineParseContent(), + name: usageName, + typeName: usageName, + linkers: linkers, + }; + + this.values.push(dataContract); + + dataContract.schemas.ref = this.schemaParserFabric.parseSchema({ $ref: $ref }); + + return dataContract; + }; + + /** + * @param name + * @returns {DataContract | null} + */ + findByLinker = (name) => { + return this.values.find((value) => value.linkers.includes(name)) || null; + }; + + /** + * @param content + * @returns {DataContract | null} + */ + findByContent = (content) => { + return ( + this.values.find( + (value) => + _.isEqual(value.content, content) || + _.isEqual(value.inlineContent, content) || + _.isEqual(value.refContent, content), + ) || null + ); + }; +} + +module.exports = { + DataContracts, +}; diff --git a/src/schema-parser/base-schema-parsers/discriminator.js b/src/schema-parser/base-schema-parsers/discriminator.js index af45c082..137308b1 100644 --- a/src/schema-parser/base-schema-parsers/discriminator.js +++ b/src/schema-parser/base-schema-parsers/discriminator.js @@ -210,6 +210,19 @@ class DiscriminatorSchemaParser extends MonoSchemaParser { prefixes: this.config.extractingOptions.discriminatorAbstractPrefix, resolver: this.config.extractingOptions.discriminatorAbstractResolver, }); + this.dataContracts.add({ + schema: { ...schema, internal: true }, + nameBuilder: () => { + const usageName = this.schemaUtils.resolveTypeName(this.typeName, { + prefixes: this.config.extractingOptions.discriminatorAbstractPrefix, + resolver: this.config.extractingOptions.discriminatorAbstractResolver, + }); + return { + usageName, + original: this.typeName, + }; + }, + }); const component = this.schemaComponentsMap.createComponent( this.schemaComponentsMap.createRef(["components", "schemas", typeName]), { diff --git a/src/schema-parser/base-schema-parsers/enum.js b/src/schema-parser/base-schema-parsers/enum.js index ec5e78d1..46681043 100644 --- a/src/schema-parser/base-schema-parsers/enum.js +++ b/src/schema-parser/base-schema-parsers/enum.js @@ -14,17 +14,21 @@ class EnumSchemaParser extends MonoSchemaParser { } extractEnum = (pathTypeName) => { - const generatedTypeName = this.schemaUtils.resolveTypeName(pathTypeName, { - suffixes: this.config.extractingOptions.enumSuffix, - resolver: this.config.extractingOptions.enumNameResolver, - }); - const customComponent = this.schemaComponentsMap.createComponent( - this.schemaComponentsMap.createRef(["components", "schemas", generatedTypeName]), - { - ...this.schema, + const dataContract = this.dataContracts.add({ + schema: { ...this.schema }, + nameBuilder: () => { + const usageName = this.schemaUtils.resolveTypeName(pathTypeName, { + suffixes: this.config.extractingOptions.enumSuffix, + resolver: this.config.extractingOptions.enumNameResolver, + }); + + return { + usageName, + original: pathTypeName, + }; }, - ); - return this.schemaParserFabric.parseSchema(customComponent); + }); + return dataContract.schemas.ref; }; parse() { @@ -34,8 +38,7 @@ class EnumSchemaParser extends MonoSchemaParser { return this.extractEnum(pathTypeName); } - const refType = this.schemaUtils.getSchemaRefType(this.schema); - const $ref = (refType && refType.$ref) || null; + const dc = this.schemaUtils.findDataContract(this.schema); // fix schema when enum has length 1+ but value is [] if (Array.isArray(this.schema.enum)) { @@ -104,8 +107,8 @@ class EnumSchemaParser extends MonoSchemaParser { return { ...(_.isObject(this.schema) ? this.schema : {}), - $ref: $ref, - typeName: this.typeName || ($ref && refType.typeName) || null, + $ref: dc?.$ref, + typeName: this.typeName || (dc?.$ref && dc?.name) || null, $parsedSchema: true, schemaType: SCHEMA_TYPES.ENUM, type: SCHEMA_TYPES.ENUM, diff --git a/src/schema-parser/base-schema-parsers/object.js b/src/schema-parser/base-schema-parsers/object.js index fe28e137..43b41a21 100644 --- a/src/schema-parser/base-schema-parsers/object.js +++ b/src/schema-parser/base-schema-parsers/object.js @@ -24,9 +24,11 @@ class ObjectSchemaParser extends MonoSchemaParser { const { properties, additionalProperties } = schema || {}; const propertiesContent = _.map(properties, (property, name) => { + /** @type {DataContract|null} */ + const propDc = this.schemaUtils.findDataContract(property); const required = this.schemaUtils.isPropertyRequired(name, property, schema); - const rawTypeData = _.get(this.schemaUtils.getSchemaRefType(property), "rawTypeData", {}); - const nullable = !!(rawTypeData.nullable || property.nullable); + const parsedDcSchema = propDc?.schemas.parsed || {}; + const nullable = !!(parsedDcSchema.nullable || property.nullable); const fieldName = this.typeNameFormatter.isValidName(name) ? name : this.config.Ts.StringValue(name); const fieldValue = this.schemaParserFabric .createSchemaParser({ schema: property, schemaPath: [...this.schemaPath, name] }) @@ -40,8 +42,8 @@ class ObjectSchemaParser extends MonoSchemaParser { description: property.description || _.compact(_.map(property[this.schemaUtils.getComplexType(property)], "description"))[0] || - rawTypeData.description || - _.compact(_.map(rawTypeData[this.schemaUtils.getComplexType(rawTypeData)], "description"))[0] || + parsedDcSchema.description || + _.compact(_.map(parsedDcSchema[this.schemaUtils.getComplexType(parsedDcSchema)], "description"))[0] || "", isRequired: required, isNullable: nullable, diff --git a/src/schema-parser/mono-schema-parser.js b/src/schema-parser/mono-schema-parser.js index 66743463..f5c12f36 100644 --- a/src/schema-parser/mono-schema-parser.js +++ b/src/schema-parser/mono-schema-parser.js @@ -21,6 +21,8 @@ class MonoSchemaParser { config; /** @type {SchemaFormatters} */ schemaFormatters; + /** @type {DataContracts} */ + dataContracts; constructor(schemaParser, schema, typeName = null, schemaPath = []) { this.schemaParser = schemaParser; @@ -34,6 +36,7 @@ class MonoSchemaParser { this.schemaUtils = this.schemaParser.schemaUtils; this.config = this.schemaParser.config; this.schemaFormatters = this.schemaParser.schemaFormatters; + this.dataContracts = this.schemaParser.dataContracts; } parse() { diff --git a/src/schema-parser/schema-parser-fabric.js b/src/schema-parser/schema-parser-fabric.js index d6293c75..1b810c81 100644 --- a/src/schema-parser/schema-parser-fabric.js +++ b/src/schema-parser/schema-parser-fabric.js @@ -24,14 +24,25 @@ class SchemaParserFabric { schemaUtils; /** @type {SchemaWalker} */ schemaWalker; + /** @type {DataContracts} */ + dataContracts; - constructor({ config, logger, templatesWorker, schemaComponentsMap, typeNameFormatter, schemaWalker }) { + constructor({ + config, + logger, + templatesWorker, + schemaComponentsMap, + typeNameFormatter, + schemaWalker, + dataContracts, + }) { this.config = config; this.logger = logger; this.schemaComponentsMap = schemaComponentsMap; this.typeNameFormatter = typeNameFormatter; this.templatesWorker = templatesWorker; this.schemaWalker = schemaWalker; + this.dataContracts = dataContracts; this.schemaUtils = new SchemaUtils(this); this.schemaFormatters = new SchemaFormatters(this); } @@ -63,19 +74,6 @@ class SchemaParserFabric { return parser.schema; }; - createParsedComponent = ({ typeName, schema, schemaPath }) => { - const schemaCopy = _.cloneDeep(schema); - const customComponent = this.schemaComponentsMap.createComponent( - this.schemaComponentsMap.createRef(["components", "schemas", typeName]), - schemaCopy, - ); - const parsed = this.parseSchema(schemaCopy, null, schemaPath); - parsed.name = typeName; - customComponent.typeData = parsed; - - return customComponent; - }; - /** * * @param schema {any} diff --git a/src/schema-parser/schema-parser.js b/src/schema-parser/schema-parser.js index e00c471a..06afebfb 100644 --- a/src/schema-parser/schema-parser.js +++ b/src/schema-parser/schema-parser.js @@ -36,6 +36,8 @@ class SchemaParser { templatesWorker; /** @type {SchemaWalker} */ schemaWalker; + /** @type {DataContracts} */ + dataContracts; typeName; schema; @@ -51,6 +53,7 @@ class SchemaParser { this.schemaWalker = schemaParserFabric.schemaWalker; this.schemaFormatters = schemaParserFabric.schemaFormatters; this.schemaUtils = schemaParserFabric.schemaUtils; + this.dataContracts = schemaParserFabric.dataContracts; this.typeName = typeName || null; this.schema = schema; diff --git a/src/schema-parser/schema-utils.js b/src/schema-parser/schema-utils.js index 299a0494..a83b555d 100644 --- a/src/schema-parser/schema-utils.js +++ b/src/schema-parser/schema-utils.js @@ -13,12 +13,15 @@ class SchemaUtils { typeNameFormatter; /** @type {SchemaWalker} */ schemaWalker; + /** @type {DataContracts} */ + dataContracts; - constructor({ config, schemaComponentsMap, typeNameFormatter, schemaWalker }) { + constructor({ config, schemaComponentsMap, typeNameFormatter, schemaWalker, dataContracts }) { this.config = config; this.schemaComponentsMap = schemaComponentsMap; this.typeNameFormatter = typeNameFormatter; this.schemaWalker = schemaWalker; + this.dataContracts = dataContracts; } getRequiredProperties = (schema) => { @@ -33,10 +36,24 @@ class SchemaUtils { return schema["x-enumNames"] || schema["xEnumNames"] || schema["x-enumnames"] || schema["x-enum-varnames"]; }; + /** + * @param schema + * @returns {DataContract|null} + */ getSchemaRefType = (schema) => { if (!this.isRefSchema(schema)) return null; // const resolved = this.schemaWalker.findByRef(schema.$ref); - return this.schemaComponentsMap.get(schema.$ref); + return this.dataContracts.findByLinker(schema.$ref); + }; + + /** + * @param schema + * @returns {DataContract|null} + */ + findDataContract = (schema) => { + if (!this.isRefSchema(schema)) return null; + // const resolved = this.schemaWalker.findByRef(schema.$ref); + return this.dataContracts.findByLinker(schema.$ref); }; isPropertyRequired = (name, propertySchema, rootSchema) => { @@ -118,10 +135,11 @@ class SchemaUtils { const required = _.uniq([...this.getRequiredProperties(parentSchema), ...this.getRequiredProperties(childSchema)]); - const refData = this.getSchemaRefType(childSchema); + /** @type {DataContract | null} */ + const dc = this.findDataContract(childSchema); - if (refData) { - const refObjectProperties = _.keys((refData.rawTypeData && refData.rawTypeData.properties) || {}); + if (dc) { + const refObjectProperties = _.keys(dc.schemas.original?.properties || {}); const existedRequiredKeys = refObjectProperties.filter((key) => required.includes(key)); if (!existedRequiredKeys.length) return childSchema; @@ -156,8 +174,8 @@ class SchemaUtils { }); } else { return this.config.componentTypeNameResolver.resolve([ - ...(prefixes || []).map((prefix) => pascalCase(`${prefix} ${typeName}`)), - ...(suffixes || []).map((suffix) => pascalCase(`${typeName} ${suffix}`)), + ...(prefixes || []).map((prefix) => this.typeNameFormatter.format(pascalCase(`${prefix} ${typeName}`))), + ...(suffixes || []).map((suffix) => this.typeNameFormatter.format(pascalCase(`${typeName} ${suffix}`))), ]); } }; @@ -185,13 +203,10 @@ class SchemaUtils { getSchemaType = (schema) => { if (!schema) return this.config.Ts.Keyword.Any; - const refTypeInfo = this.getSchemaRefType(schema); + const dc = this.findDataContract(schema); - if (refTypeInfo) { - return this.checkAndAddRequiredKeys( - schema, - this.safeAddNullToType(schema, this.typeNameFormatter.format(refTypeInfo.typeName)), - ); + if (dc) { + return this.checkAndAddRequiredKeys(schema, this.safeAddNullToType(schema, dc.name)); } const primitiveType = this.getSchemaPrimitiveType(schema); @@ -223,6 +238,26 @@ class SchemaUtils { return pascalCase(camelCase(_.uniq([schemaPath[0], schemaPath[schemaPath.length - 1]]).join("_"))); }; + + getSchemaFromRequestType = (requestInfo) => { + const content = _.get(requestInfo, "content"); + + if (!content) return null; + + /* content: { "multipart/form-data": { schema: {...} }, "application/json": { schema: {...} } } */ + + /* for example: dataType = "multipart/form-data" */ + for (const dataType in content) { + if (content[dataType] && content[dataType].schema) { + return { + ...content[dataType].schema, + dataType, + }; + } + } + + return null; + }; } module.exports = { diff --git a/src/schema-routes/schema-routes.js b/src/schema-routes/schema-routes.js index ef42cbf3..885cba91 100644 --- a/src/schema-routes/schema-routes.js +++ b/src/schema-routes/schema-routes.js @@ -21,34 +21,22 @@ const CONTENT_KIND = { }; class SchemaRoutes { - /** - * @type {CodeGenConfig} - */ + /** @type {CodeGenConfig} */ config; - /** - * @type {SchemaParserFabric} - */ + /** @type {SchemaParserFabric} */ schemaParserFabric; - /** - * @type {SchemaUtils} - */ + /** @type {SchemaUtils} */ schemaUtils; - /** - * @type {TypeNameFormatter} - */ + /** @type {TypeNameFormatter} */ typeNameFormatter; - /** - * @type {SchemaComponentsMap} - */ + /** @type {SchemaComponentsMap} */ schemaComponentsMap; - /** - * @type {Logger} - */ + /** @type {Logger} */ logger; - /** - * @type {TemplatesWorker} - */ + /** @type {TemplatesWorker} */ templatesWorker; + /** @type {DataContracts} */ + dataContracts; FORM_DATA_TYPES = []; @@ -57,7 +45,15 @@ class SchemaRoutes { hasQueryRoutes = false; hasFormDataRoutes = false; - constructor({ config, schemaParserFabric, schemaComponentsMap, logger, templatesWorker, typeNameFormatter }) { + constructor({ + config, + schemaParserFabric, + schemaComponentsMap, + logger, + templatesWorker, + typeNameFormatter, + dataContracts, + }) { this.config = config; this.schemaParserFabric = schemaParserFabric; this.schemaUtils = this.schemaParserFabric.schemaUtils; @@ -65,6 +61,7 @@ class SchemaRoutes { this.schemaComponentsMap = schemaComponentsMap; this.logger = logger; this.templatesWorker = templatesWorker; + this.dataContracts = dataContracts; this.FORM_DATA_TYPES = _.uniq([ this.schemaUtils.getSchemaType({ type: "string", format: "file" }), @@ -193,17 +190,19 @@ class SchemaRoutes { }; _.each(parameters, (parameter) => { - const refTypeInfo = this.schemaParserFabric.schemaUtils.getSchemaRefType(parameter); + /** @type {DataContract | null} */ + const dc = this.schemaUtils.findDataContract(parameter); + const dcOrigSchema = dc?.schemas.original; let routeParam = null; - if (refTypeInfo && refTypeInfo.rawTypeData.in && refTypeInfo.rawTypeData) { - if (!routeParams[refTypeInfo.rawTypeData.in]) { - routeParams[refTypeInfo.rawTypeData.in] = []; + if (dcOrigSchema?.in) { + if (!routeParams[dcOrigSchema.in]) { + routeParams[dcOrigSchema.in] = []; } routeParam = { - ...refTypeInfo.rawTypeData, - ...(refTypeInfo.rawTypeData.schema || {}), + ...dcOrigSchema, + ...(dcOrigSchema.schema || {}), }; } else { if (!parameter.in) return; @@ -309,55 +308,51 @@ class SchemaRoutes { return null; }; - getTypeFromRequestInfo = ({ requestInfo, parsedSchemas, operationId, defaultType, typeName }) => { + getTypeFromRequestInfo = ({ requestInfo, operationId, defaultType, typeName }) => { // TODO: make more flexible pick schema without content type const schema = this.getSchemaFromRequestType(requestInfo); - const refTypeInfo = this.schemaParserFabric.schemaUtils.getSchemaRefType(requestInfo); + const dc = this.schemaUtils.findDataContract(requestInfo); if (schema) { const content = this.schemaParserFabric.getInlineParseContent(schema, typeName, [operationId]); - const foundedSchemaByName = _.find( - parsedSchemas, - (parsedSchema) => this.typeNameFormatter.format(parsedSchema.name) === content, - ); - const foundSchemaByContent = _.find(parsedSchemas, (parsedSchema) => _.isEqual(parsedSchema.content, content)); - - const foundSchema = foundedSchemaByName || foundSchemaByContent; + const dc = this.dataContracts.findByLinker(content) || this.dataContracts.findByContent(content); - return foundSchema ? this.typeNameFormatter.format(foundSchema.name) : content; + return dc ? dc.refContent : content; } - if (refTypeInfo) { + if (dc) { // const refTypeWithoutOpId = refType.replace(operationId, ''); // const foundedSchemaByName = _.find(parsedSchemas, ({ name }) => name === refType || name === refTypeWithoutOpId) // TODO:HACK fix problem of swagger2opeanpi - const typeNameWithoutOpId = _.replace(refTypeInfo.typeName, operationId, ""); - if (_.find(parsedSchemas, (schema) => schema.name === typeNameWithoutOpId)) { - return this.typeNameFormatter.format(typeNameWithoutOpId); + const typeNameWithoutOpId = _.replace(dc.typeName, operationId, ""); + + const dc = + this.dataContracts.findByLinker(typeNameWithoutOpId) || this.dataContracts.findByContent(typeNameWithoutOpId); + + if (dc) { + return dc.refContent; } - switch (refTypeInfo.componentName) { + switch (dc.componentName) { case "schemas": - return this.typeNameFormatter.format(refTypeInfo.typeName); + return name; case "responses": case "requestBodies": return this.schemaParserFabric.getInlineParseContent( - this.getSchemaFromRequestType(refTypeInfo.rawTypeData), - refTypeInfo.typeName || null, + this.getSchemaFromRequestType(dc.schemas.original), + dc.name || null, [operationId], ); default: - return this.schemaParserFabric.getInlineParseContent(refTypeInfo.rawTypeData, refTypeInfo.typeName || null, [ - operationId, - ]); + return dc.inlineContent; } } return defaultType || this.config.Ts.Keyword.Any; }; - getRequestInfoTypes = ({ requestInfos, parsedSchemas, operationId, defaultType }) => + getRequestInfoTypes = ({ requestInfos, operationId, defaultType }) => _.reduce( requestInfos, (acc, requestInfo, status) => { @@ -373,7 +368,6 @@ class SchemaRoutes { requestInfo, this.getTypeFromRequestInfo({ requestInfo, - parsedSchemas, operationId, defaultType, }), @@ -390,14 +384,13 @@ class SchemaRoutes { [], ); - getResponseBodyInfo = (routeInfo, routeParams, parsedSchemas) => { + getResponseBodyInfo = (routeInfo, routeParams) => { const { produces, operationId, responses } = routeInfo; const contentTypes = this.getContentTypes(responses, [...(produces || []), routeInfo["x-accepts"]]); const responseInfos = this.getRequestInfoTypes({ requestInfos: responses, - parsedSchemas, operationId, defaultType: this.config.defaultResponseType, }); @@ -479,7 +472,7 @@ class SchemaRoutes { ); }; - getRequestBodyInfo = (routeInfo, routeParams, parsedSchemas, routeName) => { + getRequestBodyInfo = (routeInfo, routeParams, routeName) => { const { requestBody, consumes, requestBodyName, operationId } = routeInfo; let schema = null; let content = null; @@ -509,7 +502,6 @@ class SchemaRoutes { requestBody, this.getTypeFromRequestInfo({ requestInfo: requestBody, - parsedSchemas, operationId, typeName, }), @@ -524,12 +516,16 @@ class SchemaRoutes { } if (schema && !schema.$ref && this.config.extractRequestBody) { - schema = this.schemaParserFabric.createParsedComponent({ - schema, - typeName, + const dc = this.dataContracts.add({ + schema: { ...schema }, + nameBuilder: () => ({ + original: routeName.usage, + usageName: typeName, + }), schemaPath: [operationId], }); - content = this.schemaParserFabric.getInlineParseContent({ $ref: schema.$ref }); + schema = dc.schemas.parsed; + content = dc.refContent; } return { @@ -594,15 +590,22 @@ class SchemaRoutes { if (fixedSchema) return fixedSchema; if (extractRequestParams) { - const generatedTypeName = this.schemaUtils.resolveTypeName(routeName.usage, { - suffixes: this.config.extractingOptions.requestParamsSuffix, - resolver: this.config.extractingOptions.requestParamsNameResolver, - }); + const dc = this.dataContracts.add({ + schema, + nameBuilder: () => { + const usageName = this.schemaUtils.resolveTypeName(routeName.usage, { + suffixes: this.config.extractingOptions.requestParamsSuffix, + resolver: this.config.extractingOptions.requestParamsNameResolver, + }); - return this.schemaParserFabric.createParsedComponent({ - typeName: generatedTypeName, - schema: schema, + return { + usageName, + original: routeName.usage, + }; + }, }); + + return dc.schemas.parsed; } return schema; @@ -610,23 +613,29 @@ class SchemaRoutes { extractResponseBodyIfItNeeded = (routeInfo, responseBodyInfo, routeName) => { if (responseBodyInfo.responses.length && responseBodyInfo.success && responseBodyInfo.success.schema) { - const typeName = this.schemaUtils.resolveTypeName(routeName.usage, { - suffixes: this.config.extractingOptions.responseBodySuffix, - resolver: this.config.extractingOptions.responseBodyNameResolver, - }); - const idx = responseBodyInfo.responses.indexOf(responseBodyInfo.success.schema); let successResponse = responseBodyInfo.success; if (successResponse.schema && !successResponse.schema.$ref) { const schema = this.getSchemaFromRequestType(successResponse.schema); - successResponse.schema = this.schemaParserFabric.createParsedComponent({ + const dc = this.dataContracts.add({ schema, - typeName, + nameBuilder: () => { + const usageName = this.schemaUtils.resolveTypeName(routeName.usage, { + suffixes: this.config.extractingOptions.responseBodySuffix, + resolver: this.config.extractingOptions.responseBodyNameResolver, + }); + + return { + usageName, + original: routeName.usage, + }; + }, schemaPath: [routeInfo.operationId], }); - successResponse.type = this.schemaParserFabric.getInlineParseContent({ $ref: successResponse.schema.$ref }); + successResponse.schema = dc.schemas.parsed; + successResponse.type = dc.refContent; if (idx > -1) { _.assign(responseBodyInfo.responses[idx], { @@ -664,12 +673,13 @@ class SchemaRoutes { null, [routeInfo.operationId], ); - const component = this.schemaComponentsMap.createComponent( - this.schemaComponentsMap.createRef(["components", "schemas", typeName]), - { ...schema }, - ); - responseBodyInfo.error.schemas = [component]; - responseBodyInfo.error.type = this.typeNameFormatter.format(component.typeName); + const dataContract = this.dataContracts.add({ + schema: { ...schema }, + $ref: this.schemaComponentsMap.createRef(["components", "schemas", typeName]), + name: typeName, + }); + responseBodyInfo.error.schemas = [dataContract.schemas.parsed]; + responseBodyInfo.error.type = dataContract.name; } }; @@ -710,7 +720,7 @@ class SchemaRoutes { return this.config.hooks.onCreateRouteName(routeNameInfo, rawRouteInfo) || routeNameInfo; }; - parseRouteInfo = (rawRouteName, routeInfo, method, usageSchema, parsedSchemas) => { + parseRouteInfo = (rawRouteName, routeInfo, method, usageSchema) => { const { security: globalSecurity } = usageSchema; const { moduleNameIndex, moduleNameFirstTag, extractRequestParams } = this.config; const { @@ -754,7 +764,7 @@ class SchemaRoutes { })); const pathArgsNames = pathArgs.map((arg) => arg.name); - const responseBodyInfo = this.getResponseBodyInfo(routeInfo, routeParams, parsedSchemas); + const responseBodyInfo = this.getResponseBodyInfo(routeInfo, routeParams); const rawRouteInfo = { ...otherInfo, @@ -779,7 +789,7 @@ class SchemaRoutes { const routeName = this.getRouteName(rawRouteInfo); - const requestBodyInfo = this.getRequestBodyInfo(routeInfo, routeParams, parsedSchemas, routeName); + const requestBodyInfo = this.getRequestBodyInfo(routeInfo, routeParams, routeName); const requestParamsSchema = this.createRequestParamsSchema({ queryParams: routeParams.query, @@ -881,7 +891,7 @@ class SchemaRoutes { }; }; - attachSchema = ({ usageSchema, parsedSchemas }) => { + attachSchema = ({ usageSchema }) => { this.config.routeNameDuplicatesMap.clear(); const pathsEntries = _.entries(usageSchema.paths); @@ -890,7 +900,7 @@ class SchemaRoutes { const routeInfosMap = this.createRequestsMap(routeInfoByMethodsMap); _.forEach(routeInfosMap, (routeInfo, method) => { - const parsedRouteInfo = this.parseRouteInfo(rawRouteName, routeInfo, method, usageSchema, parsedSchemas); + const parsedRouteInfo = this.parseRouteInfo(rawRouteName, routeInfo, method, usageSchema); const processedRouteInfo = this.config.hooks.onCreateRoute(parsedRouteInfo); const route = processedRouteInfo || parsedRouteInfo; diff --git a/src/type-name-formatter.js b/src/type-name-formatter.js index a7f8a508..74e6709c 100644 --- a/src/type-name-formatter.js +++ b/src/type-name-formatter.js @@ -21,7 +21,7 @@ class TypeNameFormatter { /** * @param name - * @param options {{ type?: FormattingSchemaType }} + * @param options {{ type?: FormattingSchemaType, onlyFormat?: boolean }} * @return {string} */ format = (name, options) => { @@ -32,8 +32,16 @@ class TypeNameFormatter { */ const schemaType = options.type || "type-name"; - const typePrefix = schemaType === "enum-key" ? this.config.enumKeyPrefix : this.config.typePrefix; - const typeSuffix = schemaType === "enum-key" ? this.config.enumKeySuffix : this.config.typeSuffix; + const typePrefix = options.onlyFormat + ? "" + : schemaType === "enum-key" + ? this.config.enumKeyPrefix + : this.config.typePrefix; + const typeSuffix = options.onlyFormat + ? "" + : schemaType === "enum-key" + ? this.config.enumKeySuffix + : this.config.typeSuffix; const hashKey = `${typePrefix}_${name}_${typeSuffix}`; diff --git a/tests/spec/extract-enums/schema.ts b/tests/spec/extract-enums/schema.ts index 2903a91c..f4481c50 100644 --- a/tests/spec/extract-enums/schema.ts +++ b/tests/spec/extract-enums/schema.ts @@ -19,6 +19,34 @@ export enum TNPEnumRootTNS { EKPTest2EKS = "test2", } +export enum TNPTreeModeEnumTNS { + EKPInvalidKey100644EKS = "100644", + EKPInvalidKey100755EKS = "100755", + EKPInvalidKey040000EKS = "040000", + EKPInvalidKey160000EKS = "160000", + EKPInvalidKey120000EKS = "120000", +} + +export enum TNPTreeModeNumEnumTNS { + EKPInvalidKey100644EKS = 100644, + EKPInvalidKey100755EKS = 100755, + EKPInvalidKey40000EKS = 40000, + EKPInvalidKey160000EKS = 160000, + EKPInvalidKey120000EKS = 120000, +} + +export enum TNPTreeTypeEnumTNS { + EKPBlobEKS = "blob", + EKPTreeEKS = "tree", + EKPCommitEKS = "commit", +} + +export enum TNPTreeBerekeEnumTNS { + EKPBlaEKS = "Bla", + EKPBlablaEKS = "Blabla", + EKPBoilerEKS = "Boiler", +} + export interface TNPTreeTNS { tree?: { mode?: TNPTreeModeEnumTNS; @@ -87,6 +115,12 @@ export enum TNPSomeInterestEnumTNS { EKP_HSDFDS_EKS = "HSDFDS", } +/** @example "APPROVED" */ +export enum TNPSuperDuperStructDtoStateEnumTNS { + EKP_NEW_EKS = "NEW", + EKP_PENDING_EKS = "PENDING", +} + export interface TNPSuperDuperStructDTOTNS { /** @example "100" */ id: number; @@ -96,49 +130,15 @@ export interface TNPSuperDuperStructDTOTNS { export type TNPNullableEnumTNS = null; -/** **Required when the state is dismissed.** The reason for dismissing or closing the alert. Can be one of: `false positive`, `won't fix`, and `used in tests`. */ -export type TNPCodeScanningAlertDismissedReasonTNS = TNPCodeScanningAlertDismissedReasonEnumTNS | null; - -export enum TNPTreeModeEnumTNS { - EKPInvalidKey100644EKS = "100644", - EKPInvalidKey100755EKS = "100755", - EKPInvalidKey040000EKS = "040000", - EKPInvalidKey160000EKS = "160000", - EKPInvalidKey120000EKS = "120000", -} - -export enum TNPTreeModeNumEnumTNS { - EKPInvalidKey100644EKS = 100644, - EKPInvalidKey100755EKS = 100755, - EKPInvalidKey40000EKS = 40000, - EKPInvalidKey160000EKS = 160000, - EKPInvalidKey120000EKS = 120000, -} - -export enum TNPTreeTypeEnumTNS { - EKPBlobEKS = "blob", - EKPTreeEKS = "tree", - EKPCommitEKS = "commit", -} - -export enum TNPTreeBerekeEnumTNS { - EKPBlaEKS = "Bla", - EKPBlablaEKS = "Blabla", - EKPBoilerEKS = "Boiler", -} - -/** @example "APPROVED" */ -export enum TNPSuperDuperStructDtoStateEnumTNS { - EKP_NEW_EKS = "NEW", - EKP_PENDING_EKS = "PENDING", -} - export enum TNPCodeScanningAlertDismissedReasonEnumTNS { EKPFalsePositiveEKS = "false positive", EKPWontFixEKS = "won't fix", EKPUsedInTestsEKS = "used in tests", } +/** **Required when the state is dismissed.** The reason for dismissing or closing the alert. Can be one of: `false positive`, `won't fix`, and `used in tests`. */ +export type TNPCodeScanningAlertDismissedReasonTNS = TNPCodeScanningAlertDismissedReasonEnumTNS | null; + /** @example "APPROVED" */ export enum TNPNameSpaceAddSuperDuperEnumTNS { EKP_NEW_EKS = "NEW",