[DefaultCodegen] Fill CodegenOperation::produces with unique media types (#343)

This commit is contained in:
Arthur Mogliev 2018-05-08 15:46:23 +04:00 committed by Jérémie Bresson
parent c0a2673b26
commit fd3b883e80
8 changed files with 163 additions and 93 deletions

View File

@ -27,8 +27,6 @@ import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.headers.Header; import io.swagger.v3.oas.models.headers.Header;
import io.swagger.v3.oas.models.media.ArraySchema; import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.ComposedSchema; import io.swagger.v3.oas.models.media.ComposedSchema;
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.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema; import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.parameters.CookieParameter; import io.swagger.v3.oas.models.parameters.CookieParameter;
@ -2142,7 +2140,7 @@ public class DefaultCodegen implements CodegenConfig {
op.responses.get(op.responses.size() - 1).hasMore = false; op.responses.get(op.responses.size() - 1).hasMore = false;
if (methodResponse != null) { if (methodResponse != null) {
final Schema responseSchema = getSchemaFromResponse(methodResponse); final Schema responseSchema = ModelUtils.getSchemaFromResponse(methodResponse);
if (responseSchema != null) { if (responseSchema != null) {
CodegenProperty cm = fromProperty("response", responseSchema); CodegenProperty cm = fromProperty("response", responseSchema);
@ -2386,7 +2384,7 @@ public class DefaultCodegen implements CodegenConfig {
} else { } else {
r.code = responseCode; r.code = responseCode;
} }
final Schema responseSchema = getSchemaFromResponse(response); final Schema responseSchema = ModelUtils.getSchemaFromResponse(response);
r.schema = responseSchema; r.schema = responseSchema;
r.message = escapeText(response.getDescription()); r.message = escapeText(response.getDescription());
// TODO need to revise and test examples in responses // TODO need to revise and test examples in responses
@ -3819,22 +3817,6 @@ public class DefaultCodegen implements CodegenConfig {
return new ArrayList<>(requestBody.getContent().keySet()).get(0); return new ArrayList<>(requestBody.getContent().keySet()).get(0);
} }
protected Schema getSchemaFromBody(RequestBody requestBody) {
return getSchemaFromContent(requestBody.getContent());
}
protected Schema getSchemaFromResponse(ApiResponse response) {
return getSchemaFromContent(response.getContent());
}
private Schema getSchemaFromContent(Content content) {
if (content == null || content.isEmpty()) {
return null;
}
MediaType mediaType = content.values().iterator().next();
return mediaType.getSchema();
}
protected Parameter getParameterFromRef(String ref, OpenAPI openAPI) { protected Parameter getParameterFromRef(String ref, OpenAPI openAPI) {
String parameterName = ref.substring(ref.lastIndexOf('/') + 1); String parameterName = ref.substring(ref.lastIndexOf('/') + 1);
Map<String, Parameter> parameterMap = openAPI.getComponents().getParameters(); Map<String, Parameter> parameterMap = openAPI.getComponents().getParameters();
@ -3944,7 +3926,7 @@ public class DefaultCodegen implements CodegenConfig {
return false; return false;
} }
Schema schema = getSchemaFromBody(requestBody); Schema schema = ModelUtils.getSchemaFromRequestBody(requestBody);
return ModelUtils.getReferencedSchema(openAPI, schema) != null; return ModelUtils.getReferencedSchema(openAPI, schema) != null;
} }
@ -3959,25 +3941,30 @@ public class DefaultCodegen implements CodegenConfig {
codegenOperation.produces = new ArrayList<>(); codegenOperation.produces = new ArrayList<>();
} }
Set<String> existingMediaTypes = new HashSet<>();
for (Map<String, String> mediaType: codegenOperation.produces) {
existingMediaTypes.add(mediaType.get("mediaType"));
}
int count = 0; int count = 0;
for (String key : produces) { for (String key : produces) {
Map<String, String> mediaType = new HashMap<String, String>(); // escape quotation to avoid code injection, "*/*" is a special case, do nothing
// escape quotation to avoid code injection String encodedKey = "*/*".equals(key)? key : escapeText(escapeQuotationMark(key));
if ("*/*".equals(key)) { // "*/*" is a special case, do nothing //Only unique media types should be added to "produces"
mediaType.put("mediaType", key); if (!existingMediaTypes.contains(encodedKey)) {
} else { Map<String, String> mediaType = new HashMap<String, String>();
mediaType.put("mediaType", escapeText(escapeQuotationMark(key))); mediaType.put("mediaType", encodedKey);
}
count += 1; count += 1;
if (count < produces.size()) { if (count < produces.size()) {
mediaType.put("hasMore", "true"); mediaType.put("hasMore", "true");
} else { } else {
mediaType.put("hasMore", null); mediaType.put("hasMore", null);
} }
codegenOperation.produces.add(mediaType); codegenOperation.produces.add(mediaType);
codegenOperation.hasProduces = Boolean.TRUE; codegenOperation.hasProduces = Boolean.TRUE;
}
} }
} }
@ -4075,7 +4062,7 @@ public class DefaultCodegen implements CodegenConfig {
public List<CodegenParameter> fromRequestBodyToFormParameters(RequestBody body, Map<String, Schema> schemas, Set<String> imports) { public List<CodegenParameter> fromRequestBodyToFormParameters(RequestBody body, Map<String, Schema> schemas, Set<String> imports) {
List<CodegenParameter> parameters = new ArrayList<CodegenParameter>(); List<CodegenParameter> parameters = new ArrayList<CodegenParameter>();
LOGGER.debug("debugging fromRequestBodyToFormParameters= " + body); LOGGER.debug("debugging fromRequestBodyToFormParameters= " + body);
Schema schema = getSchemaFromBody(body); Schema schema = ModelUtils.getSchemaFromRequestBody(body);
if (StringUtils.isNotBlank(schema.get$ref())) { if (StringUtils.isNotBlank(schema.get$ref())) {
schema = schemas.get(getSimpleRef(schema.get$ref())); schema = schemas.get(getSimpleRef(schema.get$ref()));
} }
@ -4229,7 +4216,7 @@ public class DefaultCodegen implements CodegenConfig {
String name = null; String name = null;
LOGGER.debug("Request body = " + body); LOGGER.debug("Request body = " + body);
Schema schema = getSchemaFromBody(body); Schema schema = ModelUtils.getSchemaFromRequestBody(body);
if (StringUtils.isNotBlank(schema.get$ref())) { if (StringUtils.isNotBlank(schema.get$ref())) {
name = getSimpleRef(schema.get$ref()); name = getSimpleRef(schema.get$ref());
schema = schemas.get(name); schema = schemas.get(name);

View File

@ -17,22 +17,31 @@
package org.openapitools.codegen.languages; package org.openapitools.codegen.languages;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.samskivert.mustache.Escapers; import com.samskivert.mustache.Escapers;
import com.samskivert.mustache.Mustache; import com.samskivert.mustache.Mustache;
import io.swagger.v3.oas.models.responses.ApiResponse;
import org.openapitools.codegen.*;
import org.openapitools.codegen.utils.ModelUtils;
import io.swagger.v3.oas.models.OpenAPI; import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation; import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.media.*; import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.parameters.*; import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.core.util.Json;
import org.openapitools.codegen.CodegenConfig;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.CodegenOperation;
import org.openapitools.codegen.CodegenParameter;
import org.openapitools.codegen.CodegenProperty;
import org.openapitools.codegen.CodegenSecurity;
import org.openapitools.codegen.DefaultCodegen;
import org.openapitools.codegen.utils.ModelUtils;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
abstract public class AbstractAdaCodegen extends DefaultCodegen implements CodegenConfig { abstract public class AbstractAdaCodegen extends DefaultCodegen implements CodegenConfig {
protected String packageName = "defaultPackage"; protected String packageName = "defaultPackage";
@ -397,8 +406,8 @@ abstract public class AbstractAdaCodegen extends DefaultCodegen implements Codeg
if (operation.getResponses() != null && !operation.getResponses().isEmpty()) { if (operation.getResponses() != null && !operation.getResponses().isEmpty()) {
ApiResponse methodResponse = findMethodResponse(operation.getResponses()); ApiResponse methodResponse = findMethodResponse(operation.getResponses());
if (methodResponse != null && getSchemaFromResponse(methodResponse) != null) { if (methodResponse != null && ModelUtils.getSchemaFromResponse(methodResponse) != null) {
CodegenProperty cm = fromProperty("response", getSchemaFromResponse(methodResponse)); CodegenProperty cm = fromProperty("response", ModelUtils.getSchemaFromResponse(methodResponse));
op.vendorExtensions.put("x-codegen-response", cm); op.vendorExtensions.put("x-codegen-response", cm);
if ("HttpContent".equals(cm.datatype)) { if ("HttpContent".equals(cm.datatype)) {
op.vendorExtensions.put("x-codegen-response-ishttpcontent", true); op.vendorExtensions.put("x-codegen-response-ishttpcontent", true);

View File

@ -17,33 +17,37 @@
package org.openapitools.codegen.languages; package org.openapitools.codegen.languages;
import com.sun.org.apache.xpath.internal.operations.Mod; import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.BooleanSchema;
import io.swagger.v3.oas.models.media.ByteArraySchema;
import io.swagger.v3.oas.models.media.EmailSchema;
import io.swagger.v3.oas.models.media.FileSchema;
import io.swagger.v3.oas.models.media.PasswordSchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.responses.ApiResponse; import io.swagger.v3.oas.models.responses.ApiResponse;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.CliOption; import org.openapitools.codegen.CliOption;
import org.openapitools.codegen.CodegenConfig;
import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.CodegenModel; import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.CodegenOperation; import org.openapitools.codegen.CodegenOperation;
import org.openapitools.codegen.CodegenParameter; import org.openapitools.codegen.CodegenParameter;
import org.openapitools.codegen.CodegenProperty; import org.openapitools.codegen.CodegenProperty;
import org.openapitools.codegen.CodegenType; import org.openapitools.codegen.CodegenType;
import org.openapitools.codegen.DefaultCodegen;
import org.openapitools.codegen.SupportingFile; import org.openapitools.codegen.SupportingFile;
import org.openapitools.codegen.utils.ModelUtils; import org.openapitools.codegen.utils.ModelUtils;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.media.*;
import io.swagger.v3.oas.models.info.*;
import io.swagger.v3.oas.models.parameters.*;
import io.swagger.v3.parser.util.SchemaTypeUtil;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.File; import java.io.File;
import java.util.*; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
public class ApexClientCodegen extends AbstractJavaCodegen { public class ApexClientCodegen extends AbstractJavaCodegen {
@ -434,7 +438,7 @@ public class ApexClientCodegen extends AbstractJavaCodegen {
if (op.getHasExamples()) { if (op.getHasExamples()) {
// prepare examples for Apex test classes // prepare examples for Apex test classes
ApiResponse responseProperty = findMethodResponse(operation.getResponses()); ApiResponse responseProperty = findMethodResponse(operation.getResponses());
String deserializedExample = toExampleValue(getSchemaFromResponse(responseProperty)); String deserializedExample = toExampleValue(ModelUtils.getSchemaFromResponse(responseProperty));
for (Map<String, String> example : op.examples) { for (Map<String, String> example : op.examples) {
example.put("example", escapeText(example.get("example"))); example.put("example", escapeText(example.get("example")));
example.put("deserializedExample", deserializedExample); example.put("deserializedExample", deserializedExample);

View File

@ -17,6 +17,22 @@
package org.openapitools.codegen.languages; package org.openapitools.codegen.languages;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.responses.ApiResponse;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.CodegenOperation;
import org.openapitools.codegen.CodegenParameter;
import org.openapitools.codegen.CodegenProperty;
import org.openapitools.codegen.CodegenType;
import org.openapitools.codegen.DefaultCodegen;
import org.openapitools.codegen.SupportingFile;
import org.openapitools.codegen.utils.ModelUtils;
import java.io.File; import java.io.File;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
@ -25,17 +41,6 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import io.swagger.v3.parser.util.SchemaTypeUtil;
import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.*;
import org.openapitools.codegen.utils.ModelUtils;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.*;
import io.swagger.v3.oas.models.media.*;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.parameters.*;
import io.swagger.v3.core.util.Yaml;
public class CppPistacheServerCodegen extends AbstractCppCodegen { public class CppPistacheServerCodegen extends AbstractCppCodegen {
protected String implFolder = "impl"; protected String implFolder = "impl";
@ -161,7 +166,7 @@ public class CppPistacheServerCodegen extends AbstractCppCodegen {
ApiResponse apiResponse = findMethodResponse(operation.getResponses()); ApiResponse apiResponse = findMethodResponse(operation.getResponses());
if (apiResponse != null) { if (apiResponse != null) {
Schema response = getSchemaFromResponse(apiResponse); Schema response = ModelUtils.getSchemaFromResponse(apiResponse);
if (response != null) { if (response != null) {
CodegenProperty cm = fromProperty("response", response); CodegenProperty cm = fromProperty("response", response);
op.vendorExtensions.put("x-codegen-response", cm); op.vendorExtensions.put("x-codegen-response", cm);

View File

@ -19,18 +19,16 @@ package org.openapitools.codegen.languages;
import static com.google.common.base.Strings.isNullOrEmpty; import static com.google.common.base.Strings.isNullOrEmpty;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation;
import io.swagger.v3.oas.models.media.ArraySchema;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.responses.ApiResponse;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.openapitools.codegen.CliOption;
import org.openapitools.codegen.CodegenConstants; import org.openapitools.codegen.CodegenConstants;
import org.openapitools.codegen.CodegenModel; import org.openapitools.codegen.CodegenModel;
import org.openapitools.codegen.CodegenOperation; import org.openapitools.codegen.CodegenOperation;
@ -39,11 +37,13 @@ import org.openapitools.codegen.CodegenProperty;
import org.openapitools.codegen.CodegenType; import org.openapitools.codegen.CodegenType;
import org.openapitools.codegen.SupportingFile; import org.openapitools.codegen.SupportingFile;
import org.openapitools.codegen.utils.ModelUtils; import org.openapitools.codegen.utils.ModelUtils;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.Operation; import java.util.Arrays;
import io.swagger.v3.oas.models.media.*; import java.util.Collection;
import io.swagger.v3.oas.models.responses.ApiResponse; import java.util.HashMap;
import io.swagger.v3.parser.util.SchemaTypeUtil; import java.util.HashSet;
import java.util.Map;
import java.util.Set;
public class CppRestClientCodegen extends AbstractCppCodegen { public class CppRestClientCodegen extends AbstractCppCodegen {
@ -238,7 +238,7 @@ public class CppRestClientCodegen extends AbstractCppCodegen {
ApiResponse methodResponse = findMethodResponse(operation.getResponses()); ApiResponse methodResponse = findMethodResponse(operation.getResponses());
if (methodResponse != null) { if (methodResponse != null) {
Schema response = getSchemaFromResponse(methodResponse); Schema response = ModelUtils.getSchemaFromResponse(methodResponse);
if (response != null) { if (response != null) {
CodegenProperty cm = fromProperty("response", response); CodegenProperty cm = fromProperty("response", response);
op.vendorExtensions.put("x-codegen-response", cm); op.vendorExtensions.put("x-codegen-response", cm);

View File

@ -25,6 +25,7 @@ import io.swagger.v3.oas.models.media.BinarySchema;
import io.swagger.v3.oas.models.media.BooleanSchema; import io.swagger.v3.oas.models.media.BooleanSchema;
import io.swagger.v3.oas.models.media.ByteArraySchema; import io.swagger.v3.oas.models.media.ByteArraySchema;
import io.swagger.v3.oas.models.media.ComposedSchema; import io.swagger.v3.oas.models.media.ComposedSchema;
import io.swagger.v3.oas.models.media.Content;
import io.swagger.v3.oas.models.media.DateSchema; import io.swagger.v3.oas.models.media.DateSchema;
import io.swagger.v3.oas.models.media.DateTimeSchema; import io.swagger.v3.oas.models.media.DateTimeSchema;
import io.swagger.v3.oas.models.media.EmailSchema; import io.swagger.v3.oas.models.media.EmailSchema;
@ -48,6 +49,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -365,11 +367,14 @@ public class ModelUtils {
return null; return null;
} }
if (openAPI != null && openAPI.getComponents() != null && openAPI.getComponents().getSchemas() != null) { return getSchemas(openAPI).get(name);
return openAPI.getComponents().getSchemas().get(name); }
}
return null; public static Map<String, Schema> getSchemas(OpenAPI openAPI) {
if (openAPI != null && openAPI.getComponents() != null && openAPI.getComponents().getSchemas() != null) {
return openAPI.getComponents().getSchemas();
}
return Collections.emptyMap();
} }
/** /**
@ -421,4 +426,21 @@ public class ModelUtils {
} }
return null; return null;
} }
public static Schema getSchemaFromRequestBody(RequestBody requestBody) {
return getSchemaFromContent(requestBody.getContent());
}
public static Schema getSchemaFromResponse(ApiResponse response) {
return getSchemaFromContent(response.getContent());
}
private static Schema getSchemaFromContent(Content content) {
if (content == null || content.isEmpty()) {
return null;
}
MediaType mediaType = content.values().iterator().next();
return mediaType.getSchema();
}
} }

View File

@ -27,6 +27,8 @@ import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.responses.ApiResponses; import io.swagger.v3.oas.models.responses.ApiResponses;
import io.swagger.v3.parser.core.models.ParseOptions; import io.swagger.v3.parser.core.models.ParseOptions;
import org.openapitools.codegen.utils.ModelUtils;
import org.testng.Assert; import org.testng.Assert;
import org.testng.annotations.Test; import org.testng.annotations.Test;
@ -183,9 +185,21 @@ public class DefaultCodegenTest {
final OpenAPI openAPI = new OpenAPIParser().readLocation("src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml", null, new ParseOptions()).getOpenAPI(); final OpenAPI openAPI = new OpenAPIParser().readLocation("src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml", null, new ParseOptions()).getOpenAPI();
final DefaultCodegen codegen = new DefaultCodegen(); final DefaultCodegen codegen = new DefaultCodegen();
Schema requestBodySchema = codegen.getSchemaFromBody(openAPI.getPaths().get("/fake").getGet().getRequestBody()); Schema requestBodySchema = ModelUtils.getSchemaFromRequestBody(openAPI.getPaths().get("/fake").getGet().getRequestBody());
CodegenParameter codegenParameter = codegen.fromFormProperty("enum_form_string", (Schema) requestBodySchema.getProperties().get("enum_form_string"), new HashSet<String>()); CodegenParameter codegenParameter = codegen.fromFormProperty("enum_form_string", (Schema) requestBodySchema.getProperties().get("enum_form_string"), new HashSet<String>());
Assert.assertEquals(codegenParameter.defaultValue, "-efg"); Assert.assertEquals(codegenParameter.defaultValue, "-efg");
} }
@Test
public void testEnsureNoDuplicateProduces() {
final OpenAPI openAPI = new OpenAPIParser().readLocation("src/test/resources/3_0/two-responses.yaml", null, new ParseOptions()).getOpenAPI();
final DefaultCodegen codegen = new DefaultCodegen();
Operation operation = openAPI.getPaths().get("/test").getGet();
CodegenOperation co = codegen.fromOperation("/test", "get", operation, ModelUtils.getSchemas(openAPI), openAPI);
Assert.assertEquals(co.produces.size(), 1);
Assert.assertEquals(co.produces.get(0).get("mediaType"), "application/json");
}
} }

View File

@ -0,0 +1,29 @@
openapi: 3.0.0
info:
title: Test
version: 1.0.0
servers:
- url: 'http://test/'
paths:
/test:
get:
summary: Test
responses:
'200':
description: Success
content:
application/json:
schema:
type: object
properties:
test:
type: string
'422':
description: Validation failed
content:
application/json:
schema:
type: object
properties:
test:
type: string