From 1301d368aeab8241be24395bee9ad22c2ec30c13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matheus=20Andr=C3=A9?= Date: Wed, 6 May 2026 20:26:42 -0300 Subject: [PATCH] feat: Implement support for ArrayMap data type in OpenApi2JaxRs code generation --- .../hub/api/codegen/OpenApi2JaxRs.java | 153 +++++++++++++++++- .../api/codegen/beans/CodegenJavaSchema.java | 22 ++- .../codegen/jaxrs/OpenApi2CodegenVisitor.java | 81 ++++++++-- .../pre/OpenApiMapDataTypeProcessor.java | 94 +++++++++-- .../hub/api/codegen/OpenApi2JaxRsTest.java | 9 ++ .../generated-api/pom.xml | 55 +++++++ .../java/org/example/api/ApiResource.java | 21 +++ .../org/example/api/JaxRsApplication.java | 13 ++ .../java/org/example/api/beans/ApiList.java | 29 ++++ .../src/main/resources/META-INF/openapi.json | 53 ++++++ .../OpenApi2JaxRsTest/array-map-inline.json | 53 ++++++ 11 files changed, 547 insertions(+), 36 deletions(-) create mode 100644 core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/pom.xml create mode 100644 core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/src/main/java/org/example/api/ApiResource.java create mode 100644 core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/src/main/java/org/example/api/JaxRsApplication.java create mode 100644 core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/src/main/java/org/example/api/beans/ApiList.java create mode 100644 core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/src/main/resources/META-INF/openapi.json create mode 100644 core/src/test/resources/OpenApi2JaxRsTest/array-map-inline.json diff --git a/core/src/main/java/io/apitomy/hub/api/codegen/OpenApi2JaxRs.java b/core/src/main/java/io/apitomy/hub/api/codegen/OpenApi2JaxRs.java index 4481ed7f..29e0240f 100755 --- a/core/src/main/java/io/apitomy/hub/api/codegen/OpenApi2JaxRs.java +++ b/core/src/main/java/io/apitomy/hub/api/codegen/OpenApi2JaxRs.java @@ -123,6 +123,7 @@ public class OpenApi2JaxRs { protected transient Document document; protected JaxRsProjectSettings settings; protected boolean updateOnly; + private Map requestBodyTypesByOperationId = Map.of(); private GenerationConfig config; @@ -381,6 +382,7 @@ protected CodegenInfo getInfoFromApiDoc() throws IOException { document = Library.transformDocument(document, ModelType.OPENAPI31); // Pre-process the document + requestBodyTypesByOperationId = computeRequestBodyTypes(document); document = preProcess(document); // Figure out the breakdown of the interfaces. @@ -535,6 +537,10 @@ void sortImports(Importer javaSource) { * @param interfaceInfo */ protected String generateJavaInterface(CodegenInfo info, CodegenJavaInterface interfaceInfo, String topLevelPackage) { + return generateJavaInterfaceSource(info, interfaceInfo, topLevelPackage); + } + + protected String generateJavaInterfaceSource(CodegenInfo info, CodegenJavaInterface interfaceInfo, String topLevelPackage) { String jaxRsPath = info.getContextRoot() + interfaceInfo.getPath(); final Parser markdownParser = Parser.builder().build(); final HtmlRenderer htmlRenderer = HtmlRenderer.builder().build(); @@ -639,18 +645,26 @@ protected String generateJavaInterface(CodegenInfo info, CodegenJavaInterface in .orElseGet(Stream::empty) .forEach(arg -> { String methodArgName = paramNameToJavaArgName(arg.getName()); - String defaultParamType = Object.class.getName(); + Type paramType = null; if (arg.getIn().equals("body")) { // Swagger 2.0? - defaultParamType = InputStream.class.getName(); + String replacementType = requestBodyTypesByOperationId.get(methodInfo.getOperationId()); + if (replacementType != null) { + paramType = parseType(replacementType); + } } else if (arg.getIn().equals("form") && arg.getType() != null && !arg.getType().isEmpty()) { - defaultParamType = arg.getType().get(0); + paramType = generateTypeName(arg, arg.getRequired(), arg.getType().get(0)); } - Type paramType = generateTypeName(arg, arg.getRequired(), defaultParamType); + if (paramType == null) { + String defaultParamType = arg.getIn().equals("body") + ? InputStream.class.getName() + : Object.class.getName(); + paramType = generateTypeName(arg, arg.getRequired(), defaultParamType); + } if (arg.getTypeSignature() != null) { // TODO try to find a re-usable data type that matches the type signature @@ -714,6 +728,129 @@ protected String generateJavaInterface(CodegenInfo info, CodegenJavaInterface in return generateJavaInterface(info, interfaceInfo, "jakarta"); } + private Map computeRequestBodyTypes(Document document) throws IOException { + JsonNode json = mapper.readTree(Library.writeDocumentToJSONString(document)); + Map arrayMapTypes = computeArrayMapTypes(json); + Map requestBodyTypes = new HashMap<>(); + + JsonNode paths = json.path("paths"); + if (!paths.isObject()) { + return requestBodyTypes; + } + + paths.fields().forEachRemaining(pathEntry -> { + JsonNode pathItem = pathEntry.getValue(); + pathItem.fields().forEachRemaining(methodEntry -> { + if (!Set.of("get", "put", "post", "delete", "options", "head", "patch", "trace") + .contains(methodEntry.getKey())) { + return; + } + + JsonNode operation = methodEntry.getValue(); + String operationId = textValue(operation, "operationId"); + if (operationId == null) { + return; + } + + JsonNode requestBody = operation.path("requestBody"); + String requestType = resolveBodyType(requestBody, arrayMapTypes); + if (requestType != null) { + requestBodyTypes.put(operationId, requestType); + } + }); + }); + + return requestBodyTypes; + } + + private Map computeArrayMapTypes(JsonNode json) { + Map arrayMapTypes = new HashMap<>(); + JsonNode schemas = json.path("components").path("schemas"); + if (!schemas.isObject()) { + return arrayMapTypes; + } + + schemas.fields().forEachRemaining(entry -> { + JsonNode schema = entry.getValue(); + JsonNode typeNode = schema.path("x-codegen-type"); + if (!typeNode.isTextual() || !"ArrayMap".equals(typeNode.asText())) { + return; + } + + JsonNode additionalProperties = schema.path("additionalProperties"); + if (!additionalProperties.isObject()) { + return; + } + + String valueType = resolveSchemaType(additionalProperties, arrayMapTypes); + arrayMapTypes.put(entry.getKey(), "java.util.Map"); + }); + + return arrayMapTypes; + } + + private String resolveBodyType(JsonNode requestBody, Map arrayMapTypes) { + JsonNode content = requestBody.path("content"); + if (!content.isObject() || content.size() == 0) { + return null; + } + + JsonNode mediaType = content.elements().next(); + JsonNode schema = mediaType.path("schema"); + if (!schema.isObject()) { + return null; + } + + return resolveSchemaType(schema, arrayMapTypes); + } + + private String resolveSchemaType(JsonNode schema, Map arrayMapTypes) { + JsonNode ref = schema.get("$ref"); + if (ref != null && ref.isTextual()) { + String refName = ref.asText(); + String refKey = refName.substring(refName.lastIndexOf('/') + 1); + String arrayMapType = arrayMapTypes.get(refKey); + if (arrayMapType != null) { + return arrayMapType; + } + return CodegenUtil.schemaRefToFQCN(settings, document, refName, this.settings.javaPackage + ".beans"); + } + + JsonNode type = schema.get("type"); + if (type != null && type.isTextual()) { + switch (type.asText()) { + case "array": + JsonNode items = schema.get("items"); + String itemType = items != null ? resolveSchemaType(items, arrayMapTypes) : "java.lang.Object"; + return "java.util.List<" + itemType + ">"; + case "string": + return "java.lang.String"; + case "integer": + return "java.lang.Integer"; + case "number": + return "java.lang.Double"; + case "boolean": + return "java.lang.Boolean"; + case "object": + JsonNode additionalProperties = schema.get("additionalProperties"); + if (additionalProperties != null && additionalProperties.isObject()) { + String valueType = resolveSchemaType(additionalProperties, arrayMapTypes); + return "java.util.Map"; + } + return "java.lang.Object"; + default: + return "java.lang.Object"; + } + } + + return "java.lang.Object"; + } + + private String textValue(JsonNode node, String field) { + JsonNode value = node.get(field); + return value != null && value.isTextual() ? value.asText() : null; + } + public static Properties getFormatterProperties() { Properties formattingProperties = new Properties(); formattingProperties.setProperty("org.eclipse.jdt.core.formatter.indentation.size", "2"); @@ -757,6 +894,14 @@ protected Type generateTypeName(CodegenJavaSchema schema, Boolean required, S required = Boolean.FALSE; } + String existingJavaType = schema.getExistingJavaType(); + if (existingJavaType != null && !existingJavaType.isBlank()) { + if ("APICURIO_CODEGEN_BYTE_ARRAY_REPRESENTATION".equals(existingJavaType)) { + return parseType("byte[]"); + } + return parseType(existingJavaType); + } + String collection = schema.getCollection(); List type = Optional.ofNullable(schema.getType()).orElseGet(Collections::emptyList); String format = schema.getFormat(); diff --git a/core/src/main/java/io/apitomy/hub/api/codegen/beans/CodegenJavaSchema.java b/core/src/main/java/io/apitomy/hub/api/codegen/beans/CodegenJavaSchema.java index 1c93d4a7..02e5e520 100755 --- a/core/src/main/java/io/apitomy/hub/api/codegen/beans/CodegenJavaSchema.java +++ b/core/src/main/java/io/apitomy/hub/api/codegen/beans/CodegenJavaSchema.java @@ -24,6 +24,7 @@ public class CodegenJavaSchema { private Long maxProperties; private Long minProperties; private String defaultValue; + private String existingJavaType; public void setType(String type) { this.type = Collections.singletonList(type); @@ -36,8 +37,8 @@ public boolean isNullable() { @Override public int hashCode() { return Objects.hash(collection, constant, defaultValue, exclusiveMaximum, exclusiveMinimum, format, maxItems, - maxLength, maxProperties, maximum, minItems, minLength, minProperties, minimum, pattern, type, - uniqueItems); + maxLength, maxProperties, maximum, minItems, minLength, minProperties, minimum, pattern, + existingJavaType, type, uniqueItems); } @Override @@ -57,7 +58,8 @@ public boolean equals(Object obj) { && Objects.equals(maxProperties, other.maxProperties) && Objects.equals(maximum, other.maximum) && Objects.equals(minItems, other.minItems) && Objects.equals(minLength, other.minLength) && Objects.equals(minProperties, other.minProperties) && Objects.equals(minimum, other.minimum) - && Objects.equals(type, other.type) && Objects.equals(uniqueItems, other.uniqueItems); + && Objects.equals(existingJavaType, other.existingJavaType) && Objects.equals(type, other.type) + && Objects.equals(uniqueItems, other.uniqueItems); } /** @@ -299,4 +301,18 @@ public void setDefaultValue(String defaultValue) { this.defaultValue = defaultValue; } + /** + * @return the existingJavaType + */ + public String getExistingJavaType() { + return existingJavaType; + } + + /** + * @param existingJavaType the existingJavaType to set + */ + public void setExistingJavaType(String existingJavaType) { + this.existingJavaType = existingJavaType; + } + } diff --git a/core/src/main/java/io/apitomy/hub/api/codegen/jaxrs/OpenApi2CodegenVisitor.java b/core/src/main/java/io/apitomy/hub/api/codegen/jaxrs/OpenApi2CodegenVisitor.java index e2daf8d2..10168f39 100755 --- a/core/src/main/java/io/apitomy/hub/api/codegen/jaxrs/OpenApi2CodegenVisitor.java +++ b/core/src/main/java/io/apitomy/hub/api/codegen/jaxrs/OpenApi2CodegenVisitor.java @@ -583,7 +583,14 @@ private void setSchemaProperties(CodegenJavaSchema target, OpenApi3xSchema schem return; } - OpenApi31Schema schema31 = (OpenApi31Schema) schema; + JsonNode existingJavaType = CodegenUtil.getExtension(schema, "existingJavaType"); + if (existingJavaType != null && existingJavaType.isTextual()) { + String javaType = existingJavaType.asText(); + target.setExistingJavaType(javaType); + if (javaType.startsWith("java.util.Map<")) { + target.setCollection("map"); + } + } target.setType((List) null); String $ref = schema31.get$ref(); @@ -606,17 +613,13 @@ private void setSchemaProperties(CodegenJavaSchema target, OpenApi3xSchema schem } else if (containsValue(schema31.getType(), "object")) { setIfPresent(() -> toStringList(schema31.getType()), target::setType); setIfPresent(schema::getFormat, target::setFormat); - // TODO: Consider representing object as map - //if (schema.getAdditionalProperties() != null && schema.getAdditionalProperties().isSchema()) { - // setSchemaProperties(target, (OpenApi3xSchema) schema.getAdditionalProperties().asSchema()); - //} - // - //setIfPresent(schema::isNullable, target::setNullable); - //setIfPresent(schema::getMaxProperties, value -> target.setMaxProperties(value.longValue())); - //setIfPresent(schema::getMinProperties, value -> target.setMinProperties(value.longValue())); - //target.setCollection("map"); - } else if (containsValue(schema31.getType(), "string")) { - setIfPresent(() -> toStringList(schema31.getType()), target::setType); + String mapJavaType = resolveMapJavaType(schema); + if (mapJavaType != null) { + target.setExistingJavaType(mapJavaType); + target.setCollection("map"); + } + } else if (containsValue(schema.getType(), "string")) { + setIfPresent(() -> toStringList(schema.getType()), target::setType); setIfPresent(schema::getFormat, target::setFormat); setIfPresent(schema::getMaxLength, value -> target.setMaxLength(value.longValue())); setIfPresent(schema::getMinLength, value -> target.setMinLength(value.longValue())); @@ -652,6 +655,60 @@ private void setSchemaProperties(CodegenJavaSchema target, OpenApi3xSchema schem }); } + private String resolveMapJavaType(OpenApi31Schema schema) { + if (schema.getAdditionalProperties() == null || !schema.getAdditionalProperties().isSchema()) { + JsonNode existingJavaType = CodegenUtil.getExtension(schema, "existingJavaType"); + if (existingJavaType != null && existingJavaType.isTextual()) { + return existingJavaType.asText(); + } + return null; + } + + String valueType = resolveJavaType((OpenApi31Schema) schema.getAdditionalProperties().asSchema()); + return "java.util.Map"; + } + + private String resolveJavaType(OpenApi31Schema schema) { + if (schema == null) { + return "java.lang.Object"; + } + + if (schema.get$ref() != null && !schema.get$ref().isBlank()) { + return CodegenUtil.schemaRefToFQCN(settings, (Document) schema.root(), schema.get$ref(), + this.settings.getJavaPackage() + ".beans"); + } + + if (containsValue(schema.getType(), "array")) { + String itemType = "java.lang.Object"; + if (schema.getItems() != null) { + itemType = resolveJavaType((OpenApi31Schema) schema.getItems()); + } + return "java.util.List<" + itemType + ">"; + } + + if (containsValue(schema.getType(), "string")) { + return "java.lang.String"; + } + if (containsValue(schema.getType(), "integer")) { + return "java.lang.Integer"; + } + if (containsValue(schema.getType(), "number")) { + return "java.lang.Double"; + } + if (containsValue(schema.getType(), "boolean")) { + return "java.lang.Boolean"; + } + if (containsValue(schema.getType(), "object")) { + if (schema.getAdditionalProperties() != null && schema.getAdditionalProperties().isSchema()) { + String valueType = resolveJavaType((OpenApi31Schema) schema.getAdditionalProperties().asSchema()); + return "java.util.Map"; + } + return "java.lang.Object"; + } + + return "java.lang.Object"; + } + private void setIfPresent(Supplier source, Consumer target) { T value = source.get(); diff --git a/core/src/main/java/io/apitomy/hub/api/codegen/pre/OpenApiMapDataTypeProcessor.java b/core/src/main/java/io/apitomy/hub/api/codegen/pre/OpenApiMapDataTypeProcessor.java index 376576d0..fe05653f 100755 --- a/core/src/main/java/io/apitomy/hub/api/codegen/pre/OpenApiMapDataTypeProcessor.java +++ b/core/src/main/java/io/apitomy/hub/api/codegen/pre/OpenApiMapDataTypeProcessor.java @@ -16,6 +16,8 @@ package io.apitomy.hub.api.codegen.pre; +import java.util.Set; + import com.fasterxml.jackson.databind.JsonNode; import io.apitomy.datamodels.models.Schema; @@ -25,19 +27,12 @@ import io.apitomy.hub.api.codegen.jaxrs.TraversingOpenApi31VisitorAdapter; import io.apitomy.hub.api.codegen.util.CodegenUtil; -import java.util.Map; - /** * @author eric.wittmann@gmail.com */ public class OpenApiMapDataTypeProcessor extends TraversingOpenApi31VisitorAdapter { - private static final Map EXTENSION_NAMES = Map.of( - "StringMap", - "java.util.Map", - "StringObjectMap", - "java.util.Map" - ); + private static final Set EXTENSION_NAMES = Set.of("StringMap", "StringObjectMap", "ArrayMap"); /** * @see io.apitomy.datamodels.models.openapi.v3x.v31.visitors.OpenApi31VisitorAdapter#visitSchema(io.apitomy.datamodels.models.Schema) @@ -46,20 +41,85 @@ public class OpenApiMapDataTypeProcessor extends TraversingOpenApi31VisitorAdapt public void visitSchema(Schema node) { if (NodeUtil.isDefinition(node)) { OpenApi31Schema schema = (OpenApi31Schema) node; - if (isMapType(schema)) { - schema.setAdditionalProperties(null); - String javaType = EXTENSION_NAMES.get(CodegenUtil.getExtension(schema, CodegenExtensions.TYPE).asText()); - schema.addExtraProperty("existingJavaType", factory.textNode(javaType)); + JsonNode typeExtension = CodegenUtil.getExtension(schema, CodegenExtensions.TYPE); + if (typeExtension == null || !typeExtension.isTextual() || !EXTENSION_NAMES.contains(typeExtension.asText())) { + return; + } + + OpenApi31Schema additionalProperties = schema.getAdditionalProperties() != null + && schema.getAdditionalProperties().isSchema() + ? (OpenApi31Schema) schema.getAdditionalProperties().asSchema() + : null; + + schema.setAdditionalProperties(null); + schema.addExtraProperty("existingJavaType", factory.textNode(buildJavaType(typeExtension.asText(), additionalProperties))); + } + } + + private String buildJavaType(String alias, OpenApi31Schema additionalProperties) { + if ("StringMap".equals(alias)) { + return "java.util.Map"; + } + + if ("StringObjectMap".equals(alias)) { + return "java.util.Map"; + } + + if ("ArrayMap".equals(alias)) { + String valueType = "java.lang.Object"; + if (additionalProperties != null) { + valueType = resolveJavaType(additionalProperties); } + return "java.util.Map"; } + + return "java.util.Map"; } - private boolean isMapType(OpenApi31Schema schema) { - JsonNode extension = CodegenUtil.getExtension(schema, CodegenExtensions.TYPE); - if (extension == null || !extension.isTextual()) { - return false; + private String resolveJavaType(OpenApi31Schema schema) { + if (schema == null) { + return "java.lang.Object"; + } + + if (schema.get$ref() != null && !schema.get$ref().isBlank()) { + return refToSimpleType(schema.get$ref()); + } + + if (schema.getType() != null) { + if (CodegenUtil.containsValue(schema.getType(), "array")) { + String itemType = "java.lang.Object"; + if (schema.getItems() != null) { + itemType = resolveJavaType((OpenApi31Schema) schema.getItems()); + } + return "java.util.List<" + itemType + ">"; + } + if (CodegenUtil.containsValue(schema.getType(), "string")) { + return "java.lang.String"; + } + if (CodegenUtil.containsValue(schema.getType(), "integer")) { + return "java.lang.Integer"; + } + if (CodegenUtil.containsValue(schema.getType(), "number")) { + return "java.lang.Double"; + } + if (CodegenUtil.containsValue(schema.getType(), "boolean")) { + return "java.lang.Boolean"; + } + } + + return "java.lang.Object"; + } + + private String refToSimpleType(String ref) { + int lastSlash = ref.lastIndexOf('/'); + if (lastSlash >= 0 && lastSlash < ref.length() - 1) { + return ref.substring(lastSlash + 1); + } + int lastHash = ref.lastIndexOf('#'); + if (lastHash >= 0 && lastHash < ref.length() - 1) { + return ref.substring(lastHash + 1); } - return EXTENSION_NAMES.get(extension.asText()) != null; + return ref; } } diff --git a/core/src/test/java/io/apitomy/hub/api/codegen/OpenApi2JaxRsTest.java b/core/src/test/java/io/apitomy/hub/api/codegen/OpenApi2JaxRsTest.java index c4a91592..6b9f12fc 100755 --- a/core/src/test/java/io/apitomy/hub/api/codegen/OpenApi2JaxRsTest.java +++ b/core/src/test/java/io/apitomy/hub/api/codegen/OpenApi2JaxRsTest.java @@ -156,6 +156,15 @@ public void testGenerateFull_SimpleType() throws IOException { * Test method for {@link io.apitomy.hub.api.codegen.OpenApi2JaxRs#generate()}. */ @Test + public void testGenerateFull_ArrayMapInline() throws IOException { + doFullTest("OpenApi2JaxRsTest/array-map-inline.json", UpdateOnly.no, Reactive.no, + "_expected-array-map-inline/generated-api", false); + } + + /** + * Test method for {@link io.apicurio.hub.api.codegen.OpenApi2JaxRs#generate()}. + */ + @Test public void testGenerateFull_BeanAnnotations() throws IOException { doFullTest("OpenApi2JaxRsTest/bean-annotations.json", UpdateOnly.no, Reactive.no, "_expected-bean-annotations/generated-api", false); } diff --git a/core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/pom.xml b/core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/pom.xml new file mode 100644 index 00000000..fb524a53 --- /dev/null +++ b/core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/pom.xml @@ -0,0 +1,55 @@ + + + 4.0.0 + org.example.api + generated-api + 1.0.0 + jar + array map inline api + A generated project with JAX-RS and Microprofile OpenAPI features enabled. + + + 11 + 11 + false + UTF-8 + + 3.1.0 + 3.0.2 + 4.0.1 + 2.15.1 + 4.0.2 + + + + + + com.fasterxml.jackson.core + jackson-annotations + ${version.com.fasterxml.jackson} + + + + jakarta.ws.rs + jakarta.ws.rs-api + ${version.jakarta.ws.rs-jakarta.ws.rs-api} + + + jakarta.enterprise + jakarta.enterprise.cdi-api + ${version.jakarta.enterprise-jakarta.enterprise.cdi-api} + + + jakarta.validation + jakarta.validation-api + ${version.jakarta.validation-jakarta.validation-api} + + + org.eclipse.microprofile.openapi + microprofile-openapi-api + ${version.org.eclipse.microprofile.openapi-microprofile-openapi-api} + + + diff --git a/core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/src/main/java/org/example/api/ApiResource.java b/core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/src/main/java/org/example/api/ApiResource.java new file mode 100644 index 00000000..d39a9d90 --- /dev/null +++ b/core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/src/main/java/org/example/api/ApiResource.java @@ -0,0 +1,21 @@ +package org.example.api; + +import jakarta.validation.constraints.NotNull; +import jakarta.ws.rs.Consumes; +import jakarta.ws.rs.POST; +import jakarta.ws.rs.Path; +import java.util.List; +import java.util.Map; +import org.eclipse.microprofile.openapi.annotations.Operation; +import org.example.api.beans.ApiList; + +/** + * A JAX-RS interface. An implementation of this interface must be provided. + */ +@Path("/api-lists") +public interface ApiResource { + @Operation(operationId = "createApiLists") + @POST + @Consumes("application/json") + void createApiLists(@NotNull Map> data); +} diff --git a/core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/src/main/java/org/example/api/JaxRsApplication.java b/core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/src/main/java/org/example/api/JaxRsApplication.java new file mode 100644 index 00000000..6ade2169 --- /dev/null +++ b/core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/src/main/java/org/example/api/JaxRsApplication.java @@ -0,0 +1,13 @@ +package org.example.api; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; + +/** + * The JAX-RS application. + */ +@ApplicationScoped +@ApplicationPath("/") +public class JaxRsApplication extends Application { +} diff --git a/core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/src/main/java/org/example/api/beans/ApiList.java b/core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/src/main/java/org/example/api/beans/ApiList.java new file mode 100644 index 00000000..04917b8c --- /dev/null +++ b/core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/src/main/java/org/example/api/beans/ApiList.java @@ -0,0 +1,29 @@ + +package org.example.api.beans; + +import javax.annotation.processing.Generated; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; + +@JsonInclude(JsonInclude.Include.NON_NULL) +@JsonPropertyOrder({ + "id" +}) +@Generated("jsonschema2pojo") +public class ApiList { + + @JsonProperty("id") + private String id; + + @JsonProperty("id") + public String getId() { + return id; + } + + @JsonProperty("id") + public void setId(String id) { + this.id = id; + } + +} diff --git a/core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/src/main/resources/META-INF/openapi.json b/core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/src/main/resources/META-INF/openapi.json new file mode 100644 index 00000000..19d5c5cc --- /dev/null +++ b/core/src/test/resources/OpenApi2JaxRsTest/_expected-array-map-inline/generated-api/src/main/resources/META-INF/openapi.json @@ -0,0 +1,53 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "array map inline api", + "version": "1.0.0" + }, + "paths": { + "/api-lists": { + "post": { + "tags": ["ApiLists"], + "operationId": "createApiLists", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiLists" + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "ApiList": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "ApiLists": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApiList" + } + }, + "x-codegen-inline": true, + "x-codegen-type": "ArrayMap" + } + } + } +} diff --git a/core/src/test/resources/OpenApi2JaxRsTest/array-map-inline.json b/core/src/test/resources/OpenApi2JaxRsTest/array-map-inline.json new file mode 100644 index 00000000..19d5c5cc --- /dev/null +++ b/core/src/test/resources/OpenApi2JaxRsTest/array-map-inline.json @@ -0,0 +1,53 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "array map inline api", + "version": "1.0.0" + }, + "paths": { + "/api-lists": { + "post": { + "tags": ["ApiLists"], + "operationId": "createApiLists", + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApiLists" + } + } + } + }, + "responses": { + "200": { + "description": "OK" + } + } + } + } + }, + "components": { + "schemas": { + "ApiList": { + "type": "object", + "properties": { + "id": { + "type": "string" + } + } + }, + "ApiLists": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "$ref": "#/components/schemas/ApiList" + } + }, + "x-codegen-inline": true, + "x-codegen-type": "ArrayMap" + } + } + } +}