Yelp / swagger_spec_validator

Other
104 stars 71 forks source link

jsonschema.exceptions.ValidationError in code generated for python-flask #110

Closed Alive24 closed 5 years ago

Alive24 commented 5 years ago

Hi there, I tried to get an issue solved when I was using Swagger Editor and the python-flask code it generated, and I was referred to your project.

One of their people believed that

Okay, I think I see now - looks like the validator you're using is looking at the Swagger 2.0 JSON Schema document.

This seems to be a bug with the swagger_spec_validator project not being able to consume the Swagger schema correctly, not a problem with Swagger Editor not catching something.

I'm going to close this for now, but if you file a bug with that project and they think Swagger Editor is doing something wrong, ping me here and I'd be happy to take another look.

Thanks! 😄

The link to the issue: https://github.com/swagger-api/swagger-editor/issues/1912

Thank you!

I also attached the original post just FYI.

  • OS: Win
  • Browser: Chrome
  • Version: latest
  • Method of installation: docker
  • Swagger-Editor version: 3.6.16
  • Swagger/OpenAPI version:Swagger 2.0,

Content & configuration

Tried to start debugging after installing requirements. Reported error while editor didn't report any.

Swagger/OpenAPI definition:

swagger: "2.0"
info:
  title: ProjectAgora
  description: >-
    Optional multiline or single-line description in
    [CommonMark](http://commonmark.org/help/) or HTML.
  version: 0.0.1
host: 127.0.0.1:5000
basePath: /v1
schemes: 
  - https
consumes: 
  - application/json
produces: 
  - application/json
securityDefinitions:
  APIKeyHeader: 
    type: apiKey
    in: header
    name: api_key
security:
  - APIKeyHeader: []

definitions:
  Error:
    description: 错误
    properties:
      code: 
        type: integer
        example: 401
      message: 
        type: string    
  TokenStatus:
    description: Token信息
    properties:
      token:
        type: string
      expireBy:
        type: string
      refreshed:
        type: boolean
  LoginRequest:
    description: 登陆请求
    properties:
      username:
        type: string
      password:
        type: string
  Learner:
    description: 学习者
    properties:
      id:
        description: ID
        type: integer
      name:
        description: 全名
        type: string
      nickname:
        description: 昵称
        type: string
      isMentor:
        description: 是否导师
        type: boolean
      gender:
        description: 性别
        type: string
      status:
        description: 目前状态,包括在读、在读(游学)、在读(试读)、毕业、导师等;考虑是否需要更换成integer
        type: string
      birthday:
        description: 生日
        type: string
      mainPersonalIdType:
        description: 证件类型
        type: string
      mainPersonalId:
        description: 证件号码
        type: string
      age:
        description: 年龄(应当是计算字段)
        type: number
      ethnicity:
        description: 民族
        type: string
      phoneNumber:
        description: 电话号码
        type: string
      dateOfRegistration:
        description: 加入时间
        type: string
      reasonOfRegistration:
        description: 加入原因
        type: string
      previousStatus:
        description: 加入前状态
        type: string
      dateOfLeave:
        description: 离开时间
        type: string
      reasonOfLeave:
        description: 离开原因
        type: string
      destinationOfLeave:
        description: 下阶段目的地
        type: string
      notes:
        description: 备注
        type: array
        items: 
          type: string
      mentorship:
        description: 导师关系 (object类型 - 导师类型 - learnerID), 分有负责导师mentorResponsible,协同导师mentorSupportive, 家校沟通导师mentorCommunicative)
        type: object
      salaryCard:
        description: 工资卡号
        type: string
      custodianInfo:
        description: 监护人信息
        type: object
        properties: 
          name:
            description: 姓名
            type: string
          relationship:
            description: 关系 
            type: string
      emergentContact:
        description: 紧急联系人
        type: array
        items:
          type: object
          properties:
            name: 
              description: 紧急联系人姓名
              type: string
            number:
              description: 紧急联系人电话
              type: string
      weChatContact:
        description: 微信
        type: string
      QQ:
        description: QQ
        type: string
      mailAddress:
        description: 收件地址(用以寄送书面文件)
        type: string
      email:
        description: 电子邮箱
        type: string
      medicalNotes:
        description: 医疗记录
        type: object
        properties:
          generalHealthStatus:
            description: 整体健康状况;0 - 很好(基本不生病,从不住院); 1 - 一般(偶尔生病住院); 2 - 欠佳(每个季度都曾生病住院)
            type: integer
            enum: [0, 1, 2]
          bloodType:
            description: 血型
            type: string
          lastPhysicalExam:
            description: 最近一次体检时间(以年为单位)
            type: number
          previousDiagnosis:
            description: 既往疾病
            type: array
            items:
              type: object
              properties: 
                nameOfDiagnosis:
                  description: 疾病名称
                  type: string
                hospitalOfDiagnosis:
                  description: 诊断医院
                  type: string
          regularMedication:
            description: 长期用药
            type: array
            items:
              type: object
              properties:
                nameOfMedication:
                  description: 药品名
                  type: string
                instructionOfMedication:
                  description: 用药医嘱
                  type: string
          foodAlergy:
            description: 食物过敏
            type: object
            properties:
              lactoseIntolerance: 
                description: 奶类过敏(乳糖不耐受)
                type: boolean
              eggAllergy:
                description: 禽蛋类过敏
                type: boolean
              fishAllergy:
                description: 鱼类过敏
                type: boolean
              shellAllergy:
                description: 甲壳类(虾蟹、贝类水产等)
                type: boolean
              peanutAllergy:
                description: 花生过敏
                type: boolean
              soyBeanAllergy:
                description: 大豆过敏
                type: boolean
              nutAllergy:
                description: 坚果类过敏
                type: boolean
              wheatAllergy:
                description: 小麦过敏
                type: boolean
              otherFoodAllergy:
                description: 其他食物过敏
                type: array
                items:
                  type: string
          medicationAllergy:
            description: 药物过敏
            type: object
            properties:
              antibioticsAllergy:
                description: 抗生素过敏(青霉素、氨基苄青霉素、链霉素、卡那霉素)
                type: boolean
              sulfonamidesAllergy:
                description: 磺胺类药物过敏(磺胺噻唑、磺胺嘧啶、长效磺胺、复方新诺明等)
                type: boolean
              painkillerAllergy:
                description: 解热镇痛药(阿司匹林、去痛片)
                type: boolean
              anestheticAllergy:
                description: 麻醉用药(普鲁卡因)
                type: boolean
              vaccineAllergy:
                description: 疫苗类药物
                type: boolean
              otherMedicationAllergy:
                description: 其他药物过敏
                type: array
                items:
                  type: string
    required:
      - name
      - isMentor
  Project:
    description: 项目
    properties:
      id:
        description: 项目ID
        type: integer
      name: 
        description: 项目名称
        type: string
      createdTime:
        description: 创建时间
        type: string
      createdBy:
        description: 创建人ID
        type: integer
      relatedCourseId:
        description: 关联的课程ID;自由项目的课程ID为0
        type: integer
      content: 
        description: 项目记录的条目
        type: array
        items: 
          type: object
          properties:
            timestamp:
              description: 条目时间戳
              type: string
            instruction:
              description: 课程项目中教师为各条目定义的内容(暂定使用Markdown编码)
              type: string
            record:
              description: 学生记录(暂定使用Markdown编码)
              type: string
    required:
      - name
      - createdTime
      - createdBy
      - relatedCourseId
  Course:
    description: 课程
    properties:
      id:
        description: 课程ID
        type: integer
      name:
        description: 课程名称
        type: string
      createdTime: 
        description: 创建时间 
        type: string
      createdBy:
        description: 创建人ID
        type: integer
paths:
  /login:
    get:
      summary: 获取当前Token信息
      tags: 
        - Authentication
      responses:
        '200':
          description: 获取当前Token信息成功
          schema:
            $ref: '#/definitions/TokenStatus'
        '401':
          description: 无法获取当前Token信息
          headers:
            WWW_Authenticate:
              type: string
    post:
      summary: 尝试登陆
      tags: 
        - Authentication
      parameters:
        - in: body
          name: login
          required: true
          schema:
            $ref: '#/definitions/LoginRequest'
      responses:
        '200':
          description: 登陆成功
          schema:
              $ref: '#/definitions/TokenStatus'
        '401':
          description: 登陆失败
          schema:
                $ref: "#/responses/UnauthorizedError"
  /learner:
    head:
      summary: 返回所有Learner的关键信息
      tags: 
        - Learner
      responses:
        '200':
          description: 成功
          schema:
              type: object
              properties:
                id:
                  type: integer
                  example: 1
                name:
                  type: string
                  example: John Appleseed
        '401':
          description: 登陆失败
          schema:
            $ref: "#/responses/UnauthorizedError"
    get:
      summary: 返回所有的Leaner详细信息
      tags: 
        - Learner
      description: 返回所有的Leaner详细信息
      responses:
        '200':
          description: A JSON array of user names
          schema:
              $ref: '#/definitions/Learner'
        '401':
          $ref: "#/responses/UnauthorizedError"
    post:
      summary: 创建一个Learner
      tags: 
        - Learner
      parameters:
        - in: body
          name: login
          required: true
          schema:
              $ref: '#/definitions/Learner'
      responses:
        '201':
          description: 创建成功
          schema:
            description: 成功创建的Learner的ID
            type: object
            properties:
              id:
                type: integer
        '401':
          $ref: "#/responses/UnauthorizedError"  
  '/learner/{learnerId}':
    get:
      summary: 返回一个Learner的详细信息
      tags: 
        - Learner
      parameters:
        - in: path
          name: learnerId
          required: true
          type: integer
          format: int64
          minimum: 1
      responses:
        '200':
          description: OK
          schema:
            $ref: '#/definitions/Learner'
        '401':
          $ref: "#/responses/UnauthorizedError"

responses:
  UnauthorizedError:
    description: API key is missing or invalid
    headers:
      WWW_Authenticate:
        type: string

Error information:

Traceback (most recent call last):
  File "C:\ProgramData\Anaconda3\envs\ProjectAgora\lib\site-packages\swagger_spec_validator\common.py", line 17, in wrapper
    return method(*args, **kwargs)
  File "C:\ProgramData\Anaconda3\envs\ProjectAgora\lib\site-packages\swagger_spec_validator\validator20.py", line 125, in validate_json
    cls=Draft4Validator)
  File "C:\ProgramData\Anaconda3\envs\ProjectAgora\lib\site-packages\swagger_spec_validator\ref_validators.py", line 34, in validate
    instance_cls(schema, *args, **kwargs).validate(instance)
  File "C:\ProgramData\Anaconda3\envs\ProjectAgora\lib\site-packages\jsonschema\validators.py", line 130, in validate
    raise error
jsonschema.exceptions.ValidationError: {'description': '登陆失败', 'schema': {'$ref': '#/responses/UnauthorizedError', 'x-scope': ['']}} is not valid under any of the given schemas

Failed validating 'oneOf' in schema['properties']['paths']['patternProperties']['^/']['properties']['post']['properties']['responses']['patternProperties']['^([0-9]{3})$|^(default)$']:
    {'oneOf': [{'$ref': '#/definitions/response'},
               {'$ref': '#/definitions/jsonReference'}]}

On instance['paths']['/login']['post']['responses']['401']:
    {'description': '登陆失败',
     'schema': {'$ref': '#/responses/UnauthorizedError', 'x-scope': ['']}}

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\ProgramData\Anaconda3\envs\ProjectAgora\lib\runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)
  File "C:\ProgramData\Anaconda3\envs\ProjectAgora\lib\runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "C:\Users\xct24\Documents\Git-AgoraAcademy\ProjectAgora\server\swagger_server\__main__.py", line 16, in <module>
    main()
  File "C:\Users\xct24\Documents\Git-AgoraAcademy\ProjectAgora\server\swagger_server\__main__.py", line 11, in main
    app.add_api('swagger.yaml', arguments={'title': 'ProjectAgora'})
  File "C:\ProgramData\Anaconda3\envs\ProjectAgora\lib\site-packages\connexion\apps\flask_app.py", line 54, in add_api
    api = super(FlaskApp, self).add_api(specification, **kwargs)
  File "C:\ProgramData\Anaconda3\envs\ProjectAgora\lib\site-packages\connexion\apps\abstract.py", line 159, in add_api
    options=api_options.as_dict())
  File "C:\ProgramData\Anaconda3\envs\ProjectAgora\lib\site-packages\connexion\apis\abstract.py", line 92, in __init__
    validate_spec(spec)
  File "C:\ProgramData\Anaconda3\envs\ProjectAgora\lib\site-packages\swagger_spec_validator\validator20.py", line 82, in validate_spec
    http_handlers=http_handlers)
  File "C:\ProgramData\Anaconda3\envs\ProjectAgora\lib\site-packages\swagger_spec_validator\common.py", line 22, in wrapper
    sys.exc_info()[2])
  File "C:\ProgramData\Anaconda3\envs\ProjectAgora\lib\site-packages\six.py", line 692, in reraise
    raise value.with_traceback(tb)
  File "C:\ProgramData\Anaconda3\envs\ProjectAgora\lib\site-packages\swagger_spec_validator\common.py", line 17, in wrapper
    return method(*args, **kwargs)
  File "C:\ProgramData\Anaconda3\envs\ProjectAgora\lib\site-packages\swagger_spec_validator\validator20.py", line 125, in validate_json
    cls=Draft4Validator)
  File "C:\ProgramData\Anaconda3\envs\ProjectAgora\lib\site-packages\swagger_spec_validator\ref_validators.py", line 34, in validate
    instance_cls(schema, *args, **kwargs).validate(instance)
  File "C:\ProgramData\Anaconda3\envs\ProjectAgora\lib\site-packages\jsonschema\validators.py", line 130, in validate
    raise error
swagger_spec_validator.common.SwaggerValidationError: {'description': '登陆失败', 'schema': {'$ref': '#/responses/UnauthorizedError', 'x-scope': ['']}} is not valid under any of the given schemas

Failed validating 'oneOf' in schema['properties']['paths']['patternProperties']['^/']['properties']['post']['properties']['responses']['patternProperties']['^([0-9]{3})$|^(default)$']:
    {'oneOf': [{'$ref': '#/definitions/response'},
               {'$ref': '#/definitions/jsonReference'}]}

On instance['paths']['/login']['post']['responses']['401']:
    {'description': '登陆失败',
     'schema': {'$ref': '#/responses/UnauthorizedError', 'x-scope': ['']}}

I can't find clues of what went wrong as I did follow the docs step by step. Thank you very much!

macisamuele commented 5 years ago

Hey @xct24 I've just checked your specs and I did found 2 issues on them.

In [1]: from swagger_spec_validator.validator20 import validate_spec_url
In [2]: # NOTE: I'm on OSX so the path could look different
In [3]: validate_spec_url('file:///Users/maci/Desktop/specs.yaml')
...
SwaggerValidationError: ('("{\'description\': \'登陆失败\', \'schema\': {\'$ref\': \'#/responses/UnauthorizedError\', \'x-scope\': [\'file:///Users/maci/Desktop/specs.yaml\']}} is not valid under any of the given schemas\\n\\nFailed validating \'oneOf\' in schema[\'properties\'][\'paths\'][\'patternProperties\'][\'^/\'][\'properties\'][\'head\'][\'properties\'][\'responses\'][\'patternProperties\'][\'^([0-9]{3})$|^(default)$\']:\\n    {\'oneOf\': [{\'$ref\': \'#/definitions/response\'},\\n               {\'$ref\': \'#/definitions/jsonReference\'}]}\\n\\nOn instance[\'paths\'][\'/learner\'][\'head\'][\'responses\'][\'401\']:\\n    {\'description\': \'登陆失败\',\\n     \'schema\': {\'$ref\': \'#/responses/UnauthorizedError\',\\n                \'x-scope\': [\'file:///Users/maci/Desktop/specs.yaml\']}}", <ValidationError: "{\'description\': \'登陆失败\', \'schema\': {\'$ref\': \'#/responses/UnauthorizedError\', \'x-scope\': [\'file:///Users/maci/Desktop/specs.yaml\']}} is not valid under any of the given schemas">)', SwaggerValidationError("{'description': '登陆失败', 'schema': {'$ref': '#/responses/UnauthorizedError', 'x-scope': ['file:///Users/maci/Desktop/specs.yaml']}} is not valid under any of the given schemas\n\nFailed validating 'oneOf' in schema['properties']['paths']['patternProperties']['^/']['properties']['head']['properties']['responses']['patternProperties']['^([0-9]{3})$|^(default)$']:\n    {'oneOf': [{'$ref': '#/definitions/response'},\n               {'$ref': '#/definitions/jsonReference'}]}\n\nOn instance['paths']['/learner']['head']['responses']['401']:\n    {'description': '登陆失败',\n     'schema': {'$ref': '#/responses/UnauthorizedError',\n                'x-scope': ['file:///Users/maci/Desktop/specs.yaml']}}", <ValidationError: "{'description': '登陆失败', 'schema': {'$ref': '#/responses/UnauthorizedError', 'x-scope': ['file:///Users/maci/Desktop/specs.yaml']}} is not valid under any of the given schemas">))
In [4]: # The issue is not really expressive, so I did extract the inner validation error
In [5]: try:
    ...:     validator20.validate_spec_url('file:///Users/maci/Desktop/specs.yaml')
    ...: except Exception as e:
    ...:     print(e.args[1].args[1])
    ...:
{'description': '登陆失败', 'schema': {'$ref': '#/responses/UnauthorizedError', 'x-scope': ['file:///Users/maci/Desktop/specs.yaml']}} is not valid under any of the given schemas

Failed validating 'oneOf' in schema['properties']['paths']['patternProperties']['^/']['properties']['head']['properties']['responses']['patternProperties']['^([0-9]{3})$|^(default)$']:
    {'oneOf': [{'$ref': '#/definitions/response'},
               {'$ref': '#/definitions/jsonReference'}]}

On instance['paths']['/login']['post']['responses']['401']:
    {'description': '登陆失败',
     'schema': {'$ref': '#/responses/UnauthorizedError',
                'x-scope': ['file:///Users/maci/Desktop/specs.yaml']}}

Checking the specs for instance['paths']['/login']['post']['responses']['401'] I see that it is defined as:

    '401':
          description: 登陆失败
          schema:
                $ref: "#/responses/UnauthorizedError"

but #/responses/UnauthorizedError is a valid response object and not a valid schema object. Checking other usage of the object in the specs I see something like

        '401':
          $ref: "#/responses/UnauthorizedError"

By fixing that occurrence and an other one (again reported with the same method above) the validation passes

In [7]: try:
    ...:     validator20.validate_spec_url('file:///Users/maci/Desktop/fixed.yaml')
    ...: except Exception as e:
    ...:     print(e.args[1].args[1])
    ...:

In [8]:

Spec diff

--- /Users/maci/Desktop/specs.yaml  Thu Dec 13 17:59:57 2018
+++ /Users/maci/Desktop/fixed.yaml    Thu Dec 13 18:00:11 2018
@@ -331,9 +331,7 @@
           schema:
               $ref: '#/definitions/TokenStatus'
         '401':
-          description: 登陆失败
-          schema:
-                $ref: "#/responses/UnauthorizedError"
+          $ref: "#/responses/UnauthorizedError"
   /learner:
     head:
       summary: 返回所有Learner的关键信息
@@ -352,9 +350,7 @@
                   type: string
                   example: John Appleseed
         '401':
-          description: 登陆失败
-          schema:
-            $ref: "#/responses/UnauthorizedError"
+          $ref: "#/responses/UnauthorizedError"
     get:
       summary: 返回所有的Leaner详细信息
       tags: 
Alive24 commented 5 years ago

Thank you! Problem solved