pyeve / eve-swagger

Swagger extension for Eve-powered RESTful APIs
http://python-eve.org
Other
154 stars 43 forks source link

v0.0.5 breaks swagger ui editor #30

Closed omani closed 7 years ago

omani commented 7 years ago

if I paste the json form /api-docs to http://editor.swagger.io/#/ with the new v0.0.5, swagger editor complains about the json.

I guess v0.0.5 breaks swagger ui/editor?

nicolaiarocci commented 7 years ago

@otibsa would you be willing to check this one? 0.0.5 is mostly your work anyways :)

otibsa commented 7 years ago

@omani Can you give an example of the json document that breaks the Editor?

omani commented 7 years ago

{ "basePath": "/api/v1", "consumes": [ "application/json" ], "definitions": { "hosts": { "properties": { "_id": { "format": "objectid", "type": "string" }, "alias": { "default": "", "type": "string" }, "gateway": { "default": "", "type": "string" }, "hostname": { "maxLength": 63, "minLength": 1, "type": "string" }, "options": { "default": {}, "type": "object" }, "user": { "default": "", "type": "string" } }, "required": [ "hostname" ], "type": "object" }, "recipes": { "properties": { "_id": { "format": "objectid", "type": "string" }, "file": { "type": "media" }, "name": { "$ref": "#/definitions/recipes_name" }, "tasks": { "items": { "type": "string" }, "type": "array" } }, "required": [ "name" ], "type": "object" }, "recipes_name": { "maxLength": 35, "minLength": 1, "type": "string" }, "recipes_tasks.task": null, "roles": { "properties": { "_id": { "format": "objectid", "type": "string" }, "description": { "default": "", "type": "string" }, "gateway": { "type": "string" }, "hosts": { "items": { "type": "string" }, "type": "array" }, "name": { "maxLength": 63, "minLength": 1, "type": "string" }, "options": { "default": {}, "type": "object" }, "tags": { "items": { "type": "string" }, "type": "array" }, "user": { "type": "string" } }, "required": [ "name" ], "type": "object" }, "tasks": { "properties": { "_id": { "format": "objectid", "type": "string" }, "do_parallel": { "type": "boolean" }, "hosts": { "items": { "type": "string" }, "type": "array" }, "output": { "items": { "type": "string" }, "type": "array" }, "roles": { "items": { "type": "string" }, "type": "array" }, "runtime": { "type": "string" }, "state": { "type": "object" }, "task": { "properties": { "arguments": { "type": "object" }, "recipe": { "$ref": "#/definitions/recipes_name" }, "task": { "$ref": "#/definitions/recipes_tasks.task" } }, "required": [ "task", "recipe" ], "type": "object" } }, "required": [ "task" ], "type": "object" }, "users": { "properties": { "_id": { "format": "objectid", "type": "string" }, "enabled": { "default": true, "type": "boolean" }, "password": { "type": "string" }, "reset_password": { "properties": { "current_password": { "type": "string" }, "new_password1": { "type": "string" }, "new_password2": { "type": "string" } }, "type": "object" }, "roles": { "default": "user", "items": { "type": "string" }, "type": "array" }, "settings": { "properties": { "2fa": { "default": false, "type": "boolean" }, "avatar": { "default": "", "type": "string" }, "bio": { "properties": { "company": { "default": "", "type": "string" }, "name": { "default": "", "type": "string" }, "position": { "default": "", "type": "string" } }, "type": "object" }, "email": { "default": "", "type": "string" }, "private_token": { "default": "", "type": "string" }, "ssh": { "properties": { "private_key": { "default": "", "type": "string" }, "public_key": { "default": "", "type": "string" } }, "type": "object" } }, "type": "object" }, "uid": { "maxLength": 35, "minLength": 1, "type": "string" } }, "required": [ "uid", "password" ], "type": "object" } }, "host": "127.0.0.1:5000", "info": { "contact": { "name": "nicola", "url": "http://nicolaiarocci.com" }, "description": "an API description", "license": { "name": "BSD", "url": "https://github.com/nicolaiarocci/eve-swagger/blob/master/LICENSE" }, "termsOfService": "my terms of service", "title": "My Supercool API", "version": "1.0" }, "parameters": { "hostsid": { "description": "", "format": "objectid", "in": "path", "name": "hostsId", "required": true, "type": "string" }, "recipesid": { "description": "", "format": "objectid", "in": "path", "name": "recipesId", "required": true, "type": "string" }, "rolesid": { "description": "", "format": "objectid", "in": "path", "name": "rolesId", "required": true, "type": "string" }, "tasksid": { "description": "", "format": "objectid", "in": "path", "name": "tasksId", "required": true, "type": "string" }, "usersid": { "description": "", "format": "objectid", "in": "path", "name": "usersId", "required": true, "type": "string" } }, "paths": { "/hosts": { "get": { "responses": { "200": { "description": "An array of hosts", "schema": { "items": { "$ref": "#/definitions/hosts" }, "type": "array" } } }, "summary": "Retrieves one or more hosts", "tags": [ "hosts" ] }, "post": { "parameters": [ { "in": "body", "name": "hosts", "required": true, "schema": { "$ref": "#/definitions/hosts" } } ], "responses": { "200": { "description": "operation has been successful" } }, "summary": "Stores one or more hosts", "tags": [ "hosts" ] } }, "/hosts/{hostsId}": { "delete": { "parameters": [ { "$ref": "#/parameters/hostsid" }, { "description": "Current value of the _etag field", "in": "header", "name": "If-Match", "required": true, "type": "string" } ], "responses": { "200": { "description": "hosts document deleted successfully" } }, "summary": "Deletes a hosts document", "tags": [ "hosts" ] }, "get": { "parameters": [ { "$ref": "#/parameters/hostsid" } ], "responses": { "200": { "description": "hosts document fetched successfully", "schema": { "$ref": "#/definitions/hosts" } } }, "summary": "Retrieves a hosts document", "tags": [ "hosts" ] }, "patch": { "parameters": [ { "$ref": "#/parameters/hostsid" }, { "in": "body", "name": "hosts", "required": true, "schema": { "$ref": "#/definitions/hosts" } }, { "description": "Current value of the _etag field", "in": "header", "name": "If-Match", "required": true, "type": "string" } ], "responses": { "200": { "description": "hosts document updated successfully" } }, "summary": "Updates a hosts document", "tags": [ "hosts" ] }, "put": { "parameters": [ { "$ref": "#/parameters/hostsid" }, { "in": "body", "name": "hosts", "required": true, "schema": { "$ref": "#/definitions/hosts" } }, { "description": "Current value of the _etag field", "in": "header", "name": "If-Match", "required": true, "type": "string" } ], "responses": { "200": { "description": "hosts document replaced successfully" } }, "summary": "Replaces a hosts document", "tags": [ "hosts" ] } }, "/recipes": { "get": { "responses": { "200": { "description": "An array of recipes", "schema": { "items": { "$ref": "#/definitions/recipes" }, "type": "array" } } }, "summary": "Retrieves one or more recipes", "tags": [ "recipes" ] }, "post": { "parameters": [ { "in": "body", "name": "recipes", "required": true, "schema": { "$ref": "#/definitions/recipes" } } ], "responses": { "200": { "description": "operation has been successful" } }, "summary": "Stores one or more recipes", "tags": [ "recipes" ] } }, "/recipes/{recipesId}": { "delete": { "parameters": [ { "$ref": "#/parameters/recipes__id" }, { "description": "Current value of the _etag field", "in": "header", "name": "If-Match", "required": true, "type": "string" } ], "responses": { "200": { "description": "recipes document deleted successfully" } }, "summary": "Deletes a recipes document", "tags": [ "recipes" ] }, "get": { "parameters": [ { "$ref": "#/parameters/recipesid" } ], "responses": { "200": { "description": "recipes document fetched successfully", "schema": { "$ref": "#/definitions/recipes" } } }, "summary": "Retrieves a recipes document", "tags": [ "recipes" ] }, "patch": { "parameters": [ { "$ref": "#/parameters/recipesid" }, { "in": "body", "name": "recipes", "required": true, "schema": { "$ref": "#/definitions/recipes" } }, { "description": "Current value of the _etag field", "in": "header", "name": "If-Match", "required": true, "type": "string" } ], "responses": { "200": { "description": "recipes document updated successfully" } }, "summary": "Updates a recipes document", "tags": [ "recipes" ] }, "put": { "parameters": [ { "$ref": "#/parameters/recipes__id" }, { "in": "body", "name": "recipes", "required": true, "schema": { "$ref": "#/definitions/recipes" } }, { "description": "Current value of the _etag field", "in": "header", "name": "If-Match", "required": true, "type": "string" } ], "responses": { "200": { "description": "recipes document replaced successfully" } }, "summary": "Replaces a recipes document", "tags": [ "recipes" ] } }, "/roles": { "get": { "responses": { "200": { "description": "An array of roles", "schema": { "items": { "$ref": "#/definitions/roles" }, "type": "array" } } }, "summary": "Retrieves one or more roles", "tags": [ "roles" ] }, "post": { "parameters": [ { "in": "body", "name": "roles", "required": true, "schema": { "$ref": "#/definitions/roles" } } ], "responses": { "200": { "description": "operation has been successful" } }, "summary": "Stores one or more roles", "tags": [ "roles" ] } }, "/roles/{rolesId}": { "delete": { "parameters": [ { "$ref": "#/parameters/rolesid" }, { "description": "Current value of the _etag field", "in": "header", "name": "If-Match", "required": true, "type": "string" } ], "responses": { "200": { "description": "roles document deleted successfully" } }, "summary": "Deletes a roles document", "tags": [ "roles" ] }, "get": { "parameters": [ { "$ref": "#/parameters/rolesid" } ], "responses": { "200": { "description": "roles document fetched successfully", "schema": { "$ref": "#/definitions/roles" } } }, "summary": "Retrieves a roles document", "tags": [ "roles" ] }, "patch": { "parameters": [ { "$ref": "#/parameters/rolesid" }, { "in": "body", "name": "roles", "required": true, "schema": { "$ref": "#/definitions/roles" } }, { "description": "Current value of the _etag field", "in": "header", "name": "If-Match", "required": true, "type": "string" } ], "responses": { "200": { "description": "roles document updated successfully" } }, "summary": "Updates a roles document", "tags": [ "roles" ] }, "put": { "parameters": [ { "$ref": "#/parameters/rolesid" }, { "in": "body", "name": "roles", "required": true, "schema": { "$ref": "#/definitions/roles" } }, { "description": "Current value of the _etag field", "in": "header", "name": "If-Match", "required": true, "type": "string" } ], "responses": { "200": { "description": "roles document replaced successfully" } }, "summary": "Replaces a roles document", "tags": [ "roles" ] } }, "/tasks": { "get": { "responses": { "200": { "description": "An array of tasks", "schema": { "items": { "$ref": "#/definitions/tasks" }, "type": "array" } } }, "summary": "Retrieves one or more tasks", "tags": [ "tasks" ] }, "post": { "parameters": [ { "in": "body", "name": "tasks", "required": true, "schema": { "$ref": "#/definitions/tasks" } } ], "responses": { "200": { "description": "operation has been successful" } }, "summary": "Stores one or more tasks", "tags": [ "tasks" ] } }, "/tasks/{tasksId}": { "delete": { "parameters": [ { "$ref": "#/parameters/tasks__id" }, { "description": "Current value of the _etag field", "in": "header", "name": "If-Match", "required": true, "type": "string" } ], "responses": { "200": { "description": "tasks document deleted successfully" } }, "summary": "Deletes a tasks document", "tags": [ "tasks" ] }, "get": { "parameters": [ { "$ref": "#/parameters/tasksid" } ], "responses": { "200": { "description": "tasks document fetched successfully", "schema": { "$ref": "#/definitions/tasks" } } }, "summary": "Retrieves a tasks document", "tags": [ "tasks" ] }, "patch": { "parameters": [ { "$ref": "#/parameters/tasksid" }, { "in": "body", "name": "tasks", "required": true, "schema": { "$ref": "#/definitions/tasks" } }, { "description": "Current value of the _etag field", "in": "header", "name": "If-Match", "required": true, "type": "string" } ], "responses": { "200": { "description": "tasks document updated successfully" } }, "summary": "Updates a tasks document", "tags": [ "tasks" ] }, "put": { "parameters": [ { "$ref": "#/parameters/tasks__id" }, { "in": "body", "name": "tasks", "required": true, "schema": { "$ref": "#/definitions/tasks" } }, { "description": "Current value of the _etag field", "in": "header", "name": "If-Match", "required": true, "type": "string" } ], "responses": { "200": { "description": "tasks document replaced successfully" } }, "summary": "Replaces a tasks document", "tags": [ "tasks" ] } }, "/users": { "get": { "responses": { "200": { "description": "An array of users", "schema": { "items": { "$ref": "#/definitions/users" }, "type": "array" } } }, "summary": "Retrieves one or more users", "tags": [ "users" ] }, "post": { "parameters": [ { "in": "body", "name": "users", "required": true, "schema": { "$ref": "#/definitions/users" } } ], "responses": { "200": { "description": "operation has been successful" } }, "summary": "Stores one or more users", "tags": [ "users" ] } }, "/users/{usersId}": { "delete": { "parameters": [ { "$ref": "#/parameters/usersid" }, { "description": "Current value of the _etag field", "in": "header", "name": "If-Match", "required": true, "type": "string" } ], "responses": { "200": { "description": "users document deleted successfully" } }, "summary": "Deletes a users document", "tags": [ "users" ] }, "get": { "parameters": [ { "$ref": "#/parameters/usersid" } ], "responses": { "200": { "description": "users document fetched successfully", "schema": { "$ref": "#/definitions/users" } } }, "summary": "Retrieves a users document", "tags": [ "users" ] }, "patch": { "parameters": [ { "$ref": "#/parameters/usersid" }, { "in": "body", "name": "users", "required": true, "schema": { "$ref": "#/definitions/users" } }, { "description": "Current value of the _etag field", "in": "header", "name": "If-Match", "required": true, "type": "string" } ], "responses": { "200": { "description": "users document updated successfully" } }, "summary": "Updates a users document", "tags": [ "users" ] }, "put": { "parameters": [ { "$ref": "#/parameters/users__id" }, { "in": "body", "name": "users", "required": true, "schema": { "$ref": "#/definitions/users" } }, { "description": "Current value of the _etag field", "in": "header", "name": "If-Match", "required": true, "type": "string" } ], "responses": { "200": { "description": "users document replaced successfully" } }, "summary": "Replaces a users document", "tags": [ "users" ] } } }, "produces": [ "application/json" ], "schemes": [ "http" ], "swagger": "2.0", "tags": [ { "name": "recipes" }, { "name": "tasks" }, { "name": "hosts" }, { "name": "users" }, { "name": "roles" } ] }

omani commented 7 years ago

the above json is what /api-docs responds with. I pasted this into http://editor.swagger.io

otibsa commented 7 years ago

could you also post the Eve DOMAIN definition that creates this Swagger doc? (Use something like pastebin.com for big code pastes) (don't post any sensitive information)

Or maybe create a minimal example that creates the your error.

omani commented 7 years ago

what do you mean by DOMAIN definition. I just used

app.register_blueprint(swagger)

to create the swagger.

otibsa commented 7 years ago

I mean the DOMAIN dictionary that should be in your settings.py file. (Where your API endpoints are defined) e.g.

DOMAIN = {
  'people': {
    'item_lookup_field': 'name',
    'type': 'dict',
    'schema': {
      'name': {'type': 'string', 'required': True, 'unique': True},
      'nickname': {'type': 'string'}
    }
  }
}

I don't know where the errors in Swagger Editor are coming from, but with the DOMAIN variable it would be easier to troubleshoot.

omani commented 7 years ago

ok for example: swagger editor says to:

recipes:
    properties:
      _id:
        format: objectid
        type: string
      file:
        type: media
      name:
        $ref: '#/definitions/recipes_name'
      tasks:
        items:
          type: string
        type: array
    required:
      - name
    type: object

Not a valid file definition for the field key.

the domain for this looks like this:

# SCHEMA DEFINITION
schema = {
    # Schema definition, based on Cerberus grammar. Check the Cerberus project
    # (https://github.com/nicolaiarocci/cerberus) for details.
    'name': {
        'type': 'string',
        'isValidFileName': True,
        'minlength': 1,
        'maxlength': 35,
        'required': True,
        'unique_to_user': True
    },
    'file': {
        'type': 'media'
    },
    'tasks': {
        'type': 'list'
    }
}
omani commented 7 years ago

the file directive is for gridfs in this case.

otibsa commented 7 years ago

The problem is the file field with type media. We could replace the eve-type media with Swagger-type file but it that type not allowed as a parameter in the body, only in formData. But formData can't reference a schema, and it is not allowed to have a body parameter and formData in the same operation.

So this is a bigger problem. I would suggest ignoring the error for now if possible (Swagger Editor still renders the documentation with the type media). @nicolaiarocci: Care to comment?

omani commented 7 years ago

so I can ignore the errors and it would still work?

eg. generating client libs etc.

nicolaiarocci commented 7 years ago

I'm not sure on client libs generation etc. as I don't use eve-swagger for that kind of stuff, but for documentation rendering I agree that for the time being the only solution is to ignore the error report).

omani commented 7 years ago

alright. thank you.