diff --git a/bin/configs/java-okhttp-gson.yaml b/bin/configs/java-okhttp-gson.yaml index 9d0cf6eed57..064c19197f0 100644 --- a/bin/configs/java-okhttp-gson.yaml +++ b/bin/configs/java-okhttp-gson.yaml @@ -3,6 +3,9 @@ outputDir: samples/client/petstore/java/okhttp-gson library: okhttp-gson inputSpec: modules/openapi-generator/src/test/resources/3_0/java/petstore-with-fake-endpoints-models-for-testing-okhttp-gson.yaml templateDir: modules/openapi-generator/src/main/resources/Java +nameMappings: + _type: underscoreType + type_: typeWithUnderscore additionalProperties: artifactId: petstore-okhttp-gson hideGenerationTimestamp: "true" diff --git a/docs/customization.md b/docs/customization.md index 8b368b1695d..e2ad04aa164 100644 --- a/docs/customization.md +++ b/docs/customization.md @@ -395,11 +395,34 @@ or --import-mappings Pet=my.models.MyPet --import-mappings Order=my.models.MyOrder ``` +## Name Mapping + +One can map the name of the property/parameter to something else. Consider the following schema: +``` + PropertyNameCollision: + properties: + _type: + type: string + type: + type: string + type_: + type: string + type: object +``` +`_type`, `type`, `type_` will result in property name collision in the Java client generator for example. We can resolve the issue using `nameMappings` by mapping `_type` to `underscoreType`, `type_` to `typeWithUnderscore`. + +Here is an example to use `nameMappings` in CLI: +```sh +java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/3_0/java/petstore-with-fake-endpoints-models-for-testing-okhttp-gson.yaml -o /tmp/java2/ --name-mappings _type=underscoreType, type_=typeWithUnderscore +``` + +(Not all generators support this feature yet. Please open an issue (ticket) to let us know which generators you would like to have this feature enabled and we'll prioritize accordingly.) + ## Schema Mapping One can map the schema to something else (e.g. external objects/models outside of the package) using the `schemaMappings` option, e.g. in CLI ```sh -java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/3_0/type-alias.yaml -o /tmp/java2/ --schema-mapping TypeAlias=foo.bar.TypeAlias +java -jar modules/openapi-generator-cli/target/openapi-generator-cli.jar generate -g java -i modules/openapi-generator/src/test/resources/3_0/type-alias.yaml -o /tmp/java2/ --schema-mappings TypeAlias=foo.bar.TypeAlias ``` Another example (in conjunction with --type-mappings): ```sh diff --git a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/ConfigHelp.java b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/ConfigHelp.java index 95a94832fb9..d3f3a9c824f 100644 --- a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/ConfigHelp.java +++ b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/ConfigHelp.java @@ -80,6 +80,9 @@ public class ConfigHelp extends OpenApiGeneratorCommand { @Option(name = {"--inline-schema-options"}, title = "inline schema options", description = "options for handling inline schemas in inline model resolver") private Boolean inlineSchemaOptions; + @Option(name = {"--name-mappings"}, title = "property/parameter name mappings", description = "displays the property/parameter name mappings (none)") + private Boolean nameMappings; + @Option(name = {"--openapi-normalizer"}, title = "openapi normalizer rules", description = "displays the OpenAPI normalizer rules (none)") private Boolean openapiNormalizer; @@ -497,6 +500,18 @@ public class ConfigHelp extends OpenApiGeneratorCommand { sb.append(newline); } + if (Boolean.TRUE.equals(nameMappings)) { + sb.append(newline).append("PROPERTY, PARAMETER NAME MAPPING").append(newline).append(newline); + Map map = config.nameMapping() + .entrySet() + .stream() + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> { + throw new IllegalStateException(String.format(Locale.ROOT, "Duplicated options! %s and %s", a, b)); + }, TreeMap::new)); + writePlainTextFromMap(sb, map, optIndent, optNestedIndent, "property, parameter name", "Mapped to"); + sb.append(newline); + } + if (Boolean.TRUE.equals(openapiNormalizer)) { sb.append(newline).append("OPENAPI NORMALIZER RULES").append(newline).append(newline); Map map = config.openapiNormalizer() diff --git a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Generate.java b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Generate.java index 3cb6ed516fb..0243954478e 100644 --- a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Generate.java +++ b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Generate.java @@ -182,11 +182,19 @@ public class Generate extends OpenApiGeneratorCommand { private List inlineSchemaNameMappings = new ArrayList<>(); @Option( - name = {"--inline-schema-optionss"}, + name = {"--inline-schema-options"}, title = "inline schema options", - description = "specifies the options for handling inline schemas in the inline model resolver.") + description = "specifies the options for handling inline schemas in the inline model resolver." + + " Please refer to https://github.com/OpenAPITools/openapi-generator/blob/master/docs/customization.md for a list of options.") private List inlineSchemaOptions = new ArrayList<>(); + @Option( + name = {"--name-mappings"}, + title = "property, parameter name mappings", + description = "specifies mappings between the property, parameter name and the new name in the format of param_name=paramName,prop_name=PropName." + + " You can also have multiple occurrences of this option.") + private List nameMappings = new ArrayList<>(); + @Option( name = {"--openapi-normalizer"}, title = "OpenAPI normalizer rules", @@ -467,6 +475,7 @@ public class Generate extends OpenApiGeneratorCommand { applySchemaMappingsKvpList(schemaMappings, configurator); applyInlineSchemaNameMappingsKvpList(inlineSchemaNameMappings, configurator); applyInlineSchemaOptionsKvpList(inlineSchemaOptions, configurator); + applyNameMappingsKvpList(nameMappings, configurator); applyOpenAPINormalizerKvpList(openapiNormalizer, configurator); applyTypeMappingsKvpList(typeMappings, configurator); applyAdditionalPropertiesKvpList(additionalProperties, configurator); diff --git a/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/GeneratorSettings.java b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/GeneratorSettings.java index 9d3bfec6449..4ff11a044ee 100644 --- a/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/GeneratorSettings.java +++ b/modules/openapi-generator-core/src/main/java/org/openapitools/codegen/config/GeneratorSettings.java @@ -53,6 +53,7 @@ public final class GeneratorSettings implements Serializable { private final Map schemaMappings; private final Map inlineSchemaNameMappings; private final Map inlineSchemaOptions; + private final Map nameMappings; private final Map openapiNormalizer; private final Set languageSpecificPrimitives; private final Map reservedWordsMappings; @@ -265,6 +266,15 @@ public final class GeneratorSettings implements Serializable { return inlineSchemaOptions; } + /** + * Gets property, parameter name mappings between a property, parameter name and the new name. + * + * @return the property, parameter name mappings + */ + public Map getNameMappings() { + return nameMappings; + } + /** * Gets OpenAPI normalizer rules * @@ -392,6 +402,7 @@ public final class GeneratorSettings implements Serializable { schemaMappings = Collections.unmodifiableMap(builder.schemaMappings); inlineSchemaNameMappings = Collections.unmodifiableMap(builder.inlineSchemaNameMappings); inlineSchemaOptions = Collections.unmodifiableMap(builder.inlineSchemaOptions); + nameMappings = Collections.unmodifiableMap(builder.nameMappings); openapiNormalizer = Collections.unmodifiableMap(builder.openapiNormalizer); languageSpecificPrimitives = Collections.unmodifiableSet(builder.languageSpecificPrimitives); reservedWordsMappings = Collections.unmodifiableMap(builder.reservedWordsMappings); @@ -466,6 +477,7 @@ public final class GeneratorSettings implements Serializable { schemaMappings = Collections.unmodifiableMap(new HashMap<>(0)); inlineSchemaNameMappings = Collections.unmodifiableMap(new HashMap<>(0)); inlineSchemaOptions = Collections.unmodifiableMap(new HashMap<>(0)); + nameMappings = Collections.unmodifiableMap(new HashMap<>(0)); openapiNormalizer = Collections.unmodifiableMap(new HashMap<>(0)); languageSpecificPrimitives = Collections.unmodifiableSet(new HashSet<>(0)); reservedWordsMappings = Collections.unmodifiableMap(new HashMap<>(0)); @@ -527,6 +539,9 @@ public final class GeneratorSettings implements Serializable { if (copy.getInlineSchemaOptions() != null) { builder.inlineSchemaOptions.putAll(copy.getInlineSchemaOptions()); } + if (copy.getNameMappings() != null) { + builder.nameMappings.putAll(copy.getNameMappings()); + } if (copy.getOpenAPINormalizer() != null) { builder.openapiNormalizer.putAll(copy.getOpenAPINormalizer()); } @@ -572,6 +587,7 @@ public final class GeneratorSettings implements Serializable { private Map schemaMappings; private Map inlineSchemaNameMappings; private Map inlineSchemaOptions; + private Map nameMappings; private Map openapiNormalizer; private Set languageSpecificPrimitives; private Map reservedWordsMappings; @@ -593,6 +609,7 @@ public final class GeneratorSettings implements Serializable { schemaMappings = new HashMap<>(); inlineSchemaNameMappings = new HashMap<>(); inlineSchemaOptions = new HashMap<>(); + nameMappings = new HashMap<>(); openapiNormalizer = new HashMap<>(); languageSpecificPrimitives = new HashSet<>(); reservedWordsMappings = new HashMap<>(); @@ -914,6 +931,32 @@ public final class GeneratorSettings implements Serializable { return this; } + /** + * Sets the {@code nameMappings} and returns a reference to this Builder so that the methods can be chained together. + * + * @param nameMappings the {@code nameMappings} to set + * @return a reference to this Builder + */ + public Builder withNameMappings(Map nameMappings) { + this.nameMappings = nameMappings; + return this; + } + + /** + * Sets a single {@code nameMappings} and returns a reference to this Builder so that the methods can be chained together. + * + * @param key A key for the name mapping + * @param value The value of name mapping + * @return a reference to this Builder + */ + public Builder withNameMapping(String key, String value) { + if (this.nameMappings == null) { + this.nameMappings = new HashMap<>(); + } + this.nameMappings.put(key, value); + return this; + } + /** * Sets the {@code openapiNormalizer} and returns a reference to this Builder so that the methods can be chained together. * @@ -1128,6 +1171,7 @@ public final class GeneratorSettings implements Serializable { Objects.equals(getSchemaMappings(), that.getSchemaMappings()) && Objects.equals(getInlineSchemaNameMappings(), that.getInlineSchemaNameMappings()) && Objects.equals(getInlineSchemaOptions(), that.getInlineSchemaOptions()) && + Objects.equals(getNameMappings(), that.getNameMappings()) && Objects.equals(getOpenAPINormalizer(), that.getOpenAPINormalizer()) && Objects.equals(getLanguageSpecificPrimitives(), that.getLanguageSpecificPrimitives()) && Objects.equals(getReservedWordsMappings(), that.getReservedWordsMappings()) && @@ -1160,6 +1204,7 @@ public final class GeneratorSettings implements Serializable { getSchemaMappings(), getInlineSchemaNameMappings(), getInlineSchemaOptions(), + getNameMappings(), getOpenAPINormalizer(), getLanguageSpecificPrimitives(), getReservedWordsMappings(), diff --git a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/extensions/OpenApiGeneratorGenerateExtension.kt b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/extensions/OpenApiGeneratorGenerateExtension.kt index 831e67e3dbf..bffbaa13d34 100644 --- a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/extensions/OpenApiGeneratorGenerateExtension.kt +++ b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/extensions/OpenApiGeneratorGenerateExtension.kt @@ -167,6 +167,11 @@ open class OpenApiGeneratorGenerateExtension(project: Project) { */ val inlineSchemaOptions = project.objects.mapProperty() + /** + * Specifies mappings between a property, parameter name and the new name + */ + val nameMappings = project.objects.mapProperty() + /** * Specifies mappings (rules) in OpenAPI normalizer */ diff --git a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/tasks/GenerateTask.kt b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/tasks/GenerateTask.kt index ee1995d1ed5..415ce83a443 100644 --- a/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/tasks/GenerateTask.kt +++ b/modules/openapi-generator-gradle-plugin/src/main/kotlin/org/openapitools/generator/gradle/plugin/tasks/GenerateTask.kt @@ -272,6 +272,13 @@ open class GenerateTask @Inject constructor(private val objectFactory: ObjectFac @Input val inlineSchemaOptions = project.objects.mapProperty() + /** + * Specifies mappings between the property, parameter name and the new name + */ + @Optional + @Input + val nameMappings = project.objects.mapProperty() + /** * Specifies mappings (rules) in OpenAPI normalizer */ @@ -807,6 +814,12 @@ open class GenerateTask @Inject constructor(private val objectFactory: ObjectFac } } + if (nameMappings.isPresent) { + nameMappings.get().forEach { entry -> + configurator.addNameMapping(entry.key, entry.value) + } + } + if (openapiNormalizer.isPresent) { openapiNormalizer.get().forEach { entry -> configurator.addOpenAPINormalizer(entry.key, entry.value) diff --git a/modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/CodeGenMojo.java b/modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/CodeGenMojo.java index a7d091b371b..0e073c3f980 100644 --- a/modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/CodeGenMojo.java +++ b/modules/openapi-generator-maven-plugin/src/main/java/org/openapitools/codegen/plugin/CodeGenMojo.java @@ -332,6 +332,12 @@ public class CodeGenMojo extends AbstractMojo { @Parameter(name = "inlineSchemaOptions", property = "openapi.generator.maven.plugin.inlineSchemaOptions") private List inlineSchemaOptions; + /** + * A map of property, parameter names and the new names + */ + @Parameter(name = "nameMappings", property = "openapi.generator.maven.plugin.nameMappings") + private List nameMappings; + /** * A set of rules for OpenAPI normalizer */ diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java index c3a8832357e..1ab39a82baf 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java @@ -147,6 +147,8 @@ public interface CodegenConfig { Map inlineSchemaOption(); + Map nameMapping(); + Map openapiNormalizer(); Map apiTemplateFiles(); 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 ab71d487773..9924b9ef710 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 @@ -164,6 +164,8 @@ public class DefaultCodegen implements CodegenConfig { protected Map inlineSchemaNameMapping = new HashMap<>(); // a map to store the inline schema naming conventions protected Map inlineSchemaOption = new HashMap<>(); + // a map to store the mapping between property, parameter name and the name provided by the user + protected Map nameMapping = new HashMap<>(); // a map to store the rules in OpenAPI Normalizer protected Map openapiNormalizer = new HashMap<>(); protected String modelPackage = "", apiPackage = "", fileSuffix; @@ -1199,6 +1201,11 @@ public class DefaultCodegen implements CodegenConfig { return inlineSchemaOption; } + @Override + public Map nameMapping() { + return nameMapping; + } + @Override public Map openapiNormalizer() { return openapiNormalizer; diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java index 4c9cb06de1b..7a1e3e0327a 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfigurator.java @@ -71,6 +71,7 @@ public class CodegenConfigurator { private Map schemaMappings = new HashMap<>(); private Map inlineSchemaNameMappings = new HashMap<>(); private Map inlineSchemaOptions = new HashMap<>(); + private Map nameMappings = new HashMap<>(); private Map openapiNormalizer = new HashMap<>(); private Set languageSpecificPrimitives = new HashSet<>(); private Map reservedWordsMappings = new HashMap<>(); @@ -124,6 +125,9 @@ public class CodegenConfigurator { if(generatorSettings.getInlineSchemaOptions() != null) { configurator.inlineSchemaOptions.putAll(generatorSettings.getInlineSchemaOptions()); } + if(generatorSettings.getNameMappings() != null) { + configurator.nameMappings.putAll(generatorSettings.getNameMappings()); + } if(generatorSettings.getOpenAPINormalizer() != null) { configurator.openapiNormalizer.putAll(generatorSettings.getOpenAPINormalizer()); } @@ -214,6 +218,12 @@ public class CodegenConfigurator { return this; } + public CodegenConfigurator addNameMapping(String key, String value) { + this.nameMappings.put(key, value); + generatorSettingsBuilder.withNameMapping(key, value); + return this; + } + public CodegenConfigurator addOpenAPINormalizer(String key, String value) { this.openapiNormalizer.put(key, value); generatorSettingsBuilder.withOpenAPINormalizer(key, value); @@ -392,6 +402,12 @@ public class CodegenConfigurator { return this; } + public CodegenConfigurator setNameMappings(Map nameMappings) { + this.nameMappings = nameMappings; + generatorSettingsBuilder.withNameMappings(nameMappings); + return this; + } + public CodegenConfigurator setOpenAPINormalizer(Map openapiNormalizer) { this.openapiNormalizer = openapiNormalizer; generatorSettingsBuilder.withOpenAPINormalizer(openapiNormalizer); @@ -677,6 +693,7 @@ public class CodegenConfigurator { config.schemaMapping().putAll(generatorSettings.getSchemaMappings()); config.inlineSchemaNameMapping().putAll(generatorSettings.getInlineSchemaNameMappings()); config.inlineSchemaOption().putAll(generatorSettings.getInlineSchemaOptions()); + config.nameMapping().putAll(generatorSettings.getNameMappings()); config.openapiNormalizer().putAll(generatorSettings.getOpenAPINormalizer()); config.languageSpecificPrimitives().addAll(generatorSettings.getLanguageSpecificPrimitives()); config.reservedWordsMappings().putAll(generatorSettings.getReservedWordsMappings()); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfiguratorUtils.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfiguratorUtils.java index 35d6f101286..02460bb38de 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfiguratorUtils.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/config/CodegenConfiguratorUtils.java @@ -120,6 +120,19 @@ public final class CodegenConfiguratorUtils { } } + public static void applyNameMappingsKvpList(List nameMappings, CodegenConfigurator configurator) { + for (String propString : nameMappings) { + applyNameMappingsKvp(propString, configurator); + } + } + + public static void applyNameMappingsKvp(String nameMappings, CodegenConfigurator configurator) { + final Map map = createMapFromKeyValuePairs(nameMappings); + for (Map.Entry entry : map.entrySet()) { + configurator.addNameMapping(entry.getKey().trim(), entry.getValue().trim()); + } + } + public static void applyOpenAPINormalizerKvpList(List openapiNormalizer, CodegenConfigurator configurator) { for (String propString : openapiNormalizer) { applyOpenAPINormalizerKvp(propString, configurator); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java index ea066a8beca..e106b2a6898 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/AbstractJavaCodegen.java @@ -794,6 +794,11 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code @Override public String toVarName(String name) { + // obtain the name from nameMapping directly if provided + if (nameMapping.containsKey(name)) { + return nameMapping.get(name); + } + // sanitize name name = sanitizeName(name, "\\W-[\\$]"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. @@ -853,6 +858,11 @@ public abstract class AbstractJavaCodegen extends DefaultCodegen implements Code @Override public String toParamName(String name) { + // obtain the name from nameMapping directly if provided + if (nameMapping.containsKey(name)) { + return nameMapping.get(name); + } + // to avoid conflicts with 'callback' parameter for async call if ("callback".equals(name)) { return "paramCallback"; diff --git a/modules/openapi-generator/src/test/resources/3_0/java/petstore-with-fake-endpoints-models-for-testing-okhttp-gson.yaml b/modules/openapi-generator/src/test/resources/3_0/java/petstore-with-fake-endpoints-models-for-testing-okhttp-gson.yaml index 09113b47784..adcf22f6db4 100644 --- a/modules/openapi-generator/src/test/resources/3_0/java/petstore-with-fake-endpoints-models-for-testing-okhttp-gson.yaml +++ b/modules/openapi-generator/src/test/resources/3_0/java/petstore-with-fake-endpoints-models-for-testing-okhttp-gson.yaml @@ -2326,3 +2326,12 @@ components: - available - pending - sold + PropertyNameCollision: + type: object + properties: + _type: + type: string + type: + type: string + type_: + type: string diff --git a/modules/openapi-generator/src/test/resources/3_0/property_name_collision.yaml b/modules/openapi-generator/src/test/resources/3_0/property_name_collision.yaml new file mode 100644 index 00000000000..b1b4cca586b --- /dev/null +++ b/modules/openapi-generator/src/test/resources/3_0/property_name_collision.yaml @@ -0,0 +1,22 @@ +openapi: 3.0.2 +info: + title: property name collision + version: 1.0.0 +paths: + /: + get: + description: + responses: + '200': + description: test +components: + schemas: + PropertyNameCollision: + properties: + _type: + type: string + type: + type: string + type_: + type: string + type: object diff --git a/samples/client/petstore/java/okhttp-gson/.openapi-generator/FILES b/samples/client/petstore/java/okhttp-gson/.openapi-generator/FILES index 41767204d93..8a32ed68279 100644 --- a/samples/client/petstore/java/okhttp-gson/.openapi-generator/FILES +++ b/samples/client/petstore/java/okhttp-gson/.openapi-generator/FILES @@ -75,6 +75,7 @@ docs/Pet.md docs/PetApi.md docs/PetWithRequiredTags.md docs/Pig.md +docs/PropertyNameCollision.md docs/Quadrilateral.md docs/QuadrilateralInterface.md docs/ReadOnlyFirst.md @@ -200,6 +201,7 @@ src/main/java/org/openapitools/client/model/ParentPet.java src/main/java/org/openapitools/client/model/Pet.java src/main/java/org/openapitools/client/model/PetWithRequiredTags.java src/main/java/org/openapitools/client/model/Pig.java +src/main/java/org/openapitools/client/model/PropertyNameCollision.java src/main/java/org/openapitools/client/model/Quadrilateral.java src/main/java/org/openapitools/client/model/QuadrilateralInterface.java src/main/java/org/openapitools/client/model/ReadOnlyFirst.java diff --git a/samples/client/petstore/java/okhttp-gson/README.md b/samples/client/petstore/java/okhttp-gson/README.md index b83985e1a81..cb6361af342 100644 --- a/samples/client/petstore/java/okhttp-gson/README.md +++ b/samples/client/petstore/java/okhttp-gson/README.md @@ -222,6 +222,7 @@ Class | Method | HTTP request | Description - [Pet](docs/Pet.md) - [PetWithRequiredTags](docs/PetWithRequiredTags.md) - [Pig](docs/Pig.md) + - [PropertyNameCollision](docs/PropertyNameCollision.md) - [Quadrilateral](docs/Quadrilateral.md) - [QuadrilateralInterface](docs/QuadrilateralInterface.md) - [ReadOnlyFirst](docs/ReadOnlyFirst.md) diff --git a/samples/client/petstore/java/okhttp-gson/api/openapi.yaml b/samples/client/petstore/java/okhttp-gson/api/openapi.yaml index 84eda62cb09..d8e6dd956e8 100644 --- a/samples/client/petstore/java/okhttp-gson/api/openapi.yaml +++ b/samples/client/petstore/java/okhttp-gson/api/openapi.yaml @@ -2305,6 +2305,15 @@ components: - name - photoUrls type: object + PropertyNameCollision: + properties: + _type: + type: string + type: + type: string + type_: + type: string + type: object _foo_get_default_response: example: string: diff --git a/samples/client/petstore/java/okhttp-gson/docs/PropertyNameCollision.md b/samples/client/petstore/java/okhttp-gson/docs/PropertyNameCollision.md new file mode 100644 index 00000000000..28775f9b748 --- /dev/null +++ b/samples/client/petstore/java/okhttp-gson/docs/PropertyNameCollision.md @@ -0,0 +1,15 @@ + + +# PropertyNameCollision + + +## Properties + +| Name | Type | Description | Notes | +|------------ | ------------- | ------------- | -------------| +|**underscoreType** | **String** | | [optional] | +|**type** | **String** | | [optional] | +|**typeWithUnderscore** | **String** | | [optional] | + + + diff --git a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/JSON.java b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/JSON.java index fdb580912c3..49b17f67e35 100644 --- a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/JSON.java +++ b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/JSON.java @@ -287,6 +287,7 @@ public class JSON { gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.Pet.CustomTypeAdapterFactory()); gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.PetWithRequiredTags.CustomTypeAdapterFactory()); gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.Pig.CustomTypeAdapterFactory()); + gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.PropertyNameCollision.CustomTypeAdapterFactory()); gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.Quadrilateral.CustomTypeAdapterFactory()); gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.QuadrilateralInterface.CustomTypeAdapterFactory()); gsonBuilder.registerTypeAdapterFactory(new org.openapitools.client.model.ReadOnlyFirst.CustomTypeAdapterFactory()); diff --git a/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/PropertyNameCollision.java b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/PropertyNameCollision.java new file mode 100644 index 00000000000..b75f6ab5723 --- /dev/null +++ b/samples/client/petstore/java/okhttp-gson/src/main/java/org/openapitools/client/model/PropertyNameCollision.java @@ -0,0 +1,347 @@ +/* + * OpenAPI Petstore + * This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package org.openapitools.client.model; + +import java.util.Objects; +import java.util.Arrays; +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.TypeAdapterFactory; +import com.google.gson.reflect.TypeToken; +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.openapitools.client.JSON; + +/** + * PropertyNameCollision + */ +@javax.annotation.Generated(value = "org.openapitools.codegen.languages.JavaClientCodegen") +public class PropertyNameCollision { + public static final String SERIALIZED_NAME_UNDERSCORE_TYPE = "_type"; + @SerializedName(SERIALIZED_NAME_UNDERSCORE_TYPE) + private String underscoreType; + + public static final String SERIALIZED_NAME_TYPE = "type"; + @SerializedName(SERIALIZED_NAME_TYPE) + private String type; + + public static final String SERIALIZED_NAME_TYPE_WITH_UNDERSCORE = "type_"; + @SerializedName(SERIALIZED_NAME_TYPE_WITH_UNDERSCORE) + private String typeWithUnderscore; + + public PropertyNameCollision() { + } + + public PropertyNameCollision underscoreType(String underscoreType) { + + this.underscoreType = underscoreType; + return this; + } + + /** + * Get underscoreType + * @return underscoreType + **/ + @javax.annotation.Nullable + public String getUnderscoreType() { + return underscoreType; + } + + + public void setUnderscoreType(String underscoreType) { + this.underscoreType = underscoreType; + } + + + public PropertyNameCollision type(String type) { + + this.type = type; + return this; + } + + /** + * Get type + * @return type + **/ + @javax.annotation.Nullable + public String getType() { + return type; + } + + + public void setType(String type) { + this.type = type; + } + + + public PropertyNameCollision typeWithUnderscore(String typeWithUnderscore) { + + this.typeWithUnderscore = typeWithUnderscore; + return this; + } + + /** + * Get typeWithUnderscore + * @return typeWithUnderscore + **/ + @javax.annotation.Nullable + public String getTypeWithUnderscore() { + return typeWithUnderscore; + } + + + public void setTypeWithUnderscore(String typeWithUnderscore) { + this.typeWithUnderscore = typeWithUnderscore; + } + + /** + * A container for additional, undeclared properties. + * This is a holder for any undeclared properties as specified with + * the 'additionalProperties' keyword in the OAS document. + */ + private Map additionalProperties; + + /** + * Set the additional (undeclared) property with the specified name and value. + * If the property does not already exist, create it otherwise replace it. + * + * @param key name of the property + * @param value value of the property + * @return the PropertyNameCollision instance itself + */ + public PropertyNameCollision putAdditionalProperty(String key, Object value) { + if (this.additionalProperties == null) { + this.additionalProperties = new HashMap(); + } + this.additionalProperties.put(key, value); + return this; + } + + /** + * Return the additional (undeclared) property. + * + * @return a map of objects + */ + public Map getAdditionalProperties() { + return additionalProperties; + } + + /** + * Return the additional (undeclared) property with the specified name. + * + * @param key name of the property + * @return an object + */ + public Object getAdditionalProperty(String key) { + if (this.additionalProperties == null) { + return null; + } + return this.additionalProperties.get(key); + } + + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + PropertyNameCollision propertyNameCollision = (PropertyNameCollision) o; + return Objects.equals(this.underscoreType, propertyNameCollision.underscoreType) && + Objects.equals(this.type, propertyNameCollision.type) && + Objects.equals(this.typeWithUnderscore, propertyNameCollision.typeWithUnderscore)&& + Objects.equals(this.additionalProperties, propertyNameCollision.additionalProperties); + } + + @Override + public int hashCode() { + return Objects.hash(underscoreType, type, typeWithUnderscore, additionalProperties); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class PropertyNameCollision {\n"); + sb.append(" underscoreType: ").append(toIndentedString(underscoreType)).append("\n"); + sb.append(" type: ").append(toIndentedString(type)).append("\n"); + sb.append(" typeWithUnderscore: ").append(toIndentedString(typeWithUnderscore)).append("\n"); + sb.append(" additionalProperties: ").append(toIndentedString(additionalProperties)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + + public static HashSet openapiFields; + public static HashSet openapiRequiredFields; + + static { + // a set of all properties/fields (JSON key names) + openapiFields = new HashSet(); + openapiFields.add("_type"); + openapiFields.add("type"); + openapiFields.add("type_"); + + // a set of required properties/fields (JSON key names) + openapiRequiredFields = new HashSet(); + } + + /** + * Validates the JSON Element and throws an exception if issues found + * + * @param jsonElement JSON Element + * @throws IOException if the JSON Element is invalid with respect to PropertyNameCollision + */ + public static void validateJsonElement(JsonElement jsonElement) throws IOException { + if (jsonElement == null) { + if (!PropertyNameCollision.openapiRequiredFields.isEmpty()) { // has required fields but JSON element is null + throw new IllegalArgumentException(String.format("The required field(s) %s in PropertyNameCollision is not found in the empty JSON string", PropertyNameCollision.openapiRequiredFields.toString())); + } + } + JsonObject jsonObj = jsonElement.getAsJsonObject(); + if ((jsonObj.get("_type") != null && !jsonObj.get("_type").isJsonNull()) && !jsonObj.get("_type").isJsonPrimitive()) { + throw new IllegalArgumentException(String.format("Expected the field `_type` to be a primitive type in the JSON string but got `%s`", jsonObj.get("_type").toString())); + } + if ((jsonObj.get("type") != null && !jsonObj.get("type").isJsonNull()) && !jsonObj.get("type").isJsonPrimitive()) { + throw new IllegalArgumentException(String.format("Expected the field `type` to be a primitive type in the JSON string but got `%s`", jsonObj.get("type").toString())); + } + if ((jsonObj.get("type_") != null && !jsonObj.get("type_").isJsonNull()) && !jsonObj.get("type_").isJsonPrimitive()) { + throw new IllegalArgumentException(String.format("Expected the field `type_` to be a primitive type in the JSON string but got `%s`", jsonObj.get("type_").toString())); + } + } + + public static class CustomTypeAdapterFactory implements TypeAdapterFactory { + @SuppressWarnings("unchecked") + @Override + public TypeAdapter create(Gson gson, TypeToken type) { + if (!PropertyNameCollision.class.isAssignableFrom(type.getRawType())) { + return null; // this class only serializes 'PropertyNameCollision' and its subtypes + } + final TypeAdapter elementAdapter = gson.getAdapter(JsonElement.class); + final TypeAdapter thisAdapter + = gson.getDelegateAdapter(this, TypeToken.get(PropertyNameCollision.class)); + + return (TypeAdapter) new TypeAdapter() { + @Override + public void write(JsonWriter out, PropertyNameCollision value) throws IOException { + JsonObject obj = thisAdapter.toJsonTree(value).getAsJsonObject(); + obj.remove("additionalProperties"); + // serialize additional properties + if (value.getAdditionalProperties() != null) { + for (Map.Entry entry : value.getAdditionalProperties().entrySet()) { + if (entry.getValue() instanceof String) + obj.addProperty(entry.getKey(), (String) entry.getValue()); + else if (entry.getValue() instanceof Number) + obj.addProperty(entry.getKey(), (Number) entry.getValue()); + else if (entry.getValue() instanceof Boolean) + obj.addProperty(entry.getKey(), (Boolean) entry.getValue()); + else if (entry.getValue() instanceof Character) + obj.addProperty(entry.getKey(), (Character) entry.getValue()); + else { + obj.add(entry.getKey(), gson.toJsonTree(entry.getValue()).getAsJsonObject()); + } + } + } + elementAdapter.write(out, obj); + } + + @Override + public PropertyNameCollision read(JsonReader in) throws IOException { + JsonElement jsonElement = elementAdapter.read(in); + validateJsonElement(jsonElement); + JsonObject jsonObj = jsonElement.getAsJsonObject(); + // store additional fields in the deserialized instance + PropertyNameCollision instance = thisAdapter.fromJsonTree(jsonObj); + for (Map.Entry entry : jsonObj.entrySet()) { + if (!openapiFields.contains(entry.getKey())) { + if (entry.getValue().isJsonPrimitive()) { // primitive type + if (entry.getValue().getAsJsonPrimitive().isString()) + instance.putAdditionalProperty(entry.getKey(), entry.getValue().getAsString()); + else if (entry.getValue().getAsJsonPrimitive().isNumber()) + instance.putAdditionalProperty(entry.getKey(), entry.getValue().getAsNumber()); + else if (entry.getValue().getAsJsonPrimitive().isBoolean()) + instance.putAdditionalProperty(entry.getKey(), entry.getValue().getAsBoolean()); + else + throw new IllegalArgumentException(String.format("The field `%s` has unknown primitive type. Value: %s", entry.getKey(), entry.getValue().toString())); + } else if (entry.getValue().isJsonArray()) { + instance.putAdditionalProperty(entry.getKey(), gson.fromJson(entry.getValue(), List.class)); + } else { // JSON object + instance.putAdditionalProperty(entry.getKey(), gson.fromJson(entry.getValue(), HashMap.class)); + } + } + } + return instance; + } + + }.nullSafe(); + } + } + + /** + * Create an instance of PropertyNameCollision given an JSON string + * + * @param jsonString JSON string + * @return An instance of PropertyNameCollision + * @throws IOException if the JSON string is invalid with respect to PropertyNameCollision + */ + public static PropertyNameCollision fromJson(String jsonString) throws IOException { + return JSON.getGson().fromJson(jsonString, PropertyNameCollision.class); + } + + /** + * Convert an instance of PropertyNameCollision to an JSON string + * + * @return JSON string + */ + public String toJson() { + return JSON.getGson().toJson(this); + } +} + diff --git a/samples/client/petstore/java/okhttp-gson/src/test/java/org/openapitools/client/JSONTest.java b/samples/client/petstore/java/okhttp-gson/src/test/java/org/openapitools/client/JSONTest.java index 491b649c7ee..035ae0b9967 100644 --- a/samples/client/petstore/java/okhttp-gson/src/test/java/org/openapitools/client/JSONTest.java +++ b/samples/client/petstore/java/okhttp-gson/src/test/java/org/openapitools/client/JSONTest.java @@ -728,4 +728,16 @@ public class JSONTest { ad = ad.withoutDefault(null); ad.addWithoutDefaultItem("hello world"); } + + /** + * Test property names collision. + */ + @Test + public void testPropertyNamesCollision() throws Exception { + PropertyNameCollision p = new PropertyNameCollision(); + p.setUnderscoreType("test1"); + p.setType("test2"); + p.setTypeWithUnderscore("test3"); + assertEquals(json.getGson().toJson(p), "{\"_type\":\"test1\",\"type\":\"test2\",\"type_\":\"test3\"}"); + } } diff --git a/samples/client/petstore/java/okhttp-gson/src/test/java/org/openapitools/client/model/PropertyNameCollisionTest.java b/samples/client/petstore/java/okhttp-gson/src/test/java/org/openapitools/client/model/PropertyNameCollisionTest.java new file mode 100644 index 00000000000..112efcc90a2 --- /dev/null +++ b/samples/client/petstore/java/okhttp-gson/src/test/java/org/openapitools/client/model/PropertyNameCollisionTest.java @@ -0,0 +1,63 @@ +/* + * OpenAPI Petstore + * This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\ + * + * The version of the OpenAPI document: 1.0.0 + * + * + * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). + * https://openapi-generator.tech + * Do not edit the class manually. + */ + + +package org.openapitools.client.model; + +import com.google.gson.TypeAdapter; +import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +/** + * Model tests for PropertyNameCollision + */ +public class PropertyNameCollisionTest { + private final PropertyNameCollision model = new PropertyNameCollision(); + + /** + * Model tests for PropertyNameCollision + */ + @Test + public void testPropertyNameCollision() { + // TODO: test PropertyNameCollision + } + + /** + * Test the property 'underscoreType' + */ + @Test + public void underscoreTypeTest() { + // TODO: test underscoreType + } + + /** + * Test the property 'type' + */ + @Test + public void typeTest() { + // TODO: test type + } + + /** + * Test the property 'typeWithUnderscore' + */ + @Test + public void typeWithUnderscoreTest() { + // TODO: test typeWithUnderscore + } + +}