import json import pathlib from typing import Any, cast import typer import yaml from click.exceptions import Exit from fastapi import FastAPI from url_shortener.api.app import get_app from url_shortener.config import settings cli = typer.Typer(short_help="Perform API commands.", no_args_is_help=True) @cli.command("gen-spec") def gen_spec() -> None: """ Generate OpenAPI specification. """ app = get_app() dump_schema(app, filepath=settings.openapi_filepath) typer.secho( ( f"OpenAPI specification was successfully " f"generated to `{settings.openapi_filepath}`", ), fg="green", ) @cli.command("check-spec") def check_spec() -> None: """ Check that service has the latest OpenAPI specification. """ app = get_app() if not check_schema(app, filepath=settings.openapi_filepath): typer.secho( "The OpenAPI specification for the service does not match the actual one. " "Please, generate new one with `python manage.py api gen-spec`.", fg="red", ) raise Exit(1) typer.secho("You have the latest OpenAPI specification generated.", fg="green") def dump_schema(api: FastAPI, *, filepath: pathlib.Path) -> None: with filepath.open("w+") as fp: yaml.dump(json.loads(json.dumps(api.openapi())), stream=fp, sort_keys=False) def check_schema(api: FastAPI, *, filepath: pathlib.Path) -> bool: with filepath.open() as fp: return cast(dict[str, Any], yaml.safe_load(fp)) == api.openapi()