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
2 changes: 1 addition & 1 deletion 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>6.6.3</version>
<version>6.6.4</version>
<packaging>jar</packaging>

<properties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public class SchemaFieldObjectType {
new SimpleImmutableEntry<>(TypeConstants.ZONEDDATETIME, ZONED_DATE_TIME),
new SimpleImmutableEntry<>(TypeConstants.OFFSETDATE, OFFSET_DATE_TIME),
new SimpleImmutableEntry<>(TypeConstants.OFFSETDATETIME, OFFSET_DATE_TIME),
new SimpleImmutableEntry<>(TypeConstants.MULTIPART_FILE, "MultipartFile")
new SimpleImmutableEntry<>(TypeConstants.MULTIPART_FILE, "MultipartFile"),
new SimpleImmutableEntry<>(TypeConstants.RESOURCE, "Resource")
);

private static final Map<String, String> IMPORT_TYPE_MAPPINGS = Map.ofEntries(
Expand All @@ -53,7 +54,8 @@ public class SchemaFieldObjectType {
new SimpleImmutableEntry<>(TypeConstants.ZONEDDATETIME, "java.time." + ZONED_DATE_TIME),
new SimpleImmutableEntry<>(TypeConstants.OFFSETDATE, "java.time." + OFFSET_DATE_TIME),
new SimpleImmutableEntry<>(TypeConstants.OFFSETDATETIME, "java.time." + OFFSET_DATE_TIME),
new SimpleImmutableEntry<>(TypeConstants.MULTIPART_FILE, "MultipartFile")
new SimpleImmutableEntry<>(TypeConstants.MULTIPART_FILE, "MultipartFile"),
new SimpleImmutableEntry<>(TypeConstants.RESOURCE, "org.springframework.core.io.Resource")
);

private static final Map<String, String> IMPL_TYPE_MAPPINGS = Map.ofEntries(
Expand All @@ -73,7 +75,8 @@ public class SchemaFieldObjectType {
new SimpleImmutableEntry<>(TypeConstants.ZONEDDATETIME, ZONED_DATE_TIME),
new SimpleImmutableEntry<>(TypeConstants.OFFSETDATE, OFFSET_DATE_TIME),
new SimpleImmutableEntry<>(TypeConstants.OFFSETDATETIME, OFFSET_DATE_TIME),
new SimpleImmutableEntry<>(TypeConstants.MULTIPART_FILE, "MultipartFile")
new SimpleImmutableEntry<>(TypeConstants.MULTIPART_FILE, "MultipartFile"),
new SimpleImmutableEntry<>(TypeConstants.RESOURCE, "Resource")
);

private final String baseType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ public final class TypeConstants {

public static final String MULTIPART_FILE = "multipartfile";

public static final String RESOURCE = "resource";

public static final Set<String> BASIC_OBJECT_TYPE = Set.of(NUMBER, STRING, BOOLEAN, INTEGER, ARRAY);

public static final Set<String> NO_IMPORT_TYPE = Set.of(STRING, INTEGER, OBJECT);
Expand Down Expand Up @@ -94,7 +96,8 @@ public final class TypeConstants {
ZONEDDATETIME,
OFFSETDATE,
OFFSETDATETIME,
MULTIPART_FILE
MULTIPART_FILE,
RESOURCE
);

private TypeConstants() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,11 @@ private static JsonNode getRefSchema(JsonNode schema, SpecFile specFile, GlobalO
private static SchemaFieldObjectType getObjectOrType(
final JsonNode schema, final String pojoName, final SpecFile specFile, final GlobalObject globalObject,
final Path baseDir) {
if (ApiTool.isBinary(schema)) {
// A raw binary body (e.g. application/octet-stream, type: string + format: binary,
// not wrapped in a multipart object) maps to a streaming Resource, not String.
return new SchemaFieldObjectType(TypeConstants.RESOURCE);
}
return switch (ApiTool.getType(schema)) {
case TypeConstants.OBJECT -> SchemaFieldObjectType.fromTypeList(TypeConstants.OBJECT, pojoName);
case TypeConstants.INTEGER -> new SchemaFieldObjectType(getIntegerFormat(schema));
Expand Down
16 changes: 16 additions & 0 deletions multiapi-engine/src/main/resources/templates/openapi/template.ftlh
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package <#if packageApi??>${packageApi}<#elseif package??> ${package}</#if>;
<#assign imports=[]>
<#assign model_imports=[]>
<#assign needsMultipartFile=false>
<#assign needsResource=false>
<#list pathObjects as path>
<#list path.operationObjects as operation>
<#list operation.requestObjects as request>
Expand All @@ -18,6 +19,18 @@ package <#if packageApi??>${packageApi}<#elseif package??> ${package}</#if>;
</#if>
</#list>
</#if>
<#list request.contentObjects as content>
<#if content.dataType.containsType("resource")>
<#assign needsResource=true>
</#if>
</#list>
</#list>
<#list operation.responseObjects as response>
<#list response.contentObjects as content>
<#if content.dataType.containsType("resource")>
<#assign needsResource=true>
</#if>
</#list>
</#list>
</#list>
</#list>
Expand Down Expand Up @@ -85,6 +98,9 @@ import org.springframework.web.context.request.NativeWebRequest;
<#if needsMultipartFile>
import org.springframework.web.multipart.MultipartFile;
</#if>
<#if needsResource>
import org.springframework.core.io.Resource;
</#if>

<#list model_imports as import>
import <#if packageModel??>${packageModel}<#elseif package??>${package}</#if>.${import};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import reactor.core.publisher.Mono;
import reactor.core.publisher.Flux;
import springfox.documentation.annotations.ApiIgnore;
<#assign needsFilePart=false>
<#assign needsResource=false>
<#list pathObjects as path>
<#list path.operationObjects as operation>
<#list operation.requestObjects as request>
Expand All @@ -37,12 +38,27 @@ import springfox.documentation.annotations.ApiIgnore;
</#if>
</#list>
</#if>
<#list request.contentObjects as content>
<#if content.dataType.containsType("resource")>
<#assign needsResource=true>
</#if>
</#list>
</#list>
<#list operation.responseObjects as response>
<#list response.contentObjects as content>
<#if content.dataType.containsType("resource")>
<#assign needsResource=true>
</#if>
</#list>
</#list>
</#list>
</#list>
<#if needsFilePart>
import org.springframework.http.codec.multipart.FilePart;
</#if>
<#if needsResource>
import org.springframework.core.io.Resource;
</#if>

<#assign imports=[]>
<#list pathObjects as path>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,16 @@ public final class OpenApiGeneratorFixtures {
.apiPackage("com.sngular.multifileplugin.testreactiveformdatamultipart")
.useLombokModelAnnotation(false).isReactive(true).build());

static final List<SpecFile> TEST_BINARY_BODY_RESOURCE = List
.of(SpecFile.builder().filePath("openapigenerator/testBinaryBodyResource/api-test.yml")
.apiPackage("com.sngular.multifileplugin.testbinarybodyresource")
.useLombokModelAnnotation(false).build());

static final List<SpecFile> TEST_REACTIVE_BINARY_BODY_RESOURCE = List
.of(SpecFile.builder().filePath("openapigenerator/testReactiveBinaryBodyResource/api-test.yml")
.apiPackage("com.sngular.multifileplugin.testreactivebinarybodyresource")
.useLombokModelAnnotation(false).isReactive(true).build());

static final List<SpecFile> TEST_API_TAGS_GENERATION = List
.of(SpecFile.builder().filePath("openapigenerator/testApiTagsGeneration/api-tags-test.yml")
.apiPackage("com.sngular.multifileplugin.tagsgeneration")
Expand Down Expand Up @@ -607,6 +617,38 @@ static Function<Path, Boolean> validateApiReactiveGeneration(int springBootVersi

}

static Function<Path, Boolean> validateBinaryBodyResource() {

final String DEFAULT_TARGET_API = "generated/com/sngular/multifileplugin/testbinarybodyresource";

final String DEFAULT_MODEL_API = "generated/com/sngular/multifileplugin/testbinarybodyresource/model";

final String COMMON_PATH = "openapigenerator/testBinaryBodyResource/";

final String ASSETS_PATH = COMMON_PATH + "assets/";

final List<String> expectedTestApiFile = List.of(ASSETS_PATH + "UploadApi.java");

return path -> commonTest(path, expectedTestApiFile, Collections.emptyList(), DEFAULT_TARGET_API,
DEFAULT_MODEL_API, Collections.emptyList(), null);
}

static Function<Path, Boolean> validateReactiveBinaryBodyResource() {

final String DEFAULT_TARGET_API = "generated/com/sngular/multifileplugin/testreactivebinarybodyresource";

final String DEFAULT_MODEL_API = "generated/com/sngular/multifileplugin/testreactivebinarybodyresource/model";

final String COMMON_PATH = "openapigenerator/testReactiveBinaryBodyResource/";

final String ASSETS_PATH = COMMON_PATH + "assets/";

final List<String> expectedTestApiFile = List.of(ASSETS_PATH + "UploadApi.java");

return path -> commonTest(path, expectedTestApiFile, Collections.emptyList(), DEFAULT_TARGET_API,
DEFAULT_MODEL_API, Collections.emptyList(), null);
}

static Function<Path, Boolean> validateReactiveFormDataMultipart() {

final String DEFAULT_TARGET_API = "generated/com/sngular/multifileplugin/testreactiveformdatamultipart";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ static Stream<Arguments> fileSpecToProcess() {
OpenApiGeneratorFixtures.validateApiReactiveGeneration(SPRING_BOOT_VERSION)),
Arguments.of("testReactiveFormDataMultipart", OpenApiGeneratorFixtures.TEST_REACTIVE_FORM_DATA_MULTIPART,
OpenApiGeneratorFixtures.validateReactiveFormDataMultipart()),
Arguments.of("testBinaryBodyResource", OpenApiGeneratorFixtures.TEST_BINARY_BODY_RESOURCE,
OpenApiGeneratorFixtures.validateBinaryBodyResource()),
Arguments.of("testReactiveBinaryBodyResource", OpenApiGeneratorFixtures.TEST_REACTIVE_BINARY_BODY_RESOURCE,
OpenApiGeneratorFixtures.validateReactiveBinaryBodyResource()),
Arguments.of("testApiTagsGeneration", OpenApiGeneratorFixtures.TEST_API_TAGS_GENERATION,
OpenApiGeneratorFixtures.validateTagsGeneration()),
Arguments.of("testMultipleRefGeneration", OpenApiGeneratorFixtures.TEST_MULTIPLE_REF_GENERATION,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
openapi: 3.0.2
info:
title: Testing raw binary request body
version: 1.0.0
servers:
- url: http://localhost/v1
paths:
/upload:
post:
tags:
- test
operationId: uploadBinary
requestBody:
required: true
content:
application/octet-stream:
schema:
type: string
format: binary
responses:
'200':
description:
OK
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.sngular.multifileplugin.testbinarybodyresource;

import java.util.Optional;
import java.util.List;
import java.util.Map;
import javax.validation.Valid;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import org.springframework.http.MediaType;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.core.io.Resource;


public interface UploadApi {

/**
* POST /upload
* @param resource (required)
* @return OK; (status code 200)
*/

@Operation(
operationId = "uploadBinary",
tags = {"test"},
responses = {
@ApiResponse(responseCode = "200", description = "OK")
}
)
@RequestMapping(
method = RequestMethod.POST,
value = "/upload",
produces = {"application/json"}
)

default ResponseEntity<Void> uploadBinary(@Parameter(name = "resource", description = "", required = true, schema = @Schema(description = "")) @Valid @RequestBody Resource resource) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
openapi: 3.0.2
info:
title: Testing reactive raw binary request body
version: 1.0.0
servers:
- url: http://localhost/v1
paths:
/upload:
post:
tags:
- test
operationId: uploadBinary
requestBody:
required: true
content:
application/octet-stream:
schema:
type: string
format: binary
responses:
'200':
description:
OK
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.sngular.multifileplugin.testreactivebinarybodyresource;

import java.util.List;
import java.util.Map;
import java.nio.charset.StandardCharsets;
import javax.validation.Valid;

import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import org.springframework.http.MediaType;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import reactor.core.publisher.Flux;
import springfox.documentation.annotations.ApiIgnore;
import org.springframework.core.io.Resource;


public interface UploadApi {

/**
* POST /upload
* @param resource (required)
* @return OK; (status code 200)
* @throws WebClientResponseException if an error occurs while attempting to invoke the API
*/
@Operation(
operationId = "uploadBinary",
tags = {"test"},
responses = {
@ApiResponse(responseCode = "200", description = "OK")
}
)
@RequestMapping(
method = RequestMethod.POST,
value = "/upload",
produces = {"application/json"}
)
default ResponseEntity<Void> uploadBinary(@Parameter(name = "resource", description = "", required = true, schema = @Schema(description = "")) @Valid @RequestBody Mono<Resource> resource, @ApiIgnore final ServerWebExchange exchange) {
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}

}
6 changes: 3 additions & 3 deletions scs-multiapi-gradle-plugin/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ repositories {
}

group = 'com.sngular'
version = '6.6.3'
version = '6.6.4'

def SCSMultiApiPluginGroupId = group
def SCSMultiApiPluginVersion = version
Expand All @@ -31,7 +31,7 @@ dependencies {
shadow localGroovy()
shadow gradleApi()

implementation 'com.sngular:multiapi-engine:6.6.3'
implementation 'com.sngular:multiapi-engine:6.6.4'
testImplementation 'org.assertj:assertj-core:3.24.2'
testImplementation 'com.puppycrawl.tools:checkstyle:10.12.3'
testImplementation 'org.junit.platform:junit-platform-launcher:1.9.2'
Expand Down Expand Up @@ -100,7 +100,7 @@ testing {

integrationTest(JvmTestSuite) {
dependencies {
implementation 'com.sngular:scs-multiapi-gradle-plugin:6.6.3'
implementation 'com.sngular:scs-multiapi-gradle-plugin:6.6.4'
implementation 'org.assertj:assertj-core:3.24.2'
}

Expand Down
Loading
Loading