Skip to content
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ The project is very much Work In Progress and will be published on maven central

# Release Notes
BOAT is still under development and subject to change.
## 0.17.11
* BoatJavaCodeGen, BoatSpringCodeGen
* Fix: always generate collection initializer when array is required in the schema (even if containerDefaultToNull=true)
## 0.17.10
* Boat maven plugin
* Fix: When using Multipart, generate with `@RequestPart` instead of `@RequestParam`
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.backbase.oss.codegen.java;

import io.swagger.v3.oas.models.media.Schema;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.openapitools.codegen.CodegenProperty;
import org.openapitools.codegen.utils.ModelUtils;

@NoArgsConstructor(access = AccessLevel.PRIVATE)
class BoatCodeGenUtils {

/**
* @return {@link CodegenValueType} to be used or empty if given schema is not array
*/
public static Optional<CodegenValueType> getCollectionCodegenValue(CodegenProperty cp, Schema schema,
boolean containerDefaultToNull, Map<String, String> types) {
CodegenValueType valueType = null;
if (ModelUtils.isSet(schema) && (schema.getDefault() == null)) {
valueType = CodegenValueType.of(
formatValue(cp, containerDefaultToNull, types.getOrDefault("set", "LinkedHashSet")));
} else if (ModelUtils.isArraySchema(schema) && (schema.getDefault() == null)) {
valueType = CodegenValueType.of(
formatValue(cp, containerDefaultToNull, types.getOrDefault("array", "ArrayList")));
}
return Optional.ofNullable(valueType);
}

private static String formatValue(CodegenProperty cp, boolean defaultToNull, String javaSimpleType) {
return (cp.required || !defaultToNull)
? String.format(Locale.ROOT, "new %s<>()", javaSimpleType)
: null;
}

@RequiredArgsConstructor(staticName = "of")
@Getter
static class CodegenValueType {
private final String value;
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package com.backbase.oss.codegen.java;

import static com.backbase.oss.codegen.java.BoatCodeGenUtils.getCollectionCodegenValue;

import com.backbase.oss.codegen.java.BoatCodeGenUtils.CodegenValueType;
import io.swagger.v3.oas.models.media.Schema;
import java.util.Locale;
import lombok.Getter;
import lombok.Setter;
import org.openapitools.codegen.CliOption;
Expand Down Expand Up @@ -120,27 +122,9 @@ private void processRestTemplateOpts() {

@Override
public String toDefaultValue(CodegenProperty cp, Schema schema) {
schema = ModelUtils.getReferencedSchema(this.openAPI, schema);
if (ModelUtils.isArraySchema(schema) && (schema.getDefault() == null)) {
return getArrayDefaultValue(cp, schema, containerDefaultToNull,
instantiationTypes().getOrDefault("set", "LinkedHashSet"),
instantiationTypes().getOrDefault("array", "ArrayList"));
}
return super.toDefaultValue(cp, schema);
}

public static String getArrayDefaultValue(CodegenProperty cp, Schema schema,
boolean containerDefaultToNull, String setType, String arrayType) {
if (cp.isNullable || containerDefaultToNull) {
return null;
} else {
if (ModelUtils.isSet(schema)) {
return String.format(Locale.ROOT, "new %s<>()",
setType);
} else {
return String.format(Locale.ROOT, "new %s<>()",
arrayType);
}
}
final Schema referencedSchema = ModelUtils.getReferencedSchema(this.openAPI, schema);
return getCollectionCodegenValue(cp, referencedSchema, containerDefaultToNull, instantiationTypes())
.map(CodegenValueType::getValue)
.orElseGet(() -> super.toDefaultValue(cp, referencedSchema));
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.backbase.oss.codegen.java;

import static com.backbase.oss.codegen.java.BoatCodeGenUtils.getCollectionCodegenValue;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.joining;
import static org.openapitools.codegen.utils.StringUtils.camelize;

import com.backbase.oss.codegen.java.BoatCodeGenUtils.CodegenValueType;
import com.samskivert.mustache.Mustache;
import com.samskivert.mustache.Template.Fragment;
import io.swagger.v3.oas.models.Operation;
Expand Down Expand Up @@ -258,12 +260,9 @@ public CodegenOperation fromOperation(String path, String httpMethod, Operation

@Override
public String toDefaultValue(CodegenProperty cp, Schema schema) {
schema = ModelUtils.getReferencedSchema(this.openAPI, schema);
if (ModelUtils.isArraySchema(schema) && (schema.getDefault() == null)) {
return BoatJavaCodeGen.getArrayDefaultValue(cp, schema, containerDefaultToNull,
instantiationTypes().getOrDefault("set", "LinkedHashSet"),
instantiationTypes().getOrDefault("array", "ArrayList"));
}
return super.toDefaultValue(cp, schema);
final Schema referencedSchema = ModelUtils.getReferencedSchema(this.openAPI, schema);
return getCollectionCodegenValue(cp, referencedSchema, containerDefaultToNull, instantiationTypes())
.map(CodegenValueType::getValue)
.orElseGet(() -> super.toDefaultValue(cp, referencedSchema));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
package com.backbase.oss.codegen.java;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.openapitools.codegen.languages.JavaClientCodegen.APACHE;
import static org.openapitools.codegen.languages.JavaClientCodegen.RESTTEMPLATE;

import com.github.javaparser.StaticJavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.FieldDeclaration;
import com.github.javaparser.ast.body.VariableDeclarator;
import io.swagger.parser.OpenAPIParser;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.parser.core.models.ParseOptions;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.List;
import lombok.SneakyThrows;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.openapitools.codegen.ClientOptInput;
import org.openapitools.codegen.DefaultGenerator;
import org.openapitools.codegen.languages.AbstractJavaCodegen;

class BoatCommonJavaCodeGenTests {

static final String PROP_BASE = BoatCommonJavaCodeGenTests.class.getSimpleName() + ".";
static final String TEST_OUTPUT = System.getProperty(PROP_BASE + "output",
"target/" + BoatCommonJavaCodeGenTests.class.getSimpleName());

@BeforeAll
static void before() throws IOException {
Files.createDirectories(Paths.get(TEST_OUTPUT));
FileUtils.deleteDirectory(new File(TEST_OUTPUT));
}

@ParameterizedTest
@ValueSource(strings = {"true", "false", ""})
void shouldGenerateArrayField(String containerDefaultToNull) {
generateAndAssert(javaCodeGenWithLib(RESTTEMPLATE), containerDefaultToNull);
generateAndAssert(javaCodeGenWithLib(APACHE), containerDefaultToNull);
generateAndAssert(springCodeGen(), containerDefaultToNull);
}

private static BoatJavaCodeGen javaCodeGenWithLib(String library) {
var codeGen = new BoatJavaCodeGen();
codeGen.setLibrary(library);
return codeGen;
}

private static BoatSpringCodeGen springCodeGen() {
var codeGen = new BoatSpringCodeGen();
codeGen.setLibrary("spring-boot");
return codeGen;
}

void generateAndAssert(AbstractJavaCodegen codegen, String containerDefaultToNull) {

var input = new File("src/test/resources/boat-spring/openapi.yaml");
var outputDir = TEST_OUTPUT + String.format("/generateAndAssert_%s_%s_%s", codegen.getClass().getSimpleName(),
codegen.getLibrary(), containerDefaultToNull);

codegen.setInputSpec(input.getAbsolutePath());
codegen.setOutputDir(outputDir);

OpenAPI openApiInput = new OpenAPIParser()
.readLocation(input.getAbsolutePath(), null, new ParseOptions())
.getOpenAPI();
var clientOptInput = new ClientOptInput();
clientOptInput.config(codegen);
clientOptInput.openAPI(openApiInput);
if (StringUtils.isNotBlank(containerDefaultToNull)) {
clientOptInput.getConfig().additionalProperties().put("containerDefaultToNull", containerDefaultToNull);
}

List<File> files = new DefaultGenerator().opts(clientOptInput).generate();

CompilationUnit multilinePaymentRequest = files.stream()
.filter(it -> it.getName().equals("MultiLinePaymentRequest.java"))
.findFirst()
.map(file -> {
try {
return StaticJavaParser.parse(file);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
})
.orElseThrow();

if (Boolean.parseBoolean(containerDefaultToNull)) {
// containerDefaultToNull=true
assertVariableDeclarator(multilinePaymentRequest, "lines", "ArrayList");
assertVariableDeclarator(multilinePaymentRequest, "uniqueLines", "LinkedHashSet");
assertVariableDeclarator(multilinePaymentRequest, "optionalLines", ""); // optional field, therefore null
assertVariableDeclarator(multilinePaymentRequest, "optionalUniqueLines", ""); // optional field, therefore null
} else {
// containerDefaultToNull=false or default behaviour
assertVariableDeclarator(multilinePaymentRequest, "lines", "ArrayList");
assertVariableDeclarator(multilinePaymentRequest, "uniqueLines", "LinkedHashSet");
assertVariableDeclarator(multilinePaymentRequest, "optionalLines", "ArrayList");
assertVariableDeclarator(multilinePaymentRequest, "optionalUniqueLines", "LinkedHashSet");
}
}

@SneakyThrows
private void assertVariableDeclarator(CompilationUnit requestClass, String fieldName, String declarationType) {
VariableDeclarator listDeclarator = requestClass
.findAll(FieldDeclaration.class)
.stream()
.flatMap(field -> field.getChildNodes().stream())
.filter(node -> node instanceof VariableDeclarator)
.map(VariableDeclarator.class::cast)
.filter(declarator -> declarator.getName().getIdentifier().equals(fieldName))
.findFirst()
.orElseThrow();

if (StringUtils.isNotBlank(declarationType)) {
assertTrue(listDeclarator.getInitializer().isPresent());
assertEquals(listDeclarator.getInitializer().get().toString(),
String.format("new %s<>()", declarationType));
} else {
assertFalse(listDeclarator.getInitializer().isPresent());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.body.Parameter;
import com.samskivert.mustache.Template.Fragment;
import io.swagger.v3.oas.models.Operation;
import io.swagger.parser.OpenAPIParser;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.parser.core.models.ParseOptions;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.Arrays;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
Expand All @@ -42,7 +42,7 @@ class BoatSpringCodeGenTests {
@BeforeAll
static void before() throws IOException {
Files.createDirectories(Paths.get(TEST_OUTPUT));
FileUtils.deleteDirectory(new File(TEST_OUTPUT, "src"));
FileUtils.deleteDirectory(new File(TEST_OUTPUT));
}

@Test
Expand Down
18 changes: 18 additions & 0 deletions boat-scaffold/src/test/resources/boat-spring/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,31 @@ components:
MultiLinePaymentRequest:
required:
- lines
- uniqueLines
type: object
properties:
lines:
type: array
description: Payment request details
items:
$ref: '#/components/schemas/PaymentRequestLine'
uniqueLines:
type: array
uniqueItems: true
description: Payment request details
items:
$ref: '#/components/schemas/PaymentRequestLine'
optionalLines:
type: array
description: Payment request optional details
items:
$ref: '#/components/schemas/PaymentRequestLine'
optionalUniqueLines:
type: array
uniqueItems: true
description: Payment request optional details
items:
$ref: '#/components/schemas/PaymentRequestLine'
PaymentRequestLine:
required:
- accountId
Expand Down