Skip to content

Commit

Permalink
Merge pull request #676 from Backbase/fix/free-form-object-issue
Browse files Browse the repository at this point in the history
Fix/Free-Form Object issue with `boat-swift5` generator
  • Loading branch information
DevAgani authored Oct 24, 2023
2 parents 6b49778 + 71db033 commit df4a7e7
Show file tree
Hide file tree
Showing 4 changed files with 262 additions and 79 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -771,30 +771,30 @@ public void execute() throws MojoExecutionException, MojoFailureException {
GlobalSettings.setProperty(CodegenConstants.WITH_XML, withXml.toString());

if (configOptions != null) {
// Retained for backwards-compataibility with configOptions -> instantiation-types
// Retained for backwards-compatibility with configOptions -> instantiation-types
if (instantiationTypes == null && configOptions.containsKey(INSTANTIATION_TYPES)) {
applyInstantiationTypesKvp(configOptions.get(INSTANTIATION_TYPES).toString(),
configurator);
}

// Retained for backwards-compataibility with configOptions -> import-mappings
// Retained for backwards-compatibility with configOptions -> import-mappings
if (importMappings == null && configOptions.containsKey(IMPORT_MAPPINGS)) {
applyImportMappingsKvp(configOptions.get(IMPORT_MAPPINGS).toString(),
configurator);
}

// Retained for backwards-compataibility with configOptions -> type-mappings
// Retained for backwards-compatibility with configOptions -> type-mappings
if (typeMappings == null && configOptions.containsKey(TYPE_MAPPINGS)) {
applyTypeMappingsKvp(configOptions.get(TYPE_MAPPINGS).toString(), configurator);
}

// Retained for backwards-compataibility with configOptions -> language-specific-primitives
// Retained for backwards-compatibility with configOptions -> language-specific-primitives
if (languageSpecificPrimitives == null && configOptions.containsKey(LANGUAGE_SPECIFIC_PRIMITIVES)) {
applyLanguageSpecificPrimitivesCsv(configOptions
.get(LANGUAGE_SPECIFIC_PRIMITIVES).toString(), configurator);
}

// Retained for backwards-compataibility with configOptions -> additional-properties
// Retained for backwards-compatibility with configOptions -> additional-properties
if (additionalProperties == null && configOptions.containsKey(ADDITIONAL_PROPERTIES)) {
applyAdditionalPropertiesKvp(configOptions.get(ADDITIONAL_PROPERTIES).toString(),
configurator);
Expand All @@ -804,7 +804,7 @@ public void execute() throws MojoExecutionException, MojoFailureException {
applyServerVariablesKvp(configOptions.get(SERVER_VARIABLES).toString(), configurator);
}

// Retained for backwards-compataibility with configOptions -> reserved-words-mappings
// Retained for backwards-compatibility with configOptions -> reserved-words-mappings
if (reservedWordsMappings == null && configOptions.containsKey(RESERVED_WORDS_MAPPINGS)) {
applyReservedWordsMappingsKvp(configOptions.get(RESERVED_WORDS_MAPPINGS)
.toString(), configurator);
Expand Down Expand Up @@ -960,8 +960,8 @@ protected Collection<String> getGeneratorSpecificSupportingFiles() {
/**
* Calculate openapi specification file hash. If specification is hosted on remote resource it is downloaded first
*
* @param inputSpecFile - Openapi specification input file to calculate it's hash.
* Does not taken into account if input spec is hosted on remote resource
* @param inputSpecFile - Openapi specification input file to calculate its hash.
* Does not take into account if input spec is hosted on remote resource
* @return openapi specification file hash
* @throws IOException When cannot read the file
*/
Expand Down Expand Up @@ -1019,8 +1019,8 @@ private URL inputSpecRemoteUrl() {
/**
* Get specification hash file.
*
* @param inputSpecFile - Openapi specification input file to calculate it's hash.
* Does not taken into account if input spec is hosted on remote resource
* @param inputSpecFile - Openapi specification input file to calculate its hash.
* Does not take into account if input spec is hosted on remote resource
* @return a file with previously calculated hash
*/
private File getHashFile(File inputSpecFile) {
Expand Down Expand Up @@ -1073,7 +1073,7 @@ private void addCompileSourceRootIfConfigured() {
* config.additionalProperties (configuration/configOptions) to proper booleans.
* This enables mustache files to handle the properties better.
*
* @param config GodeGen config
* @param config CodeGen config
*/
private void adjustAdditionalProperties(final CodegenConfig config) {
Map<String, Object> configAdditionalProperties = config.additionalProperties();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.swagger.v3.oas.models.media.Schema;
import org.openapitools.codegen.CodegenConfig;
import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.CodegenProperty;
import org.openapitools.codegen.SupportingFile;
import org.openapitools.codegen.meta.GeneratorMetadata;
import org.openapitools.codegen.meta.Stability;
Expand Down Expand Up @@ -31,6 +32,12 @@ public BoatSwift5Codegen() {

// Set the default template directory
embeddedTemplateDir = templateDir = getName();

// Type mappings //
this.typeMapping.remove("object");
this.typeMapping.remove("AnyType");
this.typeMapping.put("object", "Any");
this.typeMapping.put("AnyType", "Any");
}

@Override
Expand Down Expand Up @@ -62,6 +69,53 @@ public String getTypeDeclaration(Schema p) {
return super.getTypeDeclaration(p);
}

@Override
public CodegenModel fromModel(String name, Schema model) {
Map<String, Schema> allDefinitions = ModelUtils.getSchemas(this.openAPI);
CodegenModel codegenModel = super.fromModel(name, model);
if (codegenModel.description != null) {
codegenModel.imports.add("ApiModel");
}

fixAllFreeFormObject(codegenModel);

return codegenModel;
}

/*
This is added as a compatibility requirement for API specs containing free form objects
missing `additionalProperties` property
*/
private void fixAllFreeFormObject(CodegenModel codegenModel) {
this.fixFreeFormObject(codegenModel.vars);
this.fixFreeFormObject(codegenModel.optionalVars);
this.fixFreeFormObject(codegenModel.requiredVars);
this.fixFreeFormObject(codegenModel.parentVars);
this.fixFreeFormObject(codegenModel.allVars);
this.fixFreeFormObject(codegenModel.readOnlyVars);
this.fixFreeFormObject(codegenModel.readWriteVars);
}

/*
If a property has both isFreeFormObject and isMapContainer true make isFreeFormObject false
This way when we have a free form object in the spec that has a typed value it will be
treated as a Dictionary
*/
private void fixFreeFormObject(List<CodegenProperty> codegenProperties) {
for (CodegenProperty codegenProperty : codegenProperties) {
if (codegenProperty.isFreeFormObject && codegenProperty.isMap && !codegenProperty.items.isFreeFormObject) {
codegenProperty.isFreeFormObject = false;
}

if (codegenProperty.isArray && codegenProperty.items.isFreeFormObject) {
codegenProperty.isFreeFormObject = true;
if (codegenProperty.additionalProperties == null) {
codegenProperty.isMap = false;
}
}
}
}

// Fix for inheritance bug
@Override
public Map<String, ModelsMap> postProcessAllModels(Map<String, ModelsMap> objs) {
Expand Down Expand Up @@ -113,7 +167,7 @@ private void fixInheritance(CodegenModel codegenModel, CodegenModel parentModel)
@Override
public void postProcess() {
System.out.println("################################################################################");
System.out.println("# Thanks for using BOAT Swift OpenAPI Generator. #");
System.out.println("# Thanks for using BOAT Swift 5 OpenAPI Generator. #");
System.out.println("################################################################################");
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,33 @@
{{#allParams}}
{{#isEnum}}
{{> modelInlineEnumDeclaration}}
{{/isEnum}}
{{#isEnum}}
{{> modelInlineEnumDeclaration}}
{{/isEnum}}
{{/allParams}}
{{#allParams}}
{{#isEnum}}
{{#description}}/** {{description}} */
{{/description}}{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} let {{paramName}}: {{{datatypeWithEnum}}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^required}}?{{/required}}
{{/isEnum}}
{{^isEnum}}
{{#description}}/** {{description}} */
{{/description}}{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} let {{paramName}}: {{{dataType}}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^required}}?{{/required}}
{{#objcCompatible}}
{{#vendorExtensions.x-swift-optional-scalar}}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} var {{paramName}}Num: NSNumber? {
get {
return {{paramName}}.map({ return NSNumber(value: $0) })
}
}
{{/vendorExtensions.x-swift-optional-scalar}}
{{/objcCompatible}}
{{/isEnum}}
{{#isEnum}}
{{#description}}/** {{description}} */
{{/description}}{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} let {{paramName}}: {{{datatypeWithEnum}}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^required}}?{{/required}}
{{/isEnum}}
{{^isEnum}}
{{#description}}/** {{description}} */
{{/description}}{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} let {{paramName}}: {{{dataType}}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^required}}?{{/required}}
{{#objcCompatible}}
{{#vendorExtensions.x-swift-optional-scalar}}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} var {{paramName}}Num: NSNumber? {
get {
return {{paramName}}.map({ return NSNumber(value: $0) })
}
}
{{/vendorExtensions.x-swift-optional-scalar}}
{{/objcCompatible}}
{{/isEnum}}
{{/allParams}}

{{#hasParams}}
internal init({{#allParams}}{{paramName}}: {{#isEnum}}{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{dataType}}}{{/isEnum}}{{^required}}?{{/required}}{{^-last}}, {{/-last}}{{/allParams}}) {
{{#allParams}}
{{#allParams}}
self.{{paramName}} = {{paramName}}
{{/allParams}}
{{/allParams}}
}
{{/hasParams}}
{{#hasParams}}
Expand All @@ -36,47 +36,47 @@
{{#allParams}}
{{#description}}/** {{description}} */
{{/description}}{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} {{#required}}let{{/required}}{{^required}}private(set) var{{/required}} {{> api_param_builder}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^required}}?{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/required}}
{{^isEnum}}
{{#objcCompatible}}
{{#vendorExtensions.x-swift-optional-scalar}}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} var {{paramName}}Num: NSNumber? {
get {
return {{paramName}}.map({ return NSNumber(value: $0) })
}
}
{{/vendorExtensions.x-swift-optional-scalar}}
{{/objcCompatible}}
{{/isEnum}}
{{^isEnum}}
{{#objcCompatible}}
{{#vendorExtensions.x-swift-optional-scalar}}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} var {{paramName}}Num: NSNumber? {
get {
return {{paramName}}.map({ return NSNumber(value: $0) })
}
}
{{/vendorExtensions.x-swift-optional-scalar}}
{{/objcCompatible}}
{{/isEnum}}
{{/allParams}}

{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} init({{#requiredParams}}{{^-first}}, {{/-first}}{{> api_param_builder}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/requiredParams}}) {
{{#requiredParams}}
self.{{paramName}} = {{paramName}}
{{/requiredParams}}
}
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} init({{#requiredParams}}{{^-first}}, {{/-first}}{{> api_param_builder}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/requiredParams}}) {
{{#requiredParams}}
self.{{paramName}} = {{paramName}}
{{/requiredParams}}
}

{{#optionalParams}}
/// Setter method for {{paramName}} property.
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} func set({{> api_param_builder}}?) -> Self {
self.{{paramName}} = {{paramName}}
return self
self.{{paramName}} = {{paramName}}
return self
}
{{/optionalParams}}

/// Builder initializer method for {{operationIdCamelCase}}RequestParams DTO.
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} func build() -> {{operationIdCamelCase}}RequestParams {
return {{operationIdCamelCase}}RequestParams({{#allParams}}{{paramName}}: {{paramName}}{{^-last}},
{{/-last}}{{/allParams}})
}
/// Builder initializer method for {{operationIdCamelCase}}RequestParams DTO.
{{#nonPublicApi}}internal{{/nonPublicApi}}{{^nonPublicApi}}public{{/nonPublicApi}} func build() -> {{operationIdCamelCase}}RequestParams {
return {{operationIdCamelCase}}RequestParams({{#allParams}}{{paramName}}: {{paramName}}{{^-last}},
{{/-last}}{{/allParams}})
}

public static func ==(lhs: Builder, rhs: Builder) -> Bool {
return {{^hasParams}}true{{/hasParams}}{{#allParams}}{{#isFreeFormObject}}{{#isMap}}lhs.{{paramName}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^required}}?{{/required}}.mapValues { AnyCodable($0) } == rhs.{{paramName}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^required}}?{{/required}}.mapValues { AnyCodable($0) }{{/isMap}}{{^isMap}}AnyCodable(lhs.{{paramName}}) == AnyCodable(rhs.{{paramName}}){{/isMap}}{{/isFreeFormObject}}{{^isFreeFormObject}}lhs.{{paramName}}{{#isArray}}{{^required}}?{{/required}}.description{{/isArray}} == rhs.{{paramName}}{{#isArray}}{{^required}}?{{/required}}.description{{/isArray}}{{/isFreeFormObject}}{{^-last}} &&
{{/-last}}{{/allParams}}
}
public static func ==(lhs: Builder, rhs: Builder) -> Bool {
return {{^hasParams}}true{{/hasParams}}{{#allParams}}{{#isFreeFormObject}}AnyCodable(lhs.{{paramName}}) == AnyCodable(rhs.{{paramName}}){{/isFreeFormObject}}{{^isFreeFormObject}}lhs.{{paramName}}{{#isArray}}{{^required}}?{{/required}}.description{{/isArray}} == rhs.{{paramName}}{{#isArray}}{{^required}}?{{/required}}.description{{/isArray}}{{/isFreeFormObject}}{{^-last}} &&
{{/-last}}{{/allParams}}
}
}
{{/hasParams}}

public static func ==(lhs: Self, rhs: Self) -> Bool {
return {{^hasParams}}true{{/hasParams}}{{#allParams}}{{#isFreeFormObject}}{{#isMap}}lhs.{{paramName}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^required}}?{{/required}}.mapValues { AnyCodable($0) } == rhs.{{paramName}}{{#required}}{{#isNullable}}?{{/isNullable}}{{/required}}{{^required}}?{{/required}}.mapValues { AnyCodable($0) }{{/isMap}}{{^isMap}}AnyCodable(lhs.{{paramName}}) == AnyCodable(rhs.{{paramName}}){{/isMap}}{{/isFreeFormObject}}{{^isFreeFormObject}}lhs.{{paramName}}{{#isArray}}{{^required}}?{{/required}}.description{{/isArray}} == rhs.{{paramName}}{{#isArray}}{{^required}}?{{/required}}.description{{/isArray}}{{/isFreeFormObject}}{{^-last}} &&
{{/-last}}{{/allParams}}
}
public static func ==(lhs: Self, rhs: Self) -> Bool {
return {{^hasParams}}true{{/hasParams}}{{#allParams}}{{#isFreeFormObject}}AnyCodable(lhs.{{paramName}}) == AnyCodable(rhs.{{paramName}}){{/isFreeFormObject}}{{^isFreeFormObject}}lhs.{{paramName}}{{#isArray}}{{^required}}?{{/required}}.description{{/isArray}} == rhs.{{paramName}}{{#isArray}}{{^required}}?{{/required}}.description{{/isArray}}{{/isFreeFormObject}}{{^-last}} &&
{{/-last}}{{/allParams}}
}
Loading

0 comments on commit df4a7e7

Please sign in to comment.