robotframework-openapidriver icon indicating copy to clipboard operation
robotframework-openapidriver copied to clipboard

ValueError: /tasks not found in paths section of the OpenAPI document.

Open phramusca opened this issue 2 years ago • 3 comments

Hi, I am trying OpenApiDriver against a simple openapi with 5 endpoints and though 9 tests are generated, only the first 3 pass. The remaining 6 all fail with "ValueError: /tasks not found in paths section of the OpenAPI document." I have no idea what this means. I have changed parameters to {id} to avoid the need of a mapping, but I am affraid I would need one but since I do not really understand the issue I do not know how to write that maping. Can you help ? Thanks

Here is the python fastapi server:

from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from typing import List

app = FastAPI()

# Pydantic model for task (excluding 'id')
class TaskCreate(BaseModel):
    title: str
    description: str
    done: bool

class Task(TaskCreate):
    id: int

# Create a list to store tasks
tasks = []

# Endpoint to create a new task
@app.post("/tasks/", response_model=Task)
def create_task(task: TaskCreate):
    task_dict = task.dict()
    task_dict['id'] = len(tasks) + 1
    new_task = Task(**task_dict)
    tasks.append(new_task)
    return new_task

# Endpoint to update a task by its ID
@app.put("/tasks/{id}/", response_model=Task)
def update_task(id: int, updated_task: TaskCreate):
    if id < 1 or id > len(tasks):
        raise HTTPException(status_code=404, detail="Task not found")

    task = tasks[id - 1]
    task.title = updated_task.title
    task.description = updated_task.description
    task.done = updated_task.done
    return task

# Endpoint to delete a task by its ID
@app.delete("/tasks/{id}/", response_model=Task)
def delete_task(id: int):
    if id < 1 or id > len(tasks):
        raise HTTPException(status_code=404, detail="Task not found")

    task = tasks.pop(id - 1)
    return task

# Endpoint to retrieve a list of all tasks
@app.get("/tasks/", response_model=List[Task])
def get_tasks():
    return tasks

# Endpoint to retrieve a task by its ID
@app.get("/tasks/{id}/", response_model=Task)
def get_task(id: int):
    if id < 1 or id > len(tasks):
        raise HTTPException(status_code=404, detail="Task not found")

    task = tasks[id - 1]
    return task

if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="0.0.0.0", port=8000)

and the generated openapi:

{"openapi":"3.1.0","info":{"title":"FastAPI","version":"0.1.0"},"paths":{"/tasks/":{"get":{"summary":"Get Tasks","operationId":"get_tasks_tasks__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Task"},"type":"array","title":"Response Get Tasks Tasks  Get"}}}}}},"post":{"summary":"Create Task","operationId":"create_task_tasks__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tasks/{id}/":{"get":{"summary":"Get Task","operationId":"get_task_tasks__id___get","parameters":[{"required":true,"schema":{"type":"integer","title":"Id"},"name":"id","in":"path"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"summary":"Update Task","operationId":"update_task_tasks__id___put","parameters":[{"required":true,"schema":{"type":"integer","title":"Id"},"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"Delete Task","operationId":"delete_task_tasks__id___delete","parameters":[{"required":true,"schema":{"type":"integer","title":"Id"},"name":"id","in":"path"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Task":{"properties":{"title":{"type":"string","title":"Title"},"description":{"type":"string","title":"Description"},"done":{"type":"boolean","title":"Done"},"id":{"type":"integer","title":"Id"}},"type":"object","required":["title","description","done","id"],"title":"Task"},"TaskCreate":{"properties":{"title":{"type":"string","title":"Title"},"description":{"type":"string","title":"Description"},"done":{"type":"boolean","title":"Done"}},"type":"object","required":["title","description","done"],"title":"TaskCreate"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}

and I used the robot test from the readme.

phramusca avatar Oct 24 '23 15:10 phramusca

I think there's an issue in the path parser that I need to look into, but I expect that changing get and post to "/tasks" (instead of "/tasks/") will resolve the issue. Trailing /'s in urls are really a pain...it basically means that "there is a next part of this route and it's nothing".


From: phramusca @.> Sent: Tuesday, October 24, 2023 5:03:47 PM To: MarketSquare/robotframework-openapidriver @.> Cc: Subscribed @.***> Subject: [MarketSquare/robotframework-openapidriver] ValueError: /tasks not found in paths section of the OpenAPI document. (Issue #35)

Hi, I am trying OpenApiDriver against a simple openapi with 5 endpoints and though 9 tests are generated, only the first 3 pass. The remaining 6 all fail with "ValueError: /tasks not found in paths section of the OpenAPI document." I have no idea what this means. I have changed parameters to {id} to avoid the need of a mapping, but I am affraid I would need one but since I do not really understand the issue I do not know how to write that maping. Can you help ? Thanks

Here is the python fastapi server:

from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import List

app = FastAPI()

Pydantic model for task (excluding 'id')

class TaskCreate(BaseModel): title: str description: str done: bool

class Task(TaskCreate): id: int

Create a list to store tasks

tasks = []

Endpoint to create a new task

@app.post("/tasks/", response_model=Task) def create_task(task: TaskCreate): task_dict = task.dict() task_dict['id'] = len(tasks) + 1 new_task = Task(**task_dict) tasks.append(new_task) return new_task

Endpoint to update a task by its ID

@app.put("/tasks/{id}/", response_model=Task) def update_task(id: int, updated_task: TaskCreate): if id < 1 or id > len(tasks): raise HTTPException(status_code=404, detail="Task not found")

task = tasks[id - 1]
task.title = updated_task.title
task.description = updated_task.description
task.done = updated_task.done
return task

Endpoint to delete a task by its ID

@app.delete("/tasks/{id}/", response_model=Task) def delete_task(id: int): if id < 1 or id > len(tasks): raise HTTPException(status_code=404, detail="Task not found")

task = tasks.pop(id - 1)
return task

Endpoint to retrieve a list of all tasks

@app.get("/tasks/", response_model=List[Task]) def get_tasks(): return tasks

Endpoint to retrieve a task by its ID

@app.get("/tasks/{id}/", response_model=Task) def get_task(id: int): if id < 1 or id > len(tasks): raise HTTPException(status_code=404, detail="Task not found")

task = tasks[id - 1]
return task

if name == "main": import uvicorn

uvicorn.run(app, host="0.0.0.0", port=8000)

and the generated openapi:

{"openapi":"3.1.0","info":{"title":"FastAPI","version":"0.1.0"},"paths":{"/tasks/":{"get":{"summary":"Get Tasks","operationId":"get_tasks_tasks__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Task"},"type":"array","title":"Response Get Tasks Tasks Get"}}}}}},"post":{"summary":"Create Task","operationId":"create_task_tasks__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tasks/{id}/":{"get":{"summary":"Get Task","operationId":"get_task_tasks__id___get","parameters":[{"required":true,"schema":{"type":"integer","title":"Id"},"name":"id","in":"path"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"summary":"Update Task","operationId":"update_task_tasks__id___put","parameters":[{"required":true,"schema":{"type":"integer","title":"Id"},"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"Delete Task","operationId":"delete_task_tasks__id___delete","parameters":[{"required":true,"schema":{"type":"integer","title":"Id"},"name":"id","in":"path"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Task":{"properties":{"title":{"type":"string","title":"Title"},"description":{"type":"string","title":"Description"},"done":{"type":"boolean","title":"Done"},"id":{"type":"integer","title":"Id"}},"type":"object","required":["title","description","done","id"],"title":"Task"},"TaskCreate":{"properties":{"title":{"type":"string","title":"Title"},"description":{"type":"string","title":"Description"},"done":{"type":"boolean","title":"Done"}},"type":"object","required":["title","description","done"],"title":"TaskCreate"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}

and I used the robot test from the readme.

— Reply to this email directly, view it on GitHubhttps://github.com/MarketSquare/robotframework-openapidriver/issues/35, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AK3CMZO3HACUG3MRX557YHDYA7KFHAVCNFSM6AAAAAA6N3IOQGVHI2DSMVQWIX3LMV43ASLTON2WKOZRHE2TSNBXGY4DCMI. You are receiving this because you are subscribed to this thread.Message ID: @.***>

robinmackaij avatar Oct 24 '23 15:10 robinmackaij

Good catch ! I removed the trailing slash on all endpoints and now I have the tests pass. It would be good to manage trailing slashes though... Thanks

----- Mail original -----

De: "Robin" @.> À: "MarketSquare/robotframework-openapidriver" @.> Cc: "phramusca" @.>, "Author" @.> Envoyé: Mardi 24 Octobre 2023 17:25:13 Objet: Re: [MarketSquare/robotframework-openapidriver] ValueError: /tasks not found in paths section of the OpenAPI document. (Issue #35)

I think there's an issue in the path parser that I need to look into, but I expect that changing get and post to "/tasks" (instead of "/tasks/") will resolve the issue. Trailing /'s in urls are really a pain...it basically means that "there is a next part of this route and it's nothing".


From: phramusca @.> Sent: Tuesday, October 24, 2023 5:03:47 PM To: MarketSquare/robotframework-openapidriver @.> Cc: Subscribed @.***> Subject: [MarketSquare/robotframework-openapidriver] ValueError: /tasks not found in paths section of the OpenAPI document. (Issue #35)

Hi, I am trying OpenApiDriver against a simple openapi with 5 endpoints and though 9 tests are generated, only the first 3 pass. The remaining 6 all fail with "ValueError: /tasks not found in paths section of the OpenAPI document." I have no idea what this means. I have changed parameters to {id} to avoid the need of a mapping, but I am affraid I would need one but since I do not really understand the issue I do not know how to write that maping. Can you help ? Thanks

Here is the python fastapi server:

from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import List

app = FastAPI()

Pydantic model for task (excluding 'id')

class TaskCreate(BaseModel): title: str description: str done: bool

class Task(TaskCreate): id: int

Create a list to store tasks

tasks = []

Endpoint to create a new task

@app.post("/tasks/", response_model=Task) def create_task(task: TaskCreate): task_dict = task.dict() task_dict['id'] = len(tasks) + 1 new_task = Task(**task_dict) tasks.append(new_task) return new_task

Endpoint to update a task by its ID

@app.put("/tasks/{id}/", response_model=Task) def update_task(id: int, updated_task: TaskCreate): if id < 1 or id > len(tasks): raise HTTPException(status_code=404, detail="Task not found")

task = tasks[id - 1] task.title = updated_task.title task.description = updated_task.description task.done = updated_task.done return task

Endpoint to delete a task by its ID

@app.delete("/tasks/{id}/", response_model=Task) def delete_task(id: int): if id < 1 or id > len(tasks): raise HTTPException(status_code=404, detail="Task not found")

task = tasks.pop(id - 1) return task

Endpoint to retrieve a list of all tasks

@app.get("/tasks/", response_model=List[Task]) def get_tasks(): return tasks

Endpoint to retrieve a task by its ID

@app.get("/tasks/{id}/", response_model=Task) def get_task(id: int): if id < 1 or id > len(tasks): raise HTTPException(status_code=404, detail="Task not found")

task = tasks[id - 1] return task

if name == "main": import uvicorn

uvicorn.run(app, host="0.0.0.0", port=8000)

and the generated openapi:

{"openapi":"3.1.0","info":{"title":"FastAPI","version":"0.1.0"},"paths":{"/tasks/":{"get":{"summary":"Get Tasks","operationId":"get_tasks_tasks__get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"items":{"$ref":"#/components/schemas/Task"},"type":"array","title":"Response Get Tasks Tasks Get"}}}}}},"post":{"summary":"Create Task","operationId":"create_task_tasks__post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/tasks/{id}/":{"get":{"summary":"Get Task","operationId":"get_task_tasks__id___get","parameters":[{"required":true,"schema":{"type":"integer","title":"Id"},"name":"id","in":"path"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"put":{"summary":"Update Task","operationId":"update_task_tasks__id___put","parameters":[{"required":true,"schema":{"type":"integer","title":"Id"},"name":"id","in":"path"}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/TaskCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"Delete Task","operationId":"delete_task_tasks__id___delete","parameters":[{"required":true,"schema":{"type":"integer","title":"Id"},"name":"id","in":"path"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Task"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"Task":{"properties":{"title":{"type":"string","title":"Title"},"description":{"type":"string","title":"Description"},"done":{"type":"boolean","title":"Done"},"id":{"type":"integer","title":"Id"}},"type":"object","required":["title","description","done","id"],"title":"Task"},"TaskCreate":{"properties":{"title":{"type":"string","title":"Title"},"description":{"type":"string","title":"Description"},"done":{"type":"boolean","title":"Done"}},"type":"object","required":["title","description","done"],"title":"TaskCreate"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"}}}}

and I used the robot test from the readme.

— Reply to this email directly, view it on GitHubhttps://github.com/MarketSquare/robotframework-openapidriver/issues/35, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AK3CMZO3HACUG3MRX557YHDYA7KFHAVCNFSM6AAAAAA6N3IOQGVHI2DSMVQWIX3LMV43ASLTON2WKOZRHE2TSNBXGY4DCMI. You are receiving this because you are subscribed to this thread.Message ID: @.***>

— Reply to this email directly, view it on GitHub , or unsubscribe . You are receiving this because you authored the thread. Message ID: <MarketSquare/robotframework-openapidriver/issues/35/1777479148 @ github . com>

phramusca avatar Oct 24 '23 15:10 phramusca

I've released a first version of the repo that brings OpenApiDriver and OpenApiLibCore together in a single package. I've implemented a possible solution to this issue in there, but I haven't been able to test it in detail. If you're willing, you could take a look: https://pypi.org/project/robotframework-openapitools/ Note that if you install openapitools, you should remove openapidriver / openapi-libcore from the project / environment; this is due to the fact that openapitools is a drop-in replacement for both, so it contains both OpenApiDriver and OpenApiLibCore.

robinmackaij avatar Mar 08 '24 16:03 robinmackaij