add enum support to php, refactor post process model enum

This commit is contained in:
wing328
2016-04-02 19:03:32 +08:00
parent f2d180f9a8
commit 800a858acb
47 changed files with 1480 additions and 663 deletions

View File

@@ -133,6 +133,99 @@ public class DefaultCodegen {
return objs;
}
/**
* post process enum defined in model's properties
*
* @param objs Map of models
* @return maps of models with better enum support
*/
public Map<String, Object> postProcessModelsEnum(Map<String, Object> objs) {
List<Object> models = (List<Object>) objs.get("models");
for (Object _mo : models) {
Map<String, Object> mo = (Map<String, Object>) _mo;
CodegenModel cm = (CodegenModel) mo.get("model");
for (CodegenProperty var : cm.vars) {
Map<String, Object> allowableValues = var.allowableValues;
// handle ArrayProperty
if (var.items != null) {
allowableValues = var.items.allowableValues;
}
if (allowableValues == null) {
continue;
}
List<String> values = (List<String>) allowableValues.get("values");
if (values == null) {
continue;
}
// put "enumVars" map into `allowableValues", including `name` and `value`
List<Map<String, String>> enumVars = new ArrayList<Map<String, String>>();
String commonPrefix = findCommonPrefixOfVars(values);
int truncateIdx = commonPrefix.length();
for (String value : values) {
Map<String, String> enumVar = new HashMap<String, String>();
String enumName;
if (truncateIdx == 0) {
enumName = value;
} else {
enumName = value.substring(truncateIdx);
if ("".equals(enumName)) {
enumName = value;
}
}
enumVar.put("name", toEnumVarName(enumName));
enumVar.put("value", value);
enumVars.add(enumVar);
}
allowableValues.put("enumVars", enumVars);
// handle default value for enum, e.g. available => StatusEnum.AVAILABLE
if (var.defaultValue != null) {
String enumName = null;
for (Map<String, String> enumVar : enumVars) {
if (var.defaultValue.equals(enumVar.get("value"))) {
enumName = enumVar.get("name");
break;
}
}
if (enumName != null) {
var.defaultValue = var.datatypeWithEnum + "." + enumName;
}
}
}
}
return objs;
}
/**
* Returns the common prefix of variables for enum naming
*
* @param vars List of variable names
* @return the common prefix for naming
*/
public String findCommonPrefixOfVars(List<String> vars) {
String prefix = StringUtils.getCommonPrefix(vars.toArray(new String[vars.size()]));
// exclude trailing characters that should be part of a valid variable
// e.g. ["status-on", "status-off"] => "status-" (not "status-o")
return prefix.replaceAll("[a-zA-Z0-9]+\\z", "");
}
/**
* Return the sanitized variable name for enum
*
* @param value enum variable name
* @return the sanitized variable name for enum
*/
public String toEnumVarName(String value) {
String var = value.replaceAll("\\W+", "_").toUpperCase();
if (var.matches("\\d.*")) {
return "_" + var;
} else {
return var;
}
}
// override with any special post-processing
@SuppressWarnings("static-method")

View File

@@ -436,14 +436,16 @@ public class CSharpClientCodegen extends AbstractCSharpCodegen {
return codegenModel;
}
private String findCommonPrefixOfVars(List<String> vars) {
@Override
public String findCommonPrefixOfVars(List<String> vars) {
String prefix = StringUtils.getCommonPrefix(vars.toArray(new String[vars.size()]));
// exclude trailing characters that should be part of a valid variable
// e.g. ["status-on", "status-off"] => "status-" (not "status-o")
return prefix.replaceAll("[a-zA-Z0-9]+\\z", "");
}
private String toEnumVarName(String value) {
@Override
public String toEnumVarName(String value) {
String var = value.replaceAll("_", " ");
var = WordUtils.capitalizeFully(var);
var = var.replaceAll("\\W+", "");

View File

@@ -697,63 +697,7 @@ public class JavaClientCodegen extends DefaultCodegen implements CodegenConfig {
@Override
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
List<Object> models = (List<Object>) objs.get("models");
for (Object _mo : models) {
Map<String, Object> mo = (Map<String, Object>) _mo;
CodegenModel cm = (CodegenModel) mo.get("model");
for (CodegenProperty var : cm.vars) {
Map<String, Object> allowableValues = var.allowableValues;
// handle ArrayProperty
if (var.items != null) {
allowableValues = var.items.allowableValues;
}
if (allowableValues == null) {
continue;
}
List<String> values = (List<String>) allowableValues.get("values");
if (values == null) {
continue;
}
// put "enumVars" map into `allowableValues", including `name` and `value`
List<Map<String, String>> enumVars = new ArrayList<Map<String, String>>();
String commonPrefix = findCommonPrefixOfVars(values);
int truncateIdx = commonPrefix.length();
for (String value : values) {
Map<String, String> enumVar = new HashMap<String, String>();
String enumName;
if (truncateIdx == 0) {
enumName = value;
} else {
enumName = value.substring(truncateIdx);
if ("".equals(enumName)) {
enumName = value;
}
}
enumVar.put("name", toEnumVarName(enumName));
enumVar.put("value", value);
enumVars.add(enumVar);
}
allowableValues.put("enumVars", enumVars);
// handle default value for enum, e.g. available => StatusEnum.AVAILABLE
if (var.defaultValue != null) {
String enumName = null;
for (Map<String, String> enumVar : enumVars) {
if (var.defaultValue.equals(enumVar.get("value"))) {
enumName = enumVar.get("name");
break;
}
}
if (enumName != null) {
var.defaultValue = var.datatypeWithEnum + "." + enumName;
}
}
}
}
return objs;
return postProcessModelsEnum(objs);
}
@Override
@@ -850,14 +794,16 @@ public class JavaClientCodegen extends DefaultCodegen implements CodegenConfig {
return super.needToImport(type) && type.indexOf(".") < 0;
}
private static String findCommonPrefixOfVars(List<String> vars) {
@Override
public String findCommonPrefixOfVars(List<String> vars) {
String prefix = StringUtils.getCommonPrefix(vars.toArray(new String[vars.size()]));
// exclude trailing characters that should be part of a valid variable
// e.g. ["status-on", "status-off"] => "status-" (not "status-o")
return prefix.replaceAll("[a-zA-Z0-9]+\\z", "");
}
private static String toEnumVarName(String value) {
@Override
public String toEnumVarName(String value) {
String var = value.replaceAll("\\W+", "_").toUpperCase();
if (var.matches("\\d.*")) {
return "_" + var;

View File

@@ -930,14 +930,16 @@ public class JavascriptClientCodegen extends DefaultCodegen implements CodegenCo
&& !languageSpecificPrimitives.contains(type);
}
private static String findCommonPrefixOfVars(List<String> vars) {
@Override
public String findCommonPrefixOfVars(List<String> vars) {
String prefix = StringUtils.getCommonPrefix(vars.toArray(new String[vars.size()]));
// exclude trailing characters that should be part of a valid variable
// e.g. ["status-on", "status-off"] => "status-" (not "status-o")
return prefix.replaceAll("[a-zA-Z0-9]+\\z", "");
}
private static String toEnumVarName(String value) {
@Override
public String toEnumVarName(String value) {
String var = value.replaceAll("\\W+", "_").toUpperCase();
if (var.matches("\\d.*")) {
return "_" + var;

View File

@@ -4,6 +4,7 @@ import io.swagger.codegen.CliOption;
import io.swagger.codegen.CodegenConfig;
import io.swagger.codegen.CodegenConstants;
import io.swagger.codegen.CodegenParameter;
import io.swagger.codegen.CodegenProperty;
import io.swagger.codegen.CodegenType;
import io.swagger.codegen.DefaultCodegen;
import io.swagger.codegen.SupportingFile;
@@ -12,6 +13,7 @@ import io.swagger.models.properties.*;
import java.io.File;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.HashSet;
import java.util.regex.Matcher;
@@ -567,4 +569,14 @@ public class PhpClientCodegen extends DefaultCodegen implements CodegenConfig {
p.example = example;
}
@Override
public String toEnumName(CodegenProperty property) {
LOGGER.info("php toEnumName:" + underscore(property.name).toUpperCase());
return underscore(property.name).toUpperCase();
}
@Override
public Map<String, Object> postProcessModels(Map<String, Object> objs) {
return postProcessModelsEnum(objs);
}
}

View File

@@ -69,7 +69,7 @@ class ApiClient
* Constructor of the class
* @param Configuration $config config for this ApiClient
*/
public function __construct(Configuration $config = null)
public function __construct(\{{invokerPackage}}\Configuration $config = null)
{
if ($config == null) {
$config = Configuration::getDefaultConfiguration();

View File

@@ -60,7 +60,7 @@ use \{{invokerPackage}}\ObjectSerializer;
* Constructor
* @param \{{invokerPackage}}\ApiClient|null $apiClient The api client to use
*/
function __construct($apiClient = null)
function __construct(\{{invokerPackage}}\ApiClient $apiClient = null)
{
if ($apiClient == null) {
$apiClient = new ApiClient();
@@ -84,7 +84,7 @@ use \{{invokerPackage}}\ObjectSerializer;
* @param \{{invokerPackage}}\ApiClient $apiClient set the API client
* @return {{classname}}
*/
public function setApiClient(ApiClient $apiClient)
public function setApiClient(\{{invokerPackage}}\ApiClient $apiClient)
{
$this->apiClient = $apiClient;
return $this;

View File

@@ -62,20 +62,20 @@ class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}implements ArrayA
{{#vars}}'{{name}}' => '{{{datatype}}}'{{#hasMore}},
{{/hasMore}}{{/vars}}
);
static function swaggerTypes() {
return self::$swaggerTypes{{#parent}} + parent::swaggerTypes(){{/parent}};
}
/**
* Array of attributes where the key is the local name, and the value is the original name
* @var string[]
* @var string[]
*/
static $attributeMap = array(
{{#vars}}'{{name}}' => '{{baseName}}'{{#hasMore}},
{{/hasMore}}{{/vars}}
);
static function attributeMap() {
return {{#parent}}parent::attributeMap() + {{/parent}}self::$attributeMap;
}
@@ -88,7 +88,7 @@ class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}implements ArrayA
{{#vars}}'{{name}}' => '{{setter}}'{{#hasMore}},
{{/hasMore}}{{/vars}}
);
static function setters() {
return {{#parent}}parent::setters() + {{/parent}}self::$setters;
}
@@ -101,11 +101,27 @@ class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}implements ArrayA
{{#vars}}'{{name}}' => '{{getter}}'{{#hasMore}},
{{/hasMore}}{{/vars}}
);
static function getters() {
return {{#parent}}parent::getters() + {{/parent}}self::$getters;
}
{{#vars}}{{#isEnum}}{{#allowableValues}}{{#enumVars}}const {{datatypeWithEnum}}_{{{name}}} = "{{{value}}}";
{{/enumVars}}{{/allowableValues}}{{/isEnum}}{{/vars}}
{{#isEnum}}
/**
* Gets allowable values of the enum
* @return string[]
*/
public function {{getter}}AllowableValues() {
return [
{{#allowableValues}}{{#values}}self::{{datatypeWithEnum}}_{{{this}}},{{^-last}}
{{/-last}}{{/values}}{{/allowableValues}}
];
}
{{/isEnum}}
{{#vars}}
/**
* ${{name}} {{#description}}{{{description}}}{{/description}}
@@ -140,7 +156,7 @@ class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}implements ArrayA
{
return $this->{{name}};
}
/**
* Sets {{name}}
* @param {{datatype}} ${{name}} {{#description}}{{{description}}}{{/description}}
@@ -165,7 +181,7 @@ class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}implements ArrayA
{
return isset($this->$offset);
}
/**
* Gets offset.
* @param integer $offset Offset
@@ -175,7 +191,7 @@ class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}implements ArrayA
{
return $this->$offset;
}
/**
* Sets value based on offset.
* @param integer $offset Offset
@@ -186,7 +202,7 @@ class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}implements ArrayA
{
$this->$offset = $value;
}
/**
* Unsets offset.
* @param integer $offset Offset
@@ -196,7 +212,7 @@ class {{classname}} {{#parent}}extends {{{parent}}} {{/parent}}implements ArrayA
{
unset($this->$offset);
}
/**
* Gets the string presentation of the object
* @return string

View File

@@ -4,9 +4,9 @@
"description": "This is a sample server Petstore server. You can find out more about Swagger at <a href=\"http://swagger.io\">http://swagger.io</a> or on irc.freenode.net, #swagger. For this sample, you can use the api key \"special-key\" to test the authorization filters",
"version": "1.0.0",
"title": "Swagger Petstore",
"termsOfService": "http://helloreverb.com/terms/",
"termsOfService": "http://swagger.io/terms/",
"contact": {
"email": "apiteam@wordnik.com"
"email": "apiteam@swagger.io"
},
"license": {
"name": "Apache 2.0",
@@ -19,6 +19,49 @@
"http"
],
"paths": {
"/pet?testing_byte_array=true": {
"post": {
"tags": [
"pet"
],
"summary": "Fake endpoint to test byte array in body parameter for adding a new pet to the store",
"description": "",
"operationId": "addPetUsingByteArray",
"consumes": [
"application/json",
"application/xml"
],
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"in": "body",
"name": "body",
"description": "Pet object in the form of byte array",
"required": false,
"schema": {
"type": "string",
"format": "binary"
}
}
],
"responses": {
"405": {
"description": "Invalid input"
}
},
"security": [
{
"petstore_auth": [
"write:pets",
"read:pets"
]
}
]
}
},
"/pet": {
"post": {
"tags": [
@@ -113,7 +156,7 @@
"pet"
],
"summary": "Finds Pets by status",
"description": "Multiple status values can be provided with comma seperated strings",
"description": "Multiple status values can be provided with comma separated strings",
"operationId": "findPetsByStatus",
"produces": [
"application/json",
@@ -123,14 +166,23 @@
{
"name": "status",
"in": "query",
"description": "Status values that need to be considered for filter",
"description": "Status values that need to be considered for query",
"required": false,
"type": "array",
"items": {
"type": "string",
"enum": ["available", "pending", "sold"]
"enum": [
"available",
"pending",
"sold"
]
},
"collectionFormat": "multi",
"enum": [
"available",
"pending",
"sold"
],
"default": "available"
}
],
@@ -142,15 +194,6 @@
"items": {
"$ref": "#/definitions/Pet"
}
},
"examples": {
"application/json": {
"name": "Puma",
"type": "Dog",
"color": "Black",
"gender": "Female",
"breed": "Mixed"
}
}
},
"400": {
@@ -216,6 +259,150 @@
]
}
},
"/pet/{petId}?testing_byte_array=true": {
"get": {
"tags": [
"pet"
],
"summary": "Fake endpoint to test byte array return by 'Find pet by ID'",
"description": "Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error conditions",
"operationId": "",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"name": "petId",
"in": "path",
"description": "ID of pet that needs to be fetched",
"required": true,
"type": "integer",
"format": "int64"
}
],
"responses": {
"404": {
"description": "Pet not found"
},
"200": {
"description": "successful operation",
"schema": {
"type": "string",
"format": "binary"
}
},
"400": {
"description": "Invalid ID supplied"
}
},
"security": [
{
"api_key": []
},
{
"petstore_auth": [
"write:pets",
"read:pets"
]
}
]
}
},
"/pet/{petId}?response=inline_arbitrary_object": {
"get": {
"tags": [
"pet"
],
"summary": "Fake endpoint to test inline arbitrary object return by 'Find pet by ID'",
"description": "Returns a pet when ID < 10. ID > 10 or nonintegers will simulate API error conditions",
"operationId": "getPetByIdInObject",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"name": "petId",
"in": "path",
"description": "ID of pet that needs to be fetched",
"required": true,
"type": "integer",
"format": "int64"
}
],
"responses": {
"404": {
"description": "Pet not found"
},
"200": {
"description": "successful operation",
"schema": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"category": {
"type": "object"
},
"name": {
"type": "string",
"example": "doggie"
},
"photoUrls": {
"type": "array",
"xml": {
"name": "photoUrl",
"wrapped": true
},
"items": {
"type": "string"
}
},
"tags": {
"type": "array",
"xml": {
"name": "tag",
"wrapped": true
},
"items": {
"$ref": "#/definitions/Tag"
}
},
"status": {
"type": "string",
"description": "pet status in the store",
"enum": [
"available",
"pending",
"sold"
]
}
}
}
},
"400": {
"description": "Invalid ID supplied"
}
},
"security": [
{
"api_key": []
},
{
"petstore_auth": [
"write:pets",
"read:pets"
]
}
]
}
},
"/pet/{petId}": {
"get": {
"tags": [
@@ -443,6 +630,33 @@
]
}
},
"/store/inventory?response=arbitrary_object": {
"get": {
"tags": [
"store"
],
"summary": "Fake endpoint to test arbitrary object return by 'Get inventory'",
"description": "Returns an arbitrary object which is actually a map of status codes to quantities",
"operationId": "getInventoryInObject",
"produces": [
"application/json",
"application/xml"
],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"type": "object"
}
}
},
"security": [
{
"api_key": []
}
]
}
},
"/store/order": {
"post": {
"tags": [
@@ -476,7 +690,62 @@
"400": {
"description": "Invalid Order"
}
}
},
"security": [
{
"test_api_client_id": [],
"test_api_client_secret": []
}
]
}
},
"/store/findByStatus": {
"get": {
"tags": [
"store"
],
"summary": "Finds orders by status",
"description": "A single status value can be provided as a string",
"operationId": "findOrdersByStatus",
"produces": [
"application/json",
"application/xml"
],
"parameters": [
{
"name": "status",
"in": "query",
"description": "Status value that needs to be considered for query",
"required": false,
"type": "string",
"enum": [
"placed",
"approved",
"delivered"
],
"default": "placed"
}
],
"responses": {
"200": {
"description": "successful operation",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/Order"
}
}
},
"400": {
"description": "Invalid status value"
}
},
"security": [
{
"test_api_client_id": [],
"test_api_client_secret": []
}
]
}
},
"/store/order/{orderId}": {
@@ -513,7 +782,15 @@
"400": {
"description": "Invalid ID supplied"
}
}
},
"security": [
{
"test_api_key_header": []
},
{
"test_api_key_query": []
}
]
},
"delete": {
"tags": [
@@ -730,6 +1007,18 @@
"description": "successful operation",
"schema": {
"$ref": "#/definitions/User"
},
"examples": {
"application/json": {
"id": 1,
"username": "johnp",
"firstName": "John",
"lastName": "Public",
"email": "johnp@swagger.io",
"password": "-secret-",
"phone": "0123456789",
"userStatus": 0
}
}
},
"400": {
@@ -802,7 +1091,12 @@
"400": {
"description": "Invalid username supplied"
}
}
},
"security": [
{
"test_http_basic": []
}
]
}
}
},
@@ -820,6 +1114,29 @@
"write:pets": "modify pets in your account",
"read:pets": "read your pets"
}
},
"test_api_client_id": {
"type": "apiKey",
"name": "x-test_api_client_id",
"in": "header"
},
"test_api_client_secret": {
"type": "apiKey",
"name": "x-test_api_client_secret",
"in": "header"
},
"test_api_key_header": {
"type": "apiKey",
"name": "test_api_key_header",
"in": "header"
},
"test_api_key_query": {
"type": "apiKey",
"name": "test_api_key_query",
"in": "query"
},
"test_http_basic": {
"type": "basic"
}
},
"definitions": {
@@ -940,7 +1257,8 @@
"properties": {
"id": {
"type": "integer",
"format": "int64"
"format": "int64",
"readOnly": true
},
"petId": {
"type": "integer",
@@ -970,6 +1288,110 @@
"xml": {
"name": "Order"
}
},
"$special[model.name]": {
"properties": {
"$special[property.name]": {
"type": "integer",
"format": "int64"
}
},
"xml": {
"name": "$special[model.name]"
}
},
"Return": {
"descripton": "Model for testing reserved words",
"properties": {
"return": {
"type": "integer",
"format": "int32"
}
},
"xml": {
"name": "Return"
}
},
"Name": {
"descripton": "Model for testing model name same as property name",
"properties": {
"name": {
"type": "integer",
"format": "int32"
},
"snake_case": {
"type": "integer",
"format": "int32"
}
},
"xml": {
"name": "Name"
}
},
"200_response": {
"descripton": "Model for testing model name starting with number",
"properties": {
"name": {
"type": "integer",
"format": "int32"
}
},
"xml": {
"name": "Name"
}
},
"Dog" : {
"allOf" : [ {
"$ref" : "#/definitions/Animal"
}, {
"type" : "object",
"properties" : {
"breed" : {
"type" : "string"
}
}
} ]
},
"Cat" : {
"allOf" : [ {
"$ref" : "#/definitions/Animal"
}, {
"type" : "object",
"properties" : {
"declawed" : {
"type" : "boolean"
}
}
} ]
},
"Animal" : {
"type" : "object",
"discriminator": "className",
"required": [
"className"
],
"properties" : {
"className" : {
"type" : "string"
}
}
},
"Enum_Test" : {
"type" : "object",
"properties" : {
"enum_string" : {
"type" : "string",
"enum" : ["UPPER", "lower"]
},
"enum_integer" : {
"type" : "integer",
"enum" : [1, -1]
},
"enum_number" : {
"type" : "number",
"enum" : [1.1, -1.2]
}
}
}
}
}