Repository: anthonycepeda/fastapi-sqlmodel
Branch: main
Commit: 4b4b039e5d5b
Files: 28
Total size: 24.7 KB
Directory structure:
gitextract_iiii9og7/
├── .github/
│ └── workflows/
│ └── python-app.yml
├── .gitignore
├── Dockerfile
├── LICENSE
├── Pipfile
├── README.md
├── api/
│ ├── app.py
│ ├── auth/
│ │ └── __init__.py
│ ├── config.py
│ ├── database.py
│ ├── public/
│ │ ├── __init__.py
│ │ ├── health/
│ │ │ ├── crud.py
│ │ │ ├── models.py
│ │ │ └── views.py
│ │ ├── hero/
│ │ │ ├── crud.py
│ │ │ ├── models.py
│ │ │ └── views.py
│ │ └── team/
│ │ ├── crud.py
│ │ ├── models.py
│ │ └── views.py
│ └── utils/
│ ├── generic_models.py
│ ├── logger.py
│ └── mock_data_generator.py
├── asgi.py
├── entrypoint.sh
└── tavern_tests/
├── common.yaml
├── test_hero.tavern.yaml
└── test_team.tavern.yaml
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/python-app.yml
================================================
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
name: Python application
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.11
uses: actions/setup-python@v3
with:
python-version: "3.11"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install black pytest
if [ -f Pipfile ]; then pip install pipenv; pipenv install; fi
- name: Lint with black
run: |
black ./
================================================
FILE: .gitignore
================================================
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
.pylintrc
.vscode
database.db
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
================================================
FILE: Dockerfile
================================================
FROM python:3.11.7
WORKDIR /app
COPY tavern_tests /app/tavern_tests
COPY Pipfile /app
COPY Pipfile.lock /app
COPY .env /app/.env
RUN pip install --upgrade pip
RUN pip install pipenv
RUN pipenv install --system --deploy --ignore-pipfile --${PIPENV_ARGS}
RUN cat /etc/ssl/certs/ca-certificates.crt >> `python -m certifi`
COPY api/ /app/api
COPY entry_point.py /app/entry_point.py
EXPOSE 8080
ENTRYPOINT ["/entrypoint.sh"]
CMD ["uvicorn", "asgi:api", "--host", "0.0.0.0", "--port", "8080"]
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2021 Anthony Cepeda
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: Pipfile
================================================
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"
[packages]
fastapi = "*"
sqlmodel = "*"
uvicorn = "*"
pydantic-settings = "*"
[dev-packages]
black = "*"
pylint = "*"
[requires]
python_version = "3.11.7"
[pipenv]
allow_prereleases = true
================================================
FILE: README.md
================================================

## FastAPI + SQLModel Boilerplate App
A RestAPI real world app based on SQLModel [documentation example](https://sqlmodel.tiangolo.com/tutorial/), using [FastAPI](https://fastapi.tiangolo.com/) and [SQLModel](https://sqlmodel.tiangolo.com/)
### Quickstart
1. Start the App:
- Using Python:
`pipenv run python asgi.py`
- Using Docker:
`docker build -t sqlmodel-api:latest . && docker run -p 8080:8080 sqlmodel-api:latest`
2. Use Openapi at: `http://localhost:8080/#/`
### Running Tests:
While your app is running, open another terminal:
`pytest -v tavern_tests/`

================================================
FILE: api/app.py
================================================
from contextlib import asynccontextmanager
from fastapi import FastAPI
from api.config import Settings
from api.database import create_db_and_tables
from api.public import api as public_api
from api.utils.logger import logger_config
from api.utils.mock_data_generator import create_heroes_and_teams
logger = logger_config(__name__)
@asynccontextmanager
async def lifespan(app: FastAPI):
create_db_and_tables()
create_heroes_and_teams()
logger.info("startup: triggered")
yield
logger.info("shutdown: triggered")
def create_app(settings: Settings):
app = FastAPI(
title=settings.PROJECT_NAME,
version=settings.VERSION,
docs_url="/",
description=settings.DESCRIPTION,
lifespan=lifespan,
)
app.include_router(public_api)
return app
================================================
FILE: api/auth/__init__.py
================================================
import secrets
from fastapi import Depends, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from api.config import settings
basic_auth = HTTPBasic(auto_error=False)
def authent(
credentials: HTTPBasicCredentials = Depends(basic_auth),
):
if check_basic_auth_creds(credentials):
return True
raise HTTPException(status_code=403, detail="invalid user/password provided")
def check_basic_auth_creds(
credentials: HTTPBasicCredentials = Depends(basic_auth),
):
correct_username = secrets.compare_digest(
credentials.username, settings.API_USERNAME
)
correct_password = secrets.compare_digest(
credentials.password, settings.API_PASSWORD
)
if correct_username and correct_password:
return True
return False
================================================
FILE: api/config.py
================================================
import os
import secrets
from typing import Literal
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
PROJECT_NAME: str = f"SQLModel API - {os.getenv('ENV', 'development').capitalize()}"
DESCRIPTION: str = "A FastAPI + SQLModel production-ready API"
ENV: Literal["development", "staging", "production"] = "development"
VERSION: str = "0.1"
SECRET_KEY: str = secrets.token_urlsafe(32)
DATABASE_URI: str = "sqlite:////Users/anth/dev/fastapi-sqlmodel/database.db"
API_USERNAME: str = "svc_test"
API_PASSWORD: str = "superstrongpassword"
class Config:
case_sensitive = True
settings = Settings()
class TestSettings(Settings):
class Config:
case_sensitive = True
test_settings = TestSettings()
================================================
FILE: api/database.py
================================================
from sqlmodel import Session, SQLModel, create_engine
from api.config import settings
connect_args = {"check_same_thread": False}
engine = create_engine(settings.DATABASE_URI, echo=True, connect_args=connect_args)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
def get_session():
with Session(engine) as session:
yield session
================================================
FILE: api/public/__init__.py
================================================
from fastapi import APIRouter, Depends
from api.auth import authent
from api.public.health import views as health
from api.public.hero import views as heroes
from api.public.team import views as teams
api = APIRouter()
api.include_router(
health.router,
prefix="/health",
tags=["Health"],
dependencies=[Depends(authent)],
)
api.include_router(
heroes.router,
prefix="/heroes",
tags=["Heroes"],
dependencies=[Depends(authent)],
)
api.include_router(
teams.router,
prefix="/teams",
tags=["Teams"],
dependencies=[Depends(authent)],
)
================================================
FILE: api/public/health/crud.py
================================================
from fastapi import Depends
from sqlmodel import Session, text
from api.config import settings
from api.database import get_session
from api.public.health.models import Health, Stats, Status
from api.public.hero.crud import read_heroes
from api.utils.logger import logger_config
logger = logger_config(__name__)
def get_health(db: Session) -> Health:
db_status = health_db(db=db)
logger.info("%s.get_health.db_status: %s", __name__, db_status)
return Health(app_status=Status.OK, db_status=db_status, environment=settings.ENV)
def get_stats(db: Session) -> Stats:
stats = Stats(heroes=count_from_db("hero", db), teams=count_from_db("team", db))
logger.info("%sget_stats: %s", __name__, stats)
return stats
def count_from_db(table: str, db: Session = Depends(get_session)):
teams = db.exec(text(f"SELECT COUNT(id) FROM {table};")).one_or_none()
return teams[0] if teams else 0
def health_db(db: Session = Depends(get_session)) -> Status:
try:
db.exec(text(f"SELECT COUNT(id) FROM hero;")).one_or_none()
return Status.OK
except Exception as e:
logger.exception(e)
return Status.KO
================================================
FILE: api/public/health/models.py
================================================
from enum import Enum
from typing import Literal
from pydantic import BaseModel
class Status(str, Enum):
OK = "OK"
KO = "KO"
class Health(BaseModel):
app_status: Status | None
db_status: Status | None
environment: Literal["development", "staging", "production"] | None
class Stats(BaseModel):
heroes: int | None
teams: int | None
================================================
FILE: api/public/health/views.py
================================================
from fastapi import APIRouter, Depends, status
from sqlmodel import Session
from api.database import Session, get_session
from api.public.health.crud import get_health, get_stats
from api.public.health.models import Health, Stats
from api.utils.logger import logger_config
router = APIRouter()
logger = logger_config(__name__)
@router.get(
"",
response_model=Health,
status_code=status.HTTP_200_OK,
responses={200: {"model": Health}},
)
def health(db: Session = Depends(get_session)):
return get_health(db=db)
@router.get(
"/stats",
response_model=Stats,
status_code=status.HTTP_200_OK,
responses={200: {"model": Stats}},
)
def health_stats(db: Session = Depends(get_session)):
return get_stats(db=db)
================================================
FILE: api/public/hero/crud.py
================================================
from fastapi import Depends, HTTPException, status
from sqlmodel import Session, select
from api.database import get_session
from api.public.hero.models import Hero, HeroCreate, HeroUpdate
def create_hero(hero: HeroCreate, db: Session = Depends(get_session)):
hero_to_db = Hero.model_validate(hero)
db.add(hero_to_db)
db.commit()
db.refresh(hero_to_db)
return hero_to_db
def read_heroes(offset: int = 0, limit: int = 20, db: Session = Depends(get_session)):
heroes = db.exec(select(Hero).offset(offset).limit(limit)).all()
return heroes
def read_hero(hero_id: int, db: Session = Depends(get_session)):
hero = db.get(Hero, hero_id)
if not hero:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Hero not found with id: {hero_id}",
)
return hero
def update_hero(hero_id: int, hero: HeroUpdate, db: Session = Depends(get_session)):
hero_to_update = db.get(Hero, hero_id)
if not hero_to_update:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Hero not found with id: {hero_id}",
)
team_data = hero.model_dump(exclude_unset=True)
for key, value in team_data.items():
setattr(hero_to_update, key, value)
db.add(hero_to_update)
db.commit()
db.refresh(hero_to_update)
return hero_to_update
def delete_hero(hero_id: int, db: Session = Depends(get_session)):
hero = db.get(Hero, hero_id)
if not hero:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Hero not found with id: {hero_id}",
)
db.delete(hero)
db.commit()
return {"ok": True}
================================================
FILE: api/public/hero/models.py
================================================
from sqlmodel import Field, Relationship, SQLModel
from api.public.team.models import Team
from api.utils.generic_models import HeroTeamLink
class HeroBase(SQLModel):
name: str
secret_name: str
age: int | None = None
class Config:
json_schema_extra = {
"example": {
"id": 1,
"name": "Super Man",
"secret_name": "Clark Kent",
"age": 27,
"team_id": 1,
}
}
class Hero(HeroBase, table=True):
id: int | None = Field(default=None, primary_key=True)
teams: list[Team] = Relationship(back_populates="heroes", link_model=HeroTeamLink)
class HeroCreate(HeroBase):
pass
class HeroRead(HeroBase):
id: int
name: str | None = None
secret_name: str | None = None
age: int | None = None
teams: list[Team] = None
class HeroUpdate(HeroBase):
name: str | None = None
secret_name: str | None = None
age: int | None = None
teams: list[Team] = None
class Config:
json_schema_extra = {
"example": {
"name": "Super Man",
"secret_name": "Clark Kent",
"age": 27,
"team_id": 1,
}
}
================================================
FILE: api/public/hero/views.py
================================================
from fastapi import APIRouter, Depends, Query
from sqlmodel import Session
from api.database import get_session
from api.public.hero.crud import (
create_hero,
delete_hero,
read_hero,
read_heroes,
update_hero,
)
from api.public.hero.models import HeroCreate, HeroRead, HeroUpdate
router = APIRouter()
@router.post("", response_model=HeroRead)
def create_a_hero(hero: HeroCreate, db: Session = Depends(get_session)):
return create_hero(hero=hero, db=db)
@router.get("", response_model=list[HeroRead])
def get_heroes(
offset: int = 0,
limit: int = Query(default=100, lte=100),
db: Session = Depends(get_session),
):
return read_heroes(offset=offset, limit=limit, db=db)
@router.get("/{hero_id}", response_model=HeroRead)
def get_a_hero(hero_id: int, db: Session = Depends(get_session)):
return read_hero(hero_id=hero_id, db=db)
@router.patch("/{hero_id}", response_model=HeroRead)
def update_a_hero(hero_id: int, hero: HeroUpdate, db: Session = Depends(get_session)):
return update_hero(hero_id=hero_id, hero=hero, db=db)
@router.delete("/{hero_id}")
def delete_a_hero(hero_id: int, db: Session = Depends(get_session)):
return delete_hero(hero_id=hero_id, db=db)
================================================
FILE: api/public/team/crud.py
================================================
from fastapi import Depends, HTTPException, status
from sqlmodel import Session, select, text
from api.database import get_session
from api.public.team.models import Team, TeamCreate, TeamUpdate
def create_team(team: TeamCreate, db: Session = Depends(get_session)):
team_to_db = Team.model_validate(team)
db.add(team_to_db)
db.commit()
db.refresh(team_to_db)
return team_to_db
def read_teams(offset: int = 0, limit: int = 20, db: Session = Depends(get_session)):
teams = db.exec(select(Team).offset(offset).limit(limit)).all()
return teams
def read_team(team_id: int, db: Session = Depends(get_session)):
team = db.get(Team, team_id)
if not team:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Team not found with id: {team_id}",
)
return team
def update_team(team_id: int, team: TeamUpdate, db: Session = Depends(get_session)):
team_to_update = db.get(Team, team_id)
if not team_to_update:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Team not found with id: {team_id}",
)
team_data = team.model_dump(exclude_unset=True)
for key, value in team_data.items():
setattr(team_to_update, key, value)
db.add(team_to_update)
db.commit()
db.refresh(team_to_update)
return team_to_update
def delete_team(team_id: int, db: Session = Depends(get_session)):
team = db.get(Team, team_id)
if not team:
raise HTTPException(
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Team not found with id: {team_id}",
)
db.delete(team)
db.commit()
return {"ok": True}
================================================
FILE: api/public/team/models.py
================================================
from sqlmodel import Field, Relationship, SQLModel
from api.utils.generic_models import HeroTeamLink
class TeamBase(SQLModel):
name: str
headquarters: str
class Config:
json_schema_extra = {
"example": {
"name": "wonderful league",
"headquarters": "Fortress of Solitude",
}
}
class Team(TeamBase, table=True):
id: int | None = Field(default=None, primary_key=True)
heroes: list["Hero"] = Relationship(back_populates="teams", link_model=HeroTeamLink) # type: ignore
class TeamCreate(TeamBase):
pass
class TeamRead(TeamBase):
id: int
name: str | None = None
headquarters: str | None = None
heroes: list | None = None
class TeamUpdate(TeamBase):
name: str | None = None
headquarters: str | None = None
================================================
FILE: api/public/team/views.py
================================================
from fastapi import APIRouter, Depends, Query
from sqlmodel import Session
from api.database import get_session
from api.public.team.crud import (
create_team,
delete_team,
read_team,
read_teams,
update_team,
)
from api.public.team.models import TeamCreate, TeamRead, TeamUpdate
from api.utils.logger import logger_config
router = APIRouter()
logger = logger_config(__name__)
@router.post("", response_model=TeamRead)
def create_a_team(team: TeamCreate, db: Session = Depends(get_session)):
logger.info("%s.create_a_team: %s", __name__, team)
return create_team(team=team, db=db)
@router.get("", response_model=list[TeamRead])
def get_teams(
offset: int = 0,
limit: int = Query(default=100, lte=100),
db: Session = Depends(get_session),
):
logger.info("%s.get_teams: triggered", __name__)
return read_teams(offset=offset, limit=limit, db=db)
@router.get("/{team_id}", response_model=TeamRead)
def get_a_team(team_id: int, db: Session = Depends(get_session)):
logger.info("%s.get_a_team.id: %s", __name__, team_id)
return read_team(team_id=team_id, db=db)
@router.patch("/{team_id}", response_model=TeamRead)
def update_a_team(team_id: int, team: TeamUpdate, db: Session = Depends(get_session)):
logger.info("%s.update_a_team.id: %s", __name__, team_id)
return update_team(team_id=team_id, team=team, db=db)
@router.delete("/{team_id}")
def delete_a_team(team_id: int, db: Session = Depends(get_session)):
logger.info("%s.delete_a_team: %s triggered", __name__, team_id)
return delete_team(team_id=team_id, db=db)
================================================
FILE: api/utils/generic_models.py
================================================
from enum import Enum
from sqlmodel import Field, SQLModel
class HeroTeamLink(SQLModel, table=True):
team_id: int | None = Field(default=None, foreign_key="team.id", primary_key=True)
hero_id: int | None = Field(default=None, foreign_key="hero.id", primary_key=True)
================================================
FILE: api/utils/logger.py
================================================
import logging
def logger_config(module):
"""
Logger function. Extends Python loggin module and set a custom config.
params: Module Name. e.i: logger_config(__name__).
return: Custom logger_config Object.
"""
formatter = logging.Formatter("%(asctime)s %(levelname)s %(message)s")
handler = logging.StreamHandler()
handler.setFormatter(formatter)
custom_logger = logging.getLogger(module)
custom_logger.setLevel(logging.DEBUG)
custom_logger.addHandler(handler)
return custom_logger
================================================
FILE: api/utils/mock_data_generator.py
================================================
from sqlmodel import Session
from api.database import engine
from api.public.hero.models import Hero
from api.public.team.models import Team
from api.utils.logger import logger_config
logger = logger_config(__name__)
def create_heroes_and_teams():
with Session(engine) as session:
team_preventers = Team(name="Preventers", headquarters="Sharp Tower")
team_z_force = Team(name="Z-Force", headquarters="Sister Margaret's Bar")
wornderful_league = Team(
name="Wonderful-League", headquarters="Fortress of Solitude"
)
hero_deadpond = Hero(
name="Deadpond",
secret_name="Dive Wilson",
age=24,
teams=[team_z_force, team_preventers],
)
hero_rusty_man = Hero(
name="Rusty-Man",
secret_name="Tommy Sharp",
age=48,
teams=[team_preventers],
)
hero_spider_boy = Hero(
name="Spider-Boy",
secret_name="Pedro Parqueador",
age=37,
teams=[team_preventers],
)
hero_super_good_boy = Hero(
name="Super-Good-Boy",
secret_name="John Goodman",
age=30,
teams=[wornderful_league, team_z_force],
)
session.add(hero_deadpond)
session.add(hero_rusty_man)
session.add(hero_spider_boy)
session.add(hero_super_good_boy)
session.commit()
session.refresh(hero_deadpond)
session.refresh(hero_rusty_man)
session.refresh(hero_spider_boy)
session.refresh(hero_super_good_boy)
logger.info("=========== MOCK DATA CREATED ===========")
logger.info("Deadpond %s", hero_deadpond)
logger.info("Deadpond teams %s", hero_deadpond.teams)
logger.info("Rusty-Man %s", hero_rusty_man)
logger.info("Rusty-Man Teams %s", hero_rusty_man.teams)
logger.info("Spider-Boy %s", hero_spider_boy)
logger.info("Spider-Boy Teams %s", hero_spider_boy.teams)
logger.info("Super-Good-Boy %s", hero_super_good_boy)
logger.info("Super-Good-Boy Teams: %s", hero_super_good_boy.teams)
logger.info("===========================================")
================================================
FILE: asgi.py
================================================
import uvicorn
from api.app import create_app
from api.config import settings
api = create_app(settings)
if __name__ == "__main__":
uvicorn.run("asgi:api", host="0.0.0.0", port=8080, reload=True)
================================================
FILE: entrypoint.sh
================================================
#!/bin/bash
exec "$@"
================================================
FILE: tavern_tests/common.yaml
================================================
---
name: "FastAPI - SQLModel API"
description: Common Settings
variables:
host: http://localhost:8080
hero_id: 1
hero_name: "Super Man"
hero_secret_name: "Clark Kent"
hero_age: 27
team_id: 1
team_name: "wonderful league"
team_headquarters: "Fortress of Solitude"
================================================
FILE: tavern_tests/test_hero.tavern.yaml
================================================
---
test_name: Hero Tests
includes:
- !include common.yaml
stages:
- name: Create a Hero
request:
method: POST
url: "{host}/heroes"
json:
id: 1
name: "Super Man"
secret_name: "Clark Kent"
age: 27
team_id: 1
response:
status_code: 200
json: {}
- name: Get a Hero
request:
method: GET
url: "{host}/heroes/1"
response:
status_code: 200
json: []
- name: Get a Hero - Not Found
request:
method: GET
url: "{host}/heroes/99"
response:
status_code: 404
json:
detail: "Hero not found with id: 99"
- name: Get a Heroes
request:
method: GET
url: "{host}/heroes"
params:
offset: 0
limit: 100
response:
status_code: 200
json: []
- name: Update a Heroe
request:
method: PATCH
url: "{host}/heroes/1"
json:
name: "Super Man"
secret_name: "Clark Kent"
age: 28
team_id: 1
response:
status_code: 200
json:
id: 1
name: "Super Man"
secret_name: "Clark Kent"
age: 28
team_id: 1
- name: Delete a Heroe
request:
method: DELETE
url: "{host}/heroes/1"
response:
json:
ok: true
================================================
FILE: tavern_tests/test_team.tavern.yaml
================================================
---
test_name: Team Tests
includes:
- !include common.yaml
stages:
- name: Create a Team
request:
method: POST
url: "{host}/teams"
json:
name: "wonderful league"
headquarters: "Fortress of Solitude"
response:
status_code: 200
json: {}
- name: Get a Team
request:
method: GET
url: "{host}/teams/1"
response:
status_code: 200
json: []
- name: Get a Team - Not Found
request:
method: GET
url: "{host}/teams/99"
response:
status_code: 404
json:
detail: "Team not found with id: 99"
- name: Get Teams
request:
method: GET
url: "{host}/teams"
params:
offset: 0
limit: 100
response:
status_code: 200
json: []
- name: Update a Teame
request:
method: PATCH
url: "{host}/teams/1"
json:
name: "wonderful league"
headquarters: "frozen tundra"
response:
status_code: 200
json:
id: 1
name: "wonderful league"
headquarters: "frozen tundra"
- name: Delete a Teame
request:
method: DELETE
url: "{host}/teams/1"
response:
json:
ok: true