[
  {
    "path": ".github/workflows/python-app.yml",
    "content": "# This workflow will install Python dependencies, run tests and lint with a single version of Python\n# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python\n\nname: Python application\n\non:\n  push:\n    branches: [ \"main\" ]\n  pull_request:\n    branches: [ \"main\" ]\n\npermissions:\n  contents: read\n\njobs:\n  build:\n\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v3\n    - name: Set up Python 3.11\n      uses: actions/setup-python@v3\n      with:\n        python-version: \"3.11\"\n    - name: Install dependencies\n      run: |\n        python -m pip install --upgrade pip\n        pip install black pytest\n        if [ -f Pipfile ]; then pip install pipenv; pipenv install; fi\n    - name: Lint with black\n      run: |\n        black ./\n"
  },
  {
    "path": ".gitignore",
    "content": "# Byte-compiled / optimized / DLL files\n__pycache__/\n*.py[cod]\n*$py.class\n.pylintrc\n.vscode\ndatabase.db\n# C extensions\n*.so\n\n# Distribution / packaging\n.Python\nbuild/\ndevelop-eggs/\ndist/\ndownloads/\neggs/\n.eggs/\nlib/\nlib64/\nparts/\nsdist/\nvar/\nwheels/\npip-wheel-metadata/\nshare/python-wheels/\n*.egg-info/\n.installed.cfg\n*.egg\nMANIFEST\n\n# PyInstaller\n#  Usually these files are written by a python script from a template\n#  before PyInstaller builds the exe, so as to inject date/other infos into it.\n*.manifest\n*.spec\n\n# Installer logs\npip-log.txt\npip-delete-this-directory.txt\n\n# Unit test / coverage reports\nhtmlcov/\n.tox/\n.nox/\n.coverage\n.coverage.*\n.cache\nnosetests.xml\ncoverage.xml\n*.cover\n*.py,cover\n.hypothesis/\n.pytest_cache/\n\n# Translations\n*.mo\n*.pot\n\n# Django stuff:\n*.log\nlocal_settings.py\ndb.sqlite3\ndb.sqlite3-journal\n\n# Flask stuff:\ninstance/\n.webassets-cache\n\n# Scrapy stuff:\n.scrapy\n\n# Sphinx documentation\ndocs/_build/\n\n# PyBuilder\ntarget/\n\n# Jupyter Notebook\n.ipynb_checkpoints\n\n# IPython\nprofile_default/\nipython_config.py\n\n# pyenv\n.python-version\n\n# pipenv\n#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.\n#   However, in case of collaboration, if having platform-specific dependencies or dependencies\n#   having no cross-platform support, pipenv may install dependencies that don't work, or not\n#   install all needed dependencies.\n#Pipfile.lock\n\n# PEP 582; used by e.g. github.com/David-OConnor/pyflow\n__pypackages__/\n\n# Celery stuff\ncelerybeat-schedule\ncelerybeat.pid\n\n# SageMath parsed files\n*.sage.py\n\n# Environments\n.env\n.venv\nenv/\nvenv/\nENV/\nenv.bak/\nvenv.bak/\n\n# Spyder project settings\n.spyderproject\n.spyproject\n\n# Rope project settings\n.ropeproject\n\n# mkdocs documentation\n/site\n\n# mypy\n.mypy_cache/\n.dmypy.json\ndmypy.json\n\n# Pyre type checker\n.pyre/\n"
  },
  {
    "path": "Dockerfile",
    "content": "FROM python:3.11.7\n\n\n\nWORKDIR /app\n\nCOPY tavern_tests /app/tavern_tests\nCOPY Pipfile /app\nCOPY Pipfile.lock /app\nCOPY .env /app/.env\nRUN pip install --upgrade pip\nRUN pip install pipenv\nRUN pipenv install --system --deploy --ignore-pipfile --${PIPENV_ARGS}\n\nRUN cat /etc/ssl/certs/ca-certificates.crt >> `python -m certifi`\n\nCOPY api/ /app/api\nCOPY entry_point.py /app/entry_point.py\n\nEXPOSE 8080\nENTRYPOINT [\"/entrypoint.sh\"]\nCMD [\"uvicorn\", \"asgi:api\", \"--host\", \"0.0.0.0\", \"--port\", \"8080\"]"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021 Anthony Cepeda\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "Pipfile",
    "content": "[[source]]\nurl = \"https://pypi.org/simple\"\nverify_ssl = true\nname = \"pypi\"\n\n[packages]\nfastapi = \"*\"\nsqlmodel = \"*\"\nuvicorn = \"*\"\npydantic-settings = \"*\"\n\n[dev-packages]\nblack = \"*\"\npylint = \"*\"\n\n[requires]\npython_version = \"3.11.7\"\n\n[pipenv]\nallow_prereleases = true\n"
  },
  {
    "path": "README.md",
    "content": "![alt text](./img/SQLModel.png)\n## FastAPI + SQLModel Boilerplate App\nA 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/)\n\n\n### Quickstart\n1.  <b>Start the App</b>:\n  - Using Python:\n    `pipenv run python asgi.py`\n\n  - Using Docker:\n    `docker build -t sqlmodel-api:latest . && docker run -p 8080:8080 sqlmodel-api:latest`\n\n2. <b>Use Openapi at</b>: `http://localhost:8080/#/`\n\n\n### Running Tests:\nWhile your app is running, open another terminal:\n`pytest -v tavern_tests/`\n\n\n![alt text](./img/SQLModelAPI_openapi.png)"
  },
  {
    "path": "api/app.py",
    "content": "from contextlib import asynccontextmanager\n\nfrom fastapi import FastAPI\n\nfrom api.config import Settings\nfrom api.database import create_db_and_tables\nfrom api.public import api as public_api\nfrom api.utils.logger import logger_config\nfrom api.utils.mock_data_generator import create_heroes_and_teams\n\nlogger = logger_config(__name__)\n\n\n@asynccontextmanager\nasync def lifespan(app: FastAPI):\n    create_db_and_tables()\n    create_heroes_and_teams()\n\n    logger.info(\"startup: triggered\")\n\n    yield\n\n    logger.info(\"shutdown: triggered\")\n\n\ndef create_app(settings: Settings):\n    app = FastAPI(\n        title=settings.PROJECT_NAME,\n        version=settings.VERSION,\n        docs_url=\"/\",\n        description=settings.DESCRIPTION,\n        lifespan=lifespan,\n    )\n\n    app.include_router(public_api)\n\n    return app\n"
  },
  {
    "path": "api/auth/__init__.py",
    "content": "import secrets\n\nfrom fastapi import Depends, HTTPException, status\nfrom fastapi.security import HTTPBasic, HTTPBasicCredentials\n\nfrom api.config import settings\n\nbasic_auth = HTTPBasic(auto_error=False)\n\n\ndef authent(\n    credentials: HTTPBasicCredentials = Depends(basic_auth),\n):\n    if check_basic_auth_creds(credentials):\n        return True\n\n    raise HTTPException(status_code=403, detail=\"invalid user/password provided\")\n\n\ndef check_basic_auth_creds(\n    credentials: HTTPBasicCredentials = Depends(basic_auth),\n):\n    correct_username = secrets.compare_digest(\n        credentials.username, settings.API_USERNAME\n    )\n    correct_password = secrets.compare_digest(\n        credentials.password, settings.API_PASSWORD\n    )\n\n    if correct_username and correct_password:\n        return True\n\n    return False\n"
  },
  {
    "path": "api/config.py",
    "content": "import os\nimport secrets\nfrom typing import Literal\n\nfrom pydantic_settings import BaseSettings\n\n\nclass Settings(BaseSettings):\n    PROJECT_NAME: str = f\"SQLModel API - {os.getenv('ENV', 'development').capitalize()}\"\n    DESCRIPTION: str = \"A FastAPI + SQLModel production-ready API\"\n    ENV: Literal[\"development\", \"staging\", \"production\"] = \"development\"\n    VERSION: str = \"0.1\"\n    SECRET_KEY: str = secrets.token_urlsafe(32)\n    DATABASE_URI: str = \"sqlite:////Users/anth/dev/fastapi-sqlmodel/database.db\"\n    API_USERNAME: str = \"svc_test\"\n    API_PASSWORD: str = \"superstrongpassword\"\n\n    class Config:\n        case_sensitive = True\n\n\nsettings = Settings()\n\n\nclass TestSettings(Settings):\n    class Config:\n        case_sensitive = True\n\n\ntest_settings = TestSettings()\n"
  },
  {
    "path": "api/database.py",
    "content": "from sqlmodel import Session, SQLModel, create_engine\n\nfrom api.config import settings\n\nconnect_args = {\"check_same_thread\": False}\nengine = create_engine(settings.DATABASE_URI, echo=True, connect_args=connect_args)\n\n\ndef create_db_and_tables():\n    SQLModel.metadata.create_all(engine)\n\n\ndef get_session():\n    with Session(engine) as session:\n        yield session\n"
  },
  {
    "path": "api/public/__init__.py",
    "content": "from fastapi import APIRouter, Depends\n\nfrom api.auth import authent\nfrom api.public.health import views as health\nfrom api.public.hero import views as heroes\nfrom api.public.team import views as teams\n\napi = APIRouter()\n\n\napi.include_router(\n    health.router,\n    prefix=\"/health\",\n    tags=[\"Health\"],\n    dependencies=[Depends(authent)],\n)\napi.include_router(\n    heroes.router,\n    prefix=\"/heroes\",\n    tags=[\"Heroes\"],\n    dependencies=[Depends(authent)],\n)\napi.include_router(\n    teams.router,\n    prefix=\"/teams\",\n    tags=[\"Teams\"],\n    dependencies=[Depends(authent)],\n)\n"
  },
  {
    "path": "api/public/health/crud.py",
    "content": "from fastapi import Depends\nfrom sqlmodel import Session, text\n\nfrom api.config import settings\nfrom api.database import get_session\nfrom api.public.health.models import Health, Stats, Status\nfrom api.public.hero.crud import read_heroes\nfrom api.utils.logger import logger_config\n\nlogger = logger_config(__name__)\n\n\ndef get_health(db: Session) -> Health:\n    db_status = health_db(db=db)\n    logger.info(\"%s.get_health.db_status: %s\", __name__, db_status)\n    return Health(app_status=Status.OK, db_status=db_status, environment=settings.ENV)\n\n\ndef get_stats(db: Session) -> Stats:\n    stats = Stats(heroes=count_from_db(\"hero\", db), teams=count_from_db(\"team\", db))\n    logger.info(\"%sget_stats: %s\", __name__, stats)\n    return stats\n\n\ndef count_from_db(table: str, db: Session = Depends(get_session)):\n    teams = db.exec(text(f\"SELECT COUNT(id) FROM {table};\")).one_or_none()\n    return teams[0] if teams else 0\n\n\ndef health_db(db: Session = Depends(get_session)) -> Status:\n    try:\n        db.exec(text(f\"SELECT COUNT(id) FROM hero;\")).one_or_none()\n        return Status.OK\n    except Exception as e:\n        logger.exception(e)\n\n    return Status.KO\n"
  },
  {
    "path": "api/public/health/models.py",
    "content": "from enum import Enum\nfrom typing import Literal\n\nfrom pydantic import BaseModel\n\n\nclass Status(str, Enum):\n    OK = \"OK\"\n    KO = \"KO\"\n\n\nclass Health(BaseModel):\n    app_status: Status | None\n    db_status: Status | None\n    environment: Literal[\"development\", \"staging\", \"production\"] | None\n\n\nclass Stats(BaseModel):\n    heroes: int | None\n    teams: int | None\n"
  },
  {
    "path": "api/public/health/views.py",
    "content": "from fastapi import APIRouter, Depends, status\nfrom sqlmodel import Session\n\nfrom api.database import Session, get_session\nfrom api.public.health.crud import get_health, get_stats\nfrom api.public.health.models import Health, Stats\nfrom api.utils.logger import logger_config\n\nrouter = APIRouter()\nlogger = logger_config(__name__)\n\n\n@router.get(\n    \"\",\n    response_model=Health,\n    status_code=status.HTTP_200_OK,\n    responses={200: {\"model\": Health}},\n)\ndef health(db: Session = Depends(get_session)):\n    return get_health(db=db)\n\n\n@router.get(\n    \"/stats\",\n    response_model=Stats,\n    status_code=status.HTTP_200_OK,\n    responses={200: {\"model\": Stats}},\n)\ndef health_stats(db: Session = Depends(get_session)):\n    return get_stats(db=db)\n"
  },
  {
    "path": "api/public/hero/crud.py",
    "content": "from fastapi import Depends, HTTPException, status\nfrom sqlmodel import Session, select\n\nfrom api.database import get_session\nfrom api.public.hero.models import Hero, HeroCreate, HeroUpdate\n\n\ndef create_hero(hero: HeroCreate, db: Session = Depends(get_session)):\n    hero_to_db = Hero.model_validate(hero)\n    db.add(hero_to_db)\n    db.commit()\n    db.refresh(hero_to_db)\n    return hero_to_db\n\n\ndef read_heroes(offset: int = 0, limit: int = 20, db: Session = Depends(get_session)):\n    heroes = db.exec(select(Hero).offset(offset).limit(limit)).all()\n    return heroes\n\n\ndef read_hero(hero_id: int, db: Session = Depends(get_session)):\n    hero = db.get(Hero, hero_id)\n    if not hero:\n        raise HTTPException(\n            status_code=status.HTTP_404_NOT_FOUND,\n            detail=f\"Hero not found with id: {hero_id}\",\n        )\n    return hero\n\n\ndef update_hero(hero_id: int, hero: HeroUpdate, db: Session = Depends(get_session)):\n    hero_to_update = db.get(Hero, hero_id)\n    if not hero_to_update:\n        raise HTTPException(\n            status_code=status.HTTP_404_NOT_FOUND,\n            detail=f\"Hero not found with id: {hero_id}\",\n        )\n\n    team_data = hero.model_dump(exclude_unset=True)\n    for key, value in team_data.items():\n        setattr(hero_to_update, key, value)\n\n    db.add(hero_to_update)\n    db.commit()\n    db.refresh(hero_to_update)\n    return hero_to_update\n\n\ndef delete_hero(hero_id: int, db: Session = Depends(get_session)):\n    hero = db.get(Hero, hero_id)\n    if not hero:\n        raise HTTPException(\n            status_code=status.HTTP_404_NOT_FOUND,\n            detail=f\"Hero not found with id: {hero_id}\",\n        )\n\n    db.delete(hero)\n    db.commit()\n    return {\"ok\": True}\n"
  },
  {
    "path": "api/public/hero/models.py",
    "content": "from sqlmodel import Field, Relationship, SQLModel\n\nfrom api.public.team.models import Team\nfrom api.utils.generic_models import HeroTeamLink\n\n\nclass HeroBase(SQLModel):\n    name: str\n    secret_name: str\n    age: int | None = None\n\n    class Config:\n        json_schema_extra = {\n            \"example\": {\n                \"id\": 1,\n                \"name\": \"Super Man\",\n                \"secret_name\": \"Clark Kent\",\n                \"age\": 27,\n                \"team_id\": 1,\n            }\n        }\n\n\nclass Hero(HeroBase, table=True):\n    id: int | None = Field(default=None, primary_key=True)\n    teams: list[Team] = Relationship(back_populates=\"heroes\", link_model=HeroTeamLink)\n\n\nclass HeroCreate(HeroBase):\n    pass\n\n\nclass HeroRead(HeroBase):\n    id: int\n    name: str | None = None\n    secret_name: str | None = None\n    age: int | None = None\n    teams: list[Team] = None\n\n\nclass HeroUpdate(HeroBase):\n    name: str | None = None\n    secret_name: str | None = None\n    age: int | None = None\n    teams: list[Team] = None\n\n    class Config:\n        json_schema_extra = {\n            \"example\": {\n                \"name\": \"Super Man\",\n                \"secret_name\": \"Clark Kent\",\n                \"age\": 27,\n                \"team_id\": 1,\n            }\n        }\n"
  },
  {
    "path": "api/public/hero/views.py",
    "content": "from fastapi import APIRouter, Depends, Query\nfrom sqlmodel import Session\n\nfrom api.database import get_session\nfrom api.public.hero.crud import (\n    create_hero,\n    delete_hero,\n    read_hero,\n    read_heroes,\n    update_hero,\n)\nfrom api.public.hero.models import HeroCreate, HeroRead, HeroUpdate\n\nrouter = APIRouter()\n\n\n@router.post(\"\", response_model=HeroRead)\ndef create_a_hero(hero: HeroCreate, db: Session = Depends(get_session)):\n    return create_hero(hero=hero, db=db)\n\n\n@router.get(\"\", response_model=list[HeroRead])\ndef get_heroes(\n    offset: int = 0,\n    limit: int = Query(default=100, lte=100),\n    db: Session = Depends(get_session),\n):\n    return read_heroes(offset=offset, limit=limit, db=db)\n\n\n@router.get(\"/{hero_id}\", response_model=HeroRead)\ndef get_a_hero(hero_id: int, db: Session = Depends(get_session)):\n    return read_hero(hero_id=hero_id, db=db)\n\n\n@router.patch(\"/{hero_id}\", response_model=HeroRead)\ndef update_a_hero(hero_id: int, hero: HeroUpdate, db: Session = Depends(get_session)):\n    return update_hero(hero_id=hero_id, hero=hero, db=db)\n\n\n@router.delete(\"/{hero_id}\")\ndef delete_a_hero(hero_id: int, db: Session = Depends(get_session)):\n    return delete_hero(hero_id=hero_id, db=db)\n"
  },
  {
    "path": "api/public/team/crud.py",
    "content": "from fastapi import Depends, HTTPException, status\nfrom sqlmodel import Session, select, text\n\nfrom api.database import get_session\nfrom api.public.team.models import Team, TeamCreate, TeamUpdate\n\n\ndef create_team(team: TeamCreate, db: Session = Depends(get_session)):\n    team_to_db = Team.model_validate(team)\n    db.add(team_to_db)\n    db.commit()\n    db.refresh(team_to_db)\n    return team_to_db\n\n\ndef read_teams(offset: int = 0, limit: int = 20, db: Session = Depends(get_session)):\n    teams = db.exec(select(Team).offset(offset).limit(limit)).all()\n    return teams\n\n\ndef read_team(team_id: int, db: Session = Depends(get_session)):\n    team = db.get(Team, team_id)\n    if not team:\n        raise HTTPException(\n            status_code=status.HTTP_404_NOT_FOUND,\n            detail=f\"Team not found with id: {team_id}\",\n        )\n    return team\n\n\ndef update_team(team_id: int, team: TeamUpdate, db: Session = Depends(get_session)):\n    team_to_update = db.get(Team, team_id)\n    if not team_to_update:\n        raise HTTPException(\n            status_code=status.HTTP_404_NOT_FOUND,\n            detail=f\"Team not found with id: {team_id}\",\n        )\n\n    team_data = team.model_dump(exclude_unset=True)\n    for key, value in team_data.items():\n        setattr(team_to_update, key, value)\n\n    db.add(team_to_update)\n    db.commit()\n    db.refresh(team_to_update)\n    return team_to_update\n\n\ndef delete_team(team_id: int, db: Session = Depends(get_session)):\n    team = db.get(Team, team_id)\n    if not team:\n        raise HTTPException(\n            status_code=status.HTTP_404_NOT_FOUND,\n            detail=f\"Team not found with id: {team_id}\",\n        )\n\n    db.delete(team)\n    db.commit()\n    return {\"ok\": True}\n"
  },
  {
    "path": "api/public/team/models.py",
    "content": "from sqlmodel import Field, Relationship, SQLModel\n\nfrom api.utils.generic_models import HeroTeamLink\n\n\nclass TeamBase(SQLModel):\n    name: str\n    headquarters: str\n\n    class Config:\n        json_schema_extra = {\n            \"example\": {\n                \"name\": \"wonderful league\",\n                \"headquarters\": \"Fortress of Solitude\",\n            }\n        }\n\n\nclass Team(TeamBase, table=True):\n    id: int | None = Field(default=None, primary_key=True)\n\n    heroes: list[\"Hero\"] = Relationship(back_populates=\"teams\", link_model=HeroTeamLink)  # type: ignore\n\n\nclass TeamCreate(TeamBase):\n    pass\n\n\nclass TeamRead(TeamBase):\n    id: int\n    name: str | None = None\n    headquarters: str | None = None\n    heroes: list | None = None\n\n\nclass TeamUpdate(TeamBase):\n    name: str | None = None\n    headquarters: str | None = None\n"
  },
  {
    "path": "api/public/team/views.py",
    "content": "from fastapi import APIRouter, Depends, Query\nfrom sqlmodel import Session\n\nfrom api.database import get_session\nfrom api.public.team.crud import (\n    create_team,\n    delete_team,\n    read_team,\n    read_teams,\n    update_team,\n)\nfrom api.public.team.models import TeamCreate, TeamRead, TeamUpdate\nfrom api.utils.logger import logger_config\n\nrouter = APIRouter()\n\nlogger = logger_config(__name__)\n\n\n@router.post(\"\", response_model=TeamRead)\ndef create_a_team(team: TeamCreate, db: Session = Depends(get_session)):\n    logger.info(\"%s.create_a_team: %s\", __name__, team)\n    return create_team(team=team, db=db)\n\n\n@router.get(\"\", response_model=list[TeamRead])\ndef get_teams(\n    offset: int = 0,\n    limit: int = Query(default=100, lte=100),\n    db: Session = Depends(get_session),\n):\n    logger.info(\"%s.get_teams: triggered\", __name__)\n    return read_teams(offset=offset, limit=limit, db=db)\n\n\n@router.get(\"/{team_id}\", response_model=TeamRead)\ndef get_a_team(team_id: int, db: Session = Depends(get_session)):\n    logger.info(\"%s.get_a_team.id: %s\", __name__, team_id)\n    return read_team(team_id=team_id, db=db)\n\n\n@router.patch(\"/{team_id}\", response_model=TeamRead)\ndef update_a_team(team_id: int, team: TeamUpdate, db: Session = Depends(get_session)):\n    logger.info(\"%s.update_a_team.id: %s\", __name__, team_id)\n    return update_team(team_id=team_id, team=team, db=db)\n\n\n@router.delete(\"/{team_id}\")\ndef delete_a_team(team_id: int, db: Session = Depends(get_session)):\n    logger.info(\"%s.delete_a_team: %s triggered\", __name__, team_id)\n    return delete_team(team_id=team_id, db=db)\n"
  },
  {
    "path": "api/utils/generic_models.py",
    "content": "from enum import Enum\n\nfrom sqlmodel import Field, SQLModel\n\n\nclass HeroTeamLink(SQLModel, table=True):\n    team_id: int | None = Field(default=None, foreign_key=\"team.id\", primary_key=True)\n    hero_id: int | None = Field(default=None, foreign_key=\"hero.id\", primary_key=True)\n"
  },
  {
    "path": "api/utils/logger.py",
    "content": "import logging\n\n\ndef logger_config(module):\n    \"\"\"\n    Logger function. Extends Python loggin module and set a custom config.\n    params: Module Name. e.i: logger_config(__name__).\n    return: Custom logger_config Object.\n    \"\"\"\n    formatter = logging.Formatter(\"%(asctime)s %(levelname)s %(message)s\")\n    handler = logging.StreamHandler()\n    handler.setFormatter(formatter)\n\n    custom_logger = logging.getLogger(module)\n    custom_logger.setLevel(logging.DEBUG)\n\n    custom_logger.addHandler(handler)\n\n    return custom_logger\n"
  },
  {
    "path": "api/utils/mock_data_generator.py",
    "content": "from sqlmodel import Session\n\nfrom api.database import engine\nfrom api.public.hero.models import Hero\nfrom api.public.team.models import Team\nfrom api.utils.logger import logger_config\n\nlogger = logger_config(__name__)\n\n\ndef create_heroes_and_teams():\n    with Session(engine) as session:\n        team_preventers = Team(name=\"Preventers\", headquarters=\"Sharp Tower\")\n        team_z_force = Team(name=\"Z-Force\", headquarters=\"Sister Margaret's Bar\")\n        wornderful_league = Team(\n            name=\"Wonderful-League\", headquarters=\"Fortress of Solitude\"\n        )\n\n        hero_deadpond = Hero(\n            name=\"Deadpond\",\n            secret_name=\"Dive Wilson\",\n            age=24,\n            teams=[team_z_force, team_preventers],\n        )\n        hero_rusty_man = Hero(\n            name=\"Rusty-Man\",\n            secret_name=\"Tommy Sharp\",\n            age=48,\n            teams=[team_preventers],\n        )\n        hero_spider_boy = Hero(\n            name=\"Spider-Boy\",\n            secret_name=\"Pedro Parqueador\",\n            age=37,\n            teams=[team_preventers],\n        )\n        hero_super_good_boy = Hero(\n            name=\"Super-Good-Boy\",\n            secret_name=\"John Goodman\",\n            age=30,\n            teams=[wornderful_league, team_z_force],\n        )\n\n        session.add(hero_deadpond)\n        session.add(hero_rusty_man)\n        session.add(hero_spider_boy)\n        session.add(hero_super_good_boy)\n        session.commit()\n\n        session.refresh(hero_deadpond)\n        session.refresh(hero_rusty_man)\n        session.refresh(hero_spider_boy)\n        session.refresh(hero_super_good_boy)\n\n        logger.info(\"=========== MOCK DATA CREATED ===========\")\n        logger.info(\"Deadpond %s\", hero_deadpond)\n        logger.info(\"Deadpond teams %s\", hero_deadpond.teams)\n        logger.info(\"Rusty-Man %s\", hero_rusty_man)\n        logger.info(\"Rusty-Man Teams %s\", hero_rusty_man.teams)\n        logger.info(\"Spider-Boy %s\", hero_spider_boy)\n        logger.info(\"Spider-Boy Teams %s\", hero_spider_boy.teams)\n        logger.info(\"Super-Good-Boy %s\", hero_super_good_boy)\n        logger.info(\"Super-Good-Boy Teams: %s\", hero_super_good_boy.teams)\n        logger.info(\"===========================================\")\n"
  },
  {
    "path": "asgi.py",
    "content": "import uvicorn\n\nfrom api.app import create_app\nfrom api.config import settings\n\napi = create_app(settings)\n\nif __name__ == \"__main__\":\n    uvicorn.run(\"asgi:api\", host=\"0.0.0.0\", port=8080, reload=True)\n"
  },
  {
    "path": "entrypoint.sh",
    "content": "#!/bin/bash\n\nexec \"$@\""
  },
  {
    "path": "tavern_tests/common.yaml",
    "content": "---\nname: \"FastAPI - SQLModel API\"\ndescription: Common Settings\n\nvariables:\n  host: http://localhost:8080\n  hero_id: 1\n  hero_name: \"Super Man\"\n  hero_secret_name: \"Clark Kent\"\n  hero_age: 27\n  team_id: 1\n  team_name: \"wonderful league\"\n  team_headquarters: \"Fortress of Solitude\""
  },
  {
    "path": "tavern_tests/test_hero.tavern.yaml",
    "content": "---\ntest_name: Hero Tests\nincludes:\n  - !include common.yaml\n\nstages:\n  - name: Create a Hero\n    request:\n      method: POST\n      url: \"{host}/heroes\"\n      json:\n        id: 1\n        name: \"Super Man\"\n        secret_name: \"Clark Kent\"\n        age: 27\n        team_id: 1\n    response:\n      status_code: 200\n      json: {}\n  \n  - name: Get a Hero\n    request:\n      method: GET \n      url: \"{host}/heroes/1\"\n    response:\n      status_code: 200\n      json: []\n  \n  - name: Get a Hero - Not Found\n    request:\n      method: GET \n      url: \"{host}/heroes/99\"\n    response:\n      status_code: 404\n      json: \n        detail: \"Hero not found with id: 99\"\n  \n  - name: Get a Heroes\n    request:\n      method: GET \n      url: \"{host}/heroes\"\n      params:\n        offset: 0\n        limit: 100\n    response:\n      status_code: 200\n      json: []\n  \n  - name: Update a Heroe\n    request:\n      method: PATCH \n      url: \"{host}/heroes/1\"\n      json:\n        name: \"Super Man\"\n        secret_name: \"Clark Kent\"\n        age: 28\n        team_id: 1\n    response:\n      status_code: 200\n      json:\n        id: 1\n        name: \"Super Man\"\n        secret_name: \"Clark Kent\"\n        age: 28\n        team_id: 1\n\n  - name: Delete a Heroe\n    request:\n      method: DELETE \n      url: \"{host}/heroes/1\"\n    response:\n      json:\n        ok: true"
  },
  {
    "path": "tavern_tests/test_team.tavern.yaml",
    "content": "---\ntest_name: Team Tests\nincludes:\n  - !include common.yaml\n\nstages:\n  - name: Create a Team\n    request:\n      method: POST\n      url: \"{host}/teams\"\n      json:\n        name: \"wonderful league\"\n        headquarters: \"Fortress of Solitude\"\n    response:\n      status_code: 200\n      json: {}\n  \n  - name: Get a Team\n    request:\n      method: GET \n      url: \"{host}/teams/1\"\n    response:\n      status_code: 200\n      json: []\n  \n  - name: Get a Team - Not Found\n    request:\n      method: GET \n      url: \"{host}/teams/99\"\n    response:\n      status_code: 404\n      json: \n        detail: \"Team not found with id: 99\"\n  \n  - name: Get Teams\n    request:\n      method: GET \n      url: \"{host}/teams\"\n      params:\n        offset: 0\n        limit: 100\n    response:\n      status_code: 200\n      json: []\n  \n  - name: Update a Teame\n    request:\n      method: PATCH \n      url: \"{host}/teams/1\"\n      json:\n        name: \"wonderful league\"\n        headquarters: \"frozen tundra\"\n    response:\n      status_code: 200\n      json:\n        id: 1\n        name: \"wonderful league\"\n        headquarters: \"frozen tundra\"\n\n  - name: Delete a Teame\n    request:\n      method: DELETE \n      url: \"{host}/teams/1\"\n    response:\n      json:\n        ok: true"
  }
]