From 0dd8c92777da778ad0a02ace910e3210b6b58bab Mon Sep 17 00:00:00 2001 From: Sawyer Hollenshead Date: Thu, 14 Dec 2023 07:50:23 -0800 Subject: [PATCH 1/4] Fix broken Markdown references (#216) ## Changes Fix various broken Markdown [links that are now being reported by the infra template's Markdown Link Checker CI](https://github.com/navapbc/pfml-starter-kit-app/actions/runs/7202268166/job/19620064707). --- docs/app/api-details.md | 24 +------------------ .../database/database-access-management.md | 2 +- docs/app/database/database-management.md | 4 +++- docs/app/technical-overview.md | 3 +-- 4 files changed, 6 insertions(+), 27 deletions(-) diff --git a/docs/app/api-details.md b/docs/app/api-details.md index 685b4414..201c4d0c 100644 --- a/docs/app/api-details.md +++ b/docs/app/api-details.md @@ -4,28 +4,6 @@ See [Technical Overview](./technical-overview.md) for details on the technologie Each endpoint is configured in the [openapi.generated.yml](/app/openapi.generated.yml) file which provides basic request validation. Each endpoint specifies an `operationId` that maps to a function defined in the code that will handle the request. -To make handling a request easier, an [ApiContext](/app/src/util/api_context.py) exists which will fetch the DB session, request body, and current user. This can be used like so: -```py -def example_post() -> flask.Response: - with api_context_manager() as api_context: - # Work with the request body - first_name = api_context.request_body["first_name"] - - # Work with the user - current_user = api_context.current_user - - # Work with the DB session - api_context.db_session.query(..) - - # Return a response - return response_util.success_response( - message="Success", - data={"db_model_id": "1234"}, # Whatever the response object should be - status_code=201 - ) -``` -For more complex usages, it is recommended you put the implementation into a separate handler file in order to keep the API entrypoints easier to read. - # Swagger The Swagger UI can be reached locally at [http://localhost:8080/docs](http://localhost:8080/docs) when running the API. The UI is based on the [openapi.generated.yml](/app/openapi.generated.yml) file. @@ -39,7 +17,7 @@ All model schemas defined can be found at the bottom of the UI. # Routes ## Health Check -[GET /v1/healthcheck](/app/src/route/healthcheck.py) is an endpoint for checking the health of the service. It verifies that the database is reachable, and that the API service itself is up and running. +[GET /v1/healthcheck](/app/src/api/healthcheck.py) is an endpoint for checking the health of the service. It verifies that the database is reachable, and that the API service itself is up and running. Note this endpoint explicitly does not require authorization so it can be integrated with any automated monitoring you may build. diff --git a/docs/app/database/database-access-management.md b/docs/app/database/database-access-management.md index 050a1755..b74b664f 100644 --- a/docs/app/database/database-access-management.md +++ b/docs/app/database/database-access-management.md @@ -4,7 +4,7 @@ This document describes the best practices and patterns for how the application ## Client Initialization and Configuration -The database client is initialized when the application starts (see [src/\_\_main\_\_.py](../../../app/src/app.py). The database engine that is used to create acquire connections to the database is initialized using the database configuration defined in [db_config.py](../../../app/src/db/db_config.py), which is configured through environment variables. The initialized database client is then stored on the Flask app's [\`extensions\` dictionary](https://flask.palletsprojects.com/en/2.2.x/src/#flask.Flask.extensions) to be used throughout the lifetime of the application. +The database client is initialized when the application starts (see [`src/__main__.py`](../../../app/src/app.py)). The database engine that is used to acquire connections to the database is initialized using the database configuration defined in [postgres_config.py](../../../app/src/adapters/db/clients/postgres_config.py), which is configured through environment variables. The initialized database client is then stored on the Flask app's [`extensions` dictionary](https://flask.palletsprojects.com/en/2.2.x/extensions/) to be used throughout the lifetime of the application. ## Session Management diff --git a/docs/app/database/database-management.md b/docs/app/database/database-management.md index 957c601a..6c3724aa 100644 --- a/docs/app/database/database-management.md +++ b/docs/app/database/database-management.md @@ -61,8 +61,10 @@ $ make db-migrate
Example: Adding a new column to an existing table: -1. Manually update the database models with the changes ([example_models.py](/app/src/db/models/example_models.py) in this example) +1. Manually update the database models with the changes ```python +# src/db/models/example_models.py + class ExampleTable(Base): ... my_new_timestamp = Column(TIMESTAMP(timezone=True)) # Newly added line diff --git a/docs/app/technical-overview.md b/docs/app/technical-overview.md index 3ae39c59..1851e862 100644 --- a/docs/app/technical-overview.md +++ b/docs/app/technical-overview.md @@ -4,8 +4,6 @@ - [Request operations](#request-operations) - [Authentication](#authentication) - [Authorization](#authorization) -- [Running In Hosted Environments](#running-in-hosted-environments) - - [ECS](#ecs) ## Key Technologies @@ -38,6 +36,7 @@ generally preferred. [sqlalchemy-src]: https://github.com/sqlalchemy/sqlalchemy [alembic-home]: https://alembic.sqlalchemy.org/en/latest/ +[alembic-src]: https://github.com/sqlalchemy/alembic ## Request operations From bc26f0f48bd85ee87607f488ed7e71fbe9cf7859 Mon Sep 17 00:00:00 2001 From: Sawyer Hollenshead Date: Wed, 3 Jan 2024 13:58:33 -0800 Subject: [PATCH 2/4] Make update-template.sh more robust to deletions and changes to files (#217) ## Ticket Resolves #196 ## Changes - Copies the changes made in https://github.com/navapbc/template-infra/issues/352 --- .../download-and-install-template.sh | 5 +++ template-only-bin/install-template.sh | 6 ++- template-only-bin/update-template.sh | 44 +++++++++++++++++-- 3 files changed, 49 insertions(+), 6 deletions(-) diff --git a/template-only-bin/download-and-install-template.sh b/template-only-bin/download-and-install-template.sh index d2b6b896..7a5efe81 100755 --- a/template-only-bin/download-and-install-template.sh +++ b/template-only-bin/download-and-install-template.sh @@ -7,5 +7,10 @@ git clone --single-branch --branch main --depth 1 git@github.com:navapbc/templat echo "Install template" ./template-application-flask/template-only-bin/install-template.sh +# Store template version in a file +cd template-application-flask +git rev-parse HEAD >../.template-flask-version +cd - + echo "Clean up template-application-flask folder" rm -fr template-application-flask diff --git a/template-only-bin/install-template.sh b/template-only-bin/install-template.sh index d723362e..37074db0 100755 --- a/template-only-bin/install-template.sh +++ b/template-only-bin/install-template.sh @@ -1,8 +1,8 @@ #!/bin/bash # -# This script installs template-application-flask to your project. Run +# This script installs the template-application-flask to your project. Run # This script from your project's root directory. -set -euo pipefail +set -euox pipefail CUR_DIR=$(pwd) SCRIPT_DIR=$(dirname $0) @@ -10,6 +10,7 @@ TEMPLATE_DIR="$SCRIPT_DIR/.." echo "Copy files from template-application-flask" cd $TEMPLATE_DIR +# Note: Keep this list of paths in sync with INCLUDE_PATHS in update-template.sh cp -r \ .github \ .dockleconfig \ @@ -21,4 +22,5 @@ cp -r \ cd - echo "Remove files relevant only to template development" +# Note: Keep this list of paths in sync with EXCLUDE_OPT in update-template.sh rm .github/workflows/template-only-* diff --git a/template-only-bin/update-template.sh b/template-only-bin/update-template.sh index f989ef85..9f4a1b32 100755 --- a/template-only-bin/update-template.sh +++ b/template-only-bin/update-template.sh @@ -1,10 +1,46 @@ #!/bin/bash -# +# ----------------------------------------------------------------------------- # This script updates template-application-flask in your project. Run # This script from your project's root directory. +# +# Positional parameters: +# TARGET_VERSION (optional) – the version of template-application-flask to upgrade to. +# Defaults to main. +# ----------------------------------------------------------------------------- set -euo pipefail -SCRIPT_DIR=$(dirname $0) +TARGET_VERSION=${1:-"main"} + +CURRENT_VERSION=$(cat .template-flask-version) + +echo "Clone template-application-flask" +git clone https://github.com/navapbc/template-application-flask.git + +echo "Creating patch" +cd template-application-flask +git checkout $TARGET_VERSION + +# Get version hash to update .template-flask-version after patch is successful +TARGET_VERSION_HASH=$(git rev-parse HEAD) + +# Note: Keep this list in sync with the files copied in install-template.sh +INCLUDE_PATHS=" \ + github \ + .dockleconfig \ + .hadolint.yaml \ + app \ + docker-compose.yml \ + docs" +git diff $CURRENT_VERSION $TARGET_VERSION -- $INCLUDE_PATHS >patch +cd - + +echo "Applying patch" +# Note: Keep this list in sync with the removed files in install-template.sh +EXCLUDE_OPT="--exclude=.github/workflows/template-only-*" +git apply $EXCLUDE_OPT --allow-empty template-application-flask/patch + +echo "Saving new template version to .template-application-flask" +echo $TARGET_VERSION_HASH >.template-flask-version -echo "Install template" -$SCRIPT_DIR/install-template.sh +echo "Clean up template-application-flask folder" +rm -fr template-application-flask From b34be60dcecef56be1e4340de6a990fe4dea4cd5 Mon Sep 17 00:00:00 2001 From: Yoom Lam Date: Mon, 22 Jan 2024 14:03:34 -0600 Subject: [PATCH 3/4] Fix buggy md syntax in technical-overview.md (#221) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Ticket Resolves linter complaint: ``` ERROR: 1 dead links found! [✖] alembic-src → Status: 400 ``` ## Changes Use square brackets, not parentheses. ## Context for reviewers > Testing instructions, background context, more in-depth details of the implementation, and anything else you'd like to call out or ask reviewers. ## Testing The markdown linter should not cause this error. --- docs/app/technical-overview.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/app/technical-overview.md b/docs/app/technical-overview.md index 1851e862..592a7b88 100644 --- a/docs/app/technical-overview.md +++ b/docs/app/technical-overview.md @@ -19,7 +19,7 @@ generally preferred. - [OpenAPI Specification][oas-docs] - [API Flask][apiflask-home] ([source code][apiflask-src]) - [SQLAlchemy][sqlalchemy-home] ([source code][sqlalchemy-src]) -- [Alembic][alembic-home] ([source code](alembic-src)) +- [Alembic][alembic-home] ([source code][alembic-src]) - [pydantic][pydantic-home] ([source code][pydantic-src]) - [poetry](https://python-poetry.org/docs/) - Python dependency management @@ -57,4 +57,4 @@ function that is run to do the authentication. n/a - Specific user authorization is not yet implemented for this API. ### Database diagram -n/a - Database diagrams are not yet available for this application. \ No newline at end of file +n/a - Database diagrams are not yet available for this application. From 5730b467c35b6aa796706e325505f06eaeef935a Mon Sep 17 00:00:00 2001 From: Ryan Lewis <93001277+rylew1@users.noreply.github.com> Date: Mon, 15 Apr 2024 11:54:44 -0700 Subject: [PATCH 4/4] VSCode API Debugger (#222) ## Changes - Add API debugging ability from VSCode --- app/Makefile | 3 +++ app/poetry.lock | 57 +++++++++++++++++++++++++++++++++++++--- app/pyproject.toml | 1 + docker-compose.debug.yml | 26 ++++++++++++++++++ docs/app/README.md | 51 ++++++++++++++++++++++++++++++++++- 5 files changed, 134 insertions(+), 4 deletions(-) create mode 100644 docker-compose.debug.yml diff --git a/app/Makefile b/app/Makefile index 9d733bf9..f9b39024 100644 --- a/app/Makefile +++ b/app/Makefile @@ -92,6 +92,9 @@ build: start: docker compose up --detach +start-debug: + docker compose -f ../docker-compose.yml -f ../docker-compose.debug.yml up --detach + run-logs: start docker compose logs --follow --no-color $(APP_NAME) diff --git a/app/poetry.lock b/app/poetry.lock index d357dd31..758c8159 100644 --- a/app/poetry.lock +++ b/app/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.5.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand. [[package]] name = "alembic" @@ -520,6 +520,37 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] +[[package]] +name = "debugpy" +version = "1.8.1" +description = "An implementation of the Debug Adapter Protocol for Python" +optional = false +python-versions = ">=3.8" +files = [ + {file = "debugpy-1.8.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:3bda0f1e943d386cc7a0e71bfa59f4137909e2ed947fb3946c506e113000f741"}, + {file = "debugpy-1.8.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dda73bf69ea479c8577a0448f8c707691152e6c4de7f0c4dec5a4bc11dee516e"}, + {file = "debugpy-1.8.1-cp310-cp310-win32.whl", hash = "sha256:3a79c6f62adef994b2dbe9fc2cc9cc3864a23575b6e387339ab739873bea53d0"}, + {file = "debugpy-1.8.1-cp310-cp310-win_amd64.whl", hash = "sha256:7eb7bd2b56ea3bedb009616d9e2f64aab8fc7000d481faec3cd26c98a964bcdd"}, + {file = "debugpy-1.8.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:016a9fcfc2c6b57f939673c874310d8581d51a0fe0858e7fac4e240c5eb743cb"}, + {file = "debugpy-1.8.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd97ed11a4c7f6d042d320ce03d83b20c3fb40da892f994bc041bbc415d7a099"}, + {file = "debugpy-1.8.1-cp311-cp311-win32.whl", hash = "sha256:0de56aba8249c28a300bdb0672a9b94785074eb82eb672db66c8144fff673146"}, + {file = "debugpy-1.8.1-cp311-cp311-win_amd64.whl", hash = "sha256:1a9fe0829c2b854757b4fd0a338d93bc17249a3bf69ecf765c61d4c522bb92a8"}, + {file = "debugpy-1.8.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:3ebb70ba1a6524d19fa7bb122f44b74170c447d5746a503e36adc244a20ac539"}, + {file = "debugpy-1.8.1-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a2e658a9630f27534e63922ebf655a6ab60c370f4d2fc5c02a5b19baf4410ace"}, + {file = "debugpy-1.8.1-cp312-cp312-win32.whl", hash = "sha256:caad2846e21188797a1f17fc09c31b84c7c3c23baf2516fed5b40b378515bbf0"}, + {file = "debugpy-1.8.1-cp312-cp312-win_amd64.whl", hash = "sha256:edcc9f58ec0fd121a25bc950d4578df47428d72e1a0d66c07403b04eb93bcf98"}, + {file = "debugpy-1.8.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:7a3afa222f6fd3d9dfecd52729bc2e12c93e22a7491405a0ecbf9e1d32d45b39"}, + {file = "debugpy-1.8.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d915a18f0597ef685e88bb35e5d7ab968964b7befefe1aaea1eb5b2640b586c7"}, + {file = "debugpy-1.8.1-cp38-cp38-win32.whl", hash = "sha256:92116039b5500633cc8d44ecc187abe2dfa9b90f7a82bbf81d079fcdd506bae9"}, + {file = "debugpy-1.8.1-cp38-cp38-win_amd64.whl", hash = "sha256:e38beb7992b5afd9d5244e96ad5fa9135e94993b0c551ceebf3fe1a5d9beb234"}, + {file = "debugpy-1.8.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:bfb20cb57486c8e4793d41996652e5a6a885b4d9175dd369045dad59eaacea42"}, + {file = "debugpy-1.8.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efd3fdd3f67a7e576dd869c184c5dd71d9aaa36ded271939da352880c012e703"}, + {file = "debugpy-1.8.1-cp39-cp39-win32.whl", hash = "sha256:58911e8521ca0c785ac7a0539f1e77e0ce2df753f786188f382229278b4cdf23"}, + {file = "debugpy-1.8.1-cp39-cp39-win_amd64.whl", hash = "sha256:6df9aa9599eb05ca179fb0b810282255202a66835c6efb1d112d21ecb830ddd3"}, + {file = "debugpy-1.8.1-py2.py3-none-any.whl", hash = "sha256:28acbe2241222b87e255260c76741e1fbf04fdc3b6d094fcf57b6c6f75ce1242"}, + {file = "debugpy-1.8.1.zip", hash = "sha256:f696d6be15be87aef621917585f9bb94b1dc9e8aced570db1b8a6fc14e8f9b42"}, +] + [[package]] name = "docopt" version = "0.6.2" @@ -956,6 +987,16 @@ files = [ {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:f698de3fd0c4e6972b92290a45bd9b1536bffe8c6759c62471efaa8acb4c37bc"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:aa57bd9cf8ae831a362185ee444e15a93ecb2e344c8e52e4d721ea3ab6ef1823"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffcc3f7c66b5f5b7931a5aa68fc9cecc51e685ef90282f4a82f0f5e9b704ad11"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:47d4f1c5f80fc62fdd7777d0d40a2e9dda0a05883ab11374334f6c4de38adffd"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1f67c7038d560d92149c060157d623c542173016c4babc0c1913cca0564b9939"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9aad3c1755095ce347e26488214ef77e0485a3c34a50c5a5e2471dff60b9dd9c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:14ff806850827afd6b07a5f32bd917fb7f45b046ba40c57abdb636674a8b559c"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8f9293864fe09b8149f0cc42ce56e3f0e54de883a9de90cd427f191c346eb2e1"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win32.whl", hash = "sha256:715d3562f79d540f251b99ebd6d8baa547118974341db04f5ad06d5ea3eb8007"}, + {file = "MarkupSafe-2.1.3-cp312-cp312-win_amd64.whl", hash = "sha256:1b8dd8c3fd14349433c79fa8abeb573a55fc0fdd769133baac1f5e07abf54aeb"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, @@ -1643,6 +1684,7 @@ files = [ {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69b023b2b4daa7548bcfbd4aa3da05b3a74b772db9e23b982788168117739938"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:81e0b275a9ecc9c0c0c07b4b90ba548307583c125f54d5b6946cfee6360c733d"}, {file = "PyYAML-6.0.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ba336e390cd8e4d1739f42dfe9bb83a3cc2e80f567d8805e11b46f4a943f5515"}, + {file = "PyYAML-6.0.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:326c013efe8048858a6d312ddd31d56e468118ad4cdeda36c719bf5bb6192290"}, {file = "PyYAML-6.0.1-cp310-cp310-win32.whl", hash = "sha256:bd4af7373a854424dabd882decdc5579653d7868b8fb26dc7d0e99f823aa5924"}, {file = "PyYAML-6.0.1-cp310-cp310-win_amd64.whl", hash = "sha256:fd1592b3fdf65fff2ad0004b5e363300ef59ced41c2e6b3a99d4089fa8c5435d"}, {file = "PyYAML-6.0.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6965a7bc3cf88e5a1c3bd2e0b5c22f8d677dc88a455344035f03399034eb3007"}, @@ -1650,8 +1692,15 @@ files = [ {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:42f8152b8dbc4fe7d96729ec2b99c7097d656dc1213a3229ca5383f973a5ed6d"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:062582fca9fabdd2c8b54a3ef1c978d786e0f6b3a1510e0ac93ef59e0ddae2bc"}, {file = "PyYAML-6.0.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b04aac4d386b172d5b9692e2d2da8de7bfb6c387fa4f801fbf6fb2e6ba4673"}, + {file = "PyYAML-6.0.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:e7d73685e87afe9f3b36c799222440d6cf362062f78be1013661b00c5c6f678b"}, {file = "PyYAML-6.0.1-cp311-cp311-win32.whl", hash = "sha256:1635fd110e8d85d55237ab316b5b011de701ea0f29d07611174a1b42f1444741"}, {file = "PyYAML-6.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:bf07ee2fef7014951eeb99f56f39c9bb4af143d8aa3c21b1677805985307da34"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:855fb52b0dc35af121542a76b9a84f8d1cd886ea97c84703eaa6d88e37a2ad28"}, + {file = "PyYAML-6.0.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:40df9b996c2b73138957fe23a16a4f0ba614f4c0efce1e9406a184b6d07fa3a9"}, + {file = "PyYAML-6.0.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c22bec3fbe2524cde73d7ada88f6566758a8f7227bfbf93a408a9d86bcc12a0"}, + {file = "PyYAML-6.0.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8d4e9c88387b0f5c7d5f281e55304de64cf7f9c0021a3525bd3b1c542da3b0e4"}, + {file = "PyYAML-6.0.1-cp312-cp312-win32.whl", hash = "sha256:d483d2cdf104e7c9fa60c544d92981f12ad66a457afae824d146093b8c294c54"}, + {file = "PyYAML-6.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:0d3304d8c0adc42be59c5f8a4d9e3d7379e6955ad754aa9d6ab7a398b59dd1df"}, {file = "PyYAML-6.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:50550eb667afee136e9a77d6dc71ae76a44df8b3e51e41b77f6de2932bfe0f47"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fe35611261b29bd1de0070f0b2f47cb6ff71fa6595c077e42bd0c419fa27b98"}, {file = "PyYAML-6.0.1-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:704219a11b772aea0d8ecd7058d0082713c3562b4e271b849ad7dc4a5c90c13c"}, @@ -1668,6 +1717,7 @@ files = [ {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cd17c15d3bb3fa06978b4e8958dcdc6e0174ccea823003a106c7d4d7899ac5"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28c119d996beec18c05208a8bd78cbe4007878c6dd15091efb73a30e90539696"}, {file = "PyYAML-6.0.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7e07cbde391ba96ab58e532ff4803f79c4129397514e1413a7dc761ccd755735"}, + {file = "PyYAML-6.0.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:49a183be227561de579b4a36efbb21b3eab9651dd81b1858589f796549873dd6"}, {file = "PyYAML-6.0.1-cp38-cp38-win32.whl", hash = "sha256:184c5108a2aca3c5b3d3bf9395d50893a7ab82a38004c8f61c258d4428e80206"}, {file = "PyYAML-6.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:1e2722cc9fbb45d9b87631ac70924c11d3a401b2d7f410cc0e3bbf249f2dca62"}, {file = "PyYAML-6.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9eb6caa9a297fc2c2fb8862bc5370d0303ddba53ba97e71f08023b6cd73d16a8"}, @@ -1675,6 +1725,7 @@ files = [ {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5773183b6446b2c99bb77e77595dd486303b4faab2b086e7b17bc6bef28865f6"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b786eecbdf8499b9ca1d697215862083bd6d2a99965554781d0d8d1ad31e13a0"}, {file = "PyYAML-6.0.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc1bf2925a1ecd43da378f4db9e4f799775d6367bdb94671027b73b393a7c42c"}, + {file = "PyYAML-6.0.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:04ac92ad1925b2cff1db0cfebffb6ffc43457495c9b3c39d3fcae417d7125dc5"}, {file = "PyYAML-6.0.1-cp39-cp39-win32.whl", hash = "sha256:faca3bdcf85b2fc05d06ff3fbc1f83e1391b3e724afa3feba7d13eeab355484c"}, {file = "PyYAML-6.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:510c9deebc5c0225e8c96813043e62b680ba2f9c50a08d3724c7f28a747d1486"}, {file = "PyYAML-6.0.1.tar.gz", hash = "sha256:bfdf460b1736c775f2ba9f6a92bca30bc2095067b8a9d77876d1fad6cc3b4a43"}, @@ -1874,7 +1925,7 @@ files = [ ] [package.dependencies] -greenlet = {version = "!=0.4.17", markers = "platform_machine == \"win32\" or platform_machine == \"WIN32\" or platform_machine == \"AMD64\" or platform_machine == \"amd64\" or platform_machine == \"x86_64\" or platform_machine == \"ppc64le\" or platform_machine == \"aarch64\""} +greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""} mypy = {version = ">=0.910", optional = true, markers = "extra == \"mypy\""} typing-extensions = ">=4.2.0" @@ -2112,4 +2163,4 @@ files = [ [metadata] lock-version = "2.0" python-versions = "^3.10" -content-hash = "a3440ea0134e772b0a01f888be1b12e3e881fdc34577dd22a0eb9743e6f8cccb" +content-hash = "503c58c553940d63ad7e47af89eb8c410aa89b1dc416a653aea29c7ec49c9cd8" diff --git a/app/pyproject.toml b/app/pyproject.toml index 56f75bba..ff93ceca 100644 --- a/app/pyproject.toml +++ b/app/pyproject.toml @@ -40,6 +40,7 @@ pytest-watch = "^4.2.0" pytest-lazy-fixture = "^0.6.3" types-pyyaml = "^6.0.12.11" setuptools = "^68.2.2" +debugpy = "^1.8.1" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/docker-compose.debug.yml b/docker-compose.debug.yml new file mode 100644 index 00000000..16d80067 --- /dev/null +++ b/docker-compose.debug.yml @@ -0,0 +1,26 @@ +version: "3.8" + +# run with `docker compose -f` +# combines ports and env vars with the main docker-compose.yml main-app service + +services: + main-app: + build: + context: ./app + target: dev + args: + - RUN_UID=${RUN_UID:-4000} + - RUN_USER=${RUN_USER:-app} + container_name: main-app + env_file: ./app/local.env + command: [ + "poetry", "run", "python", "-m", "debugpy", + "--listen", "0.0.0.0:5678", + "--wait-for-client", "--log-to-stderr", + "-m", "flask", "--app", "src.app", "run", + "--host", "0.0.0.0", "--port", "8080", "--no-reload" + ] + ports: + - 5678:5678 + volumes: + - ./app:/app diff --git a/docs/app/README.md b/docs/app/README.md index fccfa934..7723a9ce 100644 --- a/docs/app/README.md +++ b/docs/app/README.md @@ -15,7 +15,7 @@ root │ └── src │ └── auth Authentication code for API │ └── db -│ └── models DB model definitions +│ └── models DB model definitions │ └── migrations DB migration configs │ └── versions The DB migrations │ └── logging @@ -105,3 +105,52 @@ Any environment variables specified directly in the [docker-compose](/docker-com ## Authentication This API uses a very simple [ApiKey authentication approach](https://apiflask.com/authentication/#use-external-authentication-library) which requires the caller to provide a static key. This is specified with the `API_AUTH_TOKEN` environment variable. + +## VSCode Remote Attach Container Debugging + +The API can be run in debug mode that allows for remote attach debugging (currently only supported from VSCode) to the container. + +- Requirements: + + - VSCode Python extension + - Updated Poetry with the `debugpy` dev package in `pyproject.toml` + +- First create a file `./vscode/launch.json` - as shown below. (Default name of `Python: Remote Attach`) + +- Start the server in debug mode via `make start-debug` or `make start-debug run-logs`. + - This will start the `main-app` service with port 5678 exposed. + +- The server will start in waiting mode, waiting for you to attach the debugger (see `/src/app.py`) before continuing to run. + +- Go to your VSCode debugger window and run the `Python: Remote Attach` option + +- You should now be able to hit set breakpoints throughout the API + +`./vscode/launch.json`: + +``` +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: Remote Attach", + "type": "debugpy", + "request": "attach", + "connect": { + "host": "localhost", + "port": 5678 + }, + "pathMappings": [ + { + "localRoot": "${workspaceFolder}/app", + "remoteRoot": "." + } + ], + "justMyCode": false, + } + ] +} +```