Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor: organize application as a package #140

Merged
merged 19 commits into from
Sep 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
da446ac
refactor: move db and User into database module
angela-tran Aug 27, 2022
258f958
refactor: move Database into db package
angela-tran Aug 27, 2022
e246508
refactor: move db and User into a models module inside db package
angela-tran Aug 27, 2022
24fb1ec
refactor: convert setup script into click CLI command
angela-tran Aug 27, 2022
d475f6e
refactor: convert teardown script into click CLI command
angela-tran Aug 27, 2022
7a857d9
refactor: verify module uses current_app instead of actual app object
angela-tran Aug 30, 2022
a23143b
feat: make package importable through setup.py
angela-tran Aug 30, 2022
2d65a63
ci: add variable for use in run-tests workflow
angela-tran Aug 31, 2022
75dea8e
docs: correct some details about init script
angela-tran Sep 1, 2022
7de32c1
chore: remove unnecessary parameters leftover from refactoring
angela-tran Sep 2, 2022
5458a42
refactor: move init_app to module initialization
angela-tran Sep 2, 2022
1385b77
refactor: move database commands into single file
angela-tran Sep 2, 2022
ae836f0
chore(metadata): flesh out package metadata in setup script
angela-tran Sep 2, 2022
a898926
refactor: move check_user logic into Verify
angela-tran Sep 7, 2022
8481506
refactor(tests): move ported tests into more appropriate file
angela-tran Sep 7, 2022
2a0669a
chore: switch to using absolute imports
angela-tran Sep 7, 2022
4d3bdb1
feat(container): install package into app container
angela-tran Sep 7, 2022
39aaa7b
chore(metadata): remove unneeded specification of minor Python versions
angela-tran Sep 7, 2022
fd1c915
chore: fix some docstrings for database commands
angela-tran Sep 7, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.sample
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
FLASK_APP=eligibility_server/app.py
ELIGIBILITY_SERVER_SETTINGS=../config/sample.py
2 changes: 1 addition & 1 deletion .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@ jobs:

- name: Test with pytest
run: |
python setup.py
flask init-db
coverage run -m pytest
coverage report -m
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ __pycache__/
.env
config/*
!config/sample.py
eligibility_server.egg-info
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ RUN pip install --no-cache-dir -r requirements.txt
COPY bin/ bin/
COPY eligibility_server/ eligibility_server/
COPY *.py .
COPY README.md .

# install source as a package
RUN pip install -e .

# start app
ENTRYPOINT ["/bin/bash"]
Expand Down
2 changes: 1 addition & 1 deletion bin/init.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ set -eux

# run database migrations

python setup.py
flask init-db
9 changes: 4 additions & 5 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ Once you clone the repository locally, open it within VS Code, which will prompt
2. Start the `eligibility-server` Flask app and database with `F5`
3. Now you can run tests from the container.

Starting the Dev Container will run `bin/init.sh`, which runs `setup.py` and starts the Flask app. The `setup.py` script creates the database and imports and saves users
based on the configured settings.
Starting the Dev Container will run `bin/init.sh`, which runs a command to initialize the database. More specifically, it creates the database and imports and saves users based on the configured settings.

## Run tests

Expand All @@ -80,16 +79,16 @@ The test suite runs against every pull request via a GitHub Action.

In testing the database, you may need to teardown the database and restart a database from scratch.

The teardown script removes all users and drops the database. To tear down the database, run:
The command below will remove all users and drop the database:

```bash
python teardown.py
flask drop-db
machikoyasuda marked this conversation as resolved.
Show resolved Hide resolved
```

To set up the database with a new import file or other configuration variables, after making any new environment variable changes, run:

```bash
python setup.py
flask init-db
```

## Run and develop the Documentation
Expand Down
19 changes: 4 additions & 15 deletions eligibility_server/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

from flask import Flask, jsonify, make_response
from flask_restful import Api
from flask_sqlalchemy import SQLAlchemy
from flask.logging import default_handler

from .verify import Verify
from .keypair import get_server_public_key
from eligibility_server import db
from eligibility_server.verify import Verify
from eligibility_server.keypair import get_server_public_key

app = Flask(__name__)
app.config.from_object("eligibility_server.settings")
Expand Down Expand Up @@ -67,18 +67,7 @@ def internal_server_error(error):
api = Api(app)
api.add_resource(Verify, "/verify")

db = SQLAlchemy(app)


class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
sub = db.Column(db.String, unique=True, nullable=False)
name = db.Column(db.String, unique=True, nullable=False)
types = db.Column(db.String, unique=False, nullable=False)

def __repr__(self):
return "<User %r>" % self.sub

db.init_app(app)
angela-tran marked this conversation as resolved.
Show resolved Hide resolved

if __name__ == "__main__":
app.run(host=app.config["HOST"], debug=app.config["DEBUG_MODE"], port="8000") # nosec
62 changes: 0 additions & 62 deletions eligibility_server/database.py

This file was deleted.

17 changes: 17 additions & 0 deletions eligibility_server/db/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import logging

from flask_sqlalchemy import SQLAlchemy

logger = logging.getLogger(__name__)


db = SQLAlchemy()


def init_app(app):
db.init_app(app)

from .setup import init_db_command, drop_db_command

app.cli.add_command(init_db_command)
app.cli.add_command(drop_db_command)
11 changes: 11 additions & 0 deletions eligibility_server/db/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
from eligibility_server.db import db


class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
sub = db.Column(db.String, unique=True, nullable=False)
name = db.Column(db.String, unique=True, nullable=False)
types = db.Column(db.String, unique=False, nullable=False)

def __repr__(self):
return "<User %r>" % self.sub
95 changes: 95 additions & 0 deletions eligibility_server/db/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import csv
import json

import click
thekaveman marked this conversation as resolved.
Show resolved Hide resolved
from flask import current_app
from flask_sqlalchemy import inspect

from eligibility_server.db.models import db, User


@click.command("init-db")
def init_db_command():
with current_app.app_context():
inspector = inspect(db.engine)

if inspector.get_table_names():
click.echo("Tables already exist.")
if User.query.count() == 0:
import_users()
else:
click.echo("User table already has data.")
else:
click.echo("Creating table...")
db.create_all()
click.echo("Table created.")

import_users()


def import_users():
"""
Imports user data to be added to database and saves user to database

Users can be imported from either a JSON file or CSV file, as configured
with settings. CSV files take extra setting configurations:
CSV_DELIMITER, CSV_NEWLINE, CSV_QUOTING, CSV_QUOTECHAR
"""

file_path = current_app.config["IMPORT_FILE_PATH"]
click.echo(f"Importing users from {file_path}")

file_format = file_path.split(".")[-1]

if file_format == "json":
with open(file_path) as file:
data = json.load(file)["users"]
for user in data:
save_users(user, data[user][0], str(data[user][1]))
elif file_format == "csv":
with open(file_path, newline=current_app.config["CSV_NEWLINE"], encoding="utf-8") as file:
data = csv.reader(
file,
delimiter=current_app.config["CSV_DELIMITER"],
quoting=int(current_app.config["CSV_QUOTING"]),
quotechar=current_app.config["CSV_QUOTECHAR"],
)
for user in data:
save_users(user[0], user[1], user[2])
else:
click.echo(f"Warning: File format is not supported: {file_format}")

click.echo(f"Users added: {User.query.count()}")


def save_users(sub: str, name: str, types: str):
"""
Add users to the database User table

@param sub - User's sub
@param name - User's name
@param types - Types of eligibilities, in a stringified list
"""

item = User(sub=sub, name=name, types=types)
db.session.add(item)
db.session.commit()


@click.command("drop-db")
def drop_db_command():
with current_app.app_context():
inspector = inspect(db.engine)

if inspector.get_table_names():
try:
click.echo(f"Users to be deleted: {User.query.count()}")
User.query.delete()
db.session.commit()
except Exception as e:
click.echo("Failed to query for Users", e)

db.drop_all()
click.echo("Database dropped.")
else:
click.echo("Database does not exist.")
2 changes: 1 addition & 1 deletion eligibility_server/keypair.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from jwcrypto import jwk
import requests

from . import app
from eligibility_server import app


logger = logging.getLogger(__name__)
Expand Down
Loading