Dorthu / openapi3

A Python3 OpenAPI 3 Spec Parser
BSD 3-Clause "New" or "Revised" License
116 stars 46 forks source link

Failure to parse data after get #20

Open DanBuchan opened 3 years ago

DanBuchan commented 3 years ago

I'm using Django rest framework to auto generate a schema for a very simple API, one endpoint listGenes returns a json object which is an array of objects:

[
    {
        "id": 1,
        "name": "gene1"
    },
    {
        "id": 2,
        "name": "gene2
    }.
]

openapi3 correctly parses the schema and I can access the operations but when I called the listgenes operation, api.call_listGenes(), I get the following traceback:

Traceback (most recent call last):
  File "openapi_client.py", line 13, in <module>
    gene_list = api.call_listGenes()
  File "/Users/dbuchan/.virtualenvs/advanced_web_dev/lib/python3.8/site-packages/openapi3/openapi.py", line 188, in __call__
    return self.operation(self.base_url, *args, security=self.security,
  File "/Users/dbuchan/.virtualenvs/advanced_web_dev/lib/python3.8/site-packages/openapi3/paths.py", line 287, in request
    return expected_media.schema.model(result.json())
  File "/Users/dbuchan/.virtualenvs/advanced_web_dev/lib/python3.8/site-packages/openapi3/schemas.py", line 113, in model
    return self.get_type()(data, self)
  File "/Users/dbuchan/.virtualenvs/advanced_web_dev/lib/python3.8/site-packages/openapi3/schemas.py", line 93, in get_type
    '__slots__': self.properties.keys()
AttributeError: 'NoneType' object has no attribute 'keys'

The schema is

openapi: 3.0.2
info:
  title: Genedata
  version: 1.0.0
  description: API for interacting with gene records
servers:
  - url: http://127.0.0.1:8080
paths:
  /api/gene/{id}/:
    get:
      operationId: RetrieveGene
      description: ''
      parameters:
      - name: id
        in: path
        required: true
        description: A unique integer value identifying this gene.
        schema:
          type: string
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  gene_id:
                    type: string
                    maxLength: 256
                  entity:
                    type: string
                    maxLength: 256
                  start:
                    type: integer
                    maximum: 2147483647
                    minimum: -2147483648
                  stop:
                    type: integer
                    maximum: 2147483647
                    minimum: -2147483648
                  sense:
                    type: string
                    maxLength: 1
                  start_codon:
                    type: string
                    maxLength: 1
                  ec:
                    properties:
                      id:
                        type: integer
                        readOnly: true
                      ec_name:
                        type: string
                        readOnly: true
                    type: object
                  sequencing:
                    properties:
                      id:
                        type: integer
                        readOnly: true
                      sequencing_factory:
                        type: string
                        readOnly: true
                      factory_location:
                        type: string
                        readOnly: true
                    type: object
                required:
                - gene_id
                - entity
                - sense
                - ec
                - sequencing
          description: ''
    post:
      operationId: CreateGene
      description: ''
      parameters:
      - name: id
        in: path
        required: true
        description: A unique integer value identifying this gene.
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema: &id001
              properties:
                gene_id:
                  type: string
                  maxLength: 256
                entity:
                  type: string
                  maxLength: 256
                start:
                  type: integer
                  maximum: 2147483647
                  minimum: -2147483648
                stop:
                  type: integer
                  maximum: 2147483647
                  minimum: -2147483648
                sense:
                  type: string
                  maxLength: 1
                start_codon:
                  type: string
                  maxLength: 1
                ec:
                  properties:
                    id:
                      type: integer
                      readOnly: true
                    ec_name:
                      type: string
                      readOnly: true
                  type: object
                sequencing:
                  properties:
                    id:
                      type: integer
                      readOnly: true
                    sequencing_factory:
                      type: string
                      readOnly: true
                    factory_location:
                      type: string
                      readOnly: true
                  type: object
              required:
              - gene_id
              - entity
              - sense
              - ec
              - sequencing
          application/x-www-form-urlencoded:
            schema: *id001
          multipart/form-data:
            schema: *id001
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  gene_id:
                    type: string
                    maxLength: 256
                  entity:
                    type: string
                    maxLength: 256
                  start:
                    type: integer
                    maximum: 2147483647
                    minimum: -2147483648
                  stop:
                    type: integer
                    maximum: 2147483647
                    minimum: -2147483648
                  sense:
                    type: string
                    maxLength: 1
                  start_codon:
                    type: string
                    maxLength: 1
                  ec:
                    properties:
                      id:
                        type: integer
                        readOnly: true
                      ec_name:
                        type: string
                        readOnly: true
                    type: object
                  sequencing:
                    properties:
                      id:
                        type: integer
                        readOnly: true
                      sequencing_factory:
                        type: string
                        readOnly: true
                      factory_location:
                        type: string
                        readOnly: true
                    type: object
                required:
                - gene_id
                - entity
                - sense
                - ec
                - sequencing
          description: ''
    put:
      operationId: UpdateGene
      description: ''
      parameters:
      - name: id
        in: path
        required: true
        description: A unique integer value identifying this gene.
        schema:
          type: string
      requestBody:
        content:
          application/json:
            schema: &id002
              properties:
                gene_id:
                  type: string
                  maxLength: 256
                entity:
                  type: string
                  maxLength: 256
                start:
                  type: integer
                  maximum: 2147483647
                  minimum: -2147483648
                stop:
                  type: integer
                  maximum: 2147483647
                  minimum: -2147483648
                sense:
                  type: string
                  maxLength: 1
                start_codon:
                  type: string
                  maxLength: 1
                ec:
                  properties:
                    id:
                      type: integer
                      readOnly: true
                    ec_name:
                      type: string
                      readOnly: true
                  type: object
                sequencing:
                  properties:
                    id:
                      type: integer
                      readOnly: true
                    sequencing_factory:
                      type: string
                      readOnly: true
                    factory_location:
                      type: string
                      readOnly: true
                  type: object
              required:
              - gene_id
              - entity
              - sense
              - ec
              - sequencing
          application/x-www-form-urlencoded:
            schema: *id002
          multipart/form-data:
            schema: *id002
      responses:
        '200':
          content:
            application/json:
              schema:
                properties:
                  gene_id:
                    type: string
                    maxLength: 256
                  entity:
                    type: string
                    maxLength: 256
                  start:
                    type: integer
                    maximum: 2147483647
                    minimum: -2147483648
                  stop:
                    type: integer
                    maximum: 2147483647
                    minimum: -2147483648
                  sense:
                    type: string
                    maxLength: 1
                  start_codon:
                    type: string
                    maxLength: 1
                  ec:
                    properties:
                      id:
                        type: integer
                        readOnly: true
                      ec_name:
                        type: string
                        readOnly: true
                    type: object
                  sequencing:
                    properties:
                      id:
                        type: integer
                        readOnly: true
                      sequencing_factory:
                        type: string
                        readOnly: true
                      factory_location:
                        type: string
                        readOnly: true
                    type: object
                required:
                - gene_id
                - entity
                - sense
                - ec
                - sequencing
          description: ''
    delete:
      operationId: DestroyGene
      description: ''
      parameters:
      - name: id
        in: path
        required: true
        description: A unique integer value identifying this gene.
        schema:
          type: string
      responses:
        '204':
          description: ''
  /api/genes:
    get:
      operationId: listGenes
      description: 'Get a list of genes'
      parameters: []
      responses:
        '200':
          content:
            application/json:
              schema:
                type: array
                items:
                  properties:
                    id:
                      type: integer
                      readOnly: true
                    gene_id:
                      type: string
                      maxLength: 256
                  required:
                  - gene_id
          description: 'Sucesfully returns gene list'
commonism commented 2 years ago

I guess fixed by #55

DanBuchan commented 2 years ago

That looks like it is the issue. I don't have code/system set up to confirm that this is the fix right now though.

Henchway commented 2 years ago

Still getting this exact error, not sure what i'd need to include in the schema for this to disappear. The schema was generated using FastAPI.

{
    "openapi": "3.0.2",
    "info": {
        "title": "FastAPI",
        "version": "0.1.0"
    },
    "servers": [
        {
            "url": "http://localhost:8000",
            "description": "Staging environment"
        }
    ],
    "paths": {
        "/": {
            "get": {
                "summary": "Root",
                "operationId": "root__get",
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {}
                            }
                        }
                    }
                }
            }
        },
        "/hello/{name}": {
            "get": {
                "summary": "Say Hello",
                "operationId": "say_hello_hello__name__get",
                "parameters": [
                    {
                        "required": true,
                        "schema": {
                            "title": "Name",
                            "type": "string"
                        },
                        "name": "name",
                        "in": "path"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "Successful Response",
                        "content": {
                            "application/json": {
                                "schema": {}
                            }
                        }
                    },
                    "422": {
                        "description": "Validation Error",
                        "content": {
                            "application/json": {
                                "schema": {"$ref": "#/components/schemas/HTTPValidationError"}
                            }
                        }
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "HTTPValidationError": {
                "title": "HTTPValidationError",
                "type": "object",
                "properties": {
                    "detail": {
                        "title": "Detail",
                        "type": "array",
                        "items": {"$ref": "#/components/schemas/ValidationError"}
                    }
                }
            },
            "ValidationError": {
                "title": "ValidationError",
                "required": [
                    "loc",
                    "msg",
                    "type"
                ],
                "type": "object",
                "properties": {
                    "loc": {
                        "title": "Location",
                        "type": "array",
                        "items": {"type": "string"}
                    },
                    "msg": {
                        "title": "Message",
                        "type": "string"
                    },
                    "type": {
                        "title": "Error Type",
                        "type": "string"
                    }
                }
            }
        }
    }
}
commonism commented 2 years ago

This is unrelated to the array problem.

                                "schema": {}

Can you post the API signature for rootget and say_hello_helloname__get?, I think they lack a definition for the response/return value.

Henchway commented 2 years ago

I'm hoping you mean the method signature:

from fastapi import FastAPI

app = FastAPI(servers=[{"url": "http://localhost:8000", "description": "Staging environment"}, ])

@app.get("/")
async def root():
    return {"message": "Hello World"}

@app.get("/hello/{name}")
async def say_hello(name: str):
    return {"message": f"Hello {name}"}
commonism commented 2 years ago

the response/return type is undefined.

define it:

from fastapi import FastAPI, BaseModel

app = FastAPI(servers=[{"url": "http://localhost:8000", "description": "Staging environment"}, ])

class Message(BaseModel):
    message:str

@app.get("/", response_model=Message)
async def root():
    return Message(message="Hello World")

@app.get("/hello/{name}", responses={200:{"model": Message}})
async def say_hello(name: str):
    return Message(message=f"Hello {name}")

untested.