|
29 | 29 | import java.util.List; |
30 | 30 | import java.util.Locale; |
31 | 31 | import java.util.Map; |
| 32 | +import java.util.Objects; |
| 33 | +import java.util.Optional; |
32 | 34 | import java.util.regex.Matcher; |
33 | 35 | import java.util.stream.Collectors; |
34 | 36 |
|
| 37 | +import io.swagger.v3.oas.models.media.ComposedSchema; |
| 38 | +import io.swagger.v3.oas.models.media.Content; |
| 39 | +import io.swagger.v3.oas.models.media.Schema; |
| 40 | +import io.swagger.v3.oas.models.parameters.RequestBody; |
| 41 | +import io.swagger.v3.oas.models.responses.ApiResponse; |
35 | 42 | import com.samskivert.mustache.Mustache; |
36 | 43 | import io.swagger.v3.oas.models.OpenAPI; |
37 | 44 | import io.swagger.v3.oas.models.Operation; |
38 | 45 | import io.swagger.v3.oas.models.PathItem; |
39 | | -import io.swagger.v3.oas.models.media.Schema; |
40 | 46 | import io.swagger.v3.oas.models.servers.Server; |
41 | 47 | import org.apache.commons.lang3.tuple.Pair; |
42 | 48 | import org.openapitools.codegen.CliOption; |
|
60 | 66 | import org.openapitools.codegen.meta.features.WireFormatFeature; |
61 | 67 | import org.openapitools.codegen.templating.mustache.SplitStringLambda; |
62 | 68 | import org.openapitools.codegen.templating.mustache.TrimWhitespaceLambda; |
| 69 | +import org.openapitools.codegen.utils.ModelUtils; |
63 | 70 | import org.openapitools.codegen.utils.URLPathUtils; |
64 | 71 | import org.slf4j.Logger; |
65 | 72 | import org.slf4j.LoggerFactory; |
@@ -142,6 +149,8 @@ public SpringCodegen() { |
142 | 149 | modelPackage = "org.openapitools.model"; |
143 | 150 | invokerPackage = "org.openapitools.api"; |
144 | 151 | artifactId = "openapi-spring"; |
| 152 | + useOneOfInterfaces = true; |
| 153 | + addOneOfInterfaceImports = true; |
145 | 154 |
|
146 | 155 | // clioOptions default redefinition need to be updated |
147 | 156 | updateOption(CodegenConstants.INVOKER_PACKAGE, this.getInvokerPackage()); |
@@ -603,7 +612,11 @@ public void addOperationToGroup(String tag, String resourcePath, Operation opera |
603 | 612 |
|
604 | 613 | @Override |
605 | 614 | public void preprocessOpenAPI(OpenAPI openAPI) { |
606 | | - super.preprocessOpenAPI(openAPI); |
| 615 | + if (openAPI.getComponents() != null) { |
| 616 | + preprocessInlineOneOf(openAPI); |
| 617 | + super.preprocessOpenAPI(openAPI); |
| 618 | + } |
| 619 | + |
607 | 620 | /* |
608 | 621 | * TODO the following logic should not need anymore in OAS 3.0 if |
609 | 622 | * ("/".equals(swagger.getBasePath())) { swagger.setBasePath(""); } |
@@ -1016,4 +1029,99 @@ public void setPerformBeanValidation(boolean performBeanValidation) { |
1016 | 1029 | public void setUseOptional(boolean useOptional) { |
1017 | 1030 | this.useOptional = useOptional; |
1018 | 1031 | } |
| 1032 | + |
| 1033 | + @Override |
| 1034 | + public void addImportsToOneOfInterface(List<Map<String, String>> imports) { |
| 1035 | + if (additionalProperties.containsKey(JACKSON)) { |
| 1036 | + for (String i : Arrays.asList("JsonSubTypes", "JsonTypeInfo")) { |
| 1037 | + Map<String, String> oneImport = new HashMap<>(); |
| 1038 | + oneImport.put("import", importMapping.get(i)); |
| 1039 | + if (!imports.contains(oneImport)) { |
| 1040 | + imports.add(oneImport); |
| 1041 | + } |
| 1042 | + } |
| 1043 | + } |
| 1044 | + } |
| 1045 | + |
| 1046 | + @Override |
| 1047 | + public Map<String, Object> postProcessAllModels(Map<String, Object> objs) { |
| 1048 | + Map<String, Object> postProcessedModels = super.postProcessAllModels(objs); |
| 1049 | + |
| 1050 | + for (Map.Entry<String, Object> modelsEntry : objs.entrySet()) { |
| 1051 | + Map<String, Object> modelsAttrs = (Map<String, Object>) modelsEntry.getValue(); |
| 1052 | + List<Object> models = (List<Object>) modelsAttrs.get("models"); |
| 1053 | + for (Object _mo : models) { |
| 1054 | + Map<String, Object> mo = (Map<String, Object>) _mo; |
| 1055 | + CodegenModel cm = (CodegenModel) mo.get("model"); |
| 1056 | + if (cm.oneOf.size() > 0) { |
| 1057 | + cm.vendorExtensions.put("x-deduction", true); |
| 1058 | + cm.vendorExtensions.put("x-deduction-model-names", cm.oneOf.toArray()); |
| 1059 | + } |
| 1060 | + } |
| 1061 | + } |
| 1062 | + |
| 1063 | + return postProcessedModels; |
| 1064 | + } |
| 1065 | + |
| 1066 | + private void preprocessInlineOneOf(OpenAPI openAPI) { |
| 1067 | + if (openAPI != null) { |
| 1068 | + if (openAPI.getPaths() != null) { |
| 1069 | + for (Map.Entry<String, PathItem> openAPIGetPathsEntry : openAPI.getPaths().entrySet()) { |
| 1070 | + String pathname = openAPIGetPathsEntry.getKey(); |
| 1071 | + PathItem path = openAPIGetPathsEntry.getValue(); |
| 1072 | + if (path.readOperations() == null) { |
| 1073 | + continue; |
| 1074 | + } |
| 1075 | + for (Operation operation : path.readOperations()) { |
| 1076 | + boolean hasBodyParameter = hasBodyParameter(openAPI, operation); |
| 1077 | + |
| 1078 | + // OpenAPI parser do not add Inline One Of models in Operations to Components/Schemas |
| 1079 | + if (hasBodyParameter(openAPI, operation)) { |
| 1080 | + Optional.ofNullable(operation.getRequestBody()) |
| 1081 | + .map(RequestBody::getContent) |
| 1082 | + .ifPresent(this::repairInlineOneOf); |
| 1083 | + } |
| 1084 | + if (operation.getResponses() != null) { |
| 1085 | + operation.getResponses().values().stream().map(ApiResponse::getContent) |
| 1086 | + .filter(Objects::nonNull) |
| 1087 | + .forEach(this::repairInlineOneOf); |
| 1088 | + } |
| 1089 | + } |
| 1090 | + } |
| 1091 | + } |
| 1092 | + } |
| 1093 | + } |
| 1094 | + |
| 1095 | + /** |
| 1096 | + * Add all OneOf schemas to #/components/schemas and replace them in the original content by ref schema |
| 1097 | + * Replace OneOf with unmodifiable types with an empty Schema |
| 1098 | + * |
| 1099 | + * OpenAPI Parser does not add inline OneOf schemas to models to generate |
| 1100 | + * |
| 1101 | + * @param content a 'content' section in the OAS specification. |
| 1102 | + */ |
| 1103 | + private void repairInlineOneOf(final Content content) { |
| 1104 | + content.values().forEach(mediaType -> { |
| 1105 | + final Schema<?> replacingSchema = mediaType.getSchema(); |
| 1106 | + if (isOneOfSchema(replacingSchema)) { |
| 1107 | + if (ModelUtils.isSchemaOneOfConsistsOfCustomTypes(openAPI, replacingSchema)) { |
| 1108 | + final String oneOfModelName = (String) replacingSchema.getExtensions().get("x-one-of-name"); |
| 1109 | + final Schema<?> newRefSchema = new Schema<>().$ref("#/components/schemas/" + oneOfModelName); |
| 1110 | + mediaType.setSchema(newRefSchema); |
| 1111 | + ModelUtils.getSchemas(openAPI).put(oneOfModelName, replacingSchema); |
| 1112 | + } else { |
| 1113 | + mediaType.setSchema(new Schema().type("object")); |
| 1114 | + } |
| 1115 | + } |
| 1116 | + }); |
| 1117 | + } |
| 1118 | + |
| 1119 | + private static boolean isOneOfSchema(final Schema<?> schema) { |
| 1120 | + if (schema instanceof ComposedSchema) { |
| 1121 | + ComposedSchema composedSchema = (ComposedSchema) schema; |
| 1122 | + return Optional.ofNullable(composedSchema.getProperties()).map(Map::isEmpty).orElse(true) |
| 1123 | + && Optional.ofNullable(schema.getExtensions()).map(m -> m.containsKey("x-one-of-name")).orElse(false); |
| 1124 | + } |
| 1125 | + return false; |
| 1126 | + } |
1019 | 1127 | } |
0 commit comments