Skip to content
Closed
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
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
import org.openapitools.codegen.config.CodegenConfigurator;
import org.openapitools.codegen.config.GlobalSettings;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

Expand Down Expand Up @@ -121,6 +122,10 @@ public class Generate extends OpenApiGeneratorCommand {
description = CodegenConstants.MODEL_NAME_SUFFIX_DESC)
private String modelNameSuffix;

@Option(name = {"--split-response-types"}, title = "split response types",
description = CodegenConstants.SPLIT_RESPONSE_TYPES_DESC)
private Boolean splitResponseTypes;

@Option(
name = {"--instantiation-types"},
title = "instantiation types",
Expand Down Expand Up @@ -371,6 +376,10 @@ public void execute() {
configurator.setModelNameSuffix(modelNameSuffix);
}

if (splitResponseTypes != null) {
GlobalSettings.setProperty(CodegenConstants.SPLIT_RESPONSE_TYPES, splitResponseTypes.toString());
}

if (isNotEmpty(invokerPackage)) {
configurator.setInvokerPackage(invokerPackage);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ class OpenApiGeneratorPlugin : Plugin<Project> {
generateModelDocumentation.set(generate.generateModelDocumentation)
generateApiTests.set(generate.generateApiTests)
generateApiDocumentation.set(generate.generateApiDocumentation)
splitOperation.set(generate.splitOperation)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to make similar change in the CLI as well?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The openapi generator cli module in this project

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will check

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wing328 done. please check

withXml.set(generate.withXml)
configOptions.set(generate.configOptions)
logToStderr.set(generate.logToStderr)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,11 @@ open class OpenApiGeneratorGenerateExtension(project: Project) {
*/
val generateApiDocumentation = project.objects.property<Boolean>()

/**
* Split operations that have multiple response content-types into separate operations
*/
val splitOperation = project.objects.property<Boolean>()

/**
* A special-case setting which configures some generators with XML support. In some cases,
* this forces json OR xml, so the default here is false.
Expand Down Expand Up @@ -350,6 +355,7 @@ open class OpenApiGeneratorGenerateExtension(project: Project) {
generateModelDocumentation.set(true)
generateApiTests.set(true)
generateApiDocumentation.set(true)
splitOperation.set(false)
withXml.set(false)
configOptions.set(mapOf())
validateSpec.set(true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,13 @@ open class GenerateTask : DefaultTask() {
@Input
val generateApiDocumentation = project.objects.property<Boolean>()

/**
* Split operations that have multiple response content-types into separate operations
*/
@Optional
@Input
val splitOperation = project.objects.property<Boolean>()

/**
* A special-case setting which configures some generators with XML support. In some cases,
* this forces json OR xml, so the default here is false.
Expand Down Expand Up @@ -546,6 +553,10 @@ open class GenerateTask : DefaultTask() {
GlobalSettings.setProperty(CodegenConstants.API_TESTS, generateApiTests.get().toString())
}

if (splitOperation.isPresent) {
GlobalSettings.setProperty(CodegenConstants.SPLIT_RESPONSE_TYPES, splitOperation.get().toString())
}

if (withXml.isPresent) {
GlobalSettings.setProperty(CodegenConstants.WITH_XML, withXml.get().toString())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,12 @@ public class CodeGenMojo extends AbstractMojo {
@Parameter(name = "generateApiDocumentation", property = "openapi.generator.maven.plugin.generateApiDocumentation")
private Boolean generateApiDocumentation = true;

/**
* Split operations by response content-type when it has multiple values
*/
@Parameter(name = "splitOperation", property = "openapi.generator.maven.plugin.splitOperation")
private Boolean splitOperation = false;

/**
* Generate the api documentation
*/
Expand Down Expand Up @@ -662,6 +668,7 @@ public void execute() throws MojoExecutionException {
GlobalSettings.setProperty(CodegenConstants.MODEL_DOCS, generateModelDocumentation.toString());
GlobalSettings.setProperty(CodegenConstants.API_TESTS, generateApiTests.toString());
GlobalSettings.setProperty(CodegenConstants.API_DOCS, generateApiDocumentation.toString());
GlobalSettings.setProperty(CodegenConstants.SPLIT_RESPONSE_TYPES, splitOperation.toString());
GlobalSettings.setProperty(CodegenConstants.WITH_XML, withXml.toString());

if (configOptions != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ public class CodegenConstants {
public static final String MODEL_PACKAGE = "modelPackage";
public static final String MODEL_PACKAGE_DESC = "package for generated models";

public static final String SPLIT_RESPONSE_TYPES = "splitResponseTypes";
public static final String SPLIT_RESPONSE_TYPES_DESC = "Split operation which has multiple response types into different operations per response type";

public static final String TEMPLATE_DIR = "templateDir";

public static final String ALLOW_UNICODE_IDENTIFIERS = "allowUnicodeIdentifiers";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3951,12 +3951,20 @@ protected void setNonArrayMapProperty(CodegenProperty property, String type) {
}

/**
* Override with any special handling of response codes
*
* @param responses OAS Operation's responses
* @return default method response or <code>null</code> if not found
*/
protected ApiResponse findMethodResponse(ApiResponses responses) {
public static ApiResponse findMethodResponse(ApiResponses responses) {
String code = findMethodResponseCode(responses);
return code == null ? null : responses.get(code);
}

/**
*
* @param responses OAS Operation's responses
* @return value of code that contains method response
*/
public static String findMethodResponseCode(ApiResponses responses) {
String code = null;
for (String responseCode : responses.keySet()) {
if (responseCode.startsWith("2") || responseCode.equals("default")) {
Expand All @@ -3965,10 +3973,7 @@ protected ApiResponse findMethodResponse(ApiResponses responses) {
}
}
}
if (code == null) {
return null;
}
return responses.get(code);
return code;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses;
import io.swagger.v3.oas.models.security.*;
import io.swagger.v3.oas.models.tags.Tag;
import org.apache.commons.io.FilenameUtils;
Expand Down Expand Up @@ -91,6 +95,7 @@ public class DefaultGenerator implements Generator {
private Boolean generateModelTests = null;
private Boolean generateModelDocumentation = null;
private Boolean generateMetadata = true;
private Boolean splitResponseTypes = false;
private String basePath;
private String basePathWithoutHost;
private String contextPath;
Expand Down Expand Up @@ -229,6 +234,7 @@ void configureGeneratorProperties() {
generateModelDocumentation = GlobalSettings.getProperty(CodegenConstants.MODEL_DOCS) != null ? Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.MODEL_DOCS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.MODEL_DOCS, true);
generateApiTests = GlobalSettings.getProperty(CodegenConstants.API_TESTS) != null ? Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.API_TESTS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.API_TESTS, true);
generateApiDocumentation = GlobalSettings.getProperty(CodegenConstants.API_DOCS) != null ? Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.API_DOCS)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.API_DOCS, true);
splitResponseTypes = GlobalSettings.getProperty(CodegenConstants.SPLIT_RESPONSE_TYPES) != null ? Boolean.valueOf(GlobalSettings.getProperty(CodegenConstants.SPLIT_RESPONSE_TYPES)) : getGeneratorPropertyDefaultSwitch(CodegenConstants.SPLIT_RESPONSE_TYPES, false);

// Additional properties added for tests to exclude references in project related files
config.additionalProperties().put(CodegenConstants.GENERATE_API_TESTS, generateApiTests);
Expand Down Expand Up @@ -1087,17 +1093,17 @@ public Map<String, List<CodegenOperation>> processPaths(Paths paths) {
return ops;
}

private void processOperation(String resourcePath, String httpMethod, Operation operation, Map<String, List<CodegenOperation>> operations, PathItem path) {
if (operation == null) {
private void processOperation(String resourcePath, String httpMethod, Operation inputOperation, Map<String, List<CodegenOperation>> operations, PathItem path) {
if (inputOperation == null) {
return;
}

if (GlobalSettings.getProperty("debugOperations") != null) {
LOGGER.info("processOperation: resourcePath= {}\t;{} {}\n", resourcePath, httpMethod, operation);
LOGGER.info("processOperation: resourcePath= {}\t;{} {}\n", resourcePath, httpMethod, inputOperation);
}

List<Tag> tags = new ArrayList<>();
List<String> tagNames = operation.getTags();
List<String> tagNames = inputOperation.getTags();
List<Tag> swaggerTags = openAPI.getTags();
if (tagNames != null) {
if (swaggerTags == null) {
Expand Down Expand Up @@ -1132,8 +1138,8 @@ private void processOperation(String resourcePath, String httpMethod, Operation
i'm assuming "location" == "in"
*/
Set<String> operationParameters = new HashSet<>();
if (operation.getParameters() != null) {
for (Parameter parameter : operation.getParameters()) {
if (inputOperation.getParameters() != null) {
for (Parameter parameter : inputOperation.getParameters()) {
operationParameters.add(generateParameterId(parameter));
}
}
Expand All @@ -1143,51 +1149,126 @@ private void processOperation(String resourcePath, String httpMethod, Operation
for (Parameter parameter : path.getParameters()) {
//skip propagation if a parameter with the same name is already defined at the operation level
if (!operationParameters.contains(generateParameterId(parameter))) {
operation.addParametersItem(parameter);
inputOperation.addParametersItem(parameter);
}
}
}

final Map<String, SecurityScheme> securitySchemes = openAPI.getComponents() != null ? openAPI.getComponents().getSecuritySchemes() : null;
final List<SecurityRequirement> globalSecurities = openAPI.getSecurity();
for (Tag tag : tags) {
try {
CodegenOperation codegenOperation = config.fromOperation(resourcePath, httpMethod, operation, path.getServers());
codegenOperation.tags = new ArrayList<>(tags);
config.addOperationToGroup(config.sanitizeTag(tag.getName()), resourcePath, operation, codegenOperation, operations);

List<SecurityRequirement> securities = operation.getSecurity();
if (securities != null && securities.isEmpty()) {
continue;
}
for (Operation operation : processMultipleResponseContentTypes(inputOperation)) {
for (Tag tag : tags) {
try {
CodegenOperation codegenOperation = config.fromOperation(resourcePath, httpMethod, operation, path.getServers());
codegenOperation.tags = new ArrayList<>(tags);
config.addOperationToGroup(config.sanitizeTag(tag.getName()), resourcePath, operation, codegenOperation, operations);

Map<String, SecurityScheme> authMethods = getAuthMethods(securities, securitySchemes);
List<SecurityRequirement> securities = operation.getSecurity();
if (securities != null && securities.isEmpty()) {
continue;
}

if (authMethods != null && !authMethods.isEmpty()) {
List<CodegenSecurity> fullAuthMethods = config.fromSecurity(authMethods);
codegenOperation.authMethods = filterAuthMethods(fullAuthMethods, securities);
codegenOperation.hasAuthMethods = true;
} else {
authMethods = getAuthMethods(globalSecurities, securitySchemes);
Map<String, SecurityScheme> authMethods = getAuthMethods(securities, securitySchemes);

if (authMethods != null && !authMethods.isEmpty()) {
List<CodegenSecurity> fullAuthMethods = config.fromSecurity(authMethods);
codegenOperation.authMethods = filterAuthMethods(fullAuthMethods, globalSecurities);
codegenOperation.authMethods = filterAuthMethods(fullAuthMethods, securities);
codegenOperation.hasAuthMethods = true;
} else {
authMethods = getAuthMethods(globalSecurities, securitySchemes);

if (authMethods != null && !authMethods.isEmpty()) {
List<CodegenSecurity> fullAuthMethods = config.fromSecurity(authMethods);
codegenOperation.authMethods = filterAuthMethods(fullAuthMethods, globalSecurities);
codegenOperation.hasAuthMethods = true;
}
}
}

} catch (Exception ex) {
String msg = "Could not process operation:\n" //
} catch (Exception ex) {
String msg = "Could not process operation:\n" //
+ " Tag: " + tag + "\n"//
+ " Operation: " + operation.getOperationId() + "\n" //
+ " Resource: " + httpMethod + " " + resourcePath + "\n"//
+ " Schemas: " + openAPI.getComponents().getSchemas() + "\n" //
+ " Exception: " + ex.getMessage();
throw new RuntimeException(msg, ex);
throw new RuntimeException(msg, ex);
}
}
}
}

private List<Operation> processMultipleResponseContentTypes(Operation operation) {
if (!splitResponseTypes) {
return Collections.singletonList(operation);
}

ApiResponses apiResponses = operation.getResponses();
ApiResponse methodResponse = DefaultCodegen.findMethodResponse(apiResponses);
if (methodResponse == null || methodResponse.getContent().size() < 2) {
return Collections.singletonList(operation);
}

LOGGER.info("Split operation is enabled: operation {} would be split into {} different operations", operation.getOperationId(), methodResponse.getContent().size());
List<Operation> operations = new ArrayList<>();
String methodResponseCodeWithMultipleContentTypes = DefaultCodegen.findMethodResponseCode(apiResponses);
Content methodResponseAllContentTypes = methodResponse.getContent();
for (String contentTypeName : methodResponseAllContentTypes.keySet()) {
MediaType contentType = methodResponseAllContentTypes.get(contentTypeName);

Content contentTypeWithSingeValue = new Content();
contentTypeWithSingeValue.addMediaType(contentTypeName, contentType);
ApiResponse apiResponseWithSingeContentType = copyApiResponse(methodResponse);
apiResponseWithSingeContentType.setContent(contentTypeWithSingeValue);

ApiResponses singleContentTypeApiResponses = copyApiResponses(apiResponses);
singleContentTypeApiResponses.put(methodResponseCodeWithMultipleContentTypes, apiResponseWithSingeContentType);

Operation singleContentTypeOperation = copyOperation(operation);
singleContentTypeOperation.setResponses(singleContentTypeApiResponses);
// correct operation name should be handled by final generator
singleContentTypeOperation.setOperationId(operation.getOperationId() + "-" + contentTypeName);

operations.add(singleContentTypeOperation);
}

return operations;
}

private static Operation copyOperation(Operation operation) {
Operation copy = new Operation();
copy.setTags(operation.getTags());
copy.setSummary(operation.getSummary());
copy.setDescription(operation.getDescription());
copy.setExternalDocs(operation.getExternalDocs());
copy.setOperationId(operation.getOperationId());
copy.setParameters(operation.getParameters());
copy.setRequestBody(operation.getRequestBody());
copy.setResponses(operation.getResponses());
copy.setCallbacks(operation.getCallbacks());
copy.setDeprecated(operation.getDeprecated());
copy.setSecurity(operation.getSecurity());
copy.setServers(operation.getServers());
copy.setExtensions(operation.getExtensions());
return copy;
}

private static ApiResponses copyApiResponses(ApiResponses apiResponses) {
ApiResponses copy = new ApiResponses();
copy.setExtensions(apiResponses.getExtensions());
apiResponses.forEach((code, value) -> copy.put(code, copyApiResponse(value)));
return copy;
}

private static ApiResponse copyApiResponse(ApiResponse apiResponse) {
ApiResponse copy = new ApiResponse();
copy.setDescription(apiResponse.getDescription());
copy.setHeaders(apiResponse.getHeaders());
copy.setContent(apiResponse.getContent());
copy.setLinks(apiResponse.getLinks());
copy.setExtensions(apiResponse.getExtensions());
copy.set$ref(apiResponse.get$ref());

return copy;
}

private static String generateParameterId(Parameter parameter) {
Expand Down
Loading