Skip to content

Commit

Permalink
merged main into update-validation schemas, finish SignUp tests next
Browse files Browse the repository at this point in the history
  • Loading branch information
mira-kine committed Nov 15, 2023
2 parents dc2738d + 1ff4320 commit e595814
Show file tree
Hide file tree
Showing 62 changed files with 2,879 additions and 830 deletions.
9 changes: 4 additions & 5 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
name: Run API and App Tests
on:
push:
branches: [main]
pull_request:
branches: [main]
# This is also a reusable workflow that can be called from other workflows
Expand All @@ -28,10 +27,10 @@ jobs:
- name: Install API dev Dependencies
run: |
pip install -r requirements-dev.txt
# - name: Test with tox
# run: |
# # Use tox because it is configured to test against the same package type being deployed
# tox
- name: Test with tox
run: |
# Use tox because it is configured to test against the same package type being deployed
tox
test-app:
runs-on: ubuntu-latest
defaults:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ In the production environment, each of these services along with `nginx` are dep

## Build Instructions

Before you can build the project, you will require a `.env` file containing access keys to the application third party services. Please message a team member on the [#home-unite-us slack channel](https://hackforla.slack.com/archives/CRWUG7X0C) once you've completed onboarding.
Before you can build the project, you will require a `.env` file containing access keys to the application third party services. Please message a team member on the [#home-unite-us slack channel](https://hackforla.slack.com/archives/CRWUG7X0C) once you've completed onboarding. See the [api](./api/README.md) and [app](./app/README.md) READMEs for more information about the required and optional environment variables.

Since this project is dockerized, you can choose to either build the backend and frontend apps as docker containers or directly onto your local machine. This guide will focus on docker builds, but full local build and deployment instructions can be found in the [api](./api/README.md) and [app](./app/README.md) READMEs.

Expand Down
5 changes: 5 additions & 0 deletions api/.env.dev.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# The development configuration uses 3rd party service
# API mocking, and enables debugging and server hot reloading
# by default. No additional environment variables are required,
# but see DevelopmentHUUConfig for the full range of options.
ENV='development'
9 changes: 0 additions & 9 deletions api/.env.example

This file was deleted.

15 changes: 15 additions & 0 deletions api/.env.prod.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Production configuration does not allow mocking, debugging
# or testing. Configuration values are strictly validated where possible.
# The following values are required for staging config.
# See ProductionHUUConfig for all options.
ENV='production'
COGNITO_CLIENT_ID=
COGNITO_CLIENT_SECRET=
COGNITO_REGION=
COGNITO_REDIRECT_URI=http://localhost:4040/signin
COGNITO_USER_POOL_ID=
SECRET_KEY=
COGNITO_ACCESS_ID=
COGNITO_ACCESS_KEY=
DATABASE_URL=
ROOT_URL=https://homeunite.us
15 changes: 15 additions & 0 deletions api/.env.staging.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Staging configuration does not allow mocking, but
# does allow debugging and testing.
# The following values are required for staging config.
# See StagingHUUConfig for all options.
ENV='staging'
COGNITO_CLIENT_ID=
COGNITO_CLIENT_SECRET=
COGNITO_REGION=
COGNITO_REDIRECT_URI=http://localhost:4040/signin
COGNITO_USER_POOL_ID=
SECRET_KEY=
COGNITO_ACCESS_ID=
COGNITO_ACCESS_KEY=
DATABASE_URL=
ROOT_URL=https://dev.homeunite.us
51 changes: 30 additions & 21 deletions api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,22 @@ Run `python -V` to check the Python version.

### Getting Started

For development purposes, run the following commands in the `api` directory to get started:
#### Configuration

The API application configuration must be specified before running the application. Configuration variables can be specified either as environment variables, or as entries within a `.env` file located within the `api` directory. To get started, copy the values from one of these configurations into a `.env` file:

- `.env.dev.example`
- [Recommended] This is the easiest configuration to use. It enables debugging, and mocks third party API calls so no secrets are needed to run the API.
- `.env.staging.example`
- This configuration strips all mocking from the application, but allows debugging and other testing related features.
- `.env.prod.example`
- This configuration strips all mocking and strictly validates the configuration to ensure that debugging is disabled and that the deployment is production-ready.

Using the `staging` or `production` configurations will require access to deployment secrets. If access to these secrets are needed to test a feature locally then you can request the secrets in the HUU slack channel.

#### Setup and Run

Once the `.env` file has been configured using the instructions outlined above, run the following commands in the `api` directory to install the required development dependencies and run the application.

```shell
python -m venv .venv # Creates a virtual environment in the `.venv` directory
Expand Down Expand Up @@ -61,7 +76,7 @@ The `tox` tool was installed into your virtual environment using the `pip instal

To launch the tests, run `tox`.

`tox` will run the tests for this (`api`) project using `pytest` in an isolated virtual environment that is distinct from the virtual environment that you created following the instructions from [Usage - Development](#usage---development).
`tox` will run the tests for this (`api`) project using `pytest` in an isolated virtual environment that is distinct from the virtual environment that you created following the instructions from [Usage - Development](#usage---development). By default `tox` will invoke `pytest` in `debug` mode, which uses 3rd party API mocking. The test cases can optionally be run without mocking by testing the application in `release` mode, using `tox -e releasetest` or `pytest --mode=release`.

### Alembic migrations

Expand Down Expand Up @@ -145,7 +160,7 @@ SELECT * FROM public.user;

to end the session just type `\q`.

If you want to clear the databse and start from scratch you can run:
If you want to clear the database and start from scratch you can run:

```
docker compose down -v
Expand Down Expand Up @@ -204,34 +219,28 @@ If you create any new configuration properties, please add an associative enum t

### Debugging

Debugging is enabled when using the `development` configuration. It can also be enabled on the `staging` configuration by setting the `FLASK_DEBUG` environment variable to `True`, or adding a `FLASK_DEBUG=True` to your local `.env` file. When debugging is enabled, the API server will automatically reload each time you save a change to the source code.

For Visual Studio Code users:

- Set breakpoint(s).
- Add the following config below to `api/openapi_server/.vscode/launch.json` and replace "absolute path to folder" accordingly.

```
```json
{
"name": "Python: Connexion",
"type": "python",
"request": "launch",
"module": "connexion",
"cwd": "${workspaceFolder}",
"env": {
"FLASK_APP": "openapi_server.__main__.py",
"FLASK_ENV": "development",
"FLASK_DEBUG": "1"
},
"args": [
"run",
"< absolute path to folder >/openapi_server",
"--port",
"8080"
],
}
"name": "Openapi_server module",
"type": "python",
"request": "launch",
"module": "openapi_server",
"justMyCode": false,
"cwd": "${workspaceFolder}/api"
}
```

- Go to `openapi_server/__main__.py` and select "Run" -> "Start with Debugging".

With this configuration selecting "Run" -> "Start with Debugging" will start the API with the debugger enabled.

## Usage - Production

A WSGI server is needed to run our application. A WSGI (Web Server Gateway Interface) server is a bridge between a web server and web applications. The server handles requests, invokes the web application, translates responses back to HTTP, and also manages concurrency to ensure the server can handle multiple requests simultaneously.
Expand Down
7 changes: 0 additions & 7 deletions api/configs/config_properties.py

This file was deleted.

32 changes: 0 additions & 32 deletions api/configs/configs.py

This file was deleted.

11 changes: 0 additions & 11 deletions api/configs/personal_config_example.py

This file was deleted.

108 changes: 9 additions & 99 deletions api/openapi_server/__main__.py
Original file line number Diff line number Diff line change
@@ -1,105 +1,15 @@
# Standard Lib
from os import environ as env
from pathlib import Path
from typing import Dict, Any

# Third Party
import connexion
from dotenv import load_dotenv, find_dotenv, get_key
import prance

# Local
from openapi_server import encoder
from openapi_server.models.database import DataAccessLayer
from openapi_server.exceptions import AuthError, handle_auth_error
from configs.configs import compile_config
from configs.config_properties import ConfigProperties as cp


DataAccessLayer.db_init()


def get_bundled_specs(main_file: Path) -> Dict[str, Any]:
'''
Prance is able to resolve references to local *.yaml files.
Use prance to parse the api specification document. Connexion's
default parser is not able to handle local file references, but
our api specification is split across multiple files for readability.
Args:
main_file (Path): Path to a api specification .yaml file
Returns:
Dict[str, Any]: Parsed specification file, stored in a dict
'''
parser = prance.ResolvingParser(str(main_file.absolute()), lazy=True, strict=True)
parser.parse()

return parser.specification

def get_version():
from importlib.metadata import version, PackageNotFoundError

try:
return version("homeuniteus-api")
except PackageNotFoundError:
# package is not installed
return "0.1.0.dev0"

def create_app():
'''
Creates a configured application that is ready to be run.
The application is configured from external environment variables stored
in either a local `.env` in a development environment or from environment
variables that are configured prior to running the application.
This function is made available at the module level so that it can
be loaded by a WSGI server in a production-like environment with
`openapi_server.__main__:create_app()`.
'''

# `connexion.App` is aliased to `connexion.FlaskApp` (as of connexion v2.13.1)
# which is the connexion layer built on top of Flask. So we think of it as
# the connexion application.
connexion_app = connexion.App(__name__)
api_spec_path = connexion_app.get_root_path() / Path('./openapi/openapi.yaml')
parsed_specs = get_bundled_specs(api_spec_path)

parsed_specs['info']['version'] = get_version()

connexion_app.add_api(parsed_specs, pythonic_params=True)
connexion_app.add_error_handler(AuthError, handle_auth_error)

ENV_FILE = find_dotenv()
if ENV_FILE:
load_dotenv(ENV_FILE)
SECRET_KEY = env.get("SECRET_KEY")
env_config_profile = get_key(find_dotenv(), "CONFIG_PROFILE")

# The underlying instance of Flask is stored in `connexion_app.app`.
# This is an instance of `flask.Flask`.
flask_app = connexion_app.app
flask_app.json_encoder = encoder.JSONEncoder
flask_app.secret_key = SECRET_KEY

# Below, the Flask configuration handler is loaded with the
# application configuration settings
flask_app.config.from_object(compile_config(env_config_profile))

return connexion_app

from openapi_server.app import create_app

if __name__ == "__main__":
# This will/should be only run in a development environment

connexion_app = create_app()
flask_app = connexion_app.app

if flask_app.environment == "production":
raise EnvironmentError("The production configuration must be run on a "
"production server. Connexion's app.run() method "
"starts a development server that should be used "
"for testing purposes only.")
connexion_app.run(
host=flask_app.config[cp.HOST.name],
port=flask_app.config[cp.PORT.name],
debug=flask_app.config[cp.DEBUG.name],
use_reloader=flask_app.config[cp.USE_RELOADER.name],
host=flask_app.config["HOST"],
port=flask_app.config["PORT"],
load_dotenv=False
)
Loading

0 comments on commit e595814

Please sign in to comment.