Skip to content
This repository has been archived by the owner on Dec 25, 2024. It is now read-only.

Commit

Permalink
v3 convert nested schemas into sequential schemas (#177)
Browse files Browse the repository at this point in the history
* Writes nested schemas earlier and uses them later

* Refactors order CodegenSchema properties are set in to match the order in getSchemas

* Adjusts ref input to setSchemaLocationInfo

* Tweak improves schema template

* Adds properties classes

* Replaces properties with field

* Uses Schema_()

* Uses dataclass properties

* Adds missing type info

* Updates all_of

* Adds any_of, all_of tuples

* Fixes types definition

* Fixed test_any_type_schema tests

* Fixes dataclass type setting issues

* Fixes simple schema format type

* Adds and uses PatternInfo for pattern schema info, validate_regex changed to validate_pattern

* Fixes not constraint tests

* Adds tuple_to_instance

* Writes out allOf/anyOf/oneOf types

* Updates classproperty decorator

* Adds enum return types

* Fixes type errors in schemas.py

* Adds schema singleton metaclass

* Replaces CodegenServer variables with object schema containing variables in properties

* Generates server variables as an object schema to keep all schemas at the root depth

* Fixes new signatures in servers schemas

* Simplifies Server variables definition

* Uses schema default for server variables

* Adds required vars in server definition

* Fixes classproperty and SchemaBase classes

* Fixes broken configuration tests

* Fixes server docs

* Adds isInlineDefinition

* Adds allOf/anyOf/oneOf/properties jsonPathPiece

* Uses jsonPathPiece in properties/allOf/anyOf/oneOf

* Fixes properties typeddict name

* Templates updated to write allOf/anyOf/oneOf/properties earlier if possible

* generates properties/allOf/anyOf/oneOf before class if possible

* Adds and uses schemas.INPUT_TYPES_ALL_INCL_SCHEMA

* Adds another usage of schemas.INPUT_TYPES_ALL_INCL_SCHEMA

* Changes SchemaBase to SingletonMeta

* Only calculates allSchemas once

* Reduces iteration through allSchemas from 2x to 1x

* Removes some getKey invocations

* Consolidates more getKey usages

* Deletes getKey with one input

* Adds comment

* Updates the definition of required properties, reduces the number of calls to getKey

* Updates getKey to use sourceJsonPath

* Adds class name generation that includes numbered suffixes when needed

* Uses pre order traversal for naming schema classes

* Fixes oneOf/anyOf/allOf type aliases

* Adds and uses schemas.INPUT_BASE_TYPES

* Adds string representation of CodegenKey

* Fixes java tests

* Samples regenerated

* Samples regenerated
  • Loading branch information
spacether committed Aug 16, 2023
1 parent 646c57c commit 9bc1ae1
Show file tree
Hide file tree
Showing 409 changed files with 13,691 additions and 18,415 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ public interface CodegenConfig {

CodegenOperation fromOperation(Operation operation, String jsonPath);

CodegenKey getKey(String key);
CodegenKey getKey(String key, String keyType);

CodegenSecurityScheme fromSecurityScheme(SecurityScheme securityScheme, String jsonPath);

Expand All @@ -156,7 +156,7 @@ public interface CodegenConfig {

List<CodegenServer> fromServers(List<Server> servers, String jsonPath);

HashMap<CodegenKey, CodegenSchema> fromServerVariables(Map<String, ServerVariable> variables, String jsonPath);
CodegenSchema fromServerVariables(Map<String, ServerVariable> variables, String jsonPath);

Map<String, String> typeMapping();

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -765,8 +765,13 @@ public String toModuleFilename(String name, String jsonPath) {
return underscore(dropDots(toModelName(name, jsonPath)));
}

/*
This method requires jsonPath to be passed in
It handles responses and schemas
*/
@Override
public String toModelName(String name, String jsonPath) {
boolean rootEntity = (jsonPath != null && jsonPath.endsWith(name));
PairCacheKey key = new PairCacheKey(name, jsonPath);
if (modelNameCache.containsKey(key)) {
return modelNameCache.get(key);
Expand Down Expand Up @@ -809,8 +814,8 @@ public String toModelName(String name, String jsonPath) {
// model name cannot use reserved keyword, e.g. return
if (isReservedWord(camelizedName)) {
String modelName = "_" + camelizedName; // e.g. return => ModelReturn (after camelize)
if (isComponent) {
LOGGER.warn("{} (reserved word) cannot be used as component name. Renamed to {}", camelizedName, modelName);
if (isComponent && rootEntity) {
LOGGER.warn("{} (reserved word) cannot be used as name. Renamed to {}", camelizedName, modelName);
}
modelNameCache.put(key, modelName);
return modelName;
Expand All @@ -820,7 +825,7 @@ public String toModelName(String name, String jsonPath) {
// model name starts with number
if (camelizedName.matches("^\\d.*")) {
String modelName = "_" + camelizedName; // e.g. return => ModelReturn (after camelize)
if (isComponent) {
if (isComponent && rootEntity) {
LOGGER.warn("{} (component name starts with number) cannot be used as name. Renamed to {}", camelizedName, modelName);
}
modelNameCache.put(key, modelName);
Expand Down Expand Up @@ -1356,7 +1361,7 @@ private String toExampleValueRecursive(String modelName, Schema schema, Object o
String schemaName = getSchemaName(mm.modelName);
Schema modelSchema = getModelNameToSchemaCache().get(schemaName);
CodegenSchema cp = new CodegenSchema();
cp.jsonPathPiece = getKey(disc.propertyName.original);
cp.jsonPathPiece = getKey(disc.propertyName.original, "misc");
cp.example = discPropNameValue;
return exampleForObjectModel(modelSchema, fullPrefix, closeChars, cp, indentationLevel, exampleLine, closingIndentation, includedSchemas);
}
Expand Down Expand Up @@ -1523,7 +1528,7 @@ private String toExampleValueRecursive(String modelName, Schema schema, Object o
String schemaName = getSchemaName(mm.modelName);
Schema modelSchema = getModelNameToSchemaCache().get(schemaName);
CodegenSchema cp = new CodegenSchema();
cp.jsonPathPiece = getKey(disc.propertyName.original);
cp.jsonPathPiece = getKey(disc.propertyName.original, "misc");
cp.example = discPropNameValue;
return exampleForObjectModel(modelSchema, fullPrefix, closeChars, cp, indentationLevel, exampleLine, closingIndentation, includedSchemas);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.openapijsonschematools.codegen.model;

import java.util.ArrayList;

/**
* A class to store inline codegenschema definitions
*/
public class ArrayListWithContext<E> extends ArrayList<E> implements InlineContext {
private boolean internalallAreInline = false;
private CodegenKey internalJsonPathPiece = null;
public boolean allAreInline() {
return internalallAreInline;
}

public void setAllAreInline(boolean allAreInline) {
this.internalallAreInline = allAreInline;
}

public CodegenKey jsonPathPiece() {
return internalJsonPathPiece;
}

public void setJsonPathPiece(CodegenKey jsonPathPiece) {
this.internalJsonPathPiece = jsonPathPiece;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ public boolean equals(Object o) {
Objects.equals(anchorPiece, that.anchorPiece);
}

@Override
public String toString() {
final StringBuilder sb = new StringBuilder("CodegenKey{");
sb.append("original=").append(original);
sb.append(", isValid=").append(isValid);
sb.append(", snakeCase=").append(snakeCase);
sb.append(", camelCase=").append(camelCase);
sb.append(", anchorPiece=").append(anchorPiece);
sb.append('}');
return sb.toString();
}

@Override
public int hashCode() {
return Objects.hash(original, isValid, snakeCase, camelCase, anchorPiece);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import io.swagger.v3.oas.models.ExternalDocumentation;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
Expand Down Expand Up @@ -46,12 +47,12 @@ public class CodegenSchema {
public LinkedHashMap<CodegenKey, CodegenSchema> requiredProperties; // used to store required info
public LinkedHashMap<EnumValue, String> enumValueToName; // enum info
public String type;
public List<CodegenSchema> allOf = null;
public List<CodegenSchema> anyOf = null;
public List<CodegenSchema> oneOf = null;
public ArrayListWithContext<CodegenSchema> allOf = null;
public ArrayListWithContext<CodegenSchema> anyOf = null;
public ArrayListWithContext<CodegenSchema> oneOf = null;
public CodegenSchema not = null;
public CodegenSchema items;
public LinkedHashMap<CodegenKey, CodegenSchema> properties;
public LinkedHashMapWithContext<CodegenKey, CodegenSchema> properties;
public CodegenSchema additionalProperties;
public String description;
public String format;
Expand Down Expand Up @@ -86,6 +87,12 @@ public class CodegenSchema {
public LinkedHashMap<CodegenKey, CodegenSchema> optionalProperties;
public boolean schemaIsFromAdditionalProperties;
public HashMap<String, SchemaTestCase> testCases = new HashMap<>();
/**
* schema/allOfType/anyOfType/oneOfType/propertiesType/importsType
* used in getAllSchemas to write type definitions for allOfType/anyOfType/oneOfType/propertiesType
*/
public String instanceType;
private ArrayList<CodegenSchema> allSchemas = null;

public boolean hasValidation() {
return maxItems != null || minItems != null || minProperties != null || maxProperties != null || minLength != null || maxLength != null || multipleOf != null || patternInfo != null || minimum != null || maximum != null || exclusiveMinimum != null || exclusiveMaximum != null || uniqueItems != null;
Expand Down Expand Up @@ -126,6 +133,118 @@ public CodegenSchema getSelfOrDeepestRef() {
return refObject;
}

/**
* Returns all schemas in post order traversal, used by templates to write schema classes
* @param schemasBeforeImports the input list that stores this and all required schemas
* @return the list that stores this and all required schemas
*/
private void getAllSchemas(ArrayList<CodegenSchema> schemasBeforeImports, ArrayList<CodegenSchema> schemasAfterImports, int level) {
/*
post order traversal using alphabetic json schema keywords as the order
keywords with schemas:
additionalProperties
allOf
anyOf
items
not
oneOf
properties
(self)
excluded:
discriminator (not actually applicable because all values would be refs and do not need to be defined)
$ref (because it is an import)
*/
if (isBooleanSchemaFalse) {
// return early for isBooleanSchemaFalse so not_ will not be written
schemasBeforeImports.add(this);
return;
}
if (additionalProperties != null) {
additionalProperties.getAllSchemas(schemasBeforeImports, schemasAfterImports, level + 1);
}
if (allOf != null) {
for (CodegenSchema someSchema: allOf) {
someSchema.getAllSchemas(schemasBeforeImports, schemasAfterImports, level + 1);
}
CodegenSchema extraSchema = new CodegenSchema();
extraSchema.instanceType = "allOfType";
extraSchema.allOf = allOf;
if (allOf.allAreInline()) {
schemasBeforeImports.add(extraSchema);
} else {
schemasAfterImports.add(extraSchema);
}
}
if (anyOf != null) {
for (CodegenSchema someSchema: anyOf) {
someSchema.getAllSchemas(schemasBeforeImports, schemasAfterImports,level + 1);
}
CodegenSchema extraSchema = new CodegenSchema();
extraSchema.instanceType = "anyOfType";
extraSchema.anyOf = anyOf;
if (anyOf.allAreInline()) {
schemasBeforeImports.add(extraSchema);
} else {
schemasAfterImports.add(extraSchema);
}
}
if (items != null) {
items.getAllSchemas(schemasBeforeImports, schemasAfterImports, level + 1);
}
if (not != null) {
not.getAllSchemas(schemasBeforeImports, schemasAfterImports, level + 1);
}
if (oneOf != null) {
for (CodegenSchema someSchema: oneOf) {
someSchema.getAllSchemas(schemasBeforeImports, schemasAfterImports, level + 1);
}
CodegenSchema extraSchema = new CodegenSchema();
extraSchema.instanceType = "oneOfType";
extraSchema.oneOf = oneOf;
if (oneOf.allAreInline()) {
schemasBeforeImports.add(extraSchema);
} else {
schemasAfterImports.add(extraSchema);
}
}
if (properties != null) {
for (CodegenSchema someSchema: properties.values()) {
someSchema.getAllSchemas(schemasBeforeImports, schemasAfterImports, level + 1);
}
CodegenSchema extraSchema = new CodegenSchema();
extraSchema.instanceType = "propertiesType";
extraSchema.properties = properties;
if (properties.allAreInline()) {
schemasBeforeImports.add(extraSchema);
} else {
schemasAfterImports.add(extraSchema);
}
}
if (refInfo != null && level > 0) {
// do not add ref to schemas
return;
}
schemasBeforeImports.add(this);
if (level == 0 && imports != null && !imports.isEmpty()) {
CodegenSchema extraSchema = new CodegenSchema();
extraSchema.instanceType = "importsType";
extraSchema.imports = imports;
schemasBeforeImports.add(extraSchema);
}
}

public ArrayList<CodegenSchema> getSchemas() {
if (allSchemas == null) {
ArrayList<CodegenSchema> schemasBeforeImports = new ArrayList<>();
ArrayList<CodegenSchema> schemasAfterImports = new ArrayList<>();
getAllSchemas(schemasBeforeImports, schemasAfterImports, 0);
schemasBeforeImports.addAll(schemasAfterImports);
allSchemas = schemasBeforeImports;
}
return allSchemas;
}

public boolean isComplicated() {
// used by templates

Expand All @@ -137,7 +256,7 @@ public boolean isComplicated() {

protected void addInstanceInfo(StringBuilder sb) {
sb.append(", description='").append(description).append('\'');
sb.append(", name='").append(jsonPathPiece).append('\'');
sb.append(", jsonPathPiece='").append(jsonPathPiece).append('\'');
sb.append(", defaultValue='").append(defaultValue).append('\'');
sb.append(", title='").append(title).append('\'');
sb.append(", unescapedDescription='").append(unescapedDescription).append('\'');
Expand Down Expand Up @@ -186,6 +305,7 @@ protected void addInstanceInfo(StringBuilder sb) {
sb.append(", imports=").append(imports);
sb.append(", componentModule=").append(componentModule);
sb.append(", testCases=").append(testCases);
sb.append(", instanceType=").append(instanceType);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ public class CodegenServer {
public final String url;
public final String defaultUrl;
public final String description;
public final LinkedHashMap<CodegenKey, CodegenSchema> variables;
public final CodegenSchema variables;
public final CodegenKey jsonPathPiece;
public final boolean rootServer;

public CodegenServer(String url, String description, LinkedHashMap<CodegenKey, CodegenSchema> variables, CodegenKey jsonPathPiece, boolean rootServer) {
public CodegenServer(String url, String description, CodegenSchema variables, CodegenKey jsonPathPiece, boolean rootServer) {
this.url = url;
this.description = description;
this.variables = variables;
Expand All @@ -21,7 +21,7 @@ public CodegenServer(String url, String description, LinkedHashMap<CodegenKey, C
this.defaultUrl = url;
} else {
String defaultUrl = url;
for (CodegenSchema variable: variables.values()) {
for (CodegenSchema variable: variables.properties.values()) {
defaultUrl = defaultUrl.replace("{" + variable.jsonPathPiece.original + "}", (String) variable.defaultValue.value);
}
this.defaultUrl = defaultUrl;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.openapijsonschematools.codegen.model;

public interface InlineContext {
public boolean allAreInline();

public void setAllAreInline(boolean allAreInline);

public CodegenKey jsonPathPiece();

public void setJsonPathPiece(CodegenKey jsonPathPiece);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package org.openapijsonschematools.codegen.model;

import java.util.LinkedHashMap;

public class LinkedHashMapWithContext<K, V> extends LinkedHashMap<K, V> implements InlineContext {
private boolean internalallAreInline = false;
private CodegenKey internalJsonPathPiece = null;
public boolean allAreInline() {
return internalallAreInline;
}

public void setAllAreInline(boolean allAreInline) {
this.internalallAreInline = allAreInline;
}

public CodegenKey jsonPathPiece() {
return internalJsonPathPiece;
}

public void setJsonPathPiece(CodegenKey jsonPathPiece) {
this.internalJsonPathPiece = jsonPathPiece;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ None,
bytes,
io.FileIO,
io.BufferedReader,
'Schema',
'Schema',
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{{allOf.jsonPathPiece.camelCase}} = typing.Tuple[
{{#each allOf}}
{{#if refInfo.refClass}}
typing.Type[{{#if refInfo.refModule}}{{refInfo.refModule}}.{{/if}}{{refInfo.refClass}}],
{{else}}
typing.Type[{{jsonPathPiece.camelCase}}[schemas.U]],
{{/if}}
{{/each}}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{{anyOf.jsonPathPiece.camelCase}} = typing.Tuple[
{{#each anyOf}}
{{#if refInfo.refClass}}
typing.Type[{{#if refInfo.refModule}}{{refInfo.refModule}}.{{/if}}{{refInfo.refClass}}],
{{else}}
typing.Type[{{jsonPathPiece.camelCase}}[schemas.U]],
{{/if}}
{{/each}}
]
Loading

0 comments on commit 9bc1ae1

Please sign in to comment.