diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 8aecaeab5865..6b6258850ed6 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -34,6 +34,7 @@ import org.openapitools.codegen.api.TemplatingEngineAdapter; import org.openapitools.codegen.config.GlobalSettings; import org.openapitools.codegen.examples.ExampleGenerator; +import org.openapitools.codegen.languages.RustServerCodegen; import org.openapitools.codegen.meta.FeatureSet; import org.openapitools.codegen.meta.GeneratorMetadata; import org.openapitools.codegen.meta.Stability; @@ -4575,9 +4576,24 @@ public CodegenParameter fromParameter(Parameter parameter, Set imports) } Schema parameterSchema; + + // the parameter model name is obtained from the schema $ref + // e.g. #/components/schemas/list_pageQuery_parameter => toModelName(list_pageQuery_parameter) + String parameterModelName = null; + if (parameter.getSchema() != null) { parameterSchema = parameter.getSchema(); - CodegenProperty prop = fromProperty(parameter.getName(), parameterSchema); + parameterModelName = getParameterDataType(parameter, parameterSchema); + CodegenProperty prop; + if (this instanceof RustServerCodegen) { + // for rust server, we need to do somethings special as it uses + // $ref (e.g. #components/schemas/Pet) to determine whether it's a model + prop = fromProperty(parameter.getName(), parameterSchema); + } else if (getUseInlineModelResolver()) { + prop = fromProperty(parameter.getName(), ModelUtils.getReferencedSchema(openAPI, parameterSchema)); + } else { + prop = fromProperty(parameter.getName(), parameterSchema); + } codegenParameter.setSchema(prop); } else if (parameter.getContent() != null) { Content content = parameter.getContent(); @@ -4587,6 +4603,7 @@ public CodegenParameter fromParameter(Parameter parameter, Set imports) Map.Entry entry = content.entrySet().iterator().next(); codegenParameter.contentType = entry.getKey(); parameterSchema = entry.getValue().getSchema(); + parameterModelName = getParameterDataType(parameter, parameterSchema); } else { parameterSchema = null; } @@ -4613,11 +4630,17 @@ public CodegenParameter fromParameter(Parameter parameter, Set imports) parameterSchema = unaliasSchema(parameterSchema, Collections.emptyMap()); if (parameterSchema == null) { - LOGGER.warn("warning! Schema not found for parameter \" {} \", using String", parameter.getName()); - parameterSchema = new StringSchema().description("//TODO automatically added by openapi-generator due to missing type definition."); + LOGGER.warn("warning! Schema not found for parameter \" {} \"", parameter.getName()); finishUpdatingParameter(codegenParameter, parameter); return codegenParameter; } + + if (getUseInlineModelResolver() && !(this instanceof RustServerCodegen)) { + // for rust server, we cannot run the following as it uses + // $ref (e.g. #components/schemas/Pet) to determine whether it's a model + parameterSchema = ModelUtils.getReferencedSchema(openAPI, parameterSchema); + } + ModelUtils.syncValidationProperties(parameterSchema, codegenParameter); codegenParameter.setTypeProperties(parameterSchema); codegenParameter.setComposedSchemas(getComposedSchemas(parameterSchema)); @@ -4717,9 +4740,8 @@ public CodegenParameter fromParameter(Parameter parameter, Set imports) //} //codegenProperty.required = true; - String parameterDataType = this.getParameterDataType(parameter, parameterSchema); - if (parameterDataType != null) { - codegenParameter.dataType = parameterDataType; + if (parameterModelName != null) { + codegenParameter.dataType = parameterModelName; if (ModelUtils.isObjectSchema(parameterSchema)) { codegenProperty.complexType = codegenParameter.dataType; } @@ -4771,13 +4793,17 @@ public CodegenParameter fromParameter(Parameter parameter, Set imports) // https://swagger.io/docs/specification/serialization/ if (schema != null) { Map> properties = schema.getProperties(); - codegenParameter.items.vars = - properties.entrySet().stream() - .map(entry -> { - CodegenProperty property = fromProperty(entry.getKey(), entry.getValue()); - property.baseName = codegenParameter.baseName + "[" + entry.getKey() + "]"; - return property; - }).collect(Collectors.toList()); + if (properties != null) { + codegenParameter.items.vars = + properties.entrySet().stream() + .map(entry -> { + CodegenProperty property = fromProperty(entry.getKey(), entry.getValue()); + property.baseName = codegenParameter.baseName + "[" + entry.getKey() + "]"; + return property; + }).collect(Collectors.toList()); + } else { + //LOGGER.error("properties is null: {}", schema); + } } else { LOGGER.warn( "No object schema found for deepObject parameter{} deepObject won't have specific properties", @@ -4790,7 +4816,7 @@ public CodegenParameter fromParameter(Parameter parameter, Set imports) } /** - * Returns the data type of a parameter. + * Returns the data type of parameter. * Returns null by default to use the CodegenProperty.datatype value * * @param parameter Parameter @@ -4798,9 +4824,9 @@ public CodegenParameter fromParameter(Parameter parameter, Set imports) * @return data type */ protected String getParameterDataType(Parameter parameter, Schema schema) { - if (parameter.get$ref() != null) { - String refName = ModelUtils.getSimpleRef(parameter.get$ref()); - return toModelName(refName); + Schema unaliasSchema = ModelUtils.unaliasSchema(openAPI, schema); + if (unaliasSchema.get$ref() != null) { + return toModelName(ModelUtils.getSimpleRef(unaliasSchema.get$ref())); } return null; } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/IJsonSchemaValidationProperties.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/IJsonSchemaValidationProperties.java index d0ce99e54234..c3cc9a13032a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/IJsonSchemaValidationProperties.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/IJsonSchemaValidationProperties.java @@ -170,6 +170,9 @@ public interface IJsonSchemaValidationProperties { default void setTypeProperties(Schema p) { if (ModelUtils.isTypeObjectSchema(p)) { setIsMap(true); + if (ModelUtils.isModelWithPropertiesOnly(p)) { + setIsModel(true); + } } else if (ModelUtils.isArraySchema(p)) { setIsArray(true); } else if (ModelUtils.isFileSchema(p) && !ModelUtils.isStringSchema(p)) { @@ -219,6 +222,9 @@ default void setTypeProperties(Schema p) { setIsNull(true); } else if (ModelUtils.isAnyType(p)) { setIsAnyType(true); + if (ModelUtils.isModelWithPropertiesOnly(p)) { + setIsModel(true); + } } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java index 97d040fc95c3..648202530581 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/InlineModelResolver.java @@ -408,10 +408,10 @@ private void flattenRequestBody(String modelName, Operation operation) { /** * Flatten inline models in parameters * - * @param pathname target pathname + * @param modelName model name * @param operation target operation */ - private void flattenParameters(String pathname, Operation operation) { + private void flattenParameters(String modelName, Operation operation) { List parameters = operation.getParameters(); if (parameters == null) { return; @@ -422,39 +422,19 @@ private void flattenParameters(String pathname, Operation operation) { continue; } - Schema model = parameter.getSchema(); - if (model instanceof ObjectSchema) { - Schema obj = model; - if (obj.getType() == null || "object".equals(obj.getType())) { - if (obj.getProperties() != null && obj.getProperties().size() > 0) { - flattenProperties(openAPI, obj.getProperties(), pathname); - String modelName = resolveModelName(obj.getTitle(), parameter.getName()); - modelName = addSchemas(modelName, model); - parameter.$ref(modelName); - } - } - } else if (model instanceof ArraySchema) { - ArraySchema am = (ArraySchema) model; - Schema inner = am.getItems(); - if (inner instanceof ObjectSchema) { - ObjectSchema op = (ObjectSchema) inner; - if (op.getProperties() != null && op.getProperties().size() > 0) { - flattenProperties(openAPI, op.getProperties(), pathname); - String modelName = resolveModelName(op.getTitle(), parameter.getName()); - Schema innerModel = modelFromProperty(openAPI, op, modelName); - String existing = matchGenerated(innerModel); - if (existing != null) { - Schema schema = new Schema().$ref(existing); - schema.setRequired(op.getRequired()); - am.setItems(schema); - } else { - modelName = addSchemas(modelName, innerModel); - Schema schema = new Schema().$ref(modelName); - schema.setRequired(op.getRequired()); - am.setItems(schema); - } - } - } + Schema parameterSchema = parameter.getSchema(); + + if (parameterSchema == null) { + continue; + } + String schemaName = resolveModelName(parameterSchema.getTitle(), + (operation.getOperationId() == null ? modelName : operation.getOperationId()) + "_" + parameter.getName() + "_parameter"); + // Recursively gather/make inline models within this schema if any + gatherInlineModels(parameterSchema, schemaName); + if (isModelNeeded(parameterSchema)) { + // If this schema should be split into its own model, do so + Schema refSchema = this.makeSchemaInComponents(schemaName, parameterSchema); + parameter.setSchema(refSchema); } } } @@ -564,29 +544,7 @@ private void flattenComponents() { flattenComposedChildren(modelName + "_oneOf", m.getOneOf()); } else if (model instanceof Schema) { gatherInlineModels(model, modelName); - } /*else if (ModelUtils.isArraySchema(model)) { - ArraySchema m = (ArraySchema) model; - Schema inner = m.getItems(); - if (inner instanceof ObjectSchema) { - ObjectSchema op = (ObjectSchema) inner; - if (op.getProperties() != null && op.getProperties().size() > 0) { - String innerModelName = resolveModelName(op.getTitle(), modelName + "_inner"); - Schema innerModel = modelFromProperty(openAPI, op, innerModelName); - String existing = matchGenerated(innerModel); - if (existing == null) { - openAPI.getComponents().addSchemas(innerModelName, innerModel); - addGenerated(innerModelName, innerModel); - Schema schema = new Schema().$ref(innerModelName); - schema.setRequired(op.getRequired()); - m.setItems(schema); - } else { - Schema schema = new Schema().$ref(existing); - schema.setRequired(op.getRequired()); - m.setItems(schema); - } - } - } - }*/ + } } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java index c6d73c6c2ea5..a8da0e6dddda 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/RustServerCodegen.java @@ -26,6 +26,7 @@ import io.swagger.v3.oas.models.media.FileSchema; import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.media.XML; +import io.swagger.v3.oas.models.parameters.Parameter; import io.swagger.v3.oas.models.parameters.RequestBody; import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.servers.Server; @@ -1741,4 +1742,13 @@ protected void updatePropertyForAnyType(CodegenProperty property, Schema p) { @Override public GeneratorLanguage generatorLanguage() { return GeneratorLanguage.RUST; } + + @Override + protected String getParameterDataType(Parameter parameter, Schema schema) { + if (parameter.get$ref() != null) { + String refName = ModelUtils.getSimpleRef(parameter.get$ref()); + return toModelName(refName); + } + return null; + } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java index e575a5739c23..cb475a64c203 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java @@ -725,7 +725,7 @@ public static boolean isModel(Schema schema) { } // has properties - if (null != schema.getProperties()) { + if (null != schema.getProperties() && !schema.getProperties().isEmpty()) { return true; } @@ -733,6 +733,25 @@ public static boolean isModel(Schema schema) { return schema instanceof ComposedSchema || schema instanceof ObjectSchema; } + /** + * Check to see if the schema is a model with properties only (non-composed model) + * + * @param schema potentially containing a '$ref' + * @return true if it's a model with at least one properties + */ + public static boolean isModelWithPropertiesOnly(Schema schema) { + if (schema == null) { + return false; + } + + if (null != schema.getProperties() && !schema.getProperties().isEmpty() && // has properties + (schema.getAdditionalProperties() == null || // no additionalProperties is set + (schema.getAdditionalProperties() instanceof Boolean && !(Boolean)schema.getAdditionalProperties()))) { + return true; + } + return false; + } + public static boolean hasValidation(Schema sc) { return ( sc.getMaxItems() != null || diff --git a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java index e1b55ebd3563..b25157fa9a0c 100644 --- a/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java +++ b/modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java @@ -2061,11 +2061,12 @@ public void objectQueryParamIdentifyAsObject() { CodegenParameter parameter = codegen.fromParameter(openAPI.getPaths().get("/pony").getGet().getParameters().get(0), imports); // TODO: This must be updated to work with flattened inline models - Assert.assertEquals(parameter.dataType, "PageQuery1"); + Assert.assertEquals(parameter.dataType, "ListPageQueryParameter"); Assert.assertEquals(imports.size(), 1); - Assert.assertEquals(imports.iterator().next(), "PageQuery1"); + Assert.assertEquals(imports.iterator().next(), "ListPageQueryParameter"); Assert.assertNotNull(parameter.getSchema()); + Assert.assertEquals(parameter.getSchema().dataType, "Object"); Assert.assertEquals(parameter.getSchema().baseType, "object"); } @@ -3167,8 +3168,8 @@ public void testVarsAndRequiredVarsPresent() { // CodegenOperation puts the inline schema into schemas and refs it assertTrue(co.responses.get(0).isModel); - assertEquals(co.responses.get(0).baseType, "objectData"); - modelName = "objectData"; + assertEquals(co.responses.get(0).baseType, "objectWithOptionalAndRequiredProps_request"); + modelName = "objectWithOptionalAndRequiredProps_request"; sc = openAPI.getComponents().getSchemas().get(modelName); cm = codegen.fromModel(modelName, sc); assertEquals(cm.vars, vars); @@ -3180,7 +3181,7 @@ public void testVarsAndRequiredVarsPresent() { cm = codegen.fromModel(modelName, sc); CodegenProperty cp = cm.getVars().get(0); assertTrue(cp.isModel); - assertEquals(cp.complexType, "objectData"); + assertEquals(cp.complexType, "objectWithOptionalAndRequiredProps_request"); } @Test @@ -4005,7 +4006,9 @@ public void testRequestParameterContent() { CodegenMediaType mt = content.get("application/json"); assertNull(mt.getEncoding()); CodegenProperty cp = mt.getSchema(); + // TODO need to revise the test below assertTrue(cp.isMap); + assertTrue(cp.isModel); assertEquals(cp.complexType, "object"); assertEquals(cp.baseName, "SchemaForRequestParameterCoordinatesInlineSchemaApplicationJson"); diff --git a/samples/client/petstore/typescript-fetch/builds/enum/apis/DefaultApi.ts b/samples/client/petstore/typescript-fetch/builds/enum/apis/DefaultApi.ts index f558a4af5b5b..4ec7bd535b8e 100644 --- a/samples/client/petstore/typescript-fetch/builds/enum/apis/DefaultApi.ts +++ b/samples/client/petstore/typescript-fetch/builds/enum/apis/DefaultApi.ts @@ -37,9 +37,9 @@ export interface FakeEnumRequestGetInlineRequest { } export interface FakeEnumRequestGetRefRequest { - stringEnum?: StringEnum; + stringEnum?: FakeEnumRequestGetRefStringEnumEnum; nullableStringEnum?: StringEnum | null; - numberEnum?: NumberEnum; + numberEnum?: FakeEnumRequestGetRefNumberEnumEnum; nullableNumberEnum?: NumberEnum | null; } @@ -210,3 +210,21 @@ export const FakeEnumRequestGetInlineNumberEnumEnum = { NUMBER_3: 3 } as const; export type FakeEnumRequestGetInlineNumberEnumEnum = typeof FakeEnumRequestGetInlineNumberEnumEnum[keyof typeof FakeEnumRequestGetInlineNumberEnumEnum]; +/** + * @export + */ +export const FakeEnumRequestGetRefStringEnumEnum = { + One: 'one', + Two: 'two', + Three: 'three' +} as const; +export type FakeEnumRequestGetRefStringEnumEnum = typeof FakeEnumRequestGetRefStringEnumEnum[keyof typeof FakeEnumRequestGetRefStringEnumEnum]; +/** + * @export + */ +export const FakeEnumRequestGetRefNumberEnumEnum = { + NUMBER_1: 1, + NUMBER_2: 2, + NUMBER_3: 3 +} as const; +export type FakeEnumRequestGetRefNumberEnumEnum = typeof FakeEnumRequestGetRefNumberEnumEnum[keyof typeof FakeEnumRequestGetRefNumberEnumEnum]; diff --git a/samples/client/petstore/typescript-fetch/builds/with-string-enums/apis/DefaultApi.ts b/samples/client/petstore/typescript-fetch/builds/with-string-enums/apis/DefaultApi.ts index a0b27435bf23..14d2e8652d3b 100644 --- a/samples/client/petstore/typescript-fetch/builds/with-string-enums/apis/DefaultApi.ts +++ b/samples/client/petstore/typescript-fetch/builds/with-string-enums/apis/DefaultApi.ts @@ -37,9 +37,9 @@ export interface FakeEnumRequestGetInlineRequest { } export interface FakeEnumRequestGetRefRequest { - stringEnum?: StringEnum; + stringEnum?: FakeEnumRequestGetRefStringEnumEnum; nullableStringEnum?: StringEnum | null; - numberEnum?: NumberEnum; + numberEnum?: FakeEnumRequestGetRefNumberEnumEnum; nullableNumberEnum?: NumberEnum | null; } @@ -210,3 +210,21 @@ export enum FakeEnumRequestGetInlineNumberEnumEnum { NUMBER_2 = 2, NUMBER_3 = 3 } +/** + * @export + * @enum {string} + */ +export enum FakeEnumRequestGetRefStringEnumEnum { + One = 'one', + Two = 'two', + Three = 'three' +} +/** + * @export + * @enum {string} + */ +export enum FakeEnumRequestGetRefNumberEnumEnum { + NUMBER_1 = 1, + NUMBER_2 = 2, + NUMBER_3 = 3 +}