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

Commit 3628f73

Browse files
committed
v2 Restructures endpoint parameter, body + response information (#57)
* Restructures endpoint data, modules written for responses * Endpoint docs improved, not done yet * Updates header_params * Updates path_parameters * Updates cookie_params, updates x_params to say key + input type * Samples updated * Fixes DefaultCodegenTests
1 parent a0bfd6f commit 3628f73

File tree

1,407 files changed

+142307
-124680
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,407 files changed

+142307
-124680
lines changed

modules/openapi-json-schema-generator/src/main/java/org/openapitools/codegen/CodegenResponse.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ public class CodegenResponse implements IJsonSchemaValidationProperties {
9393
private Map<String, CodegenProperty> requiredVarsMap;
9494
private String ref;
9595
private boolean schemaIsFromAdditionalProperties;
96+
public Set<String> imports = new TreeSet<>();
9697

9798
@Override
9899
public int hashCode() {
@@ -105,7 +106,7 @@ public int hashCode() {
105106
getMinLength(), exclusiveMinimum, exclusiveMaximum, getMinimum(), getMaximum(), getPattern(),
106107
is1xx, is2xx, is3xx, is4xx, is5xx, additionalPropertiesIsAnyType, hasVars, hasRequired,
107108
hasDiscriminatorWithNonEmptyMapping, composedSchemas, hasMultipleTypes, responseHeaders, content,
108-
requiredVarsMap, ref, uniqueItemsBoolean, schemaIsFromAdditionalProperties);
109+
requiredVarsMap, ref, uniqueItemsBoolean, schemaIsFromAdditionalProperties, imports);
109110
}
110111

111112
@Override
@@ -155,6 +156,7 @@ public boolean equals(Object o) {
155156
getAdditionalPropertiesIsAnyType() == that.getAdditionalPropertiesIsAnyType() &&
156157
getHasVars() == that.getHasVars() &&
157158
getHasRequired() == that.getHasRequired() &&
159+
Objects.equals(imports, that.imports) &&
158160
Objects.equals(uniqueItemsBoolean, that.getUniqueItemsBoolean()) &&
159161
Objects.equals(ref, that.getRef()) &&
160162
Objects.equals(requiredVarsMap, that.getRequiredVarsMap()) &&
@@ -612,6 +614,7 @@ public String toString() {
612614
sb.append(", requiredVarsMap=").append(requiredVarsMap);
613615
sb.append(", ref=").append(ref);
614616
sb.append(", schemaIsFromAdditionalProperties=").append(schemaIsFromAdditionalProperties);
617+
sb.append(", imports=").append(imports);
615618
sb.append('}');
616619
return sb.toString();
617620
}

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

+11-12
Original file line numberDiff line numberDiff line change
@@ -4259,13 +4259,12 @@ public CodegenOperation fromOperation(String path,
42594259
for (Entry<String, Header> entry : headers.entrySet()) {
42604260
String headerName = entry.getKey();
42614261
Header header = ModelUtils.getReferencedHeader(this.openAPI, entry.getValue());
4262-
CodegenParameter responseHeader = headerToCodegenParameter(header, headerName, imports, String.format(Locale.ROOT, "%sResponseParameter", r.code));
4262+
CodegenParameter responseHeader = headerToCodegenParameter(header, headerName, r.imports, String.format(Locale.ROOT, "%sResponseParameter", r.code));
42634263
responseHeaders.add(responseHeader);
42644264
}
42654265
r.setResponseHeaders(responseHeaders);
42664266
}
4267-
String mediaTypeSchemaSuffix = String.format(Locale.ROOT, "%sResponseBody", r.code);
4268-
r.setContent(getContent(response.getContent(), imports, mediaTypeSchemaSuffix));
4267+
r.setContent(getContent(response.getContent(), r.imports, ""));
42694268

42704269
if (!addSchemaImportsFromV3SpecLocations) {
42714270
if (r.baseType != null &&
@@ -4385,7 +4384,7 @@ public CodegenOperation fromOperation(String path,
43854384
param = ModelUtils.getReferencedParameter(this.openAPI, param);
43864385

43874386
CodegenParameter p = fromParameter(param, imports);
4388-
p.setContent(getContent(param.getContent(), imports, "RequestParameter" + toModelName(param.getName())));
4387+
p.setContent(getContent(param.getContent(), imports, param.getName()));
43894388

43904389
// ensure unique params
43914390
if (ensureUniqueParams) {
@@ -7086,10 +7085,6 @@ protected void updateRequestBodyForString(CodegenParameter codegenParameter, Sch
70867085
codegenParameter.pattern = toRegularExpression(schema.getPattern());
70877086
}
70887087

7089-
protected String toMediaTypeSchemaName(String contentType, String mediaTypeSchemaSuffix) {
7090-
return "SchemaFor" + mediaTypeSchemaSuffix + toModelName(contentType);
7091-
}
7092-
70937088
private CodegenParameter headerToCodegenParameter(Header header, String headerName, Set<String> imports, String mediaTypeSchemaSuffix) {
70947089
if (header == null) {
70957090
return null;
@@ -7115,7 +7110,7 @@ private CodegenParameter headerToCodegenParameter(Header header, String headerNa
71157110
return param;
71167111
}
71177112

7118-
protected LinkedHashMap<String, CodegenMediaType> getContent(Content content, Set<String> imports, String mediaTypeSchemaSuffix) {
7113+
protected LinkedHashMap<String, CodegenMediaType> getContent(Content content, Set<String> imports, String schemaName) {
71197114
if (content == null) {
71207115
return null;
71217116
}
@@ -7134,7 +7129,7 @@ protected LinkedHashMap<String, CodegenMediaType> getContent(Content content, Se
71347129
for (Entry<String, Header> headerEntry : encHeaders.entrySet()) {
71357130
String headerName = headerEntry.getKey();
71367131
Header header = ModelUtils.getReferencedHeader(this.openAPI, headerEntry.getValue());
7137-
CodegenParameter param = headerToCodegenParameter(header, headerName, imports, mediaTypeSchemaSuffix);
7132+
CodegenParameter param = headerToCodegenParameter(header, headerName, imports, schemaName);
71387133
headers.add(param);
71397134
}
71407135
}
@@ -7151,8 +7146,12 @@ protected LinkedHashMap<String, CodegenMediaType> getContent(Content content, Se
71517146
}
71527147
String contentType = contentEntry.getKey();
71537148
CodegenProperty schemaProp = null;
7149+
String usedSchemaName = schemaName;
7150+
if (usedSchemaName.equals("")) {
7151+
usedSchemaName = contentType;
7152+
}
71547153
if (mt.getSchema() != null) {
7155-
schemaProp = fromProperty(toMediaTypeSchemaName(contentType, mediaTypeSchemaSuffix), mt.getSchema(), false);
7154+
schemaProp = fromProperty(usedSchemaName, mt.getSchema(), false);
71567155
}
71577156
HashMap<String, SchemaTestCase> schemaTestCases = null;
71587157
if (mt.getExtensions() != null && mt.getExtensions().containsKey(xSchemaTestExamplesKey)) {
@@ -7201,7 +7200,7 @@ public CodegenParameter fromRequestBody(RequestBody body, Set<String> imports, S
72017200
if (schema == null) {
72027201
throw new RuntimeException("Request body cannot be null. Possible cause: missing schema in body parameter (OAS v2): " + body);
72037202
}
7204-
codegenParameter.setContent(getContent(body.getContent(), imports, "RequestBody"));
7203+
codegenParameter.setContent(getContent(body.getContent(), imports, ""));
72057204

72067205
if (StringUtils.isNotBlank(schema.get$ref())) {
72077206
name = ModelUtils.getSimpleRef(schema.get$ref());

modules/openapi-json-schema-generator/src/main/java/org/openapitools/codegen/languages/PythonClientCodegen.java

+39-89
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,8 @@ protected void generateEndpoints(OperationsMap objs) {
554554
OperationMap operations = objs.getOperations();
555555
List<CodegenOperation> codegenOperations = operations.getOperation();
556556
HashMap<String, String> pathModuleToPath = new HashMap<>();
557-
// paths.some_path.post.py (single endpoint definition)
557+
// paths.some_path.post.__init__.py (single endpoint definition)
558+
// responses are adjacent to the init file
558559
for (CodegenOperation co: codegenOperations) {
559560
if (co.tags != null) {
560561
for (Tag tag: co.tags) {
@@ -576,14 +577,29 @@ protected void generateEndpoints(OperationsMap objs) {
576577
endpointMap.put("operation", co);
577578
endpointMap.put("imports", co.imports);
578579
endpointMap.put("packageName", packageName);
579-
outputFilename = packageFilename(Arrays.asList("paths", pathModuleName, co.httpMethod + ".py"));
580+
outputFilename = packageFilename(Arrays.asList("paths", pathModuleName, co.httpMethod, "__init__.py"));
580581
pathsFiles.add(Arrays.asList(endpointMap, "endpoint.handlebars", outputFilename));
582+
583+
for (CodegenResponse response: co.responses) {
584+
// paths.some_path.post.response_for_200.py (file per response)
585+
Map<String, Object> responseMap = new HashMap<>();
586+
responseMap.put("response", response);
587+
responseMap.put("packageName", packageName);
588+
String responseModuleName = "response_for_";
589+
if (response.isDefault) {
590+
responseModuleName += "default";
591+
} else {
592+
responseModuleName += response.code;
593+
}
594+
String responseFilename = packageFilename(Arrays.asList("paths", pathModuleName, co.httpMethod, responseModuleName+ ".py"));
595+
pathsFiles.add(Arrays.asList(responseMap, "endpoint_response.handlebars", responseFilename));
596+
}
581597
/*
582598
This stub file exists to allow pycharm to read and use typing.overload decorators for it to see that
583599
dict_instance["someProp"] is of type SomeClass.properties.someProp
584600
See https://youtrack.jetbrains.com/issue/PY-42137/PyCharm-type-hinting-doesnt-work-well-with-overload-decorator
585601
*/
586-
String stubOutputFilename = packageFilename(Arrays.asList("paths", pathModuleName, co.httpMethod + ".pyi"));
602+
String stubOutputFilename = packageFilename(Arrays.asList("paths", pathModuleName, co.httpMethod, "__init__.pyi"));
587603
pathsFiles.add(Arrays.asList(endpointMap, "endpoint_stub.handlebars", stubOutputFilename));
588604

589605
Map<String, Object> endpointTestMap = new HashMap<>();
@@ -751,72 +767,7 @@ public String getHelp() {
751767

752768
@Override
753769
public Schema unaliasSchema(Schema schema) {
754-
Map<String, Schema> allSchemas = ModelUtils.getSchemas(openAPI);
755-
if (allSchemas == null || allSchemas.isEmpty()) {
756-
// skip the warning as the spec can have no model defined
757-
//LOGGER.warn("allSchemas cannot be null/empty in unaliasSchema. Returned 'schema'");
758-
return schema;
759-
}
760-
761-
if (schema != null && StringUtils.isNotEmpty(schema.get$ref())) {
762-
String simpleRef = ModelUtils.getSimpleRef(schema.get$ref());
763-
if (schemaMapping.containsKey(simpleRef)) {
764-
LOGGER.debug("Schema unaliasing of {} omitted because aliased class is to be mapped to {}", simpleRef, schemaMapping.get(simpleRef));
765-
return schema;
766-
}
767-
Schema ref = allSchemas.get(simpleRef);
768-
if (ref == null) {
769-
once(LOGGER).warn("{} is not defined", schema.get$ref());
770-
return schema;
771-
} else if (ref.getEnum() != null && !ref.getEnum().isEmpty()) {
772-
// top-level enum class
773-
return schema;
774-
} else if (ModelUtils.isArraySchema(ref)) {
775-
if (ModelUtils.isGenerateAliasAsModel(ref)) {
776-
return schema; // generate a model extending array
777-
} else {
778-
return unaliasSchema(allSchemas.get(ModelUtils.getSimpleRef(schema.get$ref())));
779-
}
780-
} else if (ModelUtils.isComposedSchema(ref)) {
781-
return schema;
782-
} else if (ModelUtils.isMapSchema(ref)) {
783-
if (ref.getProperties() != null && !ref.getProperties().isEmpty()) // has at least one property
784-
return schema; // treat it as model
785-
else {
786-
if (ModelUtils.isGenerateAliasAsModel(ref)) {
787-
return schema; // generate a model extending map
788-
} else {
789-
// treat it as a typical map
790-
return unaliasSchema(allSchemas.get(ModelUtils.getSimpleRef(schema.get$ref())));
791-
}
792-
}
793-
} else if (ModelUtils.isObjectSchema(ref)) { // model
794-
if (ref.getProperties() != null && !ref.getProperties().isEmpty()) { // has at least one property
795-
return schema;
796-
} else {
797-
// free form object (type: object)
798-
if (ModelUtils.hasValidation(ref)) {
799-
return schema;
800-
} else if (getAllOfDescendants(simpleRef, openAPI).size() > 0) {
801-
return schema;
802-
}
803-
return unaliasSchema(allSchemas.get(ModelUtils.getSimpleRef(schema.get$ref())));
804-
}
805-
} else if (ModelUtils.hasValidation(ref)) {
806-
// non object non array non map schemas that have validations
807-
// are returned so we can generate those schemas as models
808-
// we do this to:
809-
// - preserve the validations in that model class in python
810-
// - use those validations when we use this schema in composed oneOf schemas
811-
return schema;
812-
} else if (Boolean.TRUE.equals(ref.getNullable()) && ref.getEnum() == null) {
813-
// non enum primitive with nullable True
814-
// we make these models so instances of this will be subclasses of this model
815-
return schema;
816-
} else {
817-
return unaliasSchema(allSchemas.get(ModelUtils.getSimpleRef(schema.get$ref())));
818-
}
819-
}
770+
// python allows schemas to be inlined at any location so unaliasing should do nothing
820771
return schema;
821772
}
822773

@@ -894,6 +845,17 @@ public String toModelImport(String name) {
894845
return "from " + packageName + "." + modelPackage() + "." + toModelFilename(name) + " import " + toModelName(name);
895846
}
896847

848+
private void fixSchemaImports(Set<String> imports) {
849+
if (imports.size() == 0) {
850+
return;
851+
}
852+
String[] modelNames = imports.toArray(new String[0]);
853+
imports.clear();
854+
for (String modelName : modelNames) {
855+
imports.add(toModelImport(modelName));
856+
}
857+
}
858+
897859
@Override
898860
@SuppressWarnings("static-method")
899861
public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<ModelMap> allModels) {
@@ -904,13 +866,9 @@ public OperationsMap postProcessOperationsWithModels(OperationsMap objs, List<Mo
904866
OperationMap val = objs.getOperations();
905867
List<CodegenOperation> operations = val.getOperation();
906868
for (CodegenOperation operation : operations) {
907-
if (operation.imports.size() == 0) {
908-
continue;
909-
}
910-
String[] modelNames = operation.imports.toArray(new String[0]);
911-
operation.imports.clear();
912-
for (String modelName : modelNames) {
913-
operation.imports.add(toModelImport(modelName));
869+
fixSchemaImports(operation.imports);
870+
for (CodegenResponse response: operation.responses) {
871+
fixSchemaImports(response.imports);
914872
}
915873
}
916874
generateEndpoints(objs);
@@ -998,18 +956,6 @@ public CodegenParameter fromParameter(Parameter parameter, Set<String> imports)
998956
break;
999957
}
1000958
}
1001-
// clone this so we can change some properties on it
1002-
CodegenProperty schemaProp = cp.getSchema();
1003-
// parameters may have valid python names like some_val or invalid ones like Content-Type
1004-
// we always set nameInSnakeCase to null so special handling will not be done for these names
1005-
// invalid python names will be handled in python by using a TypedDict which will allow us to have a type hint
1006-
// for keys that cannot be variable names to the schema baseName
1007-
if (schemaProp != null) {
1008-
schemaProp = schemaProp.clone();
1009-
schemaProp.nameInSnakeCase = null;
1010-
schemaProp.baseName = toModelName(cp.baseName) + "Schema";
1011-
cp.setSchema(schemaProp);
1012-
}
1013959
return cp;
1014960
}
1015961

@@ -2656,13 +2602,17 @@ public List<CodegenParameter> fromRequestBodyToFormParameters(RequestBody body,
26562602
Schema schema = ModelUtils.getSchemaFromRequestBody(body);
26572603
schema = ModelUtils.getReferencedSchema(this.openAPI, schema);
26582604
CodegenParameter cp = fromFormProperty("body", schema, imports);
2659-
cp.setContent(getContent(body.getContent(), imports, "RequestBody"));
2605+
cp.setContent(getContent(body.getContent(), imports, ""));
26602606
cp.isFormParam = false;
26612607
cp.isBodyParam = true;
26622608
parameters.add(cp);
26632609
return parameters;
26642610
}
26652611

2612+
protected boolean needToImport(String type) {
2613+
return true;
2614+
}
2615+
26662616
/**
26672617
* Custom version of this method so we can move the body parameter into bodyParam
26682618
*

modules/openapi-json-schema-generator/src/main/resources/python/api_client.handlebars

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# coding: utf-8
22
{{>partial_header}}
33

4-
from dataclasses import dataclass
4+
import dataclasses
55
from decimal import Decimal
66
import enum
77
import email
@@ -339,7 +339,7 @@ class JSONDetector:
339339
return False
340340

341341

342-
@dataclass
342+
@dataclasses.dataclass
343343
class ParameterBase(JSONDetector):
344344
name: str
345345
in_type: ParameterInType
@@ -779,7 +779,7 @@ class Encoding:
779779
self.allow_reserved = allow_reserved
780780

781781

782-
@dataclass
782+
@dataclasses.dataclass
783783
class MediaType:
784784
"""
785785
Used to store request and response body schema information
@@ -793,7 +793,7 @@ class MediaType:
793793
encoding: typing.Optional[typing.Dict[str, Encoding]] = None
794794

795795

796-
@dataclass
796+
@dataclasses.dataclass
797797
class ApiResponse:
798798
response: urllib3.HTTPResponse
799799
body: typing.Union[Unset, Schema]
@@ -802,8 +802,8 @@ class ApiResponse:
802802
def __init__(
803803
self,
804804
response: urllib3.HTTPResponse,
805-
body: typing.Union[Unset, typing.Type[Schema]],
806-
headers: typing.Union[Unset, typing.List[HeaderParameter]]
805+
body: typing.Union[Unset, typing.Type[Schema]] = unset,
806+
headers: typing.Union[Unset, typing.List[HeaderParameter]] = unset
807807
):
808808
"""
809809
pycharm needs this to prevent 'Unexpected argument' warnings
@@ -813,7 +813,7 @@ class ApiResponse:
813813
self.headers = headers
814814

815815

816-
@dataclass
816+
@dataclasses.dataclass
817817
class ApiResponseWithoutDeserialization(ApiResponse):
818818
response: urllib3.HTTPResponse
819819
body: typing.Union[Unset, typing.Type[Schema]] = unset

0 commit comments

Comments
 (0)