Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

312 bug duplicate name object result in incorrect code generation #329

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 58 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ As commented above, they both could be used at the same time, setting a double
<plugin>
<groupId>com.sngular</groupId>
<artifactId>scs-multiapi-maven-plugin</artifactId>
<version>5.4.2</version>
<version>5.4.3</version>
<executions>
<execution>
<id>asyncapi</id>
Expand Down Expand Up @@ -104,6 +104,32 @@ In the [AsyncApi Generator](#asyncapi-generator) and the
[OpenApi Generator](#openapi-generator) sections, you can find more information
about how they work, and the parameters and configuration options they offer.

#### Mandatory dependencies

These dependencies are used by generated code

```xml
<dependencies>
<dependency>
<groupId>io.swagger.parser.v3</groupId>
<artifactId>swagger-parser-core</artifactId>
<version>2.1.20</version>
</dependency>

<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-annotations-jakarta</artifactId>
<version>2.2.20</version>
</dependency>

<dependency>
<groupId>jakarta.validation</groupId>
<artifactId>jakarta.validation-api</artifactId>
<version>3.0.2</version>
</dependency>
</dependencies>
```

### How to configure the build file

To maintain the generation of the different types of classes independent, they
Expand All @@ -114,7 +140,7 @@ Apply the plugin in the `build.gradle` file and invoke the task.
```groovy
plugins {
id "java"
id "com.sngular.scs-multiapi-gradle-plugin' version '5.4.2"
id "com.sngular.scs-multiapi-gradle-plugin' version '5.4.3"

openapimodel {

Expand All @@ -138,6 +164,16 @@ In the [AsyncApi Generator](#asyncapi-generator) and the
[OpenApi Generator](#openapi-generator) sections, you can find more information
about how they work, and the parameters and configuration options they offer.

#### Mandatory gradle dependencies

These dependencies are used by generated code

``` gradle
implementation 'io.swagger.parser.v3:swagger-parser-core:2.1.20'
implementation 'io.swagger.core.v3:swagger-annotations-jakarta:2.2.20'
implementation 'jakarta.validation:jakarta.validation-api:3.0.2'
```

## AsyncApi Generator

### Configuration
Expand All @@ -153,7 +189,7 @@ which the plugin is designed.
<plugin>
<groupId>com.sngular</groupId>
<artifactId>scs-multiapi-maven-plugin</artifactId>
<version>5.4.2</version>
<version>5.4.3</version>
<executions>
<execution>
<phase>generate-sources</phase>
Expand Down Expand Up @@ -443,8 +479,16 @@ public interface ISubscribeOperation {

#### Bindings

Asyncapi support a way to specify specific configuration for certain protocols. Nowadays we only support Kafka specific information to define a Key form Messages as you can find [here](<https://github.com/asyncapi/bindings/blob/master/kafka/README.md>).
When a binding is specified in a message we will generate a generic class named as MessageWrapper which will contain the payload and the key used in to build a Message.
Asyncapi support a way to specify specific configuration for certain protocols.

Nowadays, we only support Kafka specific information to define a Key form.

Messages as you can find
[here](<https://github.com/asyncapi/bindings/blob/master/kafka/README.md>).

When a binding is specified in a message we will generate a generic class
named as MessageWrapper which will contain the payload and the key
used in to build a Message.
You will find such class by each api package you define.

##### Mapper
Expand Down Expand Up @@ -584,7 +628,7 @@ file. Here is an example of a basic configuration:
<plugin>
<groupId>com.sngular</groupId>
<artifactId>scs-multiapi-maven-plugin</artifactId>
<version>5.4.2</version>
<version>5.4.3</version>
<executions>
<execution>
<goals>
Expand Down Expand Up @@ -664,9 +708,14 @@ openapimodel {
specFile {
{
filePath = './src/main/resources/api/api.yml'
apiPackage = 'com.sngular.apigenerator.openapi.api'
modelPackage = 'com.sngular.apigenerator.openapi.api.model'
useTagsGroup = true
consumer {
apiPackage = 'com.sngular.apigenerator.openapi.api'
modelPackage = 'com.sngular.apigenerator.openapi.api.model'
}
supplier {
apiPackage = 'com.sngular.apigenerator.openapi.api'
modelPackage = 'com.sngular.apigenerator.openapi.api.model'
}
}
overWriteModel = true
}
Expand Down
4 changes: 2 additions & 2 deletions multiapi-engine/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>com.sngular</groupId>
<artifactId>multiapi-engine</artifactId>
<version>5.4.2</version>
<version>5.4.3</version>
<packaging>jar</packaging>

<properties>
Expand All @@ -20,7 +20,7 @@
<junit-jupiter-engine.version>5.9.2</junit-jupiter-engine.version>
<swagger-core.version>2.2.9</swagger-core.version>
<maven-surefire-plugin.version>3.0.0</maven-surefire-plugin.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

</properties>

<dependencies>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ public final void processFileSpec(final List<SpecFile> specsListFile) {
final ObjectMapper om = new ObjectMapper(new YAMLFactory());
processedOperationIds.clear();
generateExceptionTemplate = false;
for (SpecFile fileParameter : specsListFile) {
for (final SpecFile fileParameter : specsListFile) {
final Pair<InputStream, FileLocation> ymlFileAndPath;
try {
ymlFileAndPath = resolveYmlLocation(fileParameter.getFilePath());
Expand Down Expand Up @@ -471,7 +471,7 @@ private void fillTemplateFactory(
final JsonNode schemaToBuild = processedMethod.getPayload();
if (shouldBuild(schemaToBuild)) {
final var schemaObjectIt =
MapperContentUtil.mapComponentToSchemaObject(totalSchemas, className, schemaToBuild, null, operationObject.getModelNameSuffix(), parentPackage,
MapperContentUtil.mapComponentToSchemaObject(totalSchemas, className, schemaToBuild, null, operationObject.getModelNameSuffix(), parentPackage, modelPackage,
operationObject.getFormats(), operationObject.getUseTimeType()).iterator();

if (schemaObjectIt.hasNext()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

package com.sngular.api.generator.plugin.asyncapi.util;


import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
Expand Down Expand Up @@ -64,19 +66,19 @@ private MapperContentUtil() {}
public static List<SchemaObject> mapComponentToSchemaObject(
final Map<String, JsonNode> totalSchemas, final String component, final JsonNode model,
final String prefix, final String suffix, final String parentPackage,
final Map<String, String> formats, final TimeType useTimeType) {
final String modelPackage, final Map<String, String> formats, final TimeType useTimeType) {
final List<SchemaObject> schemasList = new ArrayList<>();
if (Objects.nonNull(model)) {
final Queue<String> modelToBuildList = new ConcurrentLinkedQueue<>();
final List<String> alreadyBuilt = new ArrayList<>();
schemasList.add(buildSchemaObject(totalSchemas, component, model, prefix, suffix, modelToBuildList, parentPackage, formats, useTimeType));
schemasList.add(buildSchemaObject(totalSchemas, component, model, prefix, suffix, modelToBuildList, parentPackage, modelPackage, formats, useTimeType));
while (!modelToBuildList.isEmpty()) {

final var modelToBuild = modelToBuildList.remove();
if (!alreadyBuilt.contains(modelToBuild)) {
final var path = MapperUtil.splitName(modelToBuild);
final var nexElement = buildSchemaObject(totalSchemas, modelToBuild, totalSchemas.get(getComponent(path)),
prefix, suffix, modelToBuildList, getParentName(path), formats, useTimeType);
prefix, suffix, modelToBuildList, getParentName(path), modelPackage, formats, useTimeType);
if (schemasList.contains(nexElement)) {
modelToBuildList.poll();
} else {
Expand All @@ -92,14 +94,24 @@ public static List<SchemaObject> mapComponentToSchemaObject(
private static String getParentName(final String[] path) {
final String parenName;
if (path.length > 1) {
parenName = path[path.length - 2];
parenName = String.join(".", Arrays.copyOf(path, path.length - 1));
} else {
parenName = "";
}
return parenName;
}

private static String getComponent(final String[] path) {
final String componentName;
if (path.length > 1) {
componentName = String.join(".", Arrays.copyOf(path, path.length - 1)) + "/" + path[path.length - 1];
} else {
componentName = path[0];
}
return componentName.toUpperCase();
}

private static String getSchema(final String[] path) {
final String componentName;
if (path.length > 1) {
componentName = path[path.length - 2] + "/" + path[path.length - 1];
Expand All @@ -112,9 +124,9 @@ private static String getComponent(final String[] path) {
private static SchemaObject buildSchemaObject(
final Map<String, JsonNode> totalSchemas, final String component, final JsonNode model,
final String prefix, final String suffix, final Collection<String> modelToBuildList, final String parentPackage,
final Map<String, String> formats, final TimeType useTimeType) {
final String modelPackage, final Map<String, String> formats, final TimeType useTimeType) {

final var listSchema = getFields(totalSchemas, model, true, prefix, suffix, modelToBuildList, parentPackage, formats, useTimeType);
final var listSchema = getFields(totalSchemas, model, true, prefix, suffix, modelToBuildList, modelPackage, formats, useTimeType);
final var splitPackage = MapperUtil.splitName(component);
final String className = splitPackage[splitPackage.length - 1];
return SchemaObject.builder()
Expand All @@ -130,7 +142,7 @@ private static SchemaObject buildSchemaObject(
private static List<String> getImportList(final List<SchemaFieldObject> schemaListToImport) {
final var importList = new HashSet<String>();

for (SchemaFieldObject fieldObject : schemaListToImport) {
for (final SchemaFieldObject fieldObject : schemaListToImport) {
importList.addAll(getTypeImports(fieldObject));
}
return new ArrayList<>(importList);
Expand Down Expand Up @@ -168,31 +180,31 @@ private static List<String> getTypeImports(final SchemaFieldObject fieldObject)

private static List<SchemaFieldObject> getFields(
final Map<String, JsonNode> totalSchemas, final JsonNode model, final boolean required, final String prefix,
final String suffix, final Collection<String> modelToBuildList, final String parentPackage,
final Map<String, String> formats, final TimeType useTimeType) {
final String suffix, final Collection<String> modelToBuildList,
final String modelPackage, final Map<String, String> formats, final TimeType useTimeType) {
final var fieldObjectArrayList = new ArrayList<SchemaFieldObject>();
schemaCombinatorType = null;
if (ApiTool.hasType(model)) {
if (OBJECT.equalsIgnoreCase(model.get(TYPE).textValue())) {
fieldObjectArrayList.addAll(processFieldObject(totalSchemas, model, prefix, suffix, modelToBuildList, parentPackage, formats, useTimeType));
fieldObjectArrayList.addAll(processFieldObject(totalSchemas, model, prefix, suffix, modelToBuildList, modelPackage, formats, useTimeType));
} else if (ARRAY.equalsIgnoreCase(model.get(TYPE).textValue())) {
fieldObjectArrayList.add(processFieldObjectList(totalSchemas, "", model, required, prefix, suffix, modelToBuildList, parentPackage, null, formats, useTimeType));
fieldObjectArrayList.add(processFieldObjectList(totalSchemas, "", model, required, prefix, suffix, modelToBuildList, modelPackage, null, formats, useTimeType));
} else if ("enum".equalsIgnoreCase(model.get(TYPE).textValue())) {
fieldObjectArrayList.add(processFieldObjectList(totalSchemas, "", model, required, prefix, suffix, modelToBuildList, parentPackage, null, formats, useTimeType));
fieldObjectArrayList.add(processFieldObjectList(totalSchemas, "", model, required, prefix, suffix, modelToBuildList, modelPackage, null, formats, useTimeType));
}
} else if (ApiTool.hasRef(model)) {
final var splitName = MapperUtil.splitName(ApiTool.getRefValue(model));
fieldObjectArrayList.addAll(processFieldObject(totalSchemas, totalSchemas.get(MapperUtil.buildKey(splitName)), prefix, suffix,
modelToBuildList, parentPackage, formats, useTimeType));
modelToBuildList, modelPackage, formats, useTimeType));
} else if (model.elements().hasNext()) {
fieldObjectArrayList.addAll(processFieldObject(totalSchemas, model, prefix, suffix, modelToBuildList, parentPackage, formats, useTimeType));
fieldObjectArrayList.addAll(processFieldObject(totalSchemas, model, prefix, suffix, modelToBuildList, modelPackage, formats, useTimeType));
}
return fieldObjectArrayList;
}

private static List<SchemaFieldObject> processFieldObject(
final Map<String, JsonNode> totalSchemas, final JsonNode model, final String prefix, final String suffix, final Collection<String> modelToBuildList,
final String parentPackage, final Map<String, String> formats, final TimeType useTimeType) {
final String modelPackage, final Map<String, String> formats, final TimeType useTimeType) {
final Set<String> requiredSet = new HashSet<>();
final var fieldObjectArrayList = new ArrayList<SchemaFieldObject>();
if (model.has("required")) {
Expand All @@ -206,8 +218,8 @@ private static List<SchemaFieldObject> processFieldObject(
while (propertiesIt.hasNext()) {
final var property = propertiesIt.next();
fieldObjectArrayList.add(processFieldObjectList(totalSchemas, property, model.get(PROPERTIES).path(property), requiredSet.contains(property), prefix, suffix,
modelToBuildList, parentPackage, null, formats, useTimeType));
if (model.get(PROPERTIES).path(property).has(REF) && !totalSchemas.containsKey(createKey(parentPackage, property.toUpperCase(), "/"))) {
modelToBuildList, modelPackage, null, formats, useTimeType));
if (model.get(PROPERTIES).path(property).has(REF) && !totalSchemas.containsKey(createKey(modelPackage, property.toUpperCase(), "/"))) {
modelToBuildList.add(MapperUtil.getLongRefClass(model.get(PROPERTIES).path(property)));
}
}
Expand Down Expand Up @@ -308,7 +320,7 @@ private static SchemaFieldObject processFieldObjectList(
}
} else if (ApiTool.hasRef(schema)) {
final var splitName = MapperUtil.splitName(ApiTool.getRefValue(schema));
final var solvedRef = totalSchemas.get(getComponent(splitName));
final var solvedRef = totalSchemas.get(getSchema(splitName));
fieldObject = processFieldObjectList(totalSchemas, name, solvedRef, required, prefix, suffix, modelToBuildList, modelPackage,
splitName[splitName.length - 1], formats, useTimeType);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ private static List<String> getSecurityRequirementList(final JsonNode securityNo

public static List<PathObject> mapPathObjects(final SpecFile specFile, final Collection<Map<String, JsonNode>> path, final GlobalObject globalObject, final Path baseDir) {
final List<PathObject> pathObjects = new ArrayList<>();
for (var pathMap : path) {
for (var pathItem : pathMap.entrySet()) {
for (final var pathMap : path) {
for (final var pathItem : pathMap.entrySet()) {
final PathObject pathObject = PathObject.builder()
.pathName(pathItem.getKey())
.globalObject(globalObject)
Expand All @@ -85,7 +85,7 @@ private static List<OperationObject> mapOperationObject(final SpecFile specFile,
final List<String> operationIdList = new ArrayList<>();
final var pathNode = path.getValue();
final var pathParameters = new ArrayList<ParameterObject>();
for (Iterator<Entry<String, JsonNode>> it = pathNode.fields(); it.hasNext();) {
for (final Iterator<Entry<String, JsonNode>> it = pathNode.fields(); it.hasNext();) {
final var field = it.next();
switch (field.getKey()) {
case "get":
Expand Down Expand Up @@ -218,7 +218,7 @@ private static List<ParameterObject> mapParameterObjects(
final GlobalObject globalObject, final Path baseDir) {
final List<ParameterObject> parameterObjects = new ArrayList<>();
if (Objects.nonNull(parameters) && !parameters.isEmpty()) {
for (JsonNode parameter : parameters) {
for (final JsonNode parameter : parameters) {
if (ApiTool.hasRef(parameter)) {
final JsonNode refParameter = globalObject.getParameterNode(MapperUtil.getRefSchemaName(parameter)).orElseThrow();
parameterObjects.add(buildParameterObject(specFile, globalObject, refParameter, baseDir));
Expand Down Expand Up @@ -260,7 +260,7 @@ private static List<ParameterObject> buildParameterContent(
final var content = ApiTool.getNode(parameter, CONTENT);
final var parameterName = ApiTool.getName(parameter);
final var parameterObjects = new ArrayList<ParameterObject>();
for (Iterator<JsonNode> it = content.elements(); it.hasNext();) {
for (final Iterator<JsonNode> it = content.elements(); it.hasNext();) {
final var contentType = it.next();
final String inlineParameter = INLINE_PARAMETER + safeCapitalize(contentClassName)
+ StringUtils.capitalize(parameterName);
Expand Down Expand Up @@ -337,7 +337,7 @@ private static List<ContentObject> mapContentObject(
final Path baseDir) {
final List<ContentObject> contentObjects = new ArrayList<>();
if (Objects.nonNull(content)) {
for (Iterator<String> it = content.fieldNames(); it.hasNext(); ) {
for (final Iterator<String> it = content.fieldNames(); it.hasNext();) {
final String mediaType = it.next();
final var schema = ApiTool.getNode(ApiTool.getNode(content, mediaType), SCHEMA);
final String pojoName = preparePojoName(inlineObject, schema, specFile);
Expand Down
Loading
Loading