Skip to content

Commit 35d4272

Browse files
authored
Adds free form model generation in python-experimental (#7373)
* Adds free form model generation in python-experimental * Adds hasValidation property to codegenModel * Adds separate variable val * Samples regenerated * Updates test_some_object.py * Adds two more boolean conditions * Runs ensure up to date * Updates tests to check that models are or are not generated * Removes unused import * Updates model names in java test * Removes unneeded test * Cleans up tests
1 parent 77f24a4 commit 35d4272

File tree

25 files changed

+876
-27
lines changed

25 files changed

+876
-27
lines changed

modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,4 +292,6 @@ public interface CodegenConfig {
292292
boolean isRemoveEnumValuePrefix();
293293

294294
void setRemoveEnumValuePrefix(boolean removeEnumValuePrefix);
295+
296+
Schema unaliasSchema(Schema schema, Map<String, String> usedImportMappings);
295297
}

modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenModel.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,13 @@ public void setMultipleOf(Number multipleOf) {
550550
this.multipleOf = multipleOf;
551551
}
552552

553+
// indicates if the model component has validation on the root level schema
554+
// this will be true when minItems or minProperties is set
555+
public boolean hasValidation() {
556+
boolean val = (maxItems != null || minItems != null || minProperties != null || maxProperties != null || minLength != null || maxLength != null || multipleOf != null || pattern != null || minimum != null || maximum != null || Boolean.TRUE.equals(uniqueItems) || Boolean.TRUE.equals(exclusiveMaximum) || Boolean.TRUE.equals(exclusiveMinimum));
557+
return val;
558+
}
559+
553560
public List<CodegenProperty> getReadOnlyVars() {
554561
return readOnlyVars;
555562
}

modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1971,7 +1971,7 @@ public String toOneOfName(List<String> names, ComposedSchema composedSchema) {
19711971
return "oneOf<" + String.join(",", names) + ">";
19721972
}
19731973

1974-
protected Schema unaliasSchema(Schema schema, Map<String, String> usedImportMappings) {
1974+
public Schema unaliasSchema(Schema schema, Map<String, String> usedImportMappings) {
19751975
return ModelUtils.unaliasSchema(this.openAPI, schema, usedImportMappings);
19761976
}
19771977

@@ -2491,6 +2491,9 @@ public CodegenModel fromModel(String name, Schema schema) {
24912491
} else { // type is number and without format
24922492
m.isNumber = Boolean.TRUE;
24932493
}
2494+
} else if (ModelUtils.isFreeFormObject(openAPI, schema)) {
2495+
addAdditionPropertiesToCodeGenModel(m, schema);
2496+
ModelUtils.syncValidationProperties(schema, m);
24942497
}
24952498

24962499
if (Boolean.TRUE.equals(schema.getNullable())) {
@@ -2814,7 +2817,7 @@ protected List<MappedModel> getAllOfDescendants(String thisSchemaName, OpenAPI o
28142817
String currentSchemaName = thisSchemaName;
28152818
while (true) {
28162819
for (String childName : schemas.keySet()) {
2817-
if (childName == thisSchemaName) {
2820+
if (childName.equals(thisSchemaName)) {
28182821
continue;
28192822
}
28202823
Schema child = schemas.get(childName);

modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -459,8 +459,19 @@ void generateModels(List<File> files, List<Object> allModels, List<String> unuse
459459
Schema schema = schemas.get(name);
460460

461461
if (ModelUtils.isFreeFormObject(this.openAPI, schema)) { // check to see if it'a a free-form object
462-
LOGGER.info("Model {} not generated since it's a free-form object", name);
463-
continue;
462+
// there are 3 free form use cases
463+
// 1. free form with no validation that is not allOf included in any composed schemas
464+
// 2. free form with validation
465+
// 3. free form that is allOf included in any composed schemas
466+
// this use case arises when using interface schemas
467+
// generators may choose to make models for use case 2 + 3
468+
Schema refSchema = new Schema();
469+
refSchema.set$ref("#/components/schemas/"+name);
470+
Schema unaliasedSchema = config.unaliasSchema(refSchema, config.importMapping());
471+
if (unaliasedSchema.get$ref() == null) {
472+
LOGGER.info("Model {} not generated since it's a free-form object", name);
473+
continue;
474+
}
464475
} else if (ModelUtils.isMapSchema(schema)) { // check to see if it's a "map" model
465476
// A composed schema (allOf, oneOf, anyOf) is considered a Map schema if the additionalproperties attribute is set
466477
// for that composed schema. However, in the case of a composed schema, the properties are defined or referenced

modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PythonClientExperimentalCodegen.java

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ public String getName() {
223223
}
224224

225225
@Override
226-
protected Schema unaliasSchema(Schema schema, Map<String, String> usedImportMappings) {
226+
public Schema unaliasSchema(Schema schema, Map<String, String> usedImportMappings) {
227227
Map<String, Schema> allSchemas = ModelUtils.getSchemas(openAPI);
228228
if (allSchemas == null || allSchemas.isEmpty()) {
229229
// skip the warning as the spec can have no model defined
@@ -238,19 +238,6 @@ protected Schema unaliasSchema(Schema schema, Map<String, String> usedImportMapp
238238
return schema;
239239
}
240240
Schema ref = allSchemas.get(simpleRef);
241-
Boolean hasValidation = (
242-
ref.getMaxItems() != null ||
243-
ref.getMinLength() != null ||
244-
ref.getMinItems() != null ||
245-
ref.getMultipleOf() != null ||
246-
ref.getPattern() != null ||
247-
ref.getMaxLength() != null ||
248-
ref.getMinimum() != null ||
249-
ref.getMaximum() != null ||
250-
ref.getExclusiveMaximum() != null ||
251-
ref.getExclusiveMinimum() != null ||
252-
ref.getUniqueItems() != null
253-
);
254241
if (ref == null) {
255242
once(LOGGER).warn("{} is not defined", schema.get$ref());
256243
return schema;
@@ -281,11 +268,17 @@ protected Schema unaliasSchema(Schema schema, Map<String, String> usedImportMapp
281268
} else if (ModelUtils.isObjectSchema(ref)) { // model
282269
if (ref.getProperties() != null && !ref.getProperties().isEmpty()) { // has at least one property
283270
return schema;
284-
} else { // free form object (type: object)
271+
} else {
272+
// free form object (type: object)
273+
if (ModelUtils.hasValidation(ref)) {
274+
return schema;
275+
} else if (getAllOfDescendants(simpleRef, openAPI).size() > 0) {
276+
return schema;
277+
}
285278
return unaliasSchema(allSchemas.get(ModelUtils.getSimpleRef(schema.get$ref())),
286279
usedImportMappings);
287280
}
288-
} else if (hasValidation) {
281+
} else if (ModelUtils.hasValidation(ref)) {
289282
// non object non array non map schemas that have validations
290283
// are returned so we can generate those schemas as models
291284
// we do this to:

modules/openapi-generator/src/main/java/org/openapitools/codegen/utils/ModelUtils.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -707,6 +707,24 @@ public static boolean isModel(Schema schema) {
707707
return schema instanceof ComposedSchema || schema instanceof ObjectSchema;
708708
}
709709

710+
public static boolean hasValidation(Schema sc) {
711+
return (
712+
sc.getMaxItems() != null ||
713+
sc.getMinProperties() != null ||
714+
sc.getMaxProperties() != null ||
715+
sc.getMinLength() != null ||
716+
sc.getMinItems() != null ||
717+
sc.getMultipleOf() != null ||
718+
sc.getPattern() != null ||
719+
sc.getMaxLength() != null ||
720+
sc.getMinimum() != null ||
721+
sc.getMaximum() != null ||
722+
sc.getExclusiveMaximum() != null ||
723+
sc.getExclusiveMinimum() != null ||
724+
sc.getUniqueItems() != null
725+
);
726+
}
727+
710728
/**
711729
* Check to see if the schema is a free form object.
712730
*

modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/classvars.mustache

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,10 @@
4242
}
4343

4444
validations = {
45-
{{#isAlias}}
46-
{{^isEnum}}
47-
{{^isArrayModel}}
45+
{{#hasValidation}}
4846
('value',): {
4947
{{> python-experimental/model_templates/validations }}
50-
{{/isArrayModel}}
51-
{{/isEnum}}
52-
{{/isAlias}}
48+
{{/hasValidation}}
5349
{{#requiredVars}}
5450
{{#hasValidation}}
5551
('{{name}}',): {

modules/openapi-generator/src/main/resources/python/python-experimental/model_templates/validations.mustache

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
{{#maxItems}}
88
'max_items': {{maxItems}},
99
{{/maxItems}}
10+
{{#minProperties}}
11+
'min_properties': {{minProperties}},
12+
{{/minProperties}}
13+
{{#maxProperties}}
14+
'max_properties': {{maxProperties}},
15+
{{/maxProperties}}
1016
{{#minItems}}
1117
'min_items': {{minItems}},
1218
{{/minItems}}

modules/openapi-generator/src/test/java/org/openapitools/codegen/DefaultCodegenTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import io.swagger.v3.oas.models.responses.ApiResponses;
3434
import io.swagger.v3.parser.core.models.ParseOptions;
3535

36+
import org.openapitools.codegen.config.CodegenConfigurator;
3637
import org.openapitools.codegen.templating.mustache.CamelCaseLambda;
3738
import org.openapitools.codegen.templating.mustache.IndentedLambda;
3839
import org.openapitools.codegen.templating.mustache.LowercaseLambda;
@@ -44,6 +45,7 @@
4445
import org.testng.annotations.Test;
4546

4647
import java.io.File;
48+
import java.nio.file.Files;
4749
import java.util.*;
4850
import java.util.stream.Collectors;
4951

@@ -2266,4 +2268,22 @@ public void arrayModelHasValidation() {
22662268
assertEquals((int) cm.getMinItems(), 1);
22672269
}
22682270

2271+
@Test
2272+
public void testFreeFormSchemas() throws Exception {
2273+
File output = Files.createTempDirectory("test").toFile();
2274+
2275+
final CodegenConfigurator configurator = new CodegenConfigurator()
2276+
.setGeneratorName("java")
2277+
.setInputSpec("src/test/resources/3_0/issue_7361.yaml")
2278+
.setOutputDir(output.getAbsolutePath().replace("\\", "/"));
2279+
2280+
final ClientOptInput clientOptInput = configurator.toClientOptInput();
2281+
DefaultGenerator generator = new DefaultGenerator();
2282+
List<File> files = generator.opts(clientOptInput).generate();
2283+
2284+
TestUtils.ensureDoesNotContainsFile(files, output, "src/main/java/org/openapitools/client/model/FreeFormWithValidation.java");
2285+
TestUtils.ensureDoesNotContainsFile(files, output, "src/main/java/org/openapitools/client/model/FreeFormInterface.java");
2286+
TestUtils.ensureDoesNotContainsFile(files, output, "src/main/java/org/openapitools/client/model/FreeForm.java");
2287+
output.deleteOnExit();
2288+
}
22692289
}

modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaModelEnumTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ public void overrideEnumTest() {
148148

149149
final ComposedSchema composedSchema = new ComposedSchema()
150150
.addAllOfItem(new Schema().$ref(parentModel.getName()));
151+
composedSchema.setName("sample");
151152

152153
final JavaClientCodegen codegen = new JavaClientCodegen();
153154
OpenAPI openAPI = TestUtils.createOpenAPI();

0 commit comments

Comments
 (0)