mirror of
https://github.com/OpenAPITools/openapi-generator.git
synced 2025-12-10 10:56:09 +00:00
[haskell-http-client] bug fixes; path & newtype generation issues (#6638)
* fix path generation/param-substitution issues * fix newtype de-duplication issues * refactoring only * correct version in comments * prevent duplicate MimeTypes * sort parameter newtypes
This commit is contained in:
@@ -26,7 +26,6 @@ import java.io.File;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.apache.commons.lang3.text.WordUtils;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
|
||||
@@ -34,7 +33,6 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
|
||||
// source folder where to write the files
|
||||
protected String sourceFolder = "src";
|
||||
protected String apiVersion = "0.0.1";
|
||||
|
||||
protected String artifactId = "swagger-haskell-http-client";
|
||||
protected String artifactVersion = "1.0.0";
|
||||
@@ -67,7 +65,6 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
|
||||
protected Map<String, CodegenParameter> uniqueParamsByName = new HashMap<String, CodegenParameter>();
|
||||
protected Set<String> typeNames = new HashSet<String>();
|
||||
protected Map<String, Map<String,String>> allMimeTypes = new HashMap<String, Map<String,String>>();
|
||||
protected Map<String, String> knownMimeDataTypes = new HashMap<String, String>();
|
||||
protected Map<String, Set<String>> modelMimeTypes = new HashMap<String, Set<String>>();
|
||||
protected String lastTag = "";
|
||||
@@ -124,7 +121,6 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
)
|
||||
);
|
||||
|
||||
additionalProperties.put("apiVersion", apiVersion);
|
||||
additionalProperties.put("artifactId", artifactId);
|
||||
additionalProperties.put("artifactVersion", artifactVersion);
|
||||
|
||||
@@ -398,13 +394,7 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
additionalProperties.put("configType", apiName + "Config");
|
||||
additionalProperties.put("swaggerVersion", swagger.getSwagger());
|
||||
|
||||
//copy input swagger to output folder
|
||||
try {
|
||||
String swaggerJson = Json.pretty(swagger);
|
||||
FileUtils.writeStringToFile(new File(outputFolder + File.separator + "swagger.json"), swaggerJson);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e.getMessage(), e.getCause());
|
||||
}
|
||||
WriteInputSwaggerToFile(swagger);
|
||||
|
||||
super.preprocessSwagger(swagger);
|
||||
}
|
||||
@@ -461,7 +451,6 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CodegenOperation fromOperation(String resourcePath, String httpMethod, Operation operation, Map<String, Model> definitions, Swagger swagger) {
|
||||
CodegenOperation op = super.fromOperation(resourcePath, httpMethod, operation, definitions, swagger);
|
||||
@@ -486,100 +475,20 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
if(!param.required) {
|
||||
op.vendorExtensions.put("x-hasOptionalParams", true);
|
||||
}
|
||||
if (typeMapping.containsKey(param.dataType) || param.isPrimitiveType || param.isListContainer || param.isMapContainer || param.isFile) {
|
||||
String paramNameType = toTypeName("Param", param.paramName);
|
||||
|
||||
if (uniqueParamsByName.containsKey(paramNameType)) {
|
||||
CodegenParameter lastParam = this.uniqueParamsByName.get(paramNameType);
|
||||
if (lastParam.dataType != null && lastParam.dataType.equals(param.dataType)) {
|
||||
param.vendorExtensions.put("x-duplicate", true);
|
||||
} else {
|
||||
paramNameType = paramNameType + param.dataType;
|
||||
while (typeNames.contains(paramNameType)) {
|
||||
paramNameType = generateNextName(paramNameType);
|
||||
}
|
||||
uniqueParamsByName.put(paramNameType, param);
|
||||
}
|
||||
} else {
|
||||
while (typeNames.contains(paramNameType)) {
|
||||
paramNameType = generateNextName(paramNameType);
|
||||
}
|
||||
uniqueParamsByName.put(paramNameType, param);
|
||||
}
|
||||
|
||||
param.vendorExtensions.put("x-paramNameType", paramNameType);
|
||||
typeNames.add(paramNameType);
|
||||
}
|
||||
}
|
||||
if (op.getHasPathParams()) {
|
||||
String remainingPath = op.path;
|
||||
for (CodegenParameter param : op.pathParams) {
|
||||
String[] pieces = remainingPath.split("\\{" + param.baseName + "\\}");
|
||||
if (pieces.length == 0)
|
||||
throw new RuntimeException("paramName {" + param.baseName + "} not in path " + op.path);
|
||||
if (pieces.length > 2)
|
||||
throw new RuntimeException("paramName {" + param.baseName + "} found multiple times in path " + op.path);
|
||||
if (pieces.length == 2) {
|
||||
param.vendorExtensions.put("x-pathPrefix", pieces[0]);
|
||||
remainingPath = pieces[1];
|
||||
} else {
|
||||
if (remainingPath.startsWith("{" + param.baseName + "}")) {
|
||||
remainingPath = pieces[0];
|
||||
} else {
|
||||
param.vendorExtensions.put("x-pathPrefix", pieces[0]);
|
||||
remainingPath = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
op.vendorExtensions.put("x-hasPathParams", true);
|
||||
if (remainingPath.length() > 0) {
|
||||
op.vendorExtensions.put("x-pathSuffix", remainingPath);
|
||||
}
|
||||
} else {
|
||||
op.vendorExtensions.put("x-hasPathParams", false);
|
||||
op.vendorExtensions.put("x-pathSuffix", op.path);
|
||||
}
|
||||
for (CodegenParameter param : op.queryParams) {
|
||||
}
|
||||
for (CodegenParameter param : op.headerParams) {
|
||||
}
|
||||
for (CodegenParameter param : op.bodyParams) {
|
||||
}
|
||||
for (CodegenParameter param : op.formParams) {
|
||||
deduplicateParameter(param);
|
||||
}
|
||||
|
||||
if (op.hasConsumes) {
|
||||
for (Map<String, String> m : op.consumes) {
|
||||
processMediaType(op,m);
|
||||
}
|
||||
if (isMultipartOperation(op.consumes)) {
|
||||
op.isMultipart = Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
if (op.hasProduces) {
|
||||
for (Map<String, String> m : op.produces) {
|
||||
processMediaType(op,m);
|
||||
}
|
||||
}
|
||||
processPathExpr(op);
|
||||
|
||||
String returnType = op.returnType;
|
||||
if (returnType == null || returnType.equals("null")) {
|
||||
if(op.hasProduces) {
|
||||
returnType = "res";
|
||||
op.vendorExtensions.put("x-hasUnknownReturn", true);
|
||||
} else {
|
||||
returnType = "NoContent";
|
||||
}
|
||||
}
|
||||
if (returnType.indexOf(" ") >= 0) {
|
||||
returnType = "(" + returnType + ")";
|
||||
}
|
||||
op.vendorExtensions.put("x-returnType", returnType);
|
||||
processProducesConsumes(op);
|
||||
|
||||
processReturnType(op);
|
||||
|
||||
return op;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<CodegenSecurity> fromSecurity(Map<String, SecuritySchemeDefinition> schemes) {
|
||||
List<CodegenSecurity> secs = super.fromSecurity(schemes);
|
||||
for(CodegenSecurity sec : secs) {
|
||||
@@ -603,8 +512,26 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
}
|
||||
|
||||
additionalProperties.put("x-hasUnknownMimeTypes", !unknownMimeTypes.isEmpty());
|
||||
|
||||
Collections.sort(unknownMimeTypes, new Comparator<Map<String, String>>() {
|
||||
@Override
|
||||
public int compare(Map<String, String> o1, Map<String, String> o2) {
|
||||
return o1.get(MEDIA_TYPE).compareTo(o2.get(MEDIA_TYPE));
|
||||
}
|
||||
});
|
||||
additionalProperties.put("x-unknownMimeTypes", unknownMimeTypes);
|
||||
additionalProperties.put("x-allUniqueParams", uniqueParamsByName.values());
|
||||
|
||||
ArrayList<CodegenParameter> params = new ArrayList<>(uniqueParamsByName.values());
|
||||
Collections.sort(params, new Comparator<CodegenParameter>() {
|
||||
@Override
|
||||
public int compare(CodegenParameter o1, CodegenParameter o2) {
|
||||
return
|
||||
((String) o1.vendorExtensions.get("x-paramNameType"))
|
||||
.compareTo(
|
||||
(String) o2.vendorExtensions.get("x-paramNameType"));
|
||||
}
|
||||
});
|
||||
additionalProperties.put("x-allUniqueParams", params);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -687,10 +614,114 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
return dataType != null && dataType.equals("B.ByteString");
|
||||
}
|
||||
|
||||
//copy input swagger to output folder
|
||||
private void WriteInputSwaggerToFile(Swagger swagger) {
|
||||
try {
|
||||
String swaggerJson = Json.pretty(swagger);
|
||||
FileUtils.writeStringToFile(new File(outputFolder + File.separator + "swagger.json"), swaggerJson);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e.getMessage(), e.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
private void processReturnType(CodegenOperation op) {
|
||||
String returnType = op.returnType;
|
||||
if (returnType == null || returnType.equals("null")) {
|
||||
if(op.hasProduces) {
|
||||
returnType = "res";
|
||||
op.vendorExtensions.put("x-hasUnknownReturn", true);
|
||||
} else {
|
||||
returnType = "NoContent";
|
||||
}
|
||||
}
|
||||
if (returnType.indexOf(" ") >= 0) {
|
||||
returnType = "(" + returnType + ")";
|
||||
}
|
||||
op.vendorExtensions.put("x-returnType", returnType);
|
||||
}
|
||||
|
||||
private void processProducesConsumes(CodegenOperation op) {
|
||||
if (op.hasConsumes) {
|
||||
for (Map<String, String> m : op.consumes) {
|
||||
processMediaType(op,m);
|
||||
}
|
||||
if (isMultipartOperation(op.consumes)) {
|
||||
op.isMultipart = Boolean.TRUE;
|
||||
}
|
||||
}
|
||||
if (op.hasProduces) {
|
||||
for (Map<String, String> m : op.produces) {
|
||||
processMediaType(op,m);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void deduplicateParameter(CodegenParameter param) {
|
||||
if (typeMapping.containsKey(param.dataType) || param.isPrimitiveType || param.isListContainer || param.isMapContainer || param.isFile) {
|
||||
|
||||
String paramNameType = toTypeName("Param", param.paramName);
|
||||
|
||||
if (uniqueParamsByName.containsKey(paramNameType)) {
|
||||
if(!checkParamForDuplicates(paramNameType, param)) {
|
||||
paramNameType = paramNameType + param.dataType;
|
||||
if(!checkParamForDuplicates(paramNameType, param)) {
|
||||
while (typeNames.contains(paramNameType)) {
|
||||
paramNameType = generateNextName(paramNameType);
|
||||
if(checkParamForDuplicates(paramNameType, param)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uniqueParamsByName.put(paramNameType, param);
|
||||
}
|
||||
} else {
|
||||
|
||||
while (typeNames.contains(paramNameType)) {
|
||||
paramNameType = generateNextName(paramNameType);
|
||||
if(checkParamForDuplicates(paramNameType, param)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uniqueParamsByName.put(paramNameType, param);
|
||||
}
|
||||
|
||||
param.vendorExtensions.put("x-paramNameType", paramNameType);
|
||||
typeNames.add(paramNameType);
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean checkParamForDuplicates(String paramNameType, CodegenParameter param) {
|
||||
CodegenParameter lastParam = this.uniqueParamsByName.get(paramNameType);
|
||||
if (lastParam != null && lastParam.dataType != null && lastParam.dataType.equals(param.dataType)) {
|
||||
param.vendorExtensions.put("x-duplicate", true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// build the parameterized path segments, according to pathParams
|
||||
private void processPathExpr(CodegenOperation op) {
|
||||
String xPath = "[\"" + escapeText(op.path) + "\"]";
|
||||
if (op.getHasPathParams()) {
|
||||
for (CodegenParameter param : op.pathParams) {
|
||||
xPath = xPath.replaceAll("\\{" + param.baseName + "\\}", "\",toPath " + param.paramName + ",\"");
|
||||
}
|
||||
xPath = xPath.replaceAll(",\"\",", ",");
|
||||
xPath = xPath.replaceAll("\"\",", ",");
|
||||
xPath = xPath.replaceAll(",\"\"", ",");
|
||||
xPath = xPath.replaceAll("^\\[,", "[");
|
||||
xPath = xPath.replaceAll(",\\]$", "]");
|
||||
}
|
||||
op.vendorExtensions.put("x-path", xPath);
|
||||
}
|
||||
|
||||
|
||||
private void processMediaType(CodegenOperation op, Map<String, String> m) {
|
||||
String mediaType = m.get(MEDIA_TYPE);
|
||||
|
||||
if(StringUtils.isBlank(mediaType)) return;
|
||||
if (StringUtils.isBlank(mediaType)) return;
|
||||
|
||||
String mimeType = getMimeDataType(mediaType);
|
||||
typeNames.add(mimeType);
|
||||
@@ -699,8 +730,7 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
m.put(MEDIA_IS_JSON, "true");
|
||||
}
|
||||
|
||||
allMimeTypes.put(mediaType, m);
|
||||
if(!knownMimeDataTypes.containsKey(mediaType) && !unknownMimeTypes.contains(m)) {
|
||||
if (!knownMimeDataTypes.containsValue(mimeType) && !unknownMimeTypesContainsType(mimeType)) {
|
||||
unknownMimeTypes.add(m);
|
||||
}
|
||||
for (CodegenParameter param : op.allParams) {
|
||||
@@ -712,6 +742,17 @@ public class HaskellHttpClientCodegen extends DefaultCodegen implements CodegenC
|
||||
}
|
||||
}
|
||||
|
||||
private Boolean unknownMimeTypesContainsType(String mimeType) {
|
||||
for(Map<String,String> m : unknownMimeTypes) {
|
||||
String mimeType0 = m.get(MEDIA_DATA_TYPE);
|
||||
if(mimeType0 != null && mimeType0.equals(mimeType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public String firstLetterToUpper(String word) {
|
||||
if (word.length() == 0) {
|
||||
return word;
|
||||
|
||||
@@ -88,9 +88,14 @@ import qualified Prelude as P
|
||||
-> {{/vendorExtensions.x-hasBodyOrFormParam}}{{#allParams}}{{#required}}{{#vendorExtensions.x-paramNameType}}{{{.}}}{{/vendorExtensions.x-paramNameType}}{{^vendorExtensions.x-paramNameType}}{{{dataType}}}{{/vendorExtensions.x-paramNameType}} -- ^ "{{{paramName}}}"{{#description}} - {{/description}} {{{description}}}
|
||||
-> {{/required}}{{/allParams}}{{requestType}} {{{vendorExtensions.x-operationType}}} {{#vendorExtensions.x-hasBodyOrFormParam}}contentType{{/vendorExtensions.x-hasBodyOrFormParam}}{{^vendorExtensions.x-hasBodyOrFormParam}}MimeNoContent{{/vendorExtensions.x-hasBodyOrFormParam}} {{vendorExtensions.x-returnType}}
|
||||
{{operationId}} {{#vendorExtensions.x-hasBodyOrFormParam}}_ {{/vendorExtensions.x-hasBodyOrFormParam}}{{#allParams}}{{#required}}{{#isBodyParam}}{{{paramName}}}{{/isBodyParam}}{{^isBodyParam}}({{{vendorExtensions.x-paramNameType}}} {{{paramName}}}){{/isBodyParam}} {{/required}}{{/allParams}}=
|
||||
_mkRequest "{{httpMethod}}" [{{#pathParams}}{{#vendorExtensions.x-pathPrefix}}"{{.}}",{{/vendorExtensions.x-pathPrefix}}toPath {{{paramName}}}{{#hasMore}},{{/hasMore}}{{/pathParams}}{{#vendorExtensions.x-pathSuffix}}{{#vendorExtensions.x-hasPathParams}},{{/vendorExtensions.x-hasPathParams}}"{{.}}"{{/vendorExtensions.x-pathSuffix}}]{{#allParams}}{{#required}}
|
||||
{{#isHeaderParam}}`setHeader` {{>_headerColl}} ("{{{baseName}}}", {{{paramName}}}){{/isHeaderParam}}{{#isQueryParam}}`setQuery` {{>_queryColl}} ("{{{baseName}}}", Just {{{paramName}}}){{/isQueryParam}}{{#isFormParam}}{{#isFile}}`_addMultiFormPart` NH.partFileSource "{{{baseName}}}" {{{paramName}}}{{/isFile}}{{^isFile}}{{#isMultipart}}`_addMultiFormPart` NH.partLBS "{{{baseName}}}" (mimeRender' MimeMultipartFormData {{{paramName}}}){{/isMultipart}}{{^isMultipart}}`addForm` {{>_formColl}} ("{{{baseName}}}", {{{paramName}}}){{/isMultipart}}{{/isFile}}{{/isFormParam}}{{#isBodyParam}}`setBodyParam` {{{paramName}}}{{/isBodyParam}}{{/required}}{{/allParams}}{{#authMethods}}
|
||||
`_hasAuthType` (P.Proxy :: P.Proxy {{name}}){{/authMethods}}{{#isDeprecated}}
|
||||
_mkRequest "{{httpMethod}}" {{{vendorExtensions.x-path}}}{{#authMethods}}
|
||||
`_hasAuthType` (P.Proxy :: P.Proxy {{name}}){{/authMethods}}{{#allParams}}{{#required}}{{#isHeaderParam}}
|
||||
`setHeader` {{>_headerColl}} ("{{{baseName}}}", {{{paramName}}}){{/isHeaderParam}}{{#isQueryParam}}
|
||||
`setQuery` {{>_queryColl}} ("{{{baseName}}}", Just {{{paramName}}}){{/isQueryParam}}{{#isFormParam}}{{#isFile}}
|
||||
`_addMultiFormPart` NH.partFileSource "{{{baseName}}}" {{{paramName}}}{{/isFile}}{{^isFile}}{{#isMultipart}}
|
||||
`_addMultiFormPart` NH.partLBS "{{{baseName}}}" (mimeRender' MimeMultipartFormData {{{paramName}}}){{/isMultipart}}{{^isMultipart}}
|
||||
`addForm` {{>_formColl}} ("{{{baseName}}}", {{{paramName}}}){{/isMultipart}}{{/isFile}}{{/isFormParam}}{{#isBodyParam}}
|
||||
`setBodyParam` {{{paramName}}}{{/isBodyParam}}{{/required}}{{/allParams}}{{#isDeprecated}}
|
||||
|
||||
{-# DEPRECATED {{operationId}} "" #-}{{/isDeprecated}}
|
||||
|
||||
@@ -138,13 +143,6 @@ class HasOptionalParam req param where
|
||||
|
||||
infixl 2 -&-
|
||||
|
||||
-- * Request Parameter Types
|
||||
|
||||
{{#x-allUniqueParams}}
|
||||
-- | {{{vendorExtensions.x-paramNameType}}}
|
||||
newtype {{{vendorExtensions.x-paramNameType}}} = {{{vendorExtensions.x-paramNameType}}} { un{{{vendorExtensions.x-paramNameType}}} :: {{{dataType}}} } deriving (P.Eq, P.Show{{#isBodyParam}}, A.ToJSON{{/isBodyParam}})
|
||||
{{/x-allUniqueParams}}
|
||||
|
||||
-- * {{requestType}}
|
||||
|
||||
-- | Represents a request. The "req" type variable is the request type. The "res" type variable is the response type.
|
||||
|
||||
@@ -113,6 +113,12 @@ mk{{classname}} {{#requiredVars}}{{name}} {{/requiredVars}}=
|
||||
{{/model}}
|
||||
{{/models}}
|
||||
|
||||
-- * Parameter newtypes
|
||||
|
||||
{{#x-allUniqueParams}}
|
||||
newtype {{{vendorExtensions.x-paramNameType}}} = {{{vendorExtensions.x-paramNameType}}} { un{{{vendorExtensions.x-paramNameType}}} :: {{{dataType}}} } deriving (P.Eq, P.Show{{#isBodyParam}}, A.ToJSON{{/isBodyParam}})
|
||||
{{/x-allUniqueParams}}
|
||||
|
||||
-- * Utils
|
||||
|
||||
-- | Removes Null fields. (OpenAPI-Specification 2.0 does not allow Null in JSON)
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
-- This file has been generated from package.yaml by hpack version 0.17.1.
|
||||
--
|
||||
-- see: https://github.com/sol/hpack
|
||||
|
||||
name: {{package}}
|
||||
version: 0.1.0.0
|
||||
synopsis: Auto-generated {{package}} API Client
|
||||
@@ -11,10 +7,12 @@ description: .
|
||||
host: {{host}}
|
||||
.
|
||||
base path: {{basePath}}
|
||||
{{#version}}
|
||||
.
|
||||
apiVersion: {{apiVersion}}
|
||||
{{appName}} API version: {{{.}}}
|
||||
{{/version}}
|
||||
.
|
||||
swagger version: {{swaggerVersion}}
|
||||
OpenAPI spec version: {{swaggerVersion}}
|
||||
.
|
||||
{{^hideGenerationTimestamp}}Generated on: {{generatedDate}}
|
||||
.
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
{-
|
||||
{{#appName}}
|
||||
{{{appName}}}
|
||||
{{{.}}}
|
||||
|
||||
{{/appName}}
|
||||
{{#appDescription}}
|
||||
{{{appDescription}}}
|
||||
{{#appDescription}}
|
||||
{{{.}}}
|
||||
|
||||
{{/appDescription}}
|
||||
{{#swaggerVersion}}
|
||||
OpenAPI spec version: {{{.}}}
|
||||
{{/swaggerVersion}}
|
||||
{{#version}}
|
||||
OpenAPI spec version: {{{version}}}
|
||||
{{appName}} API version: {{{.}}}
|
||||
{{/version}}
|
||||
{{#infoEmail}}
|
||||
Contact: {{{infoEmail}}}
|
||||
Contact: {{{.}}}
|
||||
{{/infoEmail}}
|
||||
Generated by Swagger Codegen (https://github.com/swagger-api/swagger-codegen.git)
|
||||
-}
|
||||
|
||||
Reference in New Issue
Block a user