Skip to content

ruanbekker/flask-api-structured-primer

Repository files navigation

flask-api-structured-primer

Structured Flask API with SQLAlchemy, Swagger, Unit Tests, CodeCoverage, etc

Table of Contents

Getting started

To view the makefile targets:

make

Local testing:

make test

Docker:

make up

Scripts is under _scripts/ or you can use the makefile targets:

make requests-create
make requests-retrieve

Directory Structure

Tree view of the directory structure:
├── 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:

find . -name "*.py" ! -path "./venv/*" -exec pylint --rcfile .pylintrc --verbose {} +
pylint output
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:

-------------------------------------------------------------------
Your code has been rated at 10.00/10 (previous run: 9.81/10, +0.19)

Run prospector:

prospector --profile .prospector.yaml
# prospector --strictness low --with-tool pydocstyle
prospector output
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:

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:

bandit -r . -ll -x ./venv
bandit output
[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:

python3 -m unittest discover -s tests --verbose
unittest output
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:

coverage run --rcfile .coveragerc -m unittest discover -s tests 
coverage output
..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:

coverage report --rcfile .coveragerc
coverage report output
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:

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 extension for a Alembic migration environment.

To run database migrations:

Setting the environment:

export FLASK_APP=app:create_app
export FLASK_ENV=development

Running the database migrations

flask db init
flask db migrate -m "Initial db migration"
flask db upgrade

The database versioning directory structure:

├── migrations
│   ├── README
│   ├── alembic.ini
│   ├── env.py
│   ├── script.py.mako
│   └── versions
├── models
│   └── product.py

Resources:

Swagger API Documentation

Swagger has been implemented with Flasggr

Swagger Documentation is available at:

About

Structured Flask API with SQLAlchemy, Swagger, Unit Tests, CodeCoverage, etc

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published