OpenAPITools / openapi-generator

OpenAPI Generator allows generation of API client libraries (SDK generation), server stubs, documentation and configuration automatically given an OpenAPI Spec (v2, v3)
https://openapi-generator.tech
Apache License 2.0
21.33k stars 6.45k forks source link

[REQ] [CORE] Please store primitive schema validations in CodegenModel #4653

Closed spacether closed 4 years ago

spacether commented 4 years ago

Is your feature request related to a problem? Please describe.

When a spec with primitive models(type=string/number/integer/boolean) then that model's validations are not included when the model for that type is generated in Java.

Looking at this samples spec:

swagger: '2.0'
info:
  description: "This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\"
  version: 1.0.0
  title: OpenAPI Petstore
  license:
    name: Apache-2.0
    url: 'http://www.apache.org/licenses/LICENSE-2.0.html'
host: petstore.swagger.io:80
basePath: /v2
tags:
  - name: fake
    description: A fake api
schemes:
  - http
paths:
  /fake/StringEnum:
    post:
      tags:
        - fake
      description: Test serialization of StringEnum
      operationId: StringEnum
      parameters:
        - name: body
          in: body
          description: Input string as post body
          schema:
            $ref: '#/definitions/StringEnum'
      responses:
        '200':
          description: Returned string
          schema:
            $ref: '#/definitions/StringEnum'
  /fake/StringRegex:
    post:
      tags:
        - fake
      description: Test serialization of StringRegex
      operationId: StringRegex
      parameters:
        - name: body
          in: body
          description: Input string as post body
          schema:
            $ref: '#/definitions/StringRegex'
      responses:
        '200':
          description: Returned string
          schema:
            $ref: '#/definitions/StringRegex'
  /fake/ObjectModelWithRefs:
    post:
      tags:
        - fake
      description: Test serialization of ObjectModelWithRefs
      operationId: ObjectModelWithRefs
      parameters:
        - name: body
          in: body
          description: Input object as post body
          schema:
            $ref: '#/definitions/ObjectModelWithRefs'
      responses:
        '200':
          description: Returned object
          schema:
            $ref: '#/definitions/ObjectModelWithRefs'
definitions:
  ObjectModelWithRefs:
    type: object
    required:
      - stringEnum
      - stringRegex
    properties:
      stringEnum:
        $ref: '#/definitions/StringEnum'
      stringRegex:
        $ref: '#/definitions/StringRegex'
  StringEnum:
    type: string
    enum:
    - "placed"
    - "approved"
    - "delivered"
  StringRegex:
    type: string
    pattern: '^\d{3}-\d{2}-\d{4}$'

Our produced model will keep the enums in the StringEnum model, but it will discard and not store the pattern in the StringRegex model because we store no validation information in CodegenModel.

Some information on isAlias: we have the flag generateAliasAsModel defined here https://github.com/OpenAPITools/openapi-generator/blob/b69b8cdd31aef20bf41022d84df08bc62aca747e/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenConstants.java#L314 described as "Generate alias to map, array as models" however some generators also use it to generate models for primitive type. For example the Python generator sets model.isAlias=true for StringRegex when using the Python client generator model.isAlias is turned on if the name of the model is in the typeAliases https://github.com/OpenAPITools/openapi-generator/blob/d7b390f328a597438edd6a72a9f1ff495ca7080a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java#L1695 typeAliases are generated here: https://github.com/OpenAPITools/openapi-generator/blob/d7b390f328a597438edd6a72a9f1ff495ca7080a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java#L1667 Items are added to typeAliases here: https://github.com/OpenAPITools/openapi-generator/blob/d7b390f328a597438edd6a72a9f1ff495ca7080a/modules/openapi-generator/src/main/java/org/openapitools/codegen/DefaultCodegen.java#L3692

Whether or not primitive models have isAlias turned on, having the validation info would let us include it in produced models.

Describe the solution you'd like

The included spec passes validation at https://apitools.dev/swagger-parser/online/ How about we include primitive model validation information in the CodegenModel class. Then we can leave it up to the devs to see if they want to produce models which include this validation info.

Reasons why this would help:

Spec Information

Prior Related Work:

Describe alternatives you've considered

One alternative is to move this primitive schema into a property and load that property into an object model. The python-experimental generator does that but it is overly complicated for a feature that the Swagger and Openapi spec supports.

Additional context

Here is the debug info when using the python generator with -DdebugSupportingFiles The StringRegex model is omitted by the generator

[main] INFO  o.o.codegen.DefaultGenerator - ############ Supporting file info ############
{
  "licenseUrl" : "http://www.apache.org/licenses/LICENSE-2.0.html",
  "appVersion" : "1.0.0",
  "generatedYear" : "2019",
  "generatorClass" : "org.openapitools.codegen.languages.PythonClientCodegen",
  "openAPI" : {
    "openapi" : "3.0.1",
    "info" : {
      "title" : "OpenAPI Petstore",
      "description" : "This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\",
      "license" : {
        "name" : "Apache-2.0",
        "url" : "http://www.apache.org/licenses/LICENSE-2.0.html"
      },
      "version" : "1.0.0"
    },
    "servers" : [ {
      "url" : "http://petstore.swagger.io:80/v2"
    } ],
    "tags" : [ {
      "name" : "fake",
      "description" : "A fake api"
    } ],
    "paths" : {
      "/fake/StringEnum" : {
        "post" : {
          "tags" : [ "fake" ],
          "description" : "Test serialization of StringEnum",
          "operationId" : "StringEnum",
          "requestBody" : {
            "description" : "Input string as post body",
            "content" : {
              "*/*" : {
                "schema" : {
                  "$ref" : "#/components/schemas/StringEnum"
                }
              }
            },
            "required" : false
          },
          "responses" : {
            "200" : {
              "description" : "Returned string",
              "content" : {
                "*/*" : {
                  "schema" : {
                    "$ref" : "#/components/schemas/StringEnum"
                  }
                }
              }
            }
          },
          "x-codegen-request-body-name" : "body"
        }
      },
      "/fake/StringRegex" : {
        "post" : {
          "tags" : [ "fake" ],
          "description" : "Test serialization of StringRegex",
          "operationId" : "StringRegex",
          "requestBody" : {
            "description" : "Input string as post body",
            "content" : {
              "*/*" : {
                "schema" : {
                  "$ref" : "#/components/schemas/StringRegex"
                }
              }
            },
            "required" : false
          },
          "responses" : {
            "200" : {
              "description" : "Returned string",
              "content" : {
                "*/*" : {
                  "schema" : {
                    "$ref" : "#/components/schemas/StringRegex"
                  }
                }
              }
            }
          },
          "x-codegen-request-body-name" : "body"
        }
      },
      "/fake/ObjectModelWithRefs" : {
        "post" : {
          "tags" : [ "fake" ],
          "description" : "Test serialization of ObjectModelWithRefs",
          "operationId" : "ObjectModelWithRefs",
          "requestBody" : {
            "description" : "Input object as post body",
            "content" : {
              "*/*" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ObjectModelWithRefs"
                }
              }
            },
            "required" : false
          },
          "responses" : {
            "200" : {
              "description" : "Returned object",
              "content" : {
                "*/*" : {
                  "schema" : {
                    "$ref" : "#/components/schemas/ObjectModelWithRefs"
                  }
                }
              }
            }
          },
          "x-codegen-request-body-name" : "body"
        }
      }
    },
    "components" : {
      "schemas" : {
        "ObjectModelWithRefs" : {
          "required" : [ "stringEnum", "stringRegex" ],
          "type" : "object",
          "properties" : {
            "stringEnum" : {
              "$ref" : "#/components/schemas/StringEnum"
            },
            "stringRegex" : {
              "pattern" : "^\\d{3}-\\d{2}-\\d{4}$",
              "type" : "string"
            }
          },
          "example" : {
            "stringRegex" : "stringRegex"
          }
        },
        "StringEnum" : {
          "type" : "string",
          "enum" : [ "placed", "approved", "delivered" ]
        },
        "StringRegex" : {
          "pattern" : "^\\d{3}-\\d{2}-\\d{4}$",
          "type" : "string"
        }
      }
    }
  },
  "scheme" : "http",
  "modelPackage" : "petstore_api.models",
  "gitHost" : "github.com",
  "templateDir" : "/Users/justin/programming/openapi-generator/modules/openapi-generator/src/main/resources/python",
  "apiDocPath" : "docs/",
  "licenseInfo" : "Apache-2.0",
  "apiFolder" : "petstore_api/api",
  "generateApis" : true,
  "generateModelDocs" : true,
  "generateModelTests" : true,
  "basePathWithoutHost" : "/v2",
  "generateApiTests" : true,
  "lambda" : {
    "lowercase" : { },
    "uppercase" : { },
    "titlecase" : { },
    "camelcase" : { },
    "indented" : { },
    "indented_8" : { },
    "indented_12" : { },
    "indented_16" : { }
  },
  "generateModels" : true,
  "servers" : [ {
    "url" : "http://petstore.swagger.io:80/v2",
    "variables" : [ ]
  } ],
  "inputSpec" : "modules/openapi-generator/src/test/resources/2_0/primitive_models_validations_enums.yaml",
  "host" : "petstore.swagger.io",
  "packageName" : "petstore_api",
  "hideGenerationTimestamp" : true,
  "unescapedAppDescription" : "This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\",
  "models" : [ {
    "importPath" : "from petstore_api.models.object_model_with_refs import ObjectModelWithRefs",
    "model" : {
      "anyOf" : [ ],
      "oneOf" : [ ],
      "allOf" : [ ],
      "name" : "ObjectModelWithRefs",
      "classname" : "ObjectModelWithRefs",
      "classVarName" : "object_model_with_refs",
      "modelJson" : "{\n  \"required\" : [ \"stringEnum\", \"stringRegex\" ],\n  \"type\" : \"object\",\n  \"properties\" : {\n    \"stringEnum\" : {\n      \"$ref\" : \"#/components/schemas/StringEnum\"\n    },\n    \"stringRegex\" : {\n      \"$ref\" : \"#/components/schemas/StringRegex\"\n    }\n  }\n}",
      "dataType" : "object",
      "classFilename" : "object_model_with_refs",
      "isAlias" : false,
      "isString" : false,
      "isInteger" : false,
      "isLong" : false,
      "isNumber" : false,
      "isNumeric" : false,
      "isFloat" : false,
      "isDouble" : false,
      "vars" : [ {
        "openApiType" : "StringEnum",
        "baseName" : "stringEnum",
        "complexType" : "StringEnum",
        "getter" : "getStringEnum",
        "setter" : "setStringEnum",
        "dataType" : "StringEnum",
        "datatypeWithEnum" : "StringEnum",
        "name" : "string_enum",
        "defaultValueWithParam" : " = data.stringEnum;",
        "baseType" : "StringEnum",
        "example" : "null",
        "jsonSchema" : "{\n  \"$ref\" : \"#/components/schemas/StringEnum\"\n}",
        "exclusiveMinimum" : false,
        "exclusiveMaximum" : false,
        "hasMore" : true,
        "required" : true,
        "secondaryParam" : false,
        "hasMoreNonReadOnly" : false,
        "isPrimitiveType" : false,
        "isModel" : false,
        "isContainer" : false,
        "isString" : false,
        "isNumeric" : false,
        "isInteger" : false,
        "isLong" : false,
        "isNumber" : false,
        "isFloat" : false,
        "isDouble" : false,
        "isByteArray" : false,
        "isBinary" : false,
        "isFile" : false,
        "isBoolean" : false,
        "isDate" : false,
        "isDateTime" : false,
        "isUuid" : false,
        "isUri" : false,
        "isEmail" : false,
        "isFreeFormObject" : false,
        "isListContainer" : false,
        "isMapContainer" : false,
        "isEnum" : false,
        "isReadOnly" : false,
        "isWriteOnly" : false,
        "isNullable" : false,
        "isSelfReference" : false,
        "allowableValues" : {
          "enumVars" : [ {
            "name" : "PLACED",
            "isString" : false,
            "value" : "\"placed\""
          }, {
            "name" : "APPROVED",
            "isString" : false,
            "value" : "\"approved\""
          }, {
            "name" : "DELIVERED",
            "isString" : false,
            "value" : "\"delivered\""
          } ],
          "values" : [ "placed", "approved", "delivered" ]
        },
        "vendorExtensions" : { },
        "hasValidation" : false,
        "isInherited" : false,
        "nameInCamelCase" : "StringEnum",
        "nameInSnakeCase" : "STRING_ENUM",
        "isXmlAttribute" : false,
        "isXmlWrapped" : false,
        "datatype" : "StringEnum",
        "iexclusiveMaximum" : false
      }, {
        "openApiType" : "string",
        "baseName" : "stringRegex",
        "getter" : "getStringRegex",
        "setter" : "setStringRegex",
        "dataType" : "str",
        "datatypeWithEnum" : "str",
        "name" : "string_regex",
        "defaultValueWithParam" : " = data.stringRegex;",
        "baseType" : "str",
        "pattern" : "/^\\d{3}-\\d{2}-\\d{4}$/",
        "example" : "null",
        "jsonSchema" : "{\n  \"pattern\" : \"^\\\\d{3}-\\\\d{2}-\\\\d{4}$\",\n  \"type\" : \"string\"\n}",
        "exclusiveMinimum" : false,
        "exclusiveMaximum" : false,
        "hasMore" : false,
        "required" : true,
        "secondaryParam" : false,
        "hasMoreNonReadOnly" : false,
        "isPrimitiveType" : true,
        "isModel" : false,
        "isContainer" : false,
        "isString" : true,
        "isNumeric" : false,
        "isInteger" : false,
        "isLong" : false,
        "isNumber" : false,
        "isFloat" : false,
        "isDouble" : false,
        "isByteArray" : false,
        "isBinary" : false,
        "isFile" : false,
        "isBoolean" : false,
        "isDate" : false,
        "isDateTime" : false,
        "isUuid" : false,
        "isUri" : false,
        "isEmail" : false,
        "isFreeFormObject" : false,
        "isListContainer" : false,
        "isMapContainer" : false,
        "isEnum" : false,
        "isReadOnly" : false,
        "isWriteOnly" : false,
        "isNullable" : false,
        "isSelfReference" : false,
        "vendorExtensions" : {
          "x-regex" : "^\\d{3}-\\d{2}-\\d{4}$",
          "x-modifiers" : [ ]
        },
        "hasValidation" : true,
        "isInherited" : false,
        "nameInCamelCase" : "StringRegex",
        "nameInSnakeCase" : "STRING_REGEX",
        "isXmlAttribute" : false,
        "isXmlWrapped" : false,
        "datatype" : "str",
        "iexclusiveMaximum" : false
      } ],
      "allVars" : [ {
        "openApiType" : "StringEnum",
        "baseName" : "stringEnum",
        "complexType" : "StringEnum",
        "getter" : "getStringEnum",
        "setter" : "setStringEnum",
        "dataType" : "StringEnum",
        "datatypeWithEnum" : "StringEnum",
        "name" : "string_enum",
        "defaultValueWithParam" : " = data.stringEnum;",
        "baseType" : "StringEnum",
        "example" : "null",
        "jsonSchema" : "{\n  \"$ref\" : \"#/components/schemas/StringEnum\"\n}",
        "exclusiveMinimum" : false,
        "exclusiveMaximum" : false,
        "hasMore" : true,
        "required" : true,
        "secondaryParam" : false,
        "hasMoreNonReadOnly" : false,
        "isPrimitiveType" : false,
        "isModel" : false,
        "isContainer" : false,
        "isString" : false,
        "isNumeric" : false,
        "isInteger" : false,
        "isLong" : false,
        "isNumber" : false,
        "isFloat" : false,
        "isDouble" : false,
        "isByteArray" : false,
        "isBinary" : false,
        "isFile" : false,
        "isBoolean" : false,
        "isDate" : false,
        "isDateTime" : false,
        "isUuid" : false,
        "isUri" : false,
        "isEmail" : false,
        "isFreeFormObject" : false,
        "isListContainer" : false,
        "isMapContainer" : false,
        "isEnum" : false,
        "isReadOnly" : false,
        "isWriteOnly" : false,
        "isNullable" : false,
        "isSelfReference" : false,
        "allowableValues" : {
          "enumVars" : [ {
            "name" : "PLACED",
            "isString" : false,
            "value" : "\"placed\""
          }, {
            "name" : "APPROVED",
            "isString" : false,
            "value" : "\"approved\""
          }, {
            "name" : "DELIVERED",
            "isString" : false,
            "value" : "\"delivered\""
          } ],
          "values" : [ "placed", "approved", "delivered" ]
        },
        "vendorExtensions" : { },
        "hasValidation" : false,
        "isInherited" : false,
        "nameInCamelCase" : "StringEnum",
        "nameInSnakeCase" : "STRING_ENUM",
        "isXmlAttribute" : false,
        "isXmlWrapped" : false,
        "datatype" : "StringEnum",
        "iexclusiveMaximum" : false
      }, {
        "openApiType" : "string",
        "baseName" : "stringRegex",
        "getter" : "getStringRegex",
        "setter" : "setStringRegex",
        "dataType" : "str",
        "datatypeWithEnum" : "str",
        "name" : "string_regex",
        "defaultValueWithParam" : " = data.stringRegex;",
        "baseType" : "str",
        "pattern" : "/^\\d{3}-\\d{2}-\\d{4}$/",
        "example" : "null",
        "jsonSchema" : "{\n  \"pattern\" : \"^\\\\d{3}-\\\\d{2}-\\\\d{4}$\",\n  \"type\" : \"string\"\n}",
        "exclusiveMinimum" : false,
        "exclusiveMaximum" : false,
        "hasMore" : false,
        "required" : true,
        "secondaryParam" : false,
        "hasMoreNonReadOnly" : false,
        "isPrimitiveType" : true,
        "isModel" : false,
        "isContainer" : false,
        "isString" : true,
        "isNumeric" : false,
        "isInteger" : false,
        "isLong" : false,
        "isNumber" : false,
        "isFloat" : false,
        "isDouble" : false,
        "isByteArray" : false,
        "isBinary" : false,
        "isFile" : false,
        "isBoolean" : false,
        "isDate" : false,
        "isDateTime" : false,
        "isUuid" : false,
        "isUri" : false,
        "isEmail" : false,
        "isFreeFormObject" : false,
        "isListContainer" : false,
        "isMapContainer" : false,
        "isEnum" : false,
        "isReadOnly" : false,
        "isWriteOnly" : false,
        "isNullable" : false,
        "isSelfReference" : false,
        "vendorExtensions" : { },
        "hasValidation" : true,
        "isInherited" : false,
        "nameInCamelCase" : "StringRegex",
        "nameInSnakeCase" : "STRING_REGEX",
        "isXmlAttribute" : false,
        "isXmlWrapped" : false,
        "datatype" : "str",
        "iexclusiveMaximum" : false
      } ],
      "requiredVars" : [ {
        "openApiType" : "StringEnum",
        "baseName" : "stringEnum",
        "complexType" : "StringEnum",
        "getter" : "getStringEnum",
        "setter" : "setStringEnum",
        "dataType" : "StringEnum",
        "datatypeWithEnum" : "StringEnum",
        "name" : "string_enum",
        "defaultValueWithParam" : " = data.stringEnum;",
        "baseType" : "StringEnum",
        "example" : "null",
        "jsonSchema" : "{\n  \"$ref\" : \"#/components/schemas/StringEnum\"\n}",
        "exclusiveMinimum" : false,
        "exclusiveMaximum" : false,
        "hasMore" : true,
        "required" : true,
        "secondaryParam" : false,
        "hasMoreNonReadOnly" : false,
        "isPrimitiveType" : false,
        "isModel" : false,
        "isContainer" : false,
        "isString" : false,
        "isNumeric" : false,
        "isInteger" : false,
        "isLong" : false,
        "isNumber" : false,
        "isFloat" : false,
        "isDouble" : false,
        "isByteArray" : false,
        "isBinary" : false,
        "isFile" : false,
        "isBoolean" : false,
        "isDate" : false,
        "isDateTime" : false,
        "isUuid" : false,
        "isUri" : false,
        "isEmail" : false,
        "isFreeFormObject" : false,
        "isListContainer" : false,
        "isMapContainer" : false,
        "isEnum" : false,
        "isReadOnly" : false,
        "isWriteOnly" : false,
        "isNullable" : false,
        "isSelfReference" : false,
        "allowableValues" : {
          "enumVars" : [ {
            "name" : "PLACED",
            "isString" : false,
            "value" : "\"placed\""
          }, {
            "name" : "APPROVED",
            "isString" : false,
            "value" : "\"approved\""
          }, {
            "name" : "DELIVERED",
            "isString" : false,
            "value" : "\"delivered\""
          } ],
          "values" : [ "placed", "approved", "delivered" ]
        },
        "vendorExtensions" : { },
        "hasValidation" : false,
        "isInherited" : false,
        "nameInCamelCase" : "StringEnum",
        "nameInSnakeCase" : "STRING_ENUM",
        "isXmlAttribute" : false,
        "isXmlWrapped" : false,
        "datatype" : "StringEnum",
        "iexclusiveMaximum" : false
      }, {
        "openApiType" : "string",
        "baseName" : "stringRegex",
        "getter" : "getStringRegex",
        "setter" : "setStringRegex",
        "dataType" : "str",
        "datatypeWithEnum" : "str",
        "name" : "string_regex",
        "defaultValueWithParam" : " = data.stringRegex;",
        "baseType" : "str",
        "pattern" : "/^\\d{3}-\\d{2}-\\d{4}$/",
        "example" : "null",
        "jsonSchema" : "{\n  \"pattern\" : \"^\\\\d{3}-\\\\d{2}-\\\\d{4}$\",\n  \"type\" : \"string\"\n}",
        "exclusiveMinimum" : false,
        "exclusiveMaximum" : false,
        "hasMore" : false,
        "required" : true,
        "secondaryParam" : false,
        "hasMoreNonReadOnly" : false,
        "isPrimitiveType" : true,
        "isModel" : false,
        "isContainer" : false,
        "isString" : true,
        "isNumeric" : false,
        "isInteger" : false,
        "isLong" : false,
        "isNumber" : false,
        "isFloat" : false,
        "isDouble" : false,
        "isByteArray" : false,
        "isBinary" : false,
        "isFile" : false,
        "isBoolean" : false,
        "isDate" : false,
        "isDateTime" : false,
        "isUuid" : false,
        "isUri" : false,
        "isEmail" : false,
        "isFreeFormObject" : false,
        "isListContainer" : false,
        "isMapContainer" : false,
        "isEnum" : false,
        "isReadOnly" : false,
        "isWriteOnly" : false,
        "isNullable" : false,
        "isSelfReference" : false,
        "vendorExtensions" : { },
        "hasValidation" : true,
        "isInherited" : false,
        "nameInCamelCase" : "StringRegex",
        "nameInSnakeCase" : "STRING_REGEX",
        "isXmlAttribute" : false,
        "isXmlWrapped" : false,
        "datatype" : "str",
        "iexclusiveMaximum" : false
      } ],
      "optionalVars" : [ ],
      "readOnlyVars" : [ ],
      "readWriteVars" : [ {
        "openApiType" : "StringEnum",
        "baseName" : "stringEnum",
        "complexType" : "StringEnum",
        "getter" : "getStringEnum",
        "setter" : "setStringEnum",
        "dataType" : "StringEnum",
        "datatypeWithEnum" : "StringEnum",
        "name" : "string_enum",
        "defaultValueWithParam" : " = data.stringEnum;",
        "baseType" : "StringEnum",
        "example" : "null",
        "jsonSchema" : "{\n  \"$ref\" : \"#/components/schemas/StringEnum\"\n}",
        "exclusiveMinimum" : false,
        "exclusiveMaximum" : false,
        "hasMore" : true,
        "required" : true,
        "secondaryParam" : false,
        "hasMoreNonReadOnly" : false,
        "isPrimitiveType" : false,
        "isModel" : false,
        "isContainer" : false,
        "isString" : false,
        "isNumeric" : false,
        "isInteger" : false,
        "isLong" : false,
        "isNumber" : false,
        "isFloat" : false,
        "isDouble" : false,
        "isByteArray" : false,
        "isBinary" : false,
        "isFile" : false,
        "isBoolean" : false,
        "isDate" : false,
        "isDateTime" : false,
        "isUuid" : false,
        "isUri" : false,
        "isEmail" : false,
        "isFreeFormObject" : false,
        "isListContainer" : false,
        "isMapContainer" : false,
        "isEnum" : false,
        "isReadOnly" : false,
        "isWriteOnly" : false,
        "isNullable" : false,
        "isSelfReference" : false,
        "allowableValues" : {
          "enumVars" : [ {
            "name" : "PLACED",
            "isString" : false,
            "value" : "\"placed\""
          }, {
            "name" : "APPROVED",
            "isString" : false,
            "value" : "\"approved\""
          }, {
            "name" : "DELIVERED",
            "isString" : false,
            "value" : "\"delivered\""
          } ],
          "values" : [ "placed", "approved", "delivered" ]
        },
        "vendorExtensions" : { },
        "hasValidation" : false,
        "isInherited" : false,
        "nameInCamelCase" : "StringEnum",
        "nameInSnakeCase" : "STRING_ENUM",
        "isXmlAttribute" : false,
        "isXmlWrapped" : false,
        "datatype" : "StringEnum",
        "iexclusiveMaximum" : false
      }, {
        "openApiType" : "string",
        "baseName" : "stringRegex",
        "getter" : "getStringRegex",
        "setter" : "setStringRegex",
        "dataType" : "str",
        "datatypeWithEnum" : "str",
        "name" : "string_regex",
        "defaultValueWithParam" : " = data.stringRegex;",
        "baseType" : "str",
        "pattern" : "/^\\d{3}-\\d{2}-\\d{4}$/",
        "example" : "null",
        "jsonSchema" : "{\n  \"pattern\" : \"^\\\\d{3}-\\\\d{2}-\\\\d{4}$\",\n  \"type\" : \"string\"\n}",
        "exclusiveMinimum" : false,
        "exclusiveMaximum" : false,
        "hasMore" : false,
        "required" : true,
        "secondaryParam" : false,
        "hasMoreNonReadOnly" : false,
        "isPrimitiveType" : true,
        "isModel" : false,
        "isContainer" : false,
        "isString" : true,
        "isNumeric" : false,
        "isInteger" : false,
        "isLong" : false,
        "isNumber" : false,
        "isFloat" : false,
        "isDouble" : false,
        "isByteArray" : false,
        "isBinary" : false,
        "isFile" : false,
        "isBoolean" : false,
        "isDate" : false,
        "isDateTime" : false,
        "isUuid" : false,
        "isUri" : false,
        "isEmail" : false,
        "isFreeFormObject" : false,
        "isListContainer" : false,
        "isMapContainer" : false,
        "isEnum" : false,
        "isReadOnly" : false,
        "isWriteOnly" : false,
        "isNullable" : false,
        "isSelfReference" : false,
        "vendorExtensions" : { },
        "hasValidation" : true,
        "isInherited" : false,
        "nameInCamelCase" : "StringRegex",
        "nameInSnakeCase" : "STRING_REGEX",
        "isXmlAttribute" : false,
        "isXmlWrapped" : false,
        "datatype" : "str",
        "iexclusiveMaximum" : false
      } ],
      "parentVars" : [ ],
      "mandatory" : [ "string_enum", "string_regex" ],
      "allMandatory" : [ "string_enum", "string_regex" ],
      "imports" : [ "StringEnum" ],
      "hasVars" : true,
      "emptyVars" : false,
      "hasMoreModels" : true,
      "hasEnums" : false,
      "isEnum" : false,
      "isNullable" : false,
      "hasRequired" : true,
      "hasOptional" : false,
      "isArrayModel" : false,
      "hasChildren" : false,
      "isMapModel" : false,
      "hasOnlyReadOnly" : false,
      "vendorExtensions" : { }
    }
  }, {
    "importPath" : "from petstore_api.models.string_enum import StringEnum",
    "model" : {
      "anyOf" : [ ],
      "oneOf" : [ ],
      "allOf" : [ ],
      "name" : "StringEnum",
      "classname" : "StringEnum",
      "classVarName" : "string_enum",
      "modelJson" : "{\n  \"type\" : \"string\",\n  \"enum\" : [ \"placed\", \"approved\", \"delivered\" ]\n}",
      "dataType" : "str",
      "classFilename" : "string_enum",
      "isAlias" : false,
      "isString" : true,
      "isInteger" : false,
      "isLong" : false,
      "isNumber" : false,
      "isNumeric" : false,
      "isFloat" : false,
      "isDouble" : false,
      "vars" : [ ],
      "allVars" : [ ],
      "requiredVars" : [ ],
      "optionalVars" : [ ],
      "readOnlyVars" : [ ],
      "readWriteVars" : [ ],
      "parentVars" : [ ],
      "allowableValues" : {
        "values" : [ "placed", "approved", "delivered" ],
        "enumVars" : [ {
          "name" : "PLACED",
          "isString" : false,
          "value" : "\"placed\""
        }, {
          "name" : "APPROVED",
          "isString" : false,
          "value" : "\"approved\""
        }, {
          "name" : "DELIVERED",
          "isString" : false,
          "value" : "\"delivered\""
        } ]
      },
      "mandatory" : [ ],
      "allMandatory" : [ ],
      "imports" : [ ],
      "hasVars" : false,
      "emptyVars" : true,
      "hasMoreModels" : false,
      "hasEnums" : false,
      "isEnum" : true,
      "isNullable" : false,
      "hasRequired" : false,
      "hasOptional" : false,
      "isArrayModel" : false,
      "hasChildren" : false,
      "isMapModel" : false,
      "hasOnlyReadOnly" : true,
      "vendorExtensions" : { }
    }
  } ],
  "appName" : "OpenAPI Petstore",
  "appDescription" : "This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \\\" \\\\",
  "contextPath" : "/v2",
  "packageVersion" : "1.0.0",
  "generateApiDocs" : true,
  "generatorVersion" : "4.2.2-SNAPSHOT",
  "releaseNote" : "Minor update",
  "version" : "1.0.0",
  "apiInfo" : {
    "apis" : [ {
      "appVersion" : "1.0.0",
      "generatorClass" : "org.openapitools.codegen.languages.PythonClientCodegen",
      "sortParamsByRequiredFlag" : true,
      "classVarName" : "fake_api",
      "generateModelDocs" : true,
      "hasImport" : true,
      "generateModelTests" : true,
      "strictSpecBehavior" : true,
      "generateApiTests" : true,
      "classFilename" : "fake_api",
      "lambda" : {
        "lowercase" : { },
        "uppercase" : { },
        "titlecase" : { },
        "camelcase" : { },
        "indented" : { },
        "indented_8" : { },
        "indented_12" : { },
        "indented_16" : { }
      },
      "generateModels" : true,
      "inputSpec" : "modules/openapi-generator/src/test/resources/2_0/primitive_models_validations_enums.yaml",
      "baseName" : "fake",
      "package" : "petstore_api.api",
      "imports" : [ {
        "import" : "from petstore_api.models.object_model_with_refs import ObjectModelWithRefs",
        "classname" : "ObjectModelWithRefs"
      }, {
        "import" : "from petstore_api.models.string_enum import StringEnum",
        "classname" : "StringEnum"
      }, {
        "import" : "from petstore_api.models.string_regex import StringRegex",
        "classname" : "StringRegex"
      } ],
      "contextPath" : "/v2",
      "appDescription" : "This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \\\" \\\\",
      "releaseNote" : "Minor update",
      "version" : "1.0.0",
      "modelDocPath" : "docs/",
      "projectName" : "petstore-api",
      "importPath" : "petstore_api.api.fake",
      "licenseUrl" : "http://www.apache.org/licenses/LICENSE-2.0.html",
      "generatedYear" : "2019",
      "modelPackage" : "petstore_api.models",
      "gitHost" : "github.com",
      "templateDir" : "/Users/justin/programming/openapi-generator/modules/openapi-generator/src/main/resources/python",
      "apiDocPath" : "docs/",
      "licenseInfo" : "Apache-2.0",
      "hasModel" : true,
      "generateApis" : true,
      "basePathWithoutHost" : "/v2",
      "operations" : {
        "classname" : "FakeApi",
        "operation" : [ {
          "responseHeaders" : [ ],
          "hasAuthMethods" : false,
          "hasConsumes" : false,
          "hasProduces" : true,
          "hasParams" : true,
          "hasOptionalParams" : true,
          "hasRequiredParams" : false,
          "returnTypeIsPrimitive" : false,
          "returnSimpleType" : true,
          "subresourceOperation" : false,
          "isMapContainer" : false,
          "isListContainer" : false,
          "isMultipart" : false,
          "hasMore" : true,
          "isResponseBinary" : false,
          "isResponseFile" : false,
          "hasReference" : true,
          "isRestfulIndex" : false,
          "isRestfulShow" : false,
          "isRestfulCreate" : false,
          "isRestfulUpdate" : false,
          "isRestfulDestroy" : false,
          "isRestful" : false,
          "isDeprecated" : false,
          "isCallbackRequest" : false,
          "path" : "/fake/ObjectModelWithRefs",
          "operationId" : "object_model_with_refs",
          "returnType" : "ObjectModelWithRefs",
          "httpMethod" : "POST",
          "returnBaseType" : "ObjectModelWithRefs",
          "unescapedNotes" : "Test serialization of ObjectModelWithRefs",
          "notes" : "Test serialization of ObjectModelWithRefs",
          "baseName" : "fake",
          "produces" : [ {
            "mediaType" : "*/*"
          } ],
          "servers" : [ ],
          "bodyParam" : {
            "isFormParam" : false,
            "isQueryParam" : false,
            "isPathParam" : false,
            "isHeaderParam" : false,
            "isCookieParam" : false,
            "isBodyParam" : true,
            "hasMore" : false,
            "isContainer" : false,
            "secondaryParam" : false,
            "isCollectionFormatMulti" : false,
            "isPrimitiveType" : false,
            "isModel" : true,
            "isExplode" : false,
            "baseName" : "body",
            "paramName" : "body",
            "dataType" : "ObjectModelWithRefs",
            "description" : "Input object as post body",
            "baseType" : "ObjectModelWithRefs",
            "example" : "petstore_api.ObjectModelWithRefs()",
            "isString" : false,
            "isNumeric" : false,
            "isInteger" : false,
            "isLong" : false,
            "isNumber" : false,
            "isFloat" : false,
            "isDouble" : false,
            "isByteArray" : false,
            "isBinary" : false,
            "isBoolean" : false,
            "isDate" : false,
            "isDateTime" : false,
            "isUuid" : false,
            "isUri" : false,
            "isEmail" : false,
            "isFreeFormObject" : false,
            "isListContainer" : false,
            "isMapContainer" : false,
            "isFile" : false,
            "isEnum" : false,
            "vendorExtensions" : { },
            "hasValidation" : false,
            "isNullable" : false,
            "required" : false,
            "exclusiveMaximum" : false,
            "exclusiveMinimum" : false,
            "uniqueItems" : false
          },
          "allParams" : [ {
            "isFormParam" : false,
            "isQueryParam" : false,
            "isPathParam" : false,
            "isHeaderParam" : false,
            "isCookieParam" : false,
            "isBodyParam" : true,
            "hasMore" : false,
            "isContainer" : false,
            "secondaryParam" : false,
            "isCollectionFormatMulti" : false,
            "isPrimitiveType" : false,
            "isModel" : true,
            "isExplode" : false,
            "baseName" : "body",
            "paramName" : "body",
            "dataType" : "ObjectModelWithRefs",
            "description" : "Input object as post body",
            "baseType" : "ObjectModelWithRefs",
            "example" : "petstore_api.ObjectModelWithRefs()",
            "isString" : false,
            "isNumeric" : false,
            "isInteger" : false,
            "isLong" : false,
            "isNumber" : false,
            "isFloat" : false,
            "isDouble" : false,
            "isByteArray" : false,
            "isBinary" : false,
            "isBoolean" : false,
            "isDate" : false,
            "isDateTime" : false,
            "isUuid" : false,
            "isUri" : false,
            "isEmail" : false,
            "isFreeFormObject" : false,
            "isListContainer" : false,
            "isMapContainer" : false,
            "isFile" : false,
            "isEnum" : false,
            "vendorExtensions" : { },
            "hasValidation" : false,
            "isNullable" : false,
            "required" : false,
            "exclusiveMaximum" : false,
            "exclusiveMinimum" : false,
            "uniqueItems" : false
          } ],
          "bodyParams" : [ {
            "isFormParam" : false,
            "isQueryParam" : false,
            "isPathParam" : false,
            "isHeaderParam" : false,
            "isCookieParam" : false,
            "isBodyParam" : true,
            "hasMore" : false,
            "isContainer" : false,
            "secondaryParam" : false,
            "isCollectionFormatMulti" : false,
            "isPrimitiveType" : false,
            "isModel" : true,
            "isExplode" : false,
            "baseName" : "body",
            "paramName" : "body",
            "dataType" : "ObjectModelWithRefs",
            "description" : "Input object as post body",
            "baseType" : "ObjectModelWithRefs",
            "example" : "petstore_api.ObjectModelWithRefs()",
            "isString" : false,
            "isNumeric" : false,
            "isInteger" : false,
            "isLong" : false,
            "isNumber" : false,
            "isFloat" : false,
            "isDouble" : false,
            "isByteArray" : false,
            "isBinary" : false,
            "isBoolean" : false,
            "isDate" : false,
            "isDateTime" : false,
            "isUuid" : false,
            "isUri" : false,
            "isEmail" : false,
            "isFreeFormObject" : false,
            "isListContainer" : false,
            "isMapContainer" : false,
            "isFile" : false,
            "isEnum" : false,
            "vendorExtensions" : { },
            "hasValidation" : false,
            "isNullable" : false,
            "required" : false,
            "exclusiveMaximum" : false,
            "exclusiveMinimum" : false,
            "uniqueItems" : false
          } ],
          "pathParams" : [ ],
          "queryParams" : [ ],
          "headerParams" : [ ],
          "formParams" : [ ],
          "cookieParams" : [ ],
          "requiredParams" : [ ],
          "optionalParams" : [ {
            "isFormParam" : false,
            "isQueryParam" : false,
            "isPathParam" : false,
            "isHeaderParam" : false,
            "isCookieParam" : false,
            "isBodyParam" : true,
            "hasMore" : false,
            "isContainer" : false,
            "secondaryParam" : false,
            "isCollectionFormatMulti" : false,
            "isPrimitiveType" : false,
            "isModel" : true,
            "isExplode" : false,
            "baseName" : "body",
            "paramName" : "body",
            "dataType" : "ObjectModelWithRefs",
            "description" : "Input object as post body",
            "baseType" : "ObjectModelWithRefs",
            "example" : "petstore_api.ObjectModelWithRefs()",
            "isString" : false,
            "isNumeric" : false,
            "isInteger" : false,
            "isLong" : false,
            "isNumber" : false,
            "isFloat" : false,
            "isDouble" : false,
            "isByteArray" : false,
            "isBinary" : false,
            "isBoolean" : false,
            "isDate" : false,
            "isDateTime" : false,
            "isUuid" : false,
            "isUri" : false,
            "isEmail" : false,
            "isFreeFormObject" : false,
            "isListContainer" : false,
            "isMapContainer" : false,
            "isFile" : false,
            "isEnum" : false,
            "vendorExtensions" : { },
            "hasValidation" : false,
            "isNullable" : false,
            "required" : false,
            "exclusiveMaximum" : false,
            "exclusiveMinimum" : false,
            "uniqueItems" : false
          } ],
          "tags" : [ {
            "name" : "fake",
            "description" : "A fake api"
          } ],
          "responses" : [ {
            "headers" : [ ],
            "code" : "200",
            "message" : "Returned object",
            "hasMore" : false,
            "dataType" : "ObjectModelWithRefs",
            "baseType" : "ObjectModelWithRefs",
            "hasHeaders" : false,
            "isString" : false,
            "isNumeric" : false,
            "isInteger" : false,
            "isLong" : false,
            "isNumber" : false,
            "isFloat" : false,
            "isDouble" : false,
            "isByteArray" : false,
            "isBoolean" : false,
            "isDate" : false,
            "isDateTime" : false,
            "isUuid" : false,
            "isEmail" : false,
            "isModel" : true,
            "isFreeFormObject" : false,
            "isDefault" : true,
            "simpleType" : true,
            "primitiveType" : false,
            "isMapContainer" : false,
            "isListContainer" : false,
            "isBinary" : false,
            "isFile" : false,
            "schema" : {
              "$ref" : "#/components/schemas/ObjectModelWithRefs"
            },
            "jsonSchema" : "{\n  \"description\" : \"Returned object\",\n  \"content\" : {\n    \"*/*\" : {\n      \"schema\" : {\n        \"$ref\" : \"#/components/schemas/ObjectModelWithRefs\"\n      }\n    }\n  }\n}",
            "vendorExtensions" : { },
            "wildcard" : false
          } ],
          "callbacks" : [ ],
          "imports" : [ "ObjectModelWithRefs" ],
          "examples" : [ {
            "contentType" : "*/*",
            "example" : "{\n  \"stringRegex\" : \"stringRegex\"\n}",
            "statusCode" : "200"
          } ],
          "requestBodyExamples" : [ {
            "contentType" : "*/*",
            "example" : "{\n  \"stringRegex\" : \"stringRegex\"\n}"
          } ],
          "vendorExtensions" : {
            "x-codegen-request-body-name" : "body"
          },
          "nickname" : "object_model_with_refs",
          "operationIdOriginal" : "ObjectModelWithRefs",
          "operationIdLowerCase" : "object_model_with_refs",
          "operationIdCamelCase" : "ObjectModelWithRefs",
          "operationIdSnakeCase" : "object_model_with_refs",
          "restfulShow" : false,
          "restfulIndex" : false,
          "restfulCreate" : false,
          "restfulUpdate" : false,
          "restfulDestroy" : false,
          "restful" : false,
          "hasQueryParams" : false,
          "hasHeaderParams" : false,
          "hasCookieParams" : false,
          "hasResponseHeaders" : false,
          "bodyAllowed" : true,
          "hasExamples" : true,
          "hasBodyParam" : true,
          "hasFormParams" : false,
          "hasPathParams" : false
        }, {
          "responseHeaders" : [ ],
          "hasAuthMethods" : false,
          "hasConsumes" : false,
          "hasProduces" : true,
          "hasParams" : true,
          "hasOptionalParams" : true,
          "hasRequiredParams" : false,
          "returnTypeIsPrimitive" : false,
          "returnSimpleType" : true,
          "subresourceOperation" : false,
          "isMapContainer" : false,
          "isListContainer" : false,
          "isMultipart" : false,
          "hasMore" : true,
          "isResponseBinary" : false,
          "isResponseFile" : false,
          "hasReference" : true,
          "isRestfulIndex" : false,
          "isRestfulShow" : false,
          "isRestfulCreate" : false,
          "isRestfulUpdate" : false,
          "isRestfulDestroy" : false,
          "isRestful" : false,
          "isDeprecated" : false,
          "isCallbackRequest" : false,
          "path" : "/fake/StringEnum",
          "operationId" : "string_enum",
          "returnType" : "StringEnum",
          "httpMethod" : "POST",
          "returnBaseType" : "StringEnum",
          "unescapedNotes" : "Test serialization of StringEnum",
          "notes" : "Test serialization of StringEnum",
          "baseName" : "fake",
          "produces" : [ {
            "mediaType" : "*/*"
          } ],
          "servers" : [ ],
          "bodyParam" : {
            "isFormParam" : false,
            "isQueryParam" : false,
            "isPathParam" : false,
            "isHeaderParam" : false,
            "isCookieParam" : false,
            "isBodyParam" : true,
            "hasMore" : false,
            "isContainer" : false,
            "secondaryParam" : false,
            "isCollectionFormatMulti" : false,
            "isPrimitiveType" : true,
            "isModel" : false,
            "isExplode" : false,
            "baseName" : "body",
            "paramName" : "body",
            "dataType" : "str",
            "description" : "Input string as post body",
            "baseType" : "str",
            "example" : "'body_example'",
            "isString" : true,
            "isNumeric" : false,
            "isInteger" : false,
            "isLong" : false,
            "isNumber" : false,
            "isFloat" : false,
            "isDouble" : false,
            "isByteArray" : false,
            "isBinary" : false,
            "isBoolean" : false,
            "isDate" : false,
            "isDateTime" : false,
            "isUuid" : false,
            "isUri" : false,
            "isEmail" : false,
            "isFreeFormObject" : false,
            "isListContainer" : false,
            "isMapContainer" : false,
            "isFile" : false,
            "isEnum" : false,
            "vendorExtensions" : { },
            "hasValidation" : false,
            "isNullable" : false,
            "required" : false,
            "exclusiveMaximum" : false,
            "exclusiveMinimum" : false,
            "uniqueItems" : false
          },
          "allParams" : [ {
            "isFormParam" : false,
            "isQueryParam" : false,
            "isPathParam" : false,
            "isHeaderParam" : false,
            "isCookieParam" : false,
            "isBodyParam" : true,
            "hasMore" : false,
            "isContainer" : false,
            "secondaryParam" : false,
            "isCollectionFormatMulti" : false,
            "isPrimitiveType" : true,
            "isModel" : false,
            "isExplode" : false,
            "baseName" : "body",
            "paramName" : "body",
            "dataType" : "str",
            "description" : "Input string as post body",
            "baseType" : "str",
            "example" : "'body_example'",
            "isString" : true,
            "isNumeric" : false,
            "isInteger" : false,
            "isLong" : false,
            "isNumber" : false,
            "isFloat" : false,
            "isDouble" : false,
            "isByteArray" : false,
            "isBinary" : false,
            "isBoolean" : false,
            "isDate" : false,
            "isDateTime" : false,
            "isUuid" : false,
            "isUri" : false,
            "isEmail" : false,
            "isFreeFormObject" : false,
            "isListContainer" : false,
            "isMapContainer" : false,
            "isFile" : false,
            "isEnum" : false,
            "vendorExtensions" : { },
            "hasValidation" : false,
            "isNullable" : false,
            "required" : false,
            "exclusiveMaximum" : false,
            "exclusiveMinimum" : false,
            "uniqueItems" : false
          } ],
          "bodyParams" : [ {
            "isFormParam" : false,
            "isQueryParam" : false,
            "isPathParam" : false,
            "isHeaderParam" : false,
            "isCookieParam" : false,
            "isBodyParam" : true,
            "hasMore" : false,
            "isContainer" : false,
            "secondaryParam" : false,
            "isCollectionFormatMulti" : false,
            "isPrimitiveType" : true,
            "isModel" : false,
            "isExplode" : false,
            "baseName" : "body",
            "paramName" : "body",
            "dataType" : "str",
            "description" : "Input string as post body",
            "baseType" : "str",
            "example" : "'body_example'",
            "isString" : true,
            "isNumeric" : false,
            "isInteger" : false,
            "isLong" : false,
            "isNumber" : false,
            "isFloat" : false,
            "isDouble" : false,
            "isByteArray" : false,
            "isBinary" : false,
            "isBoolean" : false,
            "isDate" : false,
            "isDateTime" : false,
            "isUuid" : false,
            "isUri" : false,
            "isEmail" : false,
            "isFreeFormObject" : false,
            "isListContainer" : false,
            "isMapContainer" : false,
            "isFile" : false,
            "isEnum" : false,
            "vendorExtensions" : { },
            "hasValidation" : false,
            "isNullable" : false,
            "required" : false,
            "exclusiveMaximum" : false,
            "exclusiveMinimum" : false,
            "uniqueItems" : false
          } ],
          "pathParams" : [ ],
          "queryParams" : [ ],
          "headerParams" : [ ],
          "formParams" : [ ],
          "cookieParams" : [ ],
          "requiredParams" : [ ],
          "optionalParams" : [ {
            "isFormParam" : false,
            "isQueryParam" : false,
            "isPathParam" : false,
            "isHeaderParam" : false,
            "isCookieParam" : false,
            "isBodyParam" : true,
            "hasMore" : false,
            "isContainer" : false,
            "secondaryParam" : false,
            "isCollectionFormatMulti" : false,
            "isPrimitiveType" : true,
            "isModel" : false,
            "isExplode" : false,
            "baseName" : "body",
            "paramName" : "body",
            "dataType" : "str",
            "description" : "Input string as post body",
            "baseType" : "str",
            "example" : "'body_example'",
            "isString" : true,
            "isNumeric" : false,
            "isInteger" : false,
            "isLong" : false,
            "isNumber" : false,
            "isFloat" : false,
            "isDouble" : false,
            "isByteArray" : false,
            "isBinary" : false,
            "isBoolean" : false,
            "isDate" : false,
            "isDateTime" : false,
            "isUuid" : false,
            "isUri" : false,
            "isEmail" : false,
            "isFreeFormObject" : false,
            "isListContainer" : false,
            "isMapContainer" : false,
            "isFile" : false,
            "isEnum" : false,
            "vendorExtensions" : { },
            "hasValidation" : false,
            "isNullable" : false,
            "required" : false,
            "exclusiveMaximum" : false,
            "exclusiveMinimum" : false,
            "uniqueItems" : false
          } ],
          "tags" : [ {
            "name" : "fake",
            "description" : "A fake api"
          } ],
          "responses" : [ {
            "headers" : [ ],
            "code" : "200",
            "message" : "Returned string",
            "hasMore" : false,
            "dataType" : "StringEnum",
            "baseType" : "StringEnum",
            "hasHeaders" : false,
            "isString" : false,
            "isNumeric" : false,
            "isInteger" : false,
            "isLong" : false,
            "isNumber" : false,
            "isFloat" : false,
            "isDouble" : false,
            "isByteArray" : false,
            "isBoolean" : false,
            "isDate" : false,
            "isDateTime" : false,
            "isUuid" : false,
            "isEmail" : false,
            "isModel" : true,
            "isFreeFormObject" : false,
            "isDefault" : true,
            "simpleType" : true,
            "primitiveType" : false,
            "isMapContainer" : false,
            "isListContainer" : false,
            "isBinary" : false,
            "isFile" : false,
            "schema" : {
              "$ref" : "#/components/schemas/StringEnum"
            },
            "jsonSchema" : "{\n  \"description\" : \"Returned string\",\n  \"content\" : {\n    \"*/*\" : {\n      \"schema\" : {\n        \"$ref\" : \"#/components/schemas/StringEnum\"\n      }\n    }\n  }\n}",
            "vendorExtensions" : { },
            "wildcard" : false
          } ],
          "callbacks" : [ ],
          "imports" : [ "StringEnum" ],
          "examples" : [ {
            "contentType" : "*/*",
            "example" : "null",
            "statusCode" : "200"
          } ],
          "requestBodyExamples" : [ {
            "output" : "none"
          } ],
          "vendorExtensions" : {
            "x-codegen-request-body-name" : "body"
          },
          "nickname" : "string_enum",
          "operationIdOriginal" : "StringEnum",
          "operationIdLowerCase" : "string_enum",
          "operationIdCamelCase" : "StringEnum",
          "operationIdSnakeCase" : "string_enum",
          "restfulShow" : false,
          "restfulIndex" : false,
          "restfulCreate" : false,
          "restfulUpdate" : false,
          "restfulDestroy" : false,
          "restful" : false,
          "hasQueryParams" : false,
          "hasHeaderParams" : false,
          "hasCookieParams" : false,
          "hasResponseHeaders" : false,
          "bodyAllowed" : true,
          "hasExamples" : true,
          "hasBodyParam" : true,
          "hasFormParams" : false,
          "hasPathParams" : false
        }, {
          "responseHeaders" : [ ],
          "hasAuthMethods" : false,
          "hasConsumes" : false,
          "hasProduces" : true,
          "hasParams" : true,
          "hasOptionalParams" : true,
          "hasRequiredParams" : false,
          "returnTypeIsPrimitive" : true,
          "returnSimpleType" : true,
          "subresourceOperation" : false,
          "isMapContainer" : false,
          "isListContainer" : false,
          "isMultipart" : false,
          "hasMore" : false,
          "isResponseBinary" : false,
          "isResponseFile" : false,
          "hasReference" : false,
          "isRestfulIndex" : false,
          "isRestfulShow" : false,
          "isRestfulCreate" : false,
          "isRestfulUpdate" : false,
          "isRestfulDestroy" : false,
          "isRestful" : false,
          "isDeprecated" : false,
          "isCallbackRequest" : false,
          "path" : "/fake/StringRegex",
          "operationId" : "string_regex",
          "returnType" : "str",
          "httpMethod" : "POST",
          "returnBaseType" : "str",
          "unescapedNotes" : "Test serialization of StringRegex",
          "notes" : "Test serialization of StringRegex",
          "baseName" : "fake",
          "produces" : [ {
            "mediaType" : "*/*"
          } ],
          "servers" : [ ],
          "bodyParam" : {
            "isFormParam" : false,
            "isQueryParam" : false,
            "isPathParam" : false,
            "isHeaderParam" : false,
            "isCookieParam" : false,
            "isBodyParam" : true,
            "hasMore" : false,
            "isContainer" : false,
            "secondaryParam" : false,
            "isCollectionFormatMulti" : false,
            "isPrimitiveType" : true,
            "isModel" : true,
            "isExplode" : false,
            "baseName" : "body",
            "paramName" : "body",
            "dataType" : "StringRegex",
            "description" : "Input string as post body",
            "baseType" : "StringRegex",
            "example" : "petstore_api.StringRegex()",
            "isString" : true,
            "isNumeric" : false,
            "isInteger" : false,
            "isLong" : false,
            "isNumber" : false,
            "isFloat" : false,
            "isDouble" : false,
            "isByteArray" : false,
            "isBinary" : false,
            "isBoolean" : false,
            "isDate" : false,
            "isDateTime" : false,
            "isUuid" : false,
            "isUri" : false,
            "isEmail" : false,
            "isFreeFormObject" : false,
            "isListContainer" : false,
            "isMapContainer" : false,
            "isFile" : false,
            "isEnum" : false,
            "vendorExtensions" : {
              "x-regex" : "^\\d{3}-\\d{2}-\\d{4}$",
              "x-modifiers" : [ ]
            },
            "hasValidation" : false,
            "isNullable" : false,
            "required" : false,
            "exclusiveMaximum" : false,
            "exclusiveMinimum" : false,
            "pattern" : "/^\\d{3}-\\d{2}-\\d{4}$/",
            "uniqueItems" : false
          },
          "allParams" : [ {
            "isFormParam" : false,
            "isQueryParam" : false,
            "isPathParam" : false,
            "isHeaderParam" : false,
            "isCookieParam" : false,
            "isBodyParam" : true,
            "hasMore" : false,
            "isContainer" : false,
            "secondaryParam" : false,
            "isCollectionFormatMulti" : false,
            "isPrimitiveType" : true,
            "isModel" : true,
            "isExplode" : false,
            "baseName" : "body",
            "paramName" : "body",
            "dataType" : "StringRegex",
            "description" : "Input string as post body",
            "baseType" : "StringRegex",
            "example" : "petstore_api.StringRegex()",
            "isString" : true,
            "isNumeric" : false,
            "isInteger" : false,
            "isLong" : false,
            "isNumber" : false,
            "isFloat" : false,
            "isDouble" : false,
            "isByteArray" : false,
            "isBinary" : false,
            "isBoolean" : false,
            "isDate" : false,
            "isDateTime" : false,
            "isUuid" : false,
            "isUri" : false,
            "isEmail" : false,
            "isFreeFormObject" : false,
            "isListContainer" : false,
            "isMapContainer" : false,
            "isFile" : false,
            "isEnum" : false,
            "vendorExtensions" : {
              "x-regex" : "^\\d{3}-\\d{2}-\\d{4}$",
              "x-modifiers" : [ ]
            },
            "hasValidation" : false,
            "isNullable" : false,
            "required" : false,
            "exclusiveMaximum" : false,
            "exclusiveMinimum" : false,
            "pattern" : "/^\\d{3}-\\d{2}-\\d{4}$/",
            "uniqueItems" : false
          } ],
          "bodyParams" : [ {
            "isFormParam" : false,
            "isQueryParam" : false,
            "isPathParam" : false,
            "isHeaderParam" : false,
            "isCookieParam" : false,
            "isBodyParam" : true,
            "hasMore" : false,
            "isContainer" : false,
            "secondaryParam" : false,
            "isCollectionFormatMulti" : false,
            "isPrimitiveType" : true,
            "isModel" : true,
            "isExplode" : false,
            "baseName" : "body",
            "paramName" : "body",
            "dataType" : "StringRegex",
            "description" : "Input string as post body",
            "baseType" : "StringRegex",
            "example" : "petstore_api.StringRegex()",
            "isString" : true,
            "isNumeric" : false,
            "isInteger" : false,
            "isLong" : false,
            "isNumber" : false,
            "isFloat" : false,
            "isDouble" : false,
            "isByteArray" : false,
            "isBinary" : false,
            "isBoolean" : false,
            "isDate" : false,
            "isDateTime" : false,
            "isUuid" : false,
            "isUri" : false,
            "isEmail" : false,
            "isFreeFormObject" : false,
            "isListContainer" : false,
            "isMapContainer" : false,
            "isFile" : false,
            "isEnum" : false,
            "vendorExtensions" : {
              "x-regex" : "^\\d{3}-\\d{2}-\\d{4}$",
              "x-modifiers" : [ ]
            },
            "hasValidation" : false,
            "isNullable" : false,
            "required" : false,
            "exclusiveMaximum" : false,
            "exclusiveMinimum" : false,
            "pattern" : "/^\\d{3}-\\d{2}-\\d{4}$/",
            "uniqueItems" : false
          } ],
          "pathParams" : [ ],
          "queryParams" : [ ],
          "headerParams" : [ ],
          "formParams" : [ ],
          "cookieParams" : [ ],
          "requiredParams" : [ ],
          "optionalParams" : [ {
            "isFormParam" : false,
            "isQueryParam" : false,
            "isPathParam" : false,
            "isHeaderParam" : false,
            "isCookieParam" : false,
            "isBodyParam" : true,
            "hasMore" : false,
            "isContainer" : false,
            "secondaryParam" : false,
            "isCollectionFormatMulti" : false,
            "isPrimitiveType" : true,
            "isModel" : true,
            "isExplode" : false,
            "baseName" : "body",
            "paramName" : "body",
            "dataType" : "StringRegex",
            "description" : "Input string as post body",
            "baseType" : "StringRegex",
            "example" : "petstore_api.StringRegex()",
            "isString" : true,
            "isNumeric" : false,
            "isInteger" : false,
            "isLong" : false,
            "isNumber" : false,
            "isFloat" : false,
            "isDouble" : false,
            "isByteArray" : false,
            "isBinary" : false,
            "isBoolean" : false,
            "isDate" : false,
            "isDateTime" : false,
            "isUuid" : false,
            "isUri" : false,
            "isEmail" : false,
            "isFreeFormObject" : false,
            "isListContainer" : false,
            "isMapContainer" : false,
            "isFile" : false,
            "isEnum" : false,
            "vendorExtensions" : {
              "x-regex" : "^\\d{3}-\\d{2}-\\d{4}$",
              "x-modifiers" : [ ]
            },
            "hasValidation" : false,
            "isNullable" : false,
            "required" : false,
            "exclusiveMaximum" : false,
            "exclusiveMinimum" : false,
            "pattern" : "/^\\d{3}-\\d{2}-\\d{4}$/",
            "uniqueItems" : false
          } ],
          "tags" : [ {
            "name" : "fake",
            "description" : "A fake api"
          } ],
          "responses" : [ {
            "headers" : [ ],
            "code" : "200",
            "message" : "Returned string",
            "hasMore" : false,
            "dataType" : "str",
            "baseType" : "str",
            "hasHeaders" : false,
            "isString" : true,
            "isNumeric" : false,
            "isInteger" : false,
            "isLong" : false,
            "isNumber" : false,
            "isFloat" : false,
            "isDouble" : false,
            "isByteArray" : false,
            "isBoolean" : false,
            "isDate" : false,
            "isDateTime" : false,
            "isUuid" : false,
            "isEmail" : false,
            "isModel" : false,
            "isFreeFormObject" : false,
            "isDefault" : true,
            "simpleType" : true,
            "primitiveType" : true,
            "isMapContainer" : false,
            "isListContainer" : false,
            "isBinary" : false,
            "isFile" : false,
            "schema" : {
              "pattern" : "^\\d{3}-\\d{2}-\\d{4}$",
              "type" : "string"
            },
            "jsonSchema" : "{\n  \"description\" : \"Returned string\",\n  \"content\" : {\n    \"*/*\" : {\n      \"schema\" : {\n        \"$ref\" : \"#/components/schemas/StringRegex\"\n      }\n    }\n  }\n}",
            "vendorExtensions" : { },
            "wildcard" : false
          } ],
          "callbacks" : [ ],
          "imports" : [ "StringRegex" ],
          "requestBodyExamples" : [ {
            "contentType" : "*/*",
            "example" : "null"
          } ],
          "vendorExtensions" : {
            "x-codegen-request-body-name" : "body"
          },
          "nickname" : "string_regex",
          "operationIdOriginal" : "StringRegex",
          "operationIdLowerCase" : "string_regex",
          "operationIdCamelCase" : "StringRegex",
          "operationIdSnakeCase" : "string_regex",
          "restfulShow" : false,
          "restfulIndex" : false,
          "restfulCreate" : false,
          "restfulUpdate" : false,
          "restfulDestroy" : false,
          "restful" : false,
          "hasQueryParams" : false,
          "hasHeaderParams" : false,
          "hasCookieParams" : false,
          "hasResponseHeaders" : false,
          "bodyAllowed" : true,
          "hasExamples" : false,
          "hasBodyParam" : true,
          "hasFormParams" : false,
          "hasPathParams" : false
        } ],
        "pathPrefix" : "fake_api"
      },
      "packageName" : "petstore_api",
      "hideGenerationTimestamp" : true,
      "unescapedAppDescription" : "This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \" \\",
      "appName" : "OpenAPI Petstore",
      "packageVersion" : "1.0.0",
      "generateApiDocs" : true,
      "generatorVersion" : "4.2.2-SNAPSHOT",
      "apiPackage" : "petstore_api.api",
      "basePath" : "http://petstore.swagger.io:80/v2",
      "classname" : "FakeApi",
      "gitRepoId" : "GIT_REPO_ID",
      "generatedDate" : "2019-11-30T09:05:21.413-08:00[America/Los_Angeles]",
      "appDescriptionWithNewLines" : "This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \\\" \\\\",
      "generateAliasAsModel" : "true",
      "gitUserId" : "GIT_USER_ID"
    } ]
  },
  "apiPackage" : "petstore_api.api",
  "hasServers" : true,
  "modelDocPath" : "docs/",
  "basePath" : "http://petstore.swagger.io:80/v2",
  "gitRepoId" : "GIT_REPO_ID",
  "generatedDate" : "2019-11-30T09:05:21.413-08:00[America/Los_Angeles]",
  "appDescriptionWithNewLines" : "This spec is mainly for testing Petstore server and contains fake endpoints, models. Please do not use this for any other purpose. Special characters: \\\" \\\\",
  "generateAliasAsModel" : "true",
  "projectName" : "petstore-api",
  "gitUserId" : "GIT_USER_ID"
}

Core Team @wing328 (2015/07) @jimschubert (2016/05) @cbornet (2016/05) @ackintosh (2018/02) @jmini (2018/04) @etherealjoy (2019/06)

Java Team @bbdouglas (2017/07) @sreeshas (2017/08) @jfiala (2017/08) @lukoyanov (2017/09) @cbornet (2017/09) @jeff9finger (2018/01) @karismann (2019/03) @Zomzog (2019/04) @lwlee2608 (2019/10)

wing328 commented 4 years ago

Given the following parameter description (which describes the same HTTP request payload):

      parameters:
        - name: body
          in: body
          description: Input string as post body
          schema:
            $ref: '#/definitions/StringRegex'
      parameters:
        - name: body
          in: body
          description: Input string as post body
          schema:
            type: string
            pattern: '^\d{3}-\d{2}-\d{4}$'

Are you saying defining the schema (string with pattern in this case) inline or using $ref should generate the code differently?

wing328 commented 4 years ago

generateAliasAsModel was introduced for backward compatibility in 4.x. We wanted to stop support generating array/map as a model but there were requests from IBM developers who prefer this way.

wing328 commented 4 years ago

A little bit more background on isAlias. It was introduced before we'd proper support of dereferencing schema. Now we've much better support of dereferencing schema, it may be removed in the future when we clean up the code.

wing328 commented 4 years ago

We store enums info at the model level

If I remember correctly, it was introduced to support enum class in Java (and of course not every language supports enum class natively)

wing328 commented 4 years ago

however some generators also use it to generate models for primitive type. For example the Python generator sets model.isAlias=true for StringRegex when using the Python client generator model.isAlias is turned on if the name of the model is in the typeAliases

Is it more correct to say that the Python generator doesn't generate models for primitive types at the moment but it seems to have the potential to do so using isAlias?

wing328 commented 4 years ago
      parameters:
        - name: body
          in: body
          description: Input string as post body
          schema:
            $ref: '#/definitions/StringRegex'
definitions:
  StringRegex:
    type: string
    pattern: '^\d{3}-\d{2}-\d{4}$'

I would suggest you open an issue via https://github.com/OAI/OpenAPI-Specification/issues to see if StringRegex defined above can be treated as a model instead of a primitive type (an alias to primitive type in this case).

jimschubert commented 4 years ago

This seems like it's not just string and pattern, but numbers with min/max and the inclusive and exclusive constraints are probably also affected.

I think this is more an issue with our ADT for CodegenModel and CodegenProperty. If we alias a model to a property, I think we need to retain all of the JSON Schema validation properties.

spacether commented 4 years ago

Given the following parameter description (which describes the same HTTP request payload):

      parameters:
        - name: body
          in: body
          description: Input string as post body
          schema:
            $ref: '#/definitions/StringRegex'
      parameters:
        - name: body
          in: body
          description: Input string as post body
          schema:
            type: string
            pattern: '^\d{3}-\d{2}-\d{4}$'

Are you saying defining the schema (string with pattern in this case) inline or using $ref should generate the code differently?

It would depend upon what the specific generators do with it. For python-experimental yes, we want to connect the request.body to a primitive model which stores the validations inside it. Python-experimental includes

spacether commented 4 years ago

however some generators also use it to generate models for primitive type. For example the Python generator sets model.isAlias=true for StringRegex when using the Python client generator model.isAlias is turned on if the name of the model is in the typeAliases

Is it more correct to say that the Python generator doesn't generate models for primitive types at the moment but it seems to have the potential to do so using isAlias?

We use isAlias models to store primitive schemas in object models. It's a hack and is conceptually very close to generating primitve models. The python-experimental ModelSimple models only store one property, and that property is a non-object type which contains validations or enums.

For example here is the OuterNumber ModelSimple class which stores a float with validations: https://github.com/OpenAPITools/openapi-generator/blob/master/samples/client/petstore/python-experimental/petstore_api/models/outer_number.py#L41

spacether commented 4 years ago

This seems like it's not just string and pattern, but numbers with min/max and the inclusive and exclusive constraints are probably also affected.

I think this is more an issue with our ADT for CodegenModel and CodegenProperty. If we alias a model to a property, I think we need to retain all of the JSON Schema validation properties.

I agree

wing328 commented 4 years ago

It would depend upon what the specific generators do with it.

It should be independent of generators. The below specs describe the same payload and should output the same code.

      parameters:
        - name: body
          in: body
          description: Input string as post body
          schema:
            $ref: '#/definitions/StringRegex'
      parameters:
        - name: body
          in: body
          description: Input string as post body
          schema:
            type: string
            pattern: '^\d{3}-\d{2}-\d{4}$'
spacether commented 4 years ago

One solution would be to add a field supportsPrimitiveModels to a generator. All of those which have it set to false would fully dereference primitive schemas where they are used. That's what we are currently doing. Those which have it set to true could generate primitive models and use them where referenced. Keeping the reference allows the same class to be used in multiple locations like a request and response which is helpful to users and gives us a place to put validations and enums for responsed which we don't yet have.

spacether commented 4 years ago

@wing328 do you have any ideas on how we can fix these two primitive model validation use cases?

wing328 commented 4 years ago

In the official spec (https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md), there's no such thing called "primitive model". Again I would recommend you open an issue with your suggestion on "primitive model" via https://github.com/OAI/OpenAPI-Specification/issues/new so that they can start a discussion on it and may eventually include it in the next official release.

For your use cases, what about enhancing CodegenParameter (for request) and CodegenResponse (for response) instead? (I need to perform more tests to confirm)

CodegeParameter already contains properties for validation and these properties should be copied to CodegenResponse as well.

spacether commented 4 years ago

In the official spec (https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md), there's no such thing called "primitive model". Again I would recommend you open an issue with your suggestion on "primitive model" via https://github.com/OAI/OpenAPI-Specification/issues/new so that they can start a discussion on it and may eventually include it in the next official release.

The spec stores Schemas. Per the spec those schemas can define primitives. We make models from those schemas. Spec Information

So I read that as primitive schemas with enums and validations are valid per the specs. We make models from schemas so I read that as primitive models are supported without needing to open an issue. Which part of this reasoning do you disagree with?

spacether commented 4 years ago

For your use cases, what about enhancing CodegenParameter (for request) and CodegenResponse (for response) instead? (I need to perform more tests to confirm)

CodegeParameter already contains properties for validation and these properties should be copied to CodegenResponse as well.

Adding the CodegenParameter to the response operation is a good idea and lets us store validations and enums. We may already be covered in the request as I think it uses CodegenParameters.

jimschubert commented 4 years ago

@spacether I think there's some confusion around the discussion above because of "primitive model validations". There's no such thing as a "primitive model" validation and what you're referring to is primitive "model validations".

I think the discussion in the issue is sidetracked by some miscommunication around the above. If we refer to the "model validations" rather as schema validation properties as defined in the spec (https://tools.ietf.org/html/draft-wright-json-schema-validation-00#section-5.8), I think this will help.

We do already have models for primitives: CodegenProperty and CodegenParameter. These validations already exist on CodegenModel as well. The problem is that, rather than treat schemas defined in the spec as models, we change some of those to primitives where possible. Personally, I think that if someone really wanted shared inline schemas in Yaml, they should just do it in Yaml syntax. It's possible that we've done this automatically in the tooling to provide the same usability to doc authors in both Yaml and Json.

The main issue is that these JSON Schema validation properties aren't carried over in that conversion. I would like to see two outcomes, myself:

1) allow users an option to generate code which more closely matches their spec document (don't convert StringRegex to a primitive)

2) honor property validations when a ref is flattened to a primitive (all other cases seem to retain validation properties as expected)

For option 1, I don't think it's an easy change with how tightly coupled ModelUtils is, and how structures are mutated in multiple places. I have work proposed in my Separation of Concerns project, but I've unfortunately had a lot less time to dedicate to contributing to open source since about June. I have made some slow progress toward pretty large refactors which would ease a lot of our issues, including the one mentioned above.

That said, I think a test case showing the failed conditions and their expectations helps quite a bit. Here's one which demonstrates the issue you've presented (please correct me if I'm wrong, but it seems in our above interactions that we understand the problem in the same way):

    @Test
    public void testRefModelValidationProperties(){
        OpenAPI openAPI = TestUtils.parseSpec("src/test/resources/2_0/refAliasedPrimitiveWithValidation.yml");
        ClientOptInput opts = new ClientOptInput();
        opts.setOpenAPI(openAPI);
        DefaultCodegen config = new DefaultCodegen();
        config.setStrictSpecBehavior(false);
        opts.setConfig(config);

        DefaultGenerator generator = new DefaultGenerator();
        generator.opts(opts);

        String expectedPattern = "^\\d{3}-\\d{2}-\\d{4}$";
        // NOTE: double-escaped regex for when the value is intended to be dumped in template into a String location.
        String escapedPattern = config.toRegularExpression(expectedPattern);

        Schema stringRegex = openAPI.getComponents().getSchemas().get("StringRegex");
        // Sanity check.
        Assert.assertEquals(stringRegex.getPattern(), expectedPattern);

        // Validate when we alias/unalias
        Schema unaliasedStringRegex = ModelUtils.unaliasSchema(openAPI, stringRegex);
        Assert.assertEquals(unaliasedStringRegex.getPattern(), expectedPattern);

        // Validate when converting to property
        CodegenProperty stringRegexProperty = config.fromProperty("stringRegex", stringRegex);
        Assert.assertEquals(stringRegexProperty.pattern, escapedPattern);

        // Validate when converting to parameter
        Operation operation = openAPI.getPaths().get("/fake/StringRegex").getPost();
        RequestBody body = operation.getRequestBody();
        CodegenParameter codegenParameter = config.fromRequestBody(body, new HashSet<>(), "body");

        Assert.assertEquals(codegenParameter.pattern, escapedPattern);

        // Validate when converting to response
        ApiResponse response = operation.getResponses().get("200");
        CodegenResponse codegenResponse = config.fromResponse("200", response);

        Assert.assertEquals(((Schema)codegenResponse.schema).getPattern(), expectedPattern);
    }

In this test, I would expect all cases to succeed. The last two, however, currently fail. I'm working to track down exactly why that is, and if I am able to figure it out, I'll open a PR where you can evaluate the fix.

spacether commented 4 years ago

@jimschubert I agree with your assessment. Yes, I am writing about schema validations for primitive types. Right, the response checks should fail. For the record our enum models are describing types that are string etc including primitive types.

jimschubert commented 4 years ago

@spacether would you be willing to evaluate this branch? https://github.com/jimschubert/openapi-generator/tree/minor-schema-validations-bug

The bug I found was a somewhat silly programmatic issue, considering we're intending to remove ClientInputOpts:

diff --git a/modules/openapi-generator/src/main/java/org/openapitools/codegen/ClientOptInput.java b/modules/openapi-generator/src/main/java/org/openapitools/codegen/ClientOptInput.java
index 71716ba25c2..2a6f32028ad 100644
--- a/modules/openapi-generator/src/main/java/org/openapitools/codegen/ClientOptInput.java
+++ b/modules/openapi-generator/src/main/java/org/openapitools/codegen/ClientOptInput.java
@@ -30,12 +30,12 @@
     private List<AuthorizationValue> auths;

     public ClientOptInput openAPI(OpenAPI openAPI) {
-        this.openAPI = openAPI;
+        this.setOpenAPI(openAPI);
         return this;
     }

     public ClientOptInput config(CodegenConfig codegenConfig) {
-        this.config = codegenConfig;
+        this.setConfig(codegenConfig);
         return this;
     }

@@ -72,6 +72,10 @@ public CodegenConfig getConfig() {
     @Deprecated
     public void setConfig(CodegenConfig config) {
         this.config = config;
+        // TODO: ClientOptInputs needs to be retired
+        if (this.openAPI != null) {
+            this.config.setOpenAPI(this.openAPI);
+        }
     }

     @Deprecated
@@ -86,5 +90,9 @@ public OpenAPI getOpenAPI() {
     @Deprecated
     public void setOpenAPI(OpenAPI openAPI) {
         this.openAPI = openAPI;
+        // TODO: ClientOptInputs needs to be retired
+        if (this.config != null) {
+            this.config.setOpenAPI(this.openAPI);
+        }
     }
 }

Without this change, the test I shared above will fail because some parts of the OpenAPI object model aren't able to be referenced properly.

I tried running this against the python-experimental generator, but I get an NPE due to some logic adding a value property to primitives. I think this looks like either a workaround for the issue solved by the diff above, or maybe a misuse or misunderstanding of the CodegenProperty, CodegenParameter, and CodegenResponse types.

As an example, using the csharp generator, CodegenProperty can be iterated in any of our "vars" collections. In C#, we allow the user to decide whether the model is validatable. See https://github.com/OpenAPITools/openapi-generator/blob/0c0050578f2d23255c146e690fe01f7c6b049eae/modules/openapi-generator/src/main/resources/csharp/modelGeneric.mustache#L224:

image

C# does not, however, support the validations on primitive inputs, but you could add to the api.mustache in any operation method in master:

            // body params patterns= {{#bodyParams}}{{pattern}}{{^-last}}, {{/-last}}{{/bodyParams}}
            // responses pattern= {{#responses}}{{schema.pattern}}{{^-last}}, {{/-last}}{{/responses}}

This would result in a potentially confusing pattern difference:

image

This is because body patterns are already escaped in preparation for what the generator defines as a regular expression (see DefaultCodegen#toRegularExpression)

In my branch, I've added pattern, and prepared it as an escaped string as well:

// responses pattern= {{#responses}}{{pattern}}{{^-last}}, {{/-last}}{{/responses}}

This gives us equivalent escapes:

image

If this works out for you, I think the way we could proceed to ensure we have validation properties everywhere they should be is to:

import java.math.BigDecimal;

public interface IJsonSchemaValidationProperties { BigDecimal getMaximum(); void setMaximum(BigDecimal maximum);

BigDecimal getMinimum();
void setMinimum(BigDecimal minimum);

String getPattern();
void setPattern(String pattern);

Boolean isExclusiveMaximum();
void setExclusiveMaximum(Boolean exclusiveMaximum);

Boolean isExclusiveMinimum();
void setExclusiveMinimum(Boolean exclusiveMinimum);

Integer getMinLength();
void setMinLength(Integer minLength);

Integer getMaxLength();
void setMaxLength(Integer maxLength);

Integer getMinItems();
void setMinItems(Integer minItems);

Integer getMaxItems();
void setMaxItems(Integer maxItems);

Boolean isUniqueItems();
void setUniqueItems(Boolean uniqueItems);

Integer getMinProperties();
void setMinProperties(Integer minProperties);

Integer getMaxProperties();
void setMaxProperties(Integer maxProperties);

}

* Apply this to `CodegenProperty`, `CodegenParameter`, and `CodegenResponse`
* Create a ModelUtils helper which accepts a `Schema` and `IJsonSchemaValidationProperties`. Something like this:
public static void syncValidationProperties(Schema schema, IJsonSchemaValidationProperties target){
    if (schema != null && target != null) {
        target.setPattern(schema.getPattern());
        target.setMinimum(schema.getMinimum());
        target.setMaximum(schema.getMaximum());
        target.setExclusiveMinimum(schema.getExclusiveMinimum());
        target.setExclusiveMaximum(schema.getExclusiveMaximum());
        target.setMinLength(schema.getMinLength());
        target.setMaxLength(schema.getMaxLength());
        target.setMinItems(schema.getMinItems());
        target.setMaxItems(schema.getMaxItems());
        target.setUniqueItems(schema.getUniqueItems());
        target.setMinProperties(schema.getMinProperties());
        target.setMaxProperties(schema.getMaxProperties());
    }
}

This would have to be called in all helpers which perform a conversion (fromParameter, fromProperty, fromResponse, etc).

I'd have a few concerns with my above proposal:

* Introducing the necessary getters/setters will change our exposed models which may be consumed by users programmatically existing generators
* Boolean getter/setter syntax has some special use cases with Mustache (e.g. use of "get" prefix vs "is" prefix). This would need to be tested.
* We do transformations in more than one place, so we would likely miss something.
spacether commented 4 years ago

@jimschubert the interface definition would solve my concern about the response not including the validations and enums info. Our current definition of CodegenResponse.java is missing this info at: https://github.com/OpenAPITools/openapi-generator/blob/master/modules/openapi-generator/src/main/java/org/openapitools/codegen/CodegenResponse.java

@wing328 @jimschubert if this information were included in the response then I could delete the python-experimental code which handles primitive schemas with validations or enums because all of our send/receive interfaces would do the required validation of values. Until that happens, I will need to keep using the ModelSimple classes to store enums/validation so responses can be checked when they are received from the server.

Why not just include a response.parameter as type CodegenParameter? Then we can include the required info there without having to add a new interface. One downside of that is that it is one level deeper than expected. A developer might expect to see that info as properties in the CodegenResponse class.

jimschubert commented 4 years ago

@spacether while prototyping my above proposal, I found a few issues with CodegenParameter as well. I have a fix that I've shared with the core team, and if it looks good I'll open a PR. I didn't start with the PR in case there are concerns about the changes to the types that I'm unaware of causing conflicts (example: getIExclusiveMaximum in CodegenParameter is spelled incorrectly, but I don't know if that was an uncommented workaround). My change will fix 3 bugs, at least.

I think there was some confusion within the issue about the use of "model" to refer to our Codegen* types. I think we're all on the same page that inputs and outputs for an operation should have the same validation properties if they are sourced from the same OpenAPI Schema item.

As a side note, I don't think we should have something like response.parameter as that becomes confusing. Response is the definition of the type and how it fits into the operation. If we added composition to Response, we would probably want to fully restructure it and that's a non-trivial task.

Regarding the suggestion above about supportsPrimitiveModels, I think this would extend the responsibilities of the generator beyond it's intent. For example, if your StringRegex above was an Id schema to represent a 64-bit integer it might make sense in some systems to create a class called Id with a single parameter called value or something similar. If we did this automagically, we would have to choose a parameter name which doesn't conflict with any language keywords. Then, we'd have to be concerned with serialization strategies in all generators in case someone chose that option (String inputs hydrate to an Id instance). This is a fairly huge effort. Personally, I think this would make for a great suggestion on the CLI validate command; if a Schema is used as an input and output model but has a type other than object/array/map, we could suggest restructuring the definition to add type guarantees to those instances.

spacether commented 4 years ago

The concerns raised here have been addressed in the above #4762 diff Operations now store validations and enums on operation.request.parameter, and operation.response. Even if we have no primitive models we have access to all the required validation and enum requirements for primitive, map and array data in those locations. Prior to that PR, operation.response did not include validation and enum data for primitive dataTypes.

jimschubert commented 4 years ago

@spacether thank you for verifying the fix and for summarizing here!