https://github.com/ruanbekker/flask-api-structured-primer
Structured Flask API with SQLAlchemy, Swagger, Unit Tests, CodeCoverage, etc
https://github.com/ruanbekker/flask-api-structured-primer
docker flasgger flask makefile mysql python sqlalchemy
Last synced: about 2 months ago
JSON representation
Structured Flask API with SQLAlchemy, Swagger, Unit Tests, CodeCoverage, etc
- Host: GitHub
- URL: https://github.com/ruanbekker/flask-api-structured-primer
- Owner: ruanbekker
- Created: 2023-12-22T13:08:35.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2024-01-22T20:06:54.000Z (over 2 years ago)
- Last Synced: 2025-09-07T14:56:41.401Z (10 months ago)
- Topics: docker, flasgger, flask, makefile, mysql, python, sqlalchemy
- Language: Python
- Homepage: https://ruan.dev
- Size: 70.3 KB
- Stars: 3
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# flask-api-structured-primer
Structured Flask API with SQLAlchemy, Swagger, Unit Tests, CodeCoverage, etc
## Table of Contents
- [Getting Started](#getting-started)
- [Directory Structure](#directory-structure)
- [Tests](#tests)
- [Static Code Analysis](#static-code-analysis)
- [Unit Tests](#unit-tests)
- [Code Coverage](#code-coverage)
- [Running tests with Docker](#running-tests-with-docker)
- [Database Migrations](#database-migrations)
- [API Documentation](#swagger-api-documentation)
## Getting started
To view the makefile targets:
```bash
make
```
Local testing:
```bash
make test
```
Docker:
```bash
make up
```
Scripts is under `_scripts/` or you can use the makefile targets:
```bash
make requests-create
make requests-retrieve
```
## Directory Structure
Tree view of the directory structure:
```bash
├── Dockerfile
├── Makefile
├── README.md
├── _scripts
│ ├── http
│ │ ├── create.sh
│ │ ├── delete.sh
│ │ ├── get.sh
│ │ ├── list.sh
│ │ └── update.sh
│ ├── migrations
│ │ └── db_migrations.sh
│ └── tests
│ ├── lint.sh
│ └── unit_tests.sh
├── app.py
├── config.py
├── database
│ └── db.py
├── docker-compose-jobs.yaml
├── docker-compose.yaml
├── migrations
│ ├── README
│ ├── alembic.ini
│ ├── env.py
│ ├── script.py.mako
│ └── versions
├── models
│ └── product.py
├── requirements.txt
├── services
│ └── product_service.py
├── shared
│ └── logging_utils.py
├── tests
│ └── test_product_service.py
└── views
├── healthprobe_views.py
└── product_views.py
12 directories, 27 files
```
## Tests
- static code analysis (pylint, prospector, bandit)
- unit tests (unittest)
- code coverage (coverage)
- optional: using docker to run all at once
### Static Code Analysis
To run linting:
```bash
find . -name "*.py" ! -path "./venv/*" -exec pylint --rcfile .pylintrc --verbose {} +
```
pylint output
```bash
Using config file .pylintrc
************* Module shared.logging_utils
shared/logging_utils.py:58:17: C0303: Trailing whitespace (trailing-whitespace)
shared/logging_utils.py:48:4: C0103: Variable name "ch" doesn't conform to snake_case naming style (invalid-name)
************* Module views.product_views
views/product_views.py:68:4: C0103: Variable name "e" doesn't conform to snake_case naming style (invalid-name)
views/product_views.py:88:4: C0103: Variable name "e" doesn't conform to snake_case naming style (invalid-name)
-------------------------------------------------------------------
Your code has been rated at 9.86/10 (previous run: 10.00/10, -0.14)
```
When the issues has been fixed:
```bash
-------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 9.81/10, +0.19)
```
Run prospector:
```bash
prospector --profile .prospector.yaml
# prospector --strictness low --with-tool pydocstyle
```
prospector output
```bash
Check Information
=================
Started: 2023-11-19 16:07:29.863667
Finished: 2023-11-19 16:07:44.618042
Time Taken: 14.75 seconds
Formatter: grouped
Profiles: .prospector.yaml, doc_warnings, strictness_medium, strictness_high, strictness_veryhigh, no_member_warnings
Strictness: from profile
Libraries Used: flask
Tools Run: dodgy, profile-validator, pycodestyle, pydocstyle, pyflakes, pylint
Messages Found: 0
External Config: pylint: /Users/ruan/personal/eng-python-fastapi-products/.pylintrc
```
When you have errors:
```bash
Messages
========
app.py
Line: 12
pydocstyle: D212 / Multi-line docstring summary should start at the first line
pydocstyle: D407 / Missing dashed underline after section ('Parameters')
pydocstyle: D406 / Section name should end with a newline ('Parameters', not 'Parameters:')
pydocstyle: D417 / Missing argument descriptions in the docstring (argument(s) config_class are missing descriptions in 'create_app' docstring)
pydocstyle: D413 / Missing blank line after last section ('Returns')
pydocstyle: D407 / Missing dashed underline after section ('Returns')
pydocstyle: D406 / Section name should end with a newline ('Returns', not 'Returns:')
```
Run bandit security tests:
```bash
bandit -r . -ll -x ./venv
```
bandit output
```bash
[main] INFO profile include tests: None
[main] INFO profile exclude tests: None
[main] INFO cli include tests: None
[main] INFO cli exclude tests: None
[main] INFO running on Python 3.8.18
Run started:2023-11-18 17:10:55.851705
Test results:
No issues identified.
Code scanned:
Total lines of code: 430
Total lines skipped (#nosec): 0
Run metrics:
Total issues (by severity):
Undefined: 0
Low: 0
Medium: 0
High: 0
Total issues (by confidence):
Undefined: 0
Low: 0
Medium: 0
High: 0
Files skipped (0):
```
### Unit Tests
Run unit tests:
```bash
python3 -m unittest discover -s tests --verbose
```
unittest output
```bash
test_product_model (test_product_service.ProductModelTestCase)
Test the behavior of the Product model. ... ok
test_add_product_service (test_product_service.ProductServiceLayerTestCase)
Test the 'add_product' method of the ProductService class. ... ok
test_delete_product (test_product_service.ProductServiceTestCase)
Test the deletion of a product via the API. ... 2023-11-18 19:12:26,697 - views.product_views - INFO - creating a new product
2023-11-18 19:12:26,710 - views.product_views - INFO - product was deleted: product_id=1
ok
test_product_creation (test_product_service.ProductServiceTestCase)
Test the creation of a product via the API. ... 2023-11-18 19:12:26,720 - views.product_views - INFO - creating a new product
ok
test_product_retrieval (test_product_service.ProductServiceTestCase)
Test the retrieval of a product via the API. ... 2023-11-18 19:12:26,740 - views.product_views - INFO - retrieving details for product id=1
ok
test_update_product (test_product_service.ProductServiceTestCase)
Test the updating of a product via the API. ... 2023-11-18 19:12:26,750 - views.product_views - INFO - creating a new product
2023-11-18 19:12:26,759 - views.product_views - INFO - updating product details for product id=1
ok
----------------------------------------------------------------------
Ran 6 tests in 0.121s
OK
```
### Code Coverage
Run code coverage:
```bash
coverage run --rcfile .coveragerc -m unittest discover -s tests
```
coverage output
```bash
..2023-11-18 19:13:21,309 - views.product_views - INFO - creating a new product
2023-11-18 19:13:21,327 - views.product_views - INFO - product was deleted: product_id=1
.2023-11-18 19:13:21,344 - views.product_views - INFO - creating a new product
.2023-11-18 19:13:21,371 - views.product_views - INFO - retrieving details for product id=1
.2023-11-18 19:13:21,385 - views.product_views - INFO - creating a new product
2023-11-18 19:13:21,398 - views.product_views - INFO - updating product details for product id=1
.
----------------------------------------------------------------------
Ran 6 tests in 0.161s
OK
```
Run the coverage report:
```bash
coverage report --rcfile .coveragerc
```
coverage report output
```bash
Name Stmts Miss Cover
---------------------------------------------------
app.py 17 0 100%
config.py 8 0 100%
database/db.py 5 2 60%
models/product.py 11 1 91%
services/product_service.py 35 3 91%
shared/logging_utils.py 19 1 95%
tests/test_product_service.py 85 1 99%
views/product_views.py 40 8 80%
---------------------------------------------------
TOTAL 220 16 93%
```
### Running tests with docker
Tests can be run as instructed above or can be run all at once using docker:
```bash
make test-docker
```
## Health Checks
The application has two endpoints for healthchecks:
- `/probes/health` : when application is ready
- `/probes/ready` : when database is ready
## Database Migrations
This application uses the [Flask-Migrate](https://flask-migrate.readthedocs.io/en/latest/) extension for a [Alembic](https://alembic.sqlalchemy.org/en/latest/) migration environment.
To run database migrations:
Setting the environment:
```bash
export FLASK_APP=app:create_app
export FLASK_ENV=development
```
Running the database migrations
```bash
flask db init
flask db migrate -m "Initial db migration"
flask db upgrade
```
The database versioning directory structure:
```bash
├── migrations
│ ├── README
│ ├── alembic.ini
│ ├── env.py
│ ├── script.py.mako
│ └── versions
├── models
│ └── product.py
```
Resources:
- [Database Migrations with Flask](https://code.likeagirl.io/database-migrations-in-python-with-flask-with-alembic-442d11eb14d3)
## Swagger API Documentation
Swagger has been implemented with [Flasggr](https://github.com/flasgger/flasgger)
Swagger Documentation is available at:
- http://localhost:5000/apidocs/