Skip to content

Commit 1339fa6

Browse files
authored
First commit containing the base template for a Flask API (#8)
First commit containing the base template for a Flask API
1 parent d32ded9 commit 1339fa6

File tree

88 files changed

+6455
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

88 files changed

+6455
-0
lines changed

.github/pull_request_template.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
## Ticket
2+
3+
{TICKET_LINK}
4+
5+
## Changes
6+
> What was added, updated, or removed in this PR.
7+
8+
## Context for reviewers
9+
> Testing instructions, background context, more in-depth details of the implementation, and anything else you'd like to call out or ask reviewers. Explain how the changes were verified.
10+
11+
## Testing
12+
> Screenshots, GIF demos, code examples or output to help show the changes working as expected. ProTip: you can drag and drop or paste images into this textbox.
13+

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# Mac filesystem file
2+
.DS_Store
3+
4+
5+
# IDE-specific files
6+
.vscode/*

README.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Template Application Flask
2+
3+
Template application for a basic Flask API.
4+
5+
This application should run as-is with minimal setup (see below).
6+
## Getting started
7+
8+
This application is dockerized. Take a look at [Dockerfile](./app/Dockerfile) to see how it works.
9+
10+
A very simple [docker-compose.yml](./docker-compose.yml) has been included to support local development and deployment. Take a look at [docker-compose.yml](./docker-compose.yml) for more information.
11+
12+
**How to run:**
13+
14+
1. In your terminal, `cd` to the app directory of this repo.
15+
2. Make sure you have [Docker Desktop](https://www.docker.com/products/docker-desktop/) installed & running.
16+
3. Run `make init start` to build the image and start the container.
17+
4. Navigate to `localhost:8080/v1/docs` to access the Swagger UI.
18+
5. Run `make run-logs` to see the logs of the running API container
19+
6. Run `make stop` when you are done to delete the container.
20+
21+
See: [app/README.md](/app/README.md) for further details on the API implementation.

app/.gitignore

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Python compiled/optimized files
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
6+
# Python packaging stuff
7+
dist/
8+
*.egg-info
9+
10+
# Python testing stuff
11+
.coverage*
12+
coverage.*
13+
.testmondata
14+
.pytest_cache/
15+
16+
# Python environments
17+
.env
18+
.venv
19+
20+
# mypy
21+
.mypy_cache

app/.spectral.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Spectral rule file to use in Makefile. See: https://meta.stoplight.io/docs/spectral/docs/getting-started/rulesets.md
2+
extends: spectral:oas
3+
4+
rules:
5+
operation-description: off
6+
no-$ref-siblings: info
7+
path-kebab-case:
8+
description: Paths must be kebab-case.
9+
message: "Path {{property}} must be kebab-case"
10+
severity: warn
11+
given: $.paths[*]~
12+
then:
13+
function: pattern
14+
functionOptions:
15+
match: "^(\/([a-z0-9-]+|{[^}]+}))+$"
16+
path-param-snake-case:
17+
description: Parameters in paths must be snake_case.
18+
message: "Parameter in path {{property}} must be snake_case"
19+
severity: error
20+
given: $.paths[*]~
21+
then:
22+
function: pattern
23+
functionOptions:
24+
match: "^(\/([^{^}]+|{[a-z0-9_]+}))+$"

app/Dockerfile

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Use the official python3 image based on Debian 11 "Bullseye".
2+
# https://hub.docker.com/_/python
3+
FROM python:3-slim
4+
# Keep container packages up-to-date.
5+
RUN apt update \
6+
&& apt upgrade --yes
7+
# Install poetry, the package manager.
8+
# https://python-poetry.org
9+
RUN pip install poetry
10+
11+
12+
# Set the application working directory.
13+
WORKDIR /srv
14+
15+
COPY pyproject.toml poetry.lock ./
16+
RUN poetry config virtualenvs.in-project false && poetry env use python
17+
RUN poetry install --no-root
18+
19+
# Copy application files.
20+
COPY . /srv
21+
22+
# Install application dependencies.
23+
# https://python-poetry.org/docs/basic-usage/#installing-dependencies
24+
RUN poetry install
25+
# Run the application.
26+
CMD ["poetry", "run", "python", "-m", "api"]

app/Makefile

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
##################################################
2+
# Constants
3+
##################################################
4+
5+
APP_NAME := main-app
6+
7+
# Adding this to the end of a script that outputs JSON will convert
8+
# it to a readable format with timestamps and color-coding.
9+
#
10+
# Note that you can also change the LOG_FORMAT env var to switch
11+
# between JSON & human readable format. This is left in place
12+
# in the event JSON is output from a process we don't log.
13+
DECODE_LOG := 2>&1 | python3 -u api/logging/util/decodelog.py
14+
15+
# A few commands need adjustments if they're run in CI, specify those here
16+
# TODO - when CI gets hooked up, actually test this.
17+
ifdef CI
18+
DOCKER_EXEC_ARGS := -T -e CI -e PYTEST_ADDOPTS="--color=yes"
19+
FLAKE8_FORMAT := '::warning file=api/%(path)s,line=%(row)d,col=%(col)d::%(path)s:%(row)d:%(col)d: %(code)s %(text)s'
20+
MYPY_FLAGS := --no-pretty
21+
MYPY_POSTPROC := | perl -pe "s/^(.+):(\d+):(\d+): error: (.*)/::warning file=api\/\1,line=\2,col=\3::\4/"
22+
SPECTRAL_POSTPROC := --format=text | perl -pe "s/^\/tmp\/(.+):(\d+):(\d+) (error|warning) (.*)/::warning file=api\/\1,line=\2,col=\3::\4 \5/"
23+
else
24+
FLAKE8_FORMAT := default
25+
endif
26+
27+
# By default, all python/poetry commands will run inside of the docker container
28+
# if you wish to run this natively, add PY_RUN_APPROACH=local to your environment vars
29+
# You can set this by either running `export PY_RUN_APPROACH=local` in your shell or add
30+
# it to your ~/.zshrc file (and run `source ~/.zshrc`)
31+
ifeq "$(PY_RUN_APPROACH)" "local"
32+
PY_RUN_CMD := poetry run
33+
else
34+
PY_RUN_CMD := docker-compose run $(DOCKER_EXEC_ARGS) --rm $(APP_NAME) poetry run
35+
endif
36+
37+
##################################################
38+
# API Build & Run
39+
##################################################
40+
41+
build:
42+
docker-compose build
43+
44+
start:
45+
docker-compose up --detach
46+
47+
run-logs: start
48+
docker-compose logs --follow --no-color $(APP_NAME)
49+
50+
51+
init: build init-db
52+
53+
clean-volumes: ## Remove project docker volumes (which includes the DB state)
54+
docker-compose down --volumes
55+
56+
stop:
57+
docker-compose down
58+
59+
##################################################
60+
# DB & migrations
61+
##################################################
62+
63+
#########################
64+
# DB running / setup
65+
#########################
66+
67+
# Docker starts the image for the DB but it's not quite
68+
# fully ready, so add a 5 second sleep so upgrade doesn't
69+
# fail because the DB hasn't started yet.
70+
init-db: start-db sleep-5 db-upgrade
71+
72+
start-db:
73+
docker-compose up --detach main-db
74+
75+
## Destroy current DB, setup new one
76+
db-recreate: clean-docker-volumes init-db
77+
78+
#########################
79+
# DB Migrations
80+
#########################
81+
82+
alembic_config := ./api/db/migrations/alembic.ini
83+
alembic_cmd := $(PY_RUN_CMD) alembic --config $(alembic_config)
84+
85+
db-upgrade: ## Apply pending migrations to db
86+
$(PY_RUN_CMD) db-migrate-up
87+
88+
db-downgrade: ## Rollback last migration in db
89+
$(PY_RUN_CMD) db-migrate-down
90+
91+
db-downgrade-all: ## Rollback all migrations
92+
$(PY_RUN_CMD) db-migrate-down-all
93+
94+
check-migrate-msg:
95+
ifndef MIGRATE_MSG
96+
$(error MIGRATE_MSG is undefined)
97+
endif
98+
99+
db-migrate-create: check-migrate-msg
100+
$(alembic_cmd) revision --autogenerate -m "$(MIGRATE_MSG)"
101+
102+
MIGRATE_MERGE_MSG := Merge multiple heads
103+
db-migrate-merge-heads: ## Create a new migration that depends on all existing `head`s
104+
$(alembic_cmd) merge heads -m "$(MIGRATE_MERGE_MSG)" $(args)
105+
106+
db-migrate-current: ## Show current revision for a database
107+
$(alembic_cmd) current $(args)
108+
109+
db-migrate-history: ## Show migration history
110+
$(alembic_cmd) history $(args)
111+
112+
db-migrate-heads: ## Show migrations marked as a head
113+
$(alembic_cmd) heads $(args)
114+
115+
##################################################
116+
# Testing
117+
##################################################
118+
119+
test:
120+
$(PY_RUN_CMD) pytest $(args)
121+
122+
##################################################
123+
# Formatting and linting
124+
##################################################
125+
126+
format:
127+
$(PY_RUN_CMD) isort --atomic api tests
128+
$(PY_RUN_CMD) black api tests
129+
130+
lint: lint-spectral lint-py
131+
132+
lint-py: lint-flake lint-mypy lint-poetry-version
133+
134+
lint-flake:
135+
$(PY_RUN_CMD) flake8 --format=$(FLAKE8_FORMAT) api tests
136+
137+
lint-mypy:
138+
$(PY_RUN_CMD) mypy --show-error-codes $(MYPY_FLAGS) api $(MYPY_POSTPROC)
139+
140+
lint-poetry-version: ## Check poetry version
141+
grep --quiet 'lock-version = "1.1"' poetry.lock
142+
143+
lint-spectral:
144+
docker run --rm --tty --cap-drop=ALL --network=none --read-only --volume=$(PWD):/tmp:ro \
145+
stoplight/spectral:6 lint /tmp/openapi.yml --ruleset /tmp/.spectral.yaml $(SPECTRAL_POSTPROC)
146+
147+
test-coverage:
148+
$(PY_RUN_CMD) coverage run --branch --source=api -m pytest $(args)
149+
$(PY_RUN_CMD) coverage report
150+
151+
test-coverage-report: ## Open HTML test coverage report
152+
$(PY_RUN_CMD) coverage html --directory .coverage_report
153+
open .coverage_report/index.html
154+
155+
##################################################
156+
# Scripts
157+
##################################################
158+
159+
generate-pet-csv:
160+
$(PY_RUN_CMD) generate-pet-csv
161+
162+
##################################################
163+
# Miscellaneous Utilities
164+
##################################################
165+
166+
# Pauses for 5 seconds
167+
sleep-5:
168+
sleep 5

app/README.md

Whitespace-only changes.

app/api/__init__.py

Whitespace-only changes.

app/api/__main__.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env python3
2+
3+
# If __main__.py is present in a Python module, it will be executed by default
4+
# if that module is executed, e.g., `python -m my.module`.
5+
#
6+
# https://docs.python.org/3/library/__main__.html
7+
8+
import api.app
9+
import api.logging
10+
from api.app_config import AppConfig
11+
from api.util.local import load_local_env_vars
12+
13+
logger = api.logging.get_logger(__package__)
14+
15+
16+
def main() -> None:
17+
load_local_env_vars()
18+
app_config = AppConfig()
19+
20+
api.logging.init(__package__)
21+
logger.info("Running API Application")
22+
23+
app = api.app.create_app()
24+
25+
if app_config.environment == "local":
26+
# If python files are changed, the app will auto-reload
27+
# Note this doesn't have the OpenAPI yaml file configured at the moment
28+
app.run(port=8080, use_reloader=True, reloader_type="stat")
29+
else:
30+
# Don't enable the reloader if non-local
31+
app.run(port=8080)
32+
33+
34+
main()

0 commit comments

Comments
 (0)