diff --git a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Langs.java b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Langs.java index fcdd921dfa1..fc657d2f480 100644 --- a/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Langs.java +++ b/modules/openapi-generator-cli/src/main/java/org/openapitools/codegen/cmd/Langs.java @@ -11,12 +11,12 @@ import static java.util.ServiceLoader.load; /** * User: lanwen Date: 24.03.15 Time: 20:25 */ -@Command(name = "langs", description = "Shows available langs") +@Command(name = "langs", description = "Shows available languages (generators)") public class Langs implements Runnable { @Override public void run() { LambdaIterable langs = with(load(CodegenConfig.class)).extract(on(CodegenConfig.class).getName()); - System.out.printf("Available languages: %s%n", langs); + System.out.printf("Available languages (generators): %s%n", langs); } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java index c73c84a7363..3aecd85248b 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenOperation.java @@ -32,6 +32,7 @@ public class CodegenOperation { public List queryParams = new ArrayList(); public List headerParams = new ArrayList(); public List formParams = new ArrayList(); + public List cookieParams = new ArrayList(); public List requiredParams = new ArrayList(); public List authMethods; public List tags; @@ -101,6 +102,15 @@ public class CodegenOperation { return nonempty(formParams); } + /** + * Check if there's at least one form parameter + * + * @return true if any cookie parameter exists, false otherwise + */ + public boolean getHasCookieParams() { + return nonempty(cookieParams); + } + /** * Check if there's at least one example parameter * @@ -281,6 +291,8 @@ public class CodegenOperation { return false; if (formParams != null ? !formParams.equals(that.formParams) : that.formParams != null) return false; + if (cookieParams != null ? !cookieParams.equals(that.cookieParams) : that.cookieParams != null) + return false; if (authMethods != null ? !authMethods.equals(that.authMethods) : that.authMethods != null) return false; if (tags != null ? !tags.equals(that.tags) : that.tags != null) @@ -347,6 +359,7 @@ public class CodegenOperation { result = 31 * result + (queryParams != null ? queryParams.hashCode() : 0); result = 31 * result + (headerParams != null ? headerParams.hashCode() : 0); result = 31 * result + (formParams != null ? formParams.hashCode() : 0); + result = 31 * result + (cookieParams != null ? cookieParams.hashCode() : 0); result = 31 * result + (authMethods != null ? authMethods.hashCode() : 0); result = 31 * result + (tags != null ? tags.hashCode() : 0); result = 31 * result + (responses != null ? responses.hashCode() : 0); @@ -361,4 +374,6 @@ public class CodegenOperation { result = 31 * result + (operationIdCamelCase != null ? operationIdCamelCase.hashCode() : 0); return result; } + + } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenParameter.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenParameter.java index d04eb06b701..f679f1962b9 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenParameter.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenParameter.java @@ -343,128 +343,6 @@ public class CodegenParameter { result = 31 * result + (multipleOf != null ? multipleOf.hashCode() : 0); return result; } - public boolean getSecondaryParam() { - return secondaryParam; - } - public String getBaseName() { - return baseName; - } - - public String getParamName() { - return paramName; - } - - public String getDataType() { - return dataType; - } - - public String getDatatypeWithEnum() { - return datatypeWithEnum; - } - - public String getDataFormat() { - return dataFormat; - } - - public String getCollectionFormat() { - return collectionFormat; - } - - public String getDescription() { - return description; - } - - public String getUnescapedDescription() { - return unescapedDescription; - } - - public String getBaseType() { - return baseType; - } - - public String getDefaultValue() { - return defaultValue; - } - - public String getEnumName() { - return enumName; - } - - public String getExample() { - return example; - } - - public String getJsonSchema() { - return jsonSchema; - } - - public boolean getIsNotFile() { - return notFile; - } - - public List get_enum() { - return _enum; - } - - public Map getAllowableValues() { - return allowableValues; - } - - public CodegenProperty getItems() { - return items; - } - - public Map getVendorExtensions() { - return vendorExtensions; - } - - public boolean getRequired() { - return required; - } - - public String getMaximum() { - return maximum; - } - - public boolean getExclusiveMaximum() { - return exclusiveMaximum; - } - - public String getMinimum() { - return minimum; - } - - public boolean getExclusiveMinimum() { - return exclusiveMinimum; - } - - public Integer getMaxLength() { - return maxLength; - } - - public Integer getMinLength() { - return minLength; - } - - public String getPattern() { - return pattern; - } - - public Integer getMaxItems() { - return maxItems; - } - - public Integer getMinItems() { - return minItems; - } - - public boolean getUniqueItems() { - return uniqueItems; - } - - public Number getMultipleOf() { - return multipleOf; - } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java index 17855100391..15701dc485f 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java @@ -3,6 +3,7 @@ package org.openapitools.codegen; import javax.annotation.Nullable; import java.io.File; import java.util.*; +import java.util.ArrayList; import java.util.Map.Entry; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -52,7 +53,7 @@ import org.openapitools.codegen.examples.ExampleGenerator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class DefaultCodegen { +public class DefaultCodegen implements CodegenConfig { protected static final Logger LOGGER = LoggerFactory.getLogger(DefaultCodegen.class); protected String inputSpec; @@ -164,15 +165,15 @@ public class DefaultCodegen { } // Fix up all parent and interface CodegenModel references. for (CodegenModel cm : allModels.values()) { - if (cm.parent != null) { - cm.parentModel = allModels.get(cm.parent); + if (cm.getParent() != null) { + cm.setParentModel(allModels.get(cm.getParent())); } - if (cm.interfaces != null && !cm.interfaces.isEmpty()) { - cm.interfaceModels = new ArrayList(cm.interfaces.size()); - for (String intf : cm.interfaces) { + if (cm.getInterfaces() != null && !cm.getInterfaces().isEmpty()) { + cm.setInterfaceModels(new ArrayList(cm.getInterfaces().size())); + for (String intf : cm.getInterfaces()) { CodegenModel intfModel = allModels.get(intf); if (intfModel != null) { - cm.interfaceModels.add(intfModel); + cm.getInterfaceModels().add(intfModel); } } } @@ -180,15 +181,15 @@ public class DefaultCodegen { // Let parent know about all its children for (String name : allModels.keySet()) { CodegenModel cm = allModels.get(name); - CodegenModel parent = allModels.get(cm.parent); - // if a discriminator exists on the parent, don't add this child to the inheritance hierarchy + CodegenModel parent = allModels.get(cm.getParent()); + // if a discriminator exists on the parent, don't add this child to the inheritance heirarchy // TODO Determine what to do if the parent discriminator name == the grandparent discriminator name while (parent != null) { - if (parent.children == null) { - parent.children = new ArrayList(); + if (parent.getChildren() == null) { + parent.setChildren(new ArrayList()); } - parent.children.add(cm); - if (parent.discriminator == null) { + parent.getChildren().add(cm); + if (parent.getDiscriminator() == null) { parent = allModels.get(parent.parent); } else { parent = null; @@ -936,25 +937,30 @@ public class DefaultCodegen { } paramPart.append(param.getName()).append("="); paramPart.append("{"); - // TODO the following needs to be commented out? - /* - if (qp.getCollectionFormat() != null) { + + // TODO support for multi, tsv? + if (qp.getStyle() != null) { paramPart.append(param.getName()).append("1"); - if ("csv".equals(qp.getCollectionFormat())) { - paramPart.append(","); - } else if ("pipes".equals(qp.getCollectionFormat())) { - paramPart.append("|"); - } else if ("tsv".equals(qp.getCollectionFormat())) { - paramPart.append("\t"); - } else if ("multi".equals(qp.getCollectionFormat())) { - paramPart.append("&").append(param.getName()).append("="); - paramPart.append(param.getName()).append("2"); + if (Parameter.StyleEnum.FORM.equals(qp.getStyle())) { + if (qp.getExplode() != null && qp.getExplode()) { + paramPart.append(","); + } else { + paramPart.append("&").append(param.getName()).append("="); + paramPart.append(param.getName()).append("2"); + } } + else if (Parameter.StyleEnum.PIPEDELIMITED.equals(qp.getStyle())) { + paramPart.append("|"); + } + else if (Parameter.StyleEnum.SPACEDELIMITED.equals(qp.getStyle())) { + paramPart.append("%20"); + } else { + LOGGER.warn("query parameter '" + param.getName() + "style not support: " + qp.getStyle()); + } } else { - paramPart.append(param.getName()); - } */ - paramPart.append(param.getName()); - // TODO --END + paramPart.append(param.getName()); + } + paramPart.append("}"); if (!param.getRequired()) { paramPart.append("]"); @@ -1087,12 +1093,15 @@ public class DefaultCodegen { datatype = datatype.substring("#/components/schemas/".length()); } } catch (Exception e) { - LOGGER.warn("Error obtaining the datatype from ref:" + schema + ". Datatype default to Object"); + LOGGER.warn("Error obtaining the datatype (" + datatype + ") from ref:" + schema + ". Datatype default to Object"); datatype = "Object"; } + return datatype; } - if (schema instanceof ByteArraySchema) { + if (schema instanceof StringSchema && "number".equals(schema.getFormat())) { + datatype = "BigDecimal"; + } else if (schema instanceof ByteArraySchema || (schema instanceof StringSchema && "byte".equals(schema.getFormat()))) { datatype = "ByteArray"; } else if (schema instanceof BinarySchema) { datatype = SchemaTypeUtil.BINARY_FORMAT; @@ -1123,15 +1132,14 @@ public class DefaultCodegen { } else if ( schema instanceof UUIDSchema) { datatype = "UUID"; } else if (schema instanceof StringSchema) { - datatype = "string"; - if ("number".equals(schema.getFormat())) { - datatype = "BigDecimal"; - } else { - datatype = "string"; - } + datatype = "string"; } else { if (schema != null) { - datatype = schema.getType(); + if (SchemaTypeUtil.OBJECT_TYPE.equals(schema.getType()) && schema.getAdditionalProperties() != null) { + datatype = "map"; + } else { + datatype = schema.getType(); + } } } @@ -1348,6 +1356,7 @@ public class DefaultCodegen { if (interfaces != null) { if (m.interfaces == null) m.interfaces = new ArrayList(); + for (Schema interfaceSchema : interfaces) { if (StringUtils.isBlank(interfaceSchema.get$ref())) { continue; @@ -1373,7 +1382,7 @@ public class DefaultCodegen { if (parent != null) { m.parentSchema = parentName; - m.parent = StringUtils.capitalize(modelNamePrefix + "_" + parentName + "_" + modelNameSuffix); + m.parent = toModelName(parentName); addImport(m, m.parent); if (allDefinitions != null) { if (supportsInheritance) { @@ -1403,14 +1412,14 @@ public class DefaultCodegen { //} else if (schema instanceof RefModel) { } else { m.dataType = getSchemaType(schema); - if(schema.getEnum() != null && !schema.getEnum().isEmpty()) { + if (schema.getEnum() != null && !schema.getEnum().isEmpty()) { m.isEnum = true; // comment out below as allowableValues is not set in post processing model enum m.allowableValues = new HashMap(); m.allowableValues.put("values", schema.getEnum()); } if (schema.getAdditionalProperties() != null) { - addAdditionPropertiesToCodeGenModel(m, schema); + addParentContainer(m, m.name, schema); } addVars(m, schema.getProperties(), schema.getRequired()); } @@ -1454,11 +1463,6 @@ public class DefaultCodegen { return false; } - // TODO deco this function? - protected void addAdditionPropertiesToCodeGenModel(CodegenModel model, Schema schema) { - addParentContainer(model, model.name, schema); - } - protected void addProperties(Map properties, List required, Schema schema, Map allSchemas) { if (schema instanceof ComposedSchema) { ComposedSchema composedSchema = (ComposedSchema) schema; @@ -1507,7 +1511,7 @@ public class DefaultCodegen { */ public CodegenProperty fromProperty(String name, Schema p) { if (p == null) { - LOGGER.error("unexpected missing property for name " + name); + LOGGER.error("Unexpected missing property for name " + name); return null; } @@ -1521,7 +1525,7 @@ public class DefaultCodegen { property.getter = toGetter(name); property.setter = toSetter(name); String example = toExampleValue(p); - if(!"null".equals(example)) { + if (!"null".equals(example)) { property.example = example; } property.defaultValue = toDefaultValue(p); @@ -1543,6 +1547,7 @@ public class DefaultCodegen { } String type = getSchemaType(p); + //LOGGER.info("from property = " + p); if (p instanceof IntegerSchema) { property.isNumeric = Boolean.TRUE; if (SchemaTypeUtil.INTEGER64_FORMAT.equals(p.getFormat())) { @@ -1643,11 +1648,11 @@ public class DefaultCodegen { } else { // type is number and without format property.isNumber = Boolean.TRUE; } - /* TODO fix Could not process model 'Enum_Test'.Please make sure that your schema is correct! - if (p.getEnum() != null) { - List _enum = p.getEnum(); + + if (p.getEnum() != null && !p.getEnum().isEmpty()) { + List _enum = p.getEnum(); property._enum = new ArrayList(); - for (Double i : _enum) { <<<<< line throwing exception + for (Number i : _enum) { property._enum.add(i.toString()); } property.isEnum = true; @@ -1656,7 +1661,7 @@ public class DefaultCodegen { Map allowableValues = new HashMap(); allowableValues.put("values", _enum); property.allowableValues = allowableValues; - } */ + } } if (p instanceof DateSchema) { @@ -1729,7 +1734,7 @@ public class DefaultCodegen { } CodegenProperty cp = fromProperty(itemName, ((ArraySchema) p).getItems()); updatePropertyForArray(property, cp); - } else if (p instanceof MapSchema) { + } else if (p instanceof MapSchema || p.getAdditionalProperties() != null) { property.isContainer = true; property.isMapContainer = true; property.containerType = "map"; @@ -1740,15 +1745,17 @@ public class DefaultCodegen { // handle inner property CodegenProperty cp = fromProperty("inner", (Schema) p.getAdditionalProperties()); updatePropertyForMap(property, cp); - } else { + } else { // model // TODO revise the logic below - if (StringUtils.isNotBlank(p.get$ref())) { - property.baseType = getSimpleRef(p.get$ref()); - } + //if (StringUtils.isNotBlank(p.get$ref())) { + // property.baseType = getSimpleRef(p.get$ref()); + //} // --END of revision setNonArrayMapProperty(property, type); } + + //LOGGER.info("from property end = " + property); return property; } @@ -1950,6 +1957,7 @@ public class DefaultCodegen { Operation operation, Map schemas, OpenAPI openAPI) { + //LOGGER.info("fromOperation => operation: " + operation); CodegenOperation op = CodegenModelFactory.newInstance(CodegenModelType.OPERATION); Set imports = new HashSet(); if (operation.getExtensions() != null && !operation.getExtensions().isEmpty()) { @@ -1995,7 +2003,7 @@ public class DefaultCodegen { !defaultIncludes.contains(r.baseType) && !languageSpecificPrimitives.contains(r.baseType)) { imports.add(r.baseType); - } + } r.isDefault = response == methodResponse; op.responses.add(r); if (Boolean.TRUE.equals(r.isBinary) && Boolean.TRUE.equals(r.isDefault)){ @@ -2063,7 +2071,6 @@ public class DefaultCodegen { } List parameters = operation.getParameters(); - CodegenParameter bodyParam = null; List allParams = new ArrayList(); List bodyParams = new ArrayList(); List pathParams = new ArrayList(); @@ -2073,18 +2080,38 @@ public class DefaultCodegen { List formParams = new ArrayList(); List requiredParams = new ArrayList(); - if (operation.getRequestBody() != null) { - bodyParam = fromRequestBody(operation.getRequestBody(), schemas, imports); - bodyParams.add(bodyParam); - allParams.add(bodyParam); + CodegenParameter bodyParam = null; + RequestBody requestBody = operation.getRequestBody(); + if (requestBody != null) { + if ("application/x-www-form-urlencoded".equalsIgnoreCase(getContentType(requestBody)) || + "multipart/form-data".equalsIgnoreCase(getContentType(requestBody))) { + // process form parameters + formParams = fromRequestBodyToFormParameters(requestBody, schemas, imports); + for (CodegenParameter cp: formParams) { + LOGGER.info("Adding " + cp.baseName + " to allParams list"); + allParams.add(cp.copy()); + } + } else { + // process body parameter + if (StringUtils.isNotBlank(requestBody.get$ref())) { + LOGGER.info("request body name: " + getSimpleRef(requestBody.get$ref())); + requestBody = openAPI.getComponents().getRequestBodies().get(getSimpleRef(requestBody.get$ref())); + } + bodyParam = fromRequestBody(requestBody, schemas, imports); + bodyParams.add(bodyParam); + allParams.add(bodyParam); + } } if (parameters != null) { for (Parameter param : parameters) { + if (StringUtils.isNotBlank(param.get$ref())) { + param = getParameterFromRef(param.get$ref(), openAPI); + } + CodegenParameter p = fromParameter(param, imports); // rename parameters to make sure all of them have unique names if (ensureUniqueParams) { - while (true) { boolean exists = false; for (CodegenParameter cp : allParams) { if (p.paramName.equals(cp.paramName)) { @@ -2097,21 +2124,25 @@ public class DefaultCodegen { } else { break; } - } } allParams.add(p); // Issue #2561 (neilotoole) : Moved setting of isParam flags // from here to fromParameter(). - if (param instanceof QueryParameter) { + if (param instanceof QueryParameter || "query".equalsIgnoreCase(param.getIn())) { queryParams.add(p.copy()); - } else if (param instanceof PathParameter) { + } else if (param instanceof PathParameter || "path".equalsIgnoreCase(param.getIn())) { + LOGGER.info("Adding path parameter: "+ p.paramName); pathParams.add(p.copy()); - } else if (param instanceof HeaderParameter) { + } else if (param instanceof HeaderParameter || "header".equalsIgnoreCase(param.getIn())) { headerParams.add(p.copy()); - } else if (param instanceof CookieParameter) { + } else if (param instanceof CookieParameter || "cookie".equalsIgnoreCase(param.getIn())) { cookieParams.add(p.copy()); - } + //} else if (Boolean.TRUE.equals(p.isFormParam)) { + // do nothing as form parameter has been handled above + } else { + LOGGER.warn("Unknown parameter type " + p.baseType + " for " + p.baseName); + } /* TODO need to handle body and form parameter below else if (param instanceof RequestBody) { bodyParam = p; @@ -2131,6 +2162,7 @@ public class DefaultCodegen { } } + for (String i : imports) { if (needToImport(i)) { op.imports.add(i); @@ -2157,7 +2189,7 @@ public class DefaultCodegen { op.pathParams = addHasMore(pathParams); op.queryParams = addHasMore(queryParams); op.headerParams = addHasMore(headerParams); - // op.cookieParams = cookieParams; + op.cookieParams = addHasMore(cookieParams); op.formParams = addHasMore(formParams); op.requiredParams = addHasMore(requiredParams); op.externalDocs = operation.getExternalDocs(); @@ -2198,6 +2230,7 @@ public class DefaultCodegen { r.schema = responseSchema; r.message = escapeText(response.getDescription()); // TODO need to revise and test examples in responses + // ApiResponse does not support examples at the moment //r.examples = toExamples(response.getExamples()); r.jsonSchema = Json.pretty(response); if (response.getExtensions() != null && !response.getExtensions().isEmpty()) { @@ -2299,6 +2332,7 @@ public class DefaultCodegen { if (System.getProperty("debugParser") != null) { LOGGER.info("working on Parameter " + parameter.getName()); + LOGGER.info("JSON schema: " + codegenParameter.jsonSchema); } // TODO need to revise the logic below @@ -2328,16 +2362,17 @@ public class DefaultCodegen { if (parameter.getSchema() != null) { Schema parameterSchema = parameter.getSchema(); // TDOO revise collectionFormat - String collectionFormat = "csv"; + String collectionFormat = "UNKNOWN_COLLECTION_FORMAT"; if (parameterSchema instanceof ArraySchema) { // for array parameter final ArraySchema arraySchema = (ArraySchema) parameterSchema; Schema inner = arraySchema.getItems(); if (inner == null) { - LOGGER.warn("warning! No inner type supplied for array parameter \"" + parameter.getName() + "\", using String"); - inner = new StringSchema().description("//TODO automatically added by openapi-generator"); + LOGGER.warn("warning! No inner type supplied for array parameter \"" + parameter.getName() + "\", using String"); + inner = new StringSchema().description("//TODO automatically added by openapi-generator due to missing iner type definition in the spec"); arraySchema.setItems(inner); } + collectionFormat = getCollectionFormat(parameter); CodegenProperty codegenProperty = fromProperty("inner", inner); codegenParameter.items = codegenProperty; codegenParameter.baseType = codegenProperty.datatype; @@ -2356,22 +2391,29 @@ public class DefaultCodegen { codegenParameter.baseType = codegenProperty.datatype; codegenParameter.isContainer = true; codegenParameter.isMapContainer = true; + collectionFormat = getCollectionFormat(parameter); // recursively add import while (codegenProperty != null) { imports.add(codegenProperty.baseType); codegenProperty = codegenProperty.items; } - } - +/* + } else { + Map args = new HashMap(); + String format = qp.getFormat(); + args.put(PropertyId.ENUM, qp.getEnum()); + property = PropertyBuilder.build(type, format, args); + } +*/ if (parameterSchema == null) { LOGGER.warn("warning! Schema not found for parameter \"" + parameter.getName() + "\", using String"); - parameterSchema = new StringSchema().description("//TODO automatically added by openapi-generator."); + parameterSchema = new StringSchema().description("//TODO automatically added by openapi-generator due to missing type definition."); } // TODO need to setRequired? - //property.setRequired(param.getRequired()); + //codegenParameter.setRequired(param.getRequired()); CodegenProperty codegenProperty = fromProperty(parameter.getName(), parameterSchema); // set boolean flag (e.g. isString) @@ -2528,15 +2570,17 @@ public class DefaultCodegen { // This code has been moved to here from #fromOperation // because these values should be set before calling #postProcessParameter. // See: https://github.com/swagger-api/swagger-codegen/issues/2561 - if (parameter instanceof QueryParameter) { + if (parameter instanceof QueryParameter || "query".equalsIgnoreCase(parameter.getIn())) { codegenParameter.isQueryParam = true; - } else if (parameter instanceof PathParameter) { + } else if (parameter instanceof PathParameter || "path".equalsIgnoreCase(parameter.getIn())) { codegenParameter.required = true; codegenParameter.isPathParam = true; - } else if (parameter instanceof HeaderParameter) { + } else if (parameter instanceof HeaderParameter || "header".equalsIgnoreCase(parameter.getIn())) { codegenParameter.isHeaderParam = true; - } else if (parameter instanceof CookieParameter) { + } else if (parameter instanceof CookieParameter || "cookie".equalsIgnoreCase(parameter.getIn())) { codegenParameter.isCookieParam = true; + } else { + LOGGER.warn("Unknown parameter type: " + parameter.getName()); } /* TODO: need to fix body parameter and form parameter else if (parameter instanceof BodyParameter) { @@ -2825,7 +2869,7 @@ public class DefaultCodegen { String uniqueName = co.operationId; int counter = 0; - for(CodegenOperation op : opList) { + for (CodegenOperation op : opList) { if(uniqueName.equals(op.operationId)) { uniqueName = co.operationId + "_" + counter; counter ++; @@ -2936,7 +2980,6 @@ public class DefaultCodegen { m.hasVars = true; m.hasEnums = false; - Set mandatory = required == null ? Collections. emptySet() : new TreeSet(required); addVars(m, m.vars, properties, mandatory); @@ -3624,6 +3667,19 @@ public class DefaultCodegen { } protected Schema getSchemaFromBody(RequestBody requestBody) { + /* + if (requestBody == null) { + LOGGER.warn("requestBody is null in getSchemaFromBody"); + return null; + } + if (requestBody.getContent() == null) { + LOGGER.warn("requestBody.getContent() is null in getSchemaFromBody"); + return null; + } + if ( requestBody.getContent().keySet() == null) { + LOGGER.warn("requestBody.getContent().keySet() is null in getSchemaFromBody"); + return null; + } */ String contentType = new ArrayList<>(requestBody.getContent().keySet()).get(0); MediaType mediaType = requestBody.getContent().get(contentType); return mediaType.getSchema(); @@ -3695,6 +3751,13 @@ public class DefaultCodegen { codegenOperation.hasConsumes = true; } + public static Set getConsumesInfo(Operation operation) { + if(operation.getRequestBody() == null || operation.getRequestBody().getContent() == null || operation.getRequestBody().getContent().isEmpty()) { + return null; + } + return operation.getRequestBody().getContent().keySet(); + } + private void addProducesInfo(ApiResponse response, CodegenOperation codegenOperation) { if (response == null || response.getContent() == null || response.getContent().isEmpty()) { return; @@ -3760,7 +3823,7 @@ public class DefaultCodegen { } protected String getSimpleRef(String ref) { - if (ref.startsWith("#/components/schemas/")) { + if (ref.startsWith("#/components/")) { ref = ref.substring(ref.lastIndexOf("/") + 1); } return ref; @@ -3802,7 +3865,49 @@ public class DefaultCodegen { return null; } + public List fromRequestBodyToFormParameters(RequestBody body, Map schemas, Set imports) { + List parameters = new ArrayList(); + LOGGER.info("Request body = " + body); + Schema schema = getSchemaFromBody(body); + if (schema.getProperties() != null && !schema.getProperties().isEmpty()) { + Map properties = schema.getProperties(); + for (Map.Entry entry : properties.entrySet()) { + CodegenParameter codegenParameter = CodegenModelFactory.newInstance(CodegenModelType.PARAMETER); + String name = entry.getKey(); + Schema propertySchema = entry.getValue(); + CodegenProperty codegenProperty = fromProperty(name, propertySchema); + + codegenParameter.isFormParam = Boolean.TRUE; + codegenParameter.baseName = codegenProperty.baseName; + codegenParameter.paramName = toParamName((codegenParameter.baseName)); + codegenParameter.baseType = codegenProperty.baseType; + codegenParameter.dataType = codegenProperty.datatype; + codegenParameter.description = codegenProperty.description; + setParameterBooleanFlagWithCodegenProperty(codegenParameter, codegenProperty); + setParameterExampleValue(codegenParameter); + if (codegenProperty.getVendorExtensions() != null && !codegenProperty.getVendorExtensions().isEmpty()) { + codegenParameter.vendorExtensions = codegenProperty.getVendorExtensions(); + } + if (schema.getRequired() != null && !schema.getRequired().isEmpty() && schema.getRequired().contains(codegenProperty.baseName)) { + codegenParameter.required = Boolean.TRUE; + } + //TODO collectionFormat for form parameter not yet supported + //codegenParameter.collectionFormat = getCollectionFormat(propertySchema); + parameters.add(codegenParameter); + } + } + + for (int i = 0; i < parameters.size(); i++) { + System.out.println("parmaeter name:" + parameters.get(i).baseName); + } + + return parameters; + } + public CodegenParameter fromRequestBody(RequestBody body, Map schemas, Set imports) { + if (body == null) { + LOGGER.error("body in fromRequestBody cannot be null!"); + } CodegenParameter codegenParameter = CodegenModelFactory.newInstance(CodegenModelType.PARAMETER); codegenParameter.baseName = "UNKNOWN_BASE_NAME"; codegenParameter.paramName = "UNKNOWN_PARAM_NAME"; @@ -3810,31 +3915,32 @@ public class DefaultCodegen { codegenParameter.isBodyParam = Boolean.TRUE; String name = null; + LOGGER.debug("Request body = " + body); Schema schema = getSchemaFromBody(body); if (StringUtils.isNotBlank(schema.get$ref())) { name = getSimpleRef(schema.get$ref()); schema = schemas.get(name); } - if ((SchemaTypeUtil.OBJECT_TYPE.equals(schema.getType()) - || (schema.getType() == null && schema.getProperties() != null && !schema.getProperties().isEmpty())) - && !(schema instanceof MapSchema)) { + + if (isObjectSchema(schema)) { CodegenModel codegenModel = null; if (StringUtils.isNotBlank(name)) { schema.setName(name); codegenModel = fromModel(name, schema, schemas); } if (codegenModel != null && !codegenModel.emptyVars) { - codegenParameter.paramName = codegenModel.classname.toLowerCase(); + codegenParameter.paramName = toParamName(codegenModel.name); + codegenParameter.baseType = codegenModel.classname; codegenParameter.dataType = getTypeDeclaration(codegenModel.classname); + codegenParameter.description = codegenModel.description; imports.add(codegenParameter.dataType); } else { CodegenProperty codegenProperty = fromProperty("property", schema); if (codegenProperty != null) { codegenParameter.baseType = codegenProperty.baseType; codegenParameter.dataType = codegenProperty.datatype; - codegenParameter.isPrimitiveType = codegenProperty.isPrimitiveType; - codegenParameter.isBinary = codegenProperty.isBinary; - codegenParameter.isFile = codegenProperty.isFile; + codegenParameter.description = codegenProperty.description; + LOGGER.info("Seting description to body parameter: " + codegenProperty.description); if (codegenProperty.complexType != null) { imports.add(codegenProperty.complexType); @@ -3842,8 +3948,7 @@ public class DefaultCodegen { } setParameterBooleanFlagWithCodegenProperty(codegenParameter, codegenProperty); } - } - else if (schema instanceof ArraySchema) { + } else if (schema instanceof ArraySchema) { final ArraySchema arraySchema = (ArraySchema) schema; Schema inner = arraySchema.getItems(); if (inner == null) { @@ -3865,9 +3970,9 @@ public class DefaultCodegen { codegenParameter.items = codegenProperty; codegenParameter.dataType = codegenProperty.datatype; codegenParameter.baseType = codegenProperty.complexType; - codegenParameter.isPrimitiveType = codegenProperty.isPrimitiveType; codegenParameter.isContainer = codegenProperty.isContainer; codegenParameter.isListContainer = codegenProperty.isListContainer; + codegenParameter.description = codegenProperty.description; setParameterBooleanFlagWithCodegenProperty(codegenParameter, codegenProperty); @@ -3875,8 +3980,27 @@ public class DefaultCodegen { imports.add(codegenProperty.baseType); codegenProperty = codegenProperty.items; } + } else { + LOGGER.warn("Scheme type " + schema.getType() + "not handled in reqeust body"); } + + // set the parameter's example value + // should be overridden by lang codegen + setParameterExampleValue(codegenParameter); + return codegenParameter; } + private boolean isObjectSchema (Schema schema) { + if (schema instanceof ObjectSchema) { + return true; + } + if (SchemaTypeUtil.OBJECT_TYPE.equals(schema.getType()) && !(schema instanceof MapSchema)) { + return true; + } + if (schema.getType() == null && schema.getProperties() != null && !schema.getProperties().isEmpty()) { + return true; + } + return false; + } } diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java index 96fb989956c..f7376e8e099 100644 --- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultGenerator.java @@ -151,9 +151,16 @@ public class DefaultGenerator extends AbstractGenerator implements Generator { if (!generateApiTests && !generateModelTests) { config.additionalProperties().put(CodegenConstants.EXCLUDE_TESTS, true); } + // for backward compatibility if (System.getProperty("debugSwagger") != null) { + LOGGER.info("Please use 'debugOpenAPI' instead of 'debugSwagger` moving forward."); Json.prettyPrint(openAPI); } + + if (System.getProperty("debugOpenAPI") != null) { + Json.prettyPrint(openAPI); + } + config.processOpts(); config.preprocessOpenAPI(openAPI); config.additionalProperties().put("generatorVersion", ImplementationVersion.read()); diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpClientCodegen.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpClientCodegen.java new file mode 100644 index 00000000000..22d8f4a101e --- /dev/null +++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/PhpClientCodegen.java @@ -0,0 +1,731 @@ +package org.openapitools.codegen.languages; + +import org.openapitools.codegen.CliOption; +import org.openapitools.codegen.CodegenConfig; +import org.openapitools.codegen.CodegenConstants; +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 java.io.File; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.HashSet; +import java.util.regex.Matcher; + +import org.apache.commons.lang3.StringUtils; + +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.Operation; +import io.swagger.v3.oas.models.media.*; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class PhpClientCodegen extends DefaultCodegen implements CodegenConfig { + @SuppressWarnings("hiding") + static Logger LOGGER = LoggerFactory.getLogger(PhpClientCodegen.class); + + public static final String VARIABLE_NAMING_CONVENTION = "variableNamingConvention"; + public static final String PACKAGE_PATH = "packagePath"; + public static final String SRC_BASE_PATH = "srcBasePath"; + public static final String COMPOSER_VENDOR_NAME = "composerVendorName"; + public static final String COMPOSER_PROJECT_NAME = "composerProjectName"; + protected String invokerPackage = "Swagger\\Client"; + protected String composerVendorName = null; + protected String composerProjectName = null; + protected String packagePath = "SwaggerClient-php"; + protected String artifactVersion = null; + protected String srcBasePath = "lib"; + protected String testBasePath = "test"; + protected String docsBasePath = "docs"; + protected String apiDirName = "Api"; + protected String modelDirName = "Model"; + protected String variableNamingConvention= "snake_case"; + protected String apiDocPath = docsBasePath + "/" + apiDirName; + protected String modelDocPath = docsBasePath + "/" + modelDirName; + + public PhpClientCodegen() { + super(); + + // clear import mapping (from default generator) as php does not use it + // at the moment + importMapping.clear(); + + supportsInheritance = true; + outputFolder = "generated-code" + File.separator + "php"; + modelTemplateFiles.put("model.mustache", ".php"); + apiTemplateFiles.put("api.mustache", ".php"); + modelTestTemplateFiles.put("model_test.mustache", ".php"); + apiTestTemplateFiles.put("api_test.mustache", ".php"); + embeddedTemplateDir = templateDir = "php"; + apiPackage = invokerPackage + "\\" + apiDirName; + modelPackage = invokerPackage + "\\" + modelDirName; + + modelDocTemplateFiles.put("model_doc.mustache", ".md"); + apiDocTemplateFiles.put("api_doc.mustache", ".md"); + + setReservedWordsLowerCase( + Arrays.asList( + // local variables used in api methods (endpoints) + "resourcePath", "httpBody", "queryParams", "headerParams", + "formParams", "_header_accept", "_tempBody", + + // PHP reserved words + "__halt_compiler", "abstract", "and", "array", "as", "break", "callable", "case", "catch", "class", "clone", "const", "continue", "declare", "default", "die", "do", "echo", "else", "elseif", "empty", "enddeclare", "endfor", "endforeach", "endif", "endswitch", "endwhile", "eval", "exit", "extends", "final", "for", "foreach", "function", "global", "goto", "if", "implements", "include", "include_once", "instanceof", "insteadof", "interface", "isset", "list", "namespace", "new", "or", "print", "private", "protected", "public", "require", "require_once", "return", "static", "switch", "throw", "trait", "try", "unset", "use", "var", "while", "xor") + ); + + // ref: http://php.net/manual/en/language.types.intro.php + languageSpecificPrimitives = new HashSet( + Arrays.asList( + "bool", + "boolean", + "int", + "integer", + "double", + "float", + "string", + "object", + "DateTime", + "mixed", + "number", + "void", + "byte") + ); + + instantiationTypes.put("array", "array"); + instantiationTypes.put("map", "map"); + + + // provide primitives to mustache template + List sortedLanguageSpecificPrimitives= new ArrayList(languageSpecificPrimitives); + Collections.sort(sortedLanguageSpecificPrimitives); + String primitives = "'" + StringUtils.join(sortedLanguageSpecificPrimitives, "', '") + "'"; + additionalProperties.put("primitives", primitives); + + // ref: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md#data-types + typeMapping = new HashMap(); + typeMapping.put("integer", "int"); + typeMapping.put("long", "int"); + typeMapping.put("number", "float"); + typeMapping.put("float", "float"); + typeMapping.put("double", "double"); + typeMapping.put("string", "string"); + typeMapping.put("byte", "int"); + typeMapping.put("boolean", "bool"); + typeMapping.put("date", "\\DateTime"); + typeMapping.put("Date", "\\DateTime"); + typeMapping.put("DateTime", "\\DateTime"); + typeMapping.put("file", "\\SplFileObject"); + typeMapping.put("map", "map"); + typeMapping.put("array", "array"); + typeMapping.put("list", "array"); + typeMapping.put("object", "object"); + typeMapping.put("binary", "string"); + typeMapping.put("ByteArray", "string"); + typeMapping.put("UUID", "string"); + + cliOptions.add(new CliOption(CodegenConstants.MODEL_PACKAGE, CodegenConstants.MODEL_PACKAGE_DESC)); + cliOptions.add(new CliOption(CodegenConstants.API_PACKAGE, CodegenConstants.API_PACKAGE_DESC)); + cliOptions.add(new CliOption(VARIABLE_NAMING_CONVENTION, "naming convention of variable name, e.g. camelCase.") + .defaultValue("snake_case")); + cliOptions.add(new CliOption(CodegenConstants.INVOKER_PACKAGE, "The main namespace to use for all classes. e.g. Yay\\Pets")); + cliOptions.add(new CliOption(PACKAGE_PATH, "The main package name for classes. e.g. GeneratedPetstore")); + cliOptions.add(new CliOption(SRC_BASE_PATH, "The directory under packagePath to serve as source root.")); + cliOptions.add(new CliOption(COMPOSER_VENDOR_NAME, "The vendor name used in the composer package name. The template uses {{composerVendorName}}/{{composerProjectName}} for the composer package name. e.g. yaypets. IMPORTANT NOTE (2016/03): composerVendorName will be deprecated and replaced by gitUserId in the next swagger-codegen release")); + cliOptions.add(new CliOption(CodegenConstants.GIT_USER_ID, CodegenConstants.GIT_USER_ID_DESC)); + cliOptions.add(new CliOption(COMPOSER_PROJECT_NAME, "The project name used in the composer package name. The template uses {{composerVendorName}}/{{composerProjectName}} for the composer package name. e.g. petstore-client. IMPORTANT NOTE (2016/03): composerProjectName will be deprecated and replaced by gitRepoId in the next swagger-codegen release")); + cliOptions.add(new CliOption(CodegenConstants.GIT_REPO_ID, CodegenConstants.GIT_REPO_ID_DESC)); + cliOptions.add(new CliOption(CodegenConstants.ARTIFACT_VERSION, "The version to use in the composer package version field. e.g. 1.2.3")); + cliOptions.add(new CliOption(CodegenConstants.HIDE_GENERATION_TIMESTAMP, "hides the timestamp when files were generated") + .defaultValue(Boolean.TRUE.toString())); + } + + public String getPackagePath() { + return packagePath; + } + + public String toPackagePath(String packageName, String basePath) { + return (getPackagePath() + File.separatorChar + toSrcPath(packageName, basePath)); + } + + public String toSrcPath(String packageName, String basePath) { + packageName = packageName.replace(invokerPackage, ""); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + if (basePath != null && basePath.length() > 0) { + basePath = basePath.replaceAll("[\\\\/]?$", "") + File.separatorChar; // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + } + + String regFirstPathSeparator; + if ("/".equals(File.separator)) { // for mac, linux + regFirstPathSeparator = "^/"; + } else { // for windows + regFirstPathSeparator = "^\\\\"; + } + + String regLastPathSeparator; + if ("/".equals(File.separator)) { // for mac, linux + regLastPathSeparator = "/$"; + } else { // for windows + regLastPathSeparator = "\\\\$"; + } + + return (basePath + // Replace period, backslash, forward slash with file separator in package name + + packageName.replaceAll("[\\.\\\\/]", Matcher.quoteReplacement(File.separator)) + // Trim prefix file separators from package path + .replaceAll(regFirstPathSeparator, "")) + // Trim trailing file separators from the overall path + .replaceAll(regLastPathSeparator+ "$", ""); + } + + @Override + public String escapeText(String input) { + if (input != null) { + // Trim the string to avoid leading and trailing spaces. + return super.escapeText(input).trim(); + } + return input; + } + + @Override + public CodegenType getTag() { + return CodegenType.CLIENT; + } + + @Override + public String getName() { + return "php"; + } + + @Override + public String getHelp() { + return "Generates a PHP client library."; + } + + @Override + public void processOpts() { + super.processOpts(); + + // default HIDE_GENERATION_TIMESTAMP to true + if (!additionalProperties.containsKey(CodegenConstants.HIDE_GENERATION_TIMESTAMP)) { + additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, Boolean.TRUE.toString()); + } else { + additionalProperties.put(CodegenConstants.HIDE_GENERATION_TIMESTAMP, + Boolean.valueOf(additionalProperties().get(CodegenConstants.HIDE_GENERATION_TIMESTAMP).toString())); + } + + if (additionalProperties.containsKey(PACKAGE_PATH)) { + this.setPackagePath((String) additionalProperties.get(PACKAGE_PATH)); + } else { + additionalProperties.put(PACKAGE_PATH, packagePath); + } + + if (additionalProperties.containsKey(SRC_BASE_PATH)) { + this.setSrcBasePath((String) additionalProperties.get(SRC_BASE_PATH)); + } else { + additionalProperties.put(SRC_BASE_PATH, srcBasePath); + } + + if (additionalProperties.containsKey(CodegenConstants.INVOKER_PACKAGE)) { + this.setInvokerPackage((String) additionalProperties.get(CodegenConstants.INVOKER_PACKAGE)); + + // Update the invokerPackage for the default apiPackage and modelPackage + apiPackage = invokerPackage + "\\" + apiDirName; + modelPackage = invokerPackage + "\\" + modelDirName; + } else { + additionalProperties.put(CodegenConstants.INVOKER_PACKAGE, invokerPackage); + } + + if (additionalProperties.containsKey(CodegenConstants.MODEL_PACKAGE)) { + // Update model package to contain the specified model package name and the invoker package + modelPackage = invokerPackage + "\\" + (String) additionalProperties.get(CodegenConstants.MODEL_PACKAGE); + } + additionalProperties.put(CodegenConstants.MODEL_PACKAGE, modelPackage); + + if (additionalProperties.containsKey(CodegenConstants.API_PACKAGE)) { + // Update model package to contain the specified model package name and the invoker package + apiPackage = invokerPackage + "\\" + (String) additionalProperties.get(CodegenConstants.API_PACKAGE); + } + additionalProperties.put(CodegenConstants.API_PACKAGE, apiPackage); + + if (additionalProperties.containsKey(COMPOSER_PROJECT_NAME)) { + this.setComposerProjectName((String) additionalProperties.get(COMPOSER_PROJECT_NAME)); + } else { + additionalProperties.put(COMPOSER_PROJECT_NAME, composerProjectName); + } + + if (additionalProperties.containsKey(CodegenConstants.GIT_USER_ID)) { + this.setGitUserId((String) additionalProperties.get(CodegenConstants.GIT_USER_ID)); + } else { + additionalProperties.put(CodegenConstants.GIT_USER_ID, gitUserId); + } + + if (additionalProperties.containsKey(COMPOSER_VENDOR_NAME)) { + this.setComposerVendorName((String) additionalProperties.get(COMPOSER_VENDOR_NAME)); + } else { + additionalProperties.put(COMPOSER_VENDOR_NAME, composerVendorName); + } + + if (additionalProperties.containsKey(CodegenConstants.GIT_REPO_ID)) { + this.setGitRepoId((String) additionalProperties.get(CodegenConstants.GIT_REPO_ID)); + } else { + additionalProperties.put(CodegenConstants.GIT_REPO_ID, gitRepoId); + } + + if (additionalProperties.containsKey(CodegenConstants.ARTIFACT_VERSION)) { + this.setArtifactVersion((String) additionalProperties.get(CodegenConstants.ARTIFACT_VERSION)); + } else { + additionalProperties.put(CodegenConstants.ARTIFACT_VERSION, artifactVersion); + } + + if (additionalProperties.containsKey(VARIABLE_NAMING_CONVENTION)) { + this.setParameterNamingConvention((String) additionalProperties.get(VARIABLE_NAMING_CONVENTION)); + } + + additionalProperties.put("escapedInvokerPackage", invokerPackage.replace("\\", "\\\\")); + + // make api and model src path available in mustache template + additionalProperties.put("apiSrcPath", "./" + toSrcPath(apiPackage, srcBasePath)); + additionalProperties.put("modelSrcPath", "./" + toSrcPath(modelPackage, srcBasePath)); + additionalProperties.put("apiTestPath", "./" + testBasePath + "/" + apiDirName); + additionalProperties.put("modelTestPath", "./" + testBasePath + "/" + modelDirName); + + // make api and model doc path available in mustache template + additionalProperties.put("apiDocPath", apiDocPath); + additionalProperties.put("modelDocPath", modelDocPath); + + // make test path available in mustache template + additionalProperties.put("testBasePath", testBasePath); + + supportingFiles.add(new SupportingFile("ApiException.mustache", toPackagePath(invokerPackage, srcBasePath), "ApiException.php")); + supportingFiles.add(new SupportingFile("Configuration.mustache", toPackagePath(invokerPackage, srcBasePath), "Configuration.php")); + supportingFiles.add(new SupportingFile("ObjectSerializer.mustache", toPackagePath(invokerPackage, srcBasePath), "ObjectSerializer.php")); + supportingFiles.add(new SupportingFile("ModelInterface.mustache", toPackagePath(modelPackage, srcBasePath), "ModelInterface.php")); + supportingFiles.add(new SupportingFile("HeaderSelector.mustache", toPackagePath(invokerPackage, srcBasePath), "HeaderSelector.php")); + supportingFiles.add(new SupportingFile("composer.mustache", getPackagePath(), "composer.json")); + supportingFiles.add(new SupportingFile("README.mustache", getPackagePath(), "README.md")); + supportingFiles.add(new SupportingFile("phpunit.xml.mustache", getPackagePath(), "phpunit.xml.dist")); + supportingFiles.add(new SupportingFile(".travis.yml", getPackagePath(), ".travis.yml")); + supportingFiles.add(new SupportingFile(".php_cs", getPackagePath(), ".php_cs")); + supportingFiles.add(new SupportingFile("git_push.sh.mustache", getPackagePath(), "git_push.sh")); + } + + @Override + public String escapeReservedWord(String name) { + if(this.reservedWordsMappings().containsKey(name)) { + return this.reservedWordsMappings().get(name); + } + return "_" + name; + } + + @Override + public String apiFileFolder() { + return (outputFolder + "/" + toPackagePath(apiPackage, srcBasePath)); + } + + @Override + public String modelFileFolder() { + return (outputFolder + "/" + toPackagePath(modelPackage, srcBasePath)); + } + + @Override + public String apiTestFileFolder() { + return (outputFolder + "/" + getPackagePath() + "/" + testBasePath + "/" + apiDirName); + } + + @Override + public String modelTestFileFolder() { + return (outputFolder + "/" + getPackagePath() + "/" + testBasePath + "/" + modelDirName); + } + + @Override + public String apiDocFileFolder() { + return (outputFolder + "/" + getPackagePath() + "/" + apiDocPath); + } + + @Override + public String modelDocFileFolder() { + return (outputFolder + "/" + getPackagePath() + "/" + modelDocPath); + } + + @Override + public String toModelDocFilename(String name) { + return toModelName(name); + } + + @Override + public String toApiDocFilename(String name) { + return toApiName(name); + } + + @Override + public String getTypeDeclaration(Schema p) { + if (p instanceof ArraySchema) { + ArraySchema ap = (ArraySchema) p; + Schema inner = ap.getItems(); + return getTypeDeclaration(inner) + "[]"; + } else if (p instanceof MapSchema) { + Schema inner = (Schema) p.getAdditionalProperties(); + return getSchemaType(p) + "[string," + getTypeDeclaration(inner) + "]"; + } else if (StringUtils.isNotBlank(p.get$ref())) { + String type = super.getTypeDeclaration(p); + return (!languageSpecificPrimitives.contains(type)) + ? "\\" + modelPackage + "\\" + type : type; + } + return super.getTypeDeclaration(p); + } + + @Override + public String getTypeDeclaration(String name) { + if (!languageSpecificPrimitives.contains(name)) { + return "\\" + modelPackage + "\\" + name; + } + return super.getTypeDeclaration(name); + } + + @Override + public String getSchemaType(Schema p) { + String schemaType = super.getSchemaType(p); + String type = null; + if (typeMapping.containsKey(schemaType)) { + type = typeMapping.get(schemaType); + if (languageSpecificPrimitives.contains(type)) { + return type; + } else if (instantiationTypes.containsKey(type)) { + return type; + } + } else { + type = schemaType; + } + if (type == null) { + return null; + } + return toModelName(type); + } + + public void setInvokerPackage(String invokerPackage) { + this.invokerPackage = invokerPackage; + } + + public void setArtifactVersion(String artifactVersion) { + this.artifactVersion = artifactVersion; + } + + public void setPackagePath(String packagePath) { + this.packagePath = packagePath; + } + + public void setSrcBasePath(String srcBasePath) { + this.srcBasePath = srcBasePath; + } + + public void setParameterNamingConvention(String variableNamingConvention) { + this.variableNamingConvention = variableNamingConvention; + } + + public void setComposerVendorName(String composerVendorName) { + this.composerVendorName = composerVendorName; + } + + public void setComposerProjectName(String composerProjectName) { + this.composerProjectName = composerProjectName; + } + + @Override + public String toVarName(String name) { + // sanitize name + name = sanitizeName(name); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + + if ("camelCase".equals(variableNamingConvention)) { + // return the name in camelCase style + // phone_number => phoneNumber + name = camelize(name, true); + } else { // default to snake case + // return the name in underscore style + // PhoneNumber => phone_number + name = underscore(name); + } + + // parameter name starting with number won't compile + // need to escape it by appending _ at the beginning + if (name.matches("^\\d.*")) { + name = "_" + name; + } + + return name; + } + + @Override + public String toParamName(String name) { + // should be the same as variable name + return toVarName(name); + } + + @Override + public String toModelName(String name) { + // remove [ + name = name.replaceAll("\\]", ""); + + // Note: backslash ("\\") is allowed for e.g. "\\DateTime" + name = name.replaceAll("[^\\w\\\\]+", "_"); // FIXME: a parameter should not be assigned. Also declare the methods parameters as 'final'. + + // remove dollar sign + name = name.replaceAll("$", ""); + + // model name cannot use reserved keyword + if (isReservedWord(name)) { + LOGGER.warn(name + " (reserved word) cannot be used as model name. Renamed to " + camelize("model_" + name)); + name = "model_" + name; // e.g. return => ModelReturn (after camelize) + } + + // model name starts with number + if (name.matches("^\\d.*")) { + LOGGER.warn(name + " (model name starts with number) cannot be used as model name. Renamed to " + camelize("model_" + name)); + name = "model_" + name; // e.g. 200Response => Model200Response (after camelize) + } + + // add prefix and/or suffic only if name does not start wth \ (e.g. \DateTime) + if (!name.matches("^\\\\.*")) { + if (!StringUtils.isEmpty(modelNamePrefix)) { + name = modelNamePrefix + "_" + name; + } + + if (!StringUtils.isEmpty(modelNameSuffix)) { + name = name + "_" + modelNameSuffix; + } + } + + // camelize the model name + // phone_number => PhoneNumber + return camelize(name); + } + + @Override + public String toModelFilename(String name) { + // should be the same as the model name + return toModelName(name); + } + + @Override + public String toModelTestFilename(String name) { + // should be the same as the model name + return toModelName(name) + "Test"; + } + + @Override + public String toOperationId(String operationId) { + // throw exception if method name is empty + if (StringUtils.isEmpty(operationId)) { + throw new RuntimeException("Empty method name (operationId) not allowed"); + } + + // method name cannot use reserved keyword, e.g. return + if (isReservedWord(operationId)) { + LOGGER.warn(operationId + " (reserved word) cannot be used as method name. Renamed to " + camelize(sanitizeName("call_" + operationId), true)); + operationId = "call_" + operationId; + } + + return camelize(sanitizeName(operationId), true); + } + + /** + * Return the default value of the property + * + * @param p property schema + * @return string presentation of the default value of the property + */ + @Override + public String toDefaultValue(Schema p) { + if (p instanceof StringSchema) { + StringSchema dp = (StringSchema) p; + if (dp.getDefault() != null) { + return "'" + dp.getDefault() + "'"; + } + } else if (p instanceof BooleanSchema) { + BooleanSchema dp = (BooleanSchema) p; + if (dp.getDefault() != null) { + return dp.getDefault().toString(); + } + } else if (p instanceof DateSchema) { + // TODO + } else if (p instanceof DateTimeSchema) { + // TODO + } else if (p instanceof NumberSchema) { + NumberSchema dp = (NumberSchema) p; + if (dp.getDefault() != null) { + return dp.getDefault().toString(); + } + } else if (p instanceof IntegerSchema) { + IntegerSchema dp = (IntegerSchema) p; + if (dp.getDefault() != null) { + return dp.getDefault().toString(); + } + } + + return null; + } + + @Override + public void setParameterExampleValue(CodegenParameter p) { + String example; + + if (p.defaultValue == null) { + example = p.example; + } else { + example = p.defaultValue; + } + + String type = p.baseType; + if (type == null) { + type = p.dataType; + } + + if ("String".equalsIgnoreCase(type)) { + if (example == null) { + example = p.paramName + "_example"; + } + example = "\"" + escapeText(example) + "\""; + } else if ("Integer".equals(type) || "int".equals(type)) { + if (example == null) { + example = "56"; + } + } else if ("Float".equalsIgnoreCase(type) || "Double".equalsIgnoreCase(type)) { + if (example == null) { + example = "3.4"; + } + } else if ("BOOLEAN".equalsIgnoreCase(type) || "bool".equalsIgnoreCase(type)) { + if (example == null) { + example = "True"; + } + } else if ("\\SplFileObject".equalsIgnoreCase(type)) { + if (example == null) { + example = "/path/to/file"; + } + example = "\"" + escapeText(example) + "\""; + } else if ("\\Date".equalsIgnoreCase(type)) { + if (example == null) { + example = "2013-10-20"; + } + example = "new \\DateTime(\"" + escapeText(example) + "\")"; + } else if ("\\DateTime".equalsIgnoreCase(type)) { + if (example == null) { + example = "2013-10-20T19:20:30+01:00"; + } + example = "new \\DateTime(\"" + escapeText(example) + "\")"; + } else if ("object".equals(type)) { + example = "new \\stdClass"; + } else if (!languageSpecificPrimitives.contains(type)) { + // type is a model class, e.g. User + example = "new " + getTypeDeclaration(type) + "()"; + } else { + LOGGER.warn("Type " + type + " not handled properly in setParameterExampleValue"); + } + + if (example == null) { + example = "NULL"; + } else if (Boolean.TRUE.equals(p.isListContainer)) { + example = "array(" + example + ")"; + } else if (Boolean.TRUE.equals(p.isMapContainer)) { + example = "array('key' => " + example + ")"; + } + + p.example = example; + } + + @Override + public String toEnumValue(String value, String datatype) { + if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { + return value; + } else { + return "\'" + escapeText(value) + "\'"; + } + } + + @Override + public String toEnumDefaultValue(String value, String datatype) { + return datatype + "_" + value; + } + + @Override + public String toEnumVarName(String name, String datatype) { + if (name.length() == 0) { + return "EMPTY"; + } + + // number + if ("int".equals(datatype) || "double".equals(datatype) || "float".equals(datatype)) { + String varName = name; + varName = varName.replaceAll("-", "MINUS_"); + varName = varName.replaceAll("\\+", "PLUS_"); + varName = varName.replaceAll("\\.", "_DOT_"); + return varName; + } + + // for symbol, e.g. $, # + if (getSymbolName(name) != null) { + return getSymbolName(name).toUpperCase(); + } + + // string + String enumName = sanitizeName(underscore(name).toUpperCase()); + enumName = enumName.replaceFirst("^_", ""); + enumName = enumName.replaceFirst("_$", ""); + + if (isReservedWord(enumName) || enumName.matches("\\d.*")) { // reserved word or starts with number + return escapeReservedWord(enumName); + } else { + return enumName; + } + } + + @Override + public String toEnumName(CodegenProperty property) { + String enumName = underscore(toModelName(property.name)).toUpperCase(); + + // remove [] for array or map of enum + enumName = enumName.replace("[]", ""); + + if (enumName.matches("\\d.*")) { // starts with number + return "_" + enumName; + } else { + return enumName; + } + } + + @Override + public Map postProcessModels(Map objs) { + // process enum in models + return postProcessModelsEnum(objs); + } + + @Override + public Map postProcessOperations(Map objs) { + Map operations = (Map) objs.get("operations"); + List operationList = (List) operations.get("operation"); + for (CodegenOperation op : operationList) { + // for API test method name + // e.g. public function test{{vendorExtensions.x-testOperationId}}() + op.vendorExtensions.put("x-testOperationId", camelize(op.operationId)); + } + return objs; + } + + @Override + public String escapeQuotationMark(String input) { + // remove ' to avoid code injection + return input.replace("'", ""); + } + + @Override + public String escapeUnsafeCharacters(String input) { + return input.replace("*/", "*_/").replace("/*", "/_*"); + } + +} diff --git a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig index 2d195c83122..edb14d20883 100644 --- a/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig +++ b/modules/openapi-generator/src/main/resources/META-INF/services/org.openapitools.codegen.CodegenConfig @@ -1 +1,2 @@ +org.openapitools.codegen.languages.PhpClientCodegen org.openapitools.codegen.languages.RubyClientCodegen