From 80cc6e44305bbf4917bdfe59261255b73a6792cb Mon Sep 17 00:00:00 2001 From: Drew Bednar Date: Thu, 25 Nov 2021 22:20:46 -0500 Subject: [PATCH] major api refactoring --- Makefile | 22 +++---- {backend => api}/.dockerignore | 0 {backend => api}/Dockerfile | 5 +- api/api/__init__.py | 38 +++++++++++ api/api/app.py | 21 ++++++ api/api/settings.py | 64 +++++++++++++++++++ {backend/src => api}/entrypoint.sh | 0 {backend => api}/requirements.in | 1 + {backend => api}/requirements.txt | 19 ++++-- {tests => api/tests}/__init__.py | 0 {tests => api/tests}/unit/__init__.py | 0 .../tests/unit/api}/__init__.py | 0 .../tests/unit/api}/test_settings.py | 2 +- api/wsgi.py | 6 ++ backend/src/app.py | 21 ------ backend/src/settings.py | 27 -------- 16 files changed, 157 insertions(+), 69 deletions(-) rename {backend => api}/.dockerignore (100%) rename {backend => api}/Dockerfile (82%) create mode 100644 api/api/__init__.py create mode 100644 api/api/app.py create mode 100644 api/api/settings.py rename {backend/src => api}/entrypoint.sh (100%) rename {backend => api}/requirements.in (69%) rename {backend => api}/requirements.txt (58%) rename {tests => api/tests}/__init__.py (100%) rename {tests => api/tests}/unit/__init__.py (100%) rename {tests/unit/backend => api/tests/unit/api}/__init__.py (100%) rename {tests/unit/backend => api/tests/unit/api}/test_settings.py (96%) create mode 100644 api/wsgi.py delete mode 100644 backend/src/app.py delete mode 100644 backend/src/settings.py diff --git a/Makefile b/Makefile index ec74c09..943074d 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ REGISTRY_NAME=registry.runcible.io FRONTEND_IMAGE_NAME=$(REGISTRY_NAME)/demo-helm-frontend -FRONTEND_IMAGE_VERSION=1.1.0-dev -BACKEND_IMAGE_NAME=$(REGISTRY_NAME)/demo-helm-backend -BACKEND_IMAGE_VERSION=1.1.0-dev +FRONTEND_IMAGE_VERSION=1.2.0-dev +API_IMAGE_NAME=$(REGISTRY_NAME)/demo-helm-api +API_IMAGE_VERSION=1.2.0-dev push-app-image: build-app-image docker push $(FRONTEND_IMAGE_NAME):$(FRONTEND_IMAGE_VERSION) @@ -10,18 +10,18 @@ push-app-image: build-app-image build-app-image: docker build -t $(FRONTEND_IMAGE_NAME):$(FRONTEND_IMAGE_VERSION) ./app -push-backend-image: build-backend-image - docker push $(BACKEND_IMAGE_NAME):$(BACKEND_IMAGE_VERSION) +push-api-image: build-api-image + docker push $(API_IMAGE_NAME):$(API_IMAGE_VERSION) -build-backend-image: - docker build -t $(BACKEND_IMAGE_NAME):$(BACKEND_IMAGE_VERSION) ./backend +build-api-image: + docker build -t $(API_IMAGE_NAME):$(API_IMAGE_VERSION) ./api -update-backend-deps: - pip-compile -o backend/requirements.txt backend/requirements.in +update-API-deps: + pip-compile -o api/requirements.txt api/requirements.in sync-virtualenv: - pip-sync dev_requirements.txt backend/requirements.txt + pip-sync dev_requirements.txt api/requirements.txt check-images: echo $(FRONTEND_IMAGE_NAME):$(FRONTEND_IMAGE_VERSION) - echo $(BACKEND_IMAGE_NAME):$(BACKEND_IMAGE_VERSION) + echo $(API_IMAGE_NAME):$(API_IMAGE_VERSION) diff --git a/backend/.dockerignore b/api/.dockerignore similarity index 100% rename from backend/.dockerignore rename to api/.dockerignore diff --git a/backend/Dockerfile b/api/Dockerfile similarity index 82% rename from backend/Dockerfile rename to api/Dockerfile index 88d7e72..63957a8 100644 --- a/backend/Dockerfile +++ b/api/Dockerfile @@ -18,7 +18,8 @@ COPY requirements.txt . RUN pip install --no-cache -r requirements.txt # Install app -COPY src/* ./ +COPY wsgi.py ./ +COPY api/ ./api/ # Chown all the files to the app user RUN chown -R app:app $APP_HOME @@ -28,4 +29,4 @@ USER app ENTRYPOINT ["/usr/bin/tini", "--"] -CMD ["gunicorn", "--log-level=debug", "-b", "0.0.0.0:5000", "app:app"] +CMD ["gunicorn", "--log-level=debug", "-b", "0.0.0.0:5000", "wsgi:app"] diff --git a/api/api/__init__.py b/api/api/__init__.py new file mode 100644 index 0000000..23d2b2d --- /dev/null +++ b/api/api/__init__.py @@ -0,0 +1,38 @@ +import os + +from flask import Flask +from flask_cors import CORS +from flask_sqlalchemy import SQLAlchemy + + +from api.settings import app_settings + +# instantiate the extensions +db = SQLAlchemy() +cors = CORS() + + +def create_app(script_info=None): + + app = Flask(__name__) + + # set config + config_engine = app_settings[os.getenv("APP_SETTINGS", "dev")]() + app.config.from_object(config_engine) + + # set up extensions + db.init_app(app) + cors.init_app(app) + + # migrate.init_app(app, db) + + # register blueprints + from .app import main_blueprint + + app.register_blueprint(main_blueprint) + # from project.api.users import users_blueprint + # app.register_blueprint(users_blueprint) + + # shell context for flask cli + app.shell_context_processor({"app": app, "db": db}) + return app diff --git a/api/api/app.py b/api/api/app.py new file mode 100644 index 0000000..f9dd01b --- /dev/null +++ b/api/api/app.py @@ -0,0 +1,21 @@ +import os +from flask import Blueprint + + +main_blueprint = Blueprint("main", __name__) + + +__version__ = "1.2.0" + + +@main_blueprint.route("/ping") +@main_blueprint.route("/") +def pong(): + """A simple ping route.""" + return {"message": "pong"} + + +@main_blueprint.route("/env") +def env(): + """Exposes the environment variables.""" + return dict(os.environ) diff --git a/api/api/settings.py b/api/api/settings.py new file mode 100644 index 0000000..4794b06 --- /dev/null +++ b/api/api/settings.py @@ -0,0 +1,64 @@ +import os + + +# TODO Add postgres params option +def build_postgres_uri(host, user, dbname, passwd=None, port="5432", sslmode=None): + """Builds a PostgreSQL uri. + + See https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING for additional details. + """ + + if passwd: + uri = f"postgresql://{user}:{passwd}@{host}:{port}/{dbname}" + else: + uri = f"postgresql://{user}@{host}:{port}/{dbname}" + if sslmode: + uri = f"{uri}?sslmode={sslmode}" + return uri + + +class BaseConfigEngine: + """Base configuration engine""" + + POSTGRES_HOST = os.getenv("POSTGRES_HOST", default="localhost") + POSTGRES_USER = os.getenv("POSTGRES_USER", default="postgres") + POSTGRES_APP_DATABASE = os.getenv("POSTGRES_APP_DATABASE", default="postgres") + POSTGRES_PASSWORD = os.getenv("POSTGRES_PASSWORD") + POSTGRES_PORT = os.getenv("POSTGRES_PORT", default="5432") + POSTGRES_SSL_MODE = os.getenv("POSTGRES_SSL_MODE", default="prefer") + POSTGRES_PARAMS = os.getenv("POSTGRES_PARAMS") + SECRET_KEY = os.getenv("SECRET_KEY", "defaultsecretkey") + SQLALCHEMY_TRACK_MODIFICATIONS = False + + @property + def SQLALCHEMY_DATABASE_URI(self): # Note: all caps + return build_postgres_uri( + self.POSTGRES_HOST, + self.POSTGRES_USER, + self.POSTGRES_APP_DATABASE, + self.POSTGRES_PASSWORD, + self.POSTGRES_PORT, + self.POSTGRES_SSL_MODE, + ) + + +class DevConfigEngine(BaseConfigEngine): + DEBUG = True + + +class TestingConfigEngine(BaseConfigEngine): + TESTING = True + + +class ProductionConfigEngine(BaseConfigEngine): + DEBUG = False + + +app_settings = { + "dev": DevConfigEngine, + "development": DevConfigEngine, + "test": TestingConfigEngine, + "testing": TestingConfigEngine, + "prod": ProductionConfigEngine, + "production": ProductionConfigEngine, +} diff --git a/backend/src/entrypoint.sh b/api/entrypoint.sh similarity index 100% rename from backend/src/entrypoint.sh rename to api/entrypoint.sh diff --git a/backend/requirements.in b/api/requirements.in similarity index 69% rename from backend/requirements.in rename to api/requirements.in index 4cec435..2b544ea 100644 --- a/backend/requirements.in +++ b/api/requirements.in @@ -3,4 +3,5 @@ flask==2.0.2 psycopg2-binary==2.9.2 gunicorn==20.1.0 flask-cors==3.0.10 +flask_sqlalchemy==2.5.1 sqlalchemy==1.4.27 \ No newline at end of file diff --git a/backend/requirements.txt b/api/requirements.txt similarity index 58% rename from backend/requirements.txt rename to api/requirements.txt index 722a3b6..d98a706 100644 --- a/backend/requirements.txt +++ b/api/requirements.txt @@ -2,20 +2,23 @@ # This file is autogenerated by pip-compile with python 3.8 # To update, run: # -# pip-compile requirements.in +# pip-compile --output-file=./api/requirements.txt api/requirements.in # click==8.0.3 # via flask flask==2.0.2 # via - # -r requirements.in + # -r api/requirements.in # flask-cors + # flask-sqlalchemy flask-cors==3.0.10 - # via -r requirements.in + # via -r api/requirements.in +flask_sqlalchemy==2.5.1 + # via -r api/requirements.in greenlet==1.1.2 # via sqlalchemy gunicorn==20.1.0 - # via -r requirements.in + # via -r api/requirements.in itsdangerous==2.0.1 # via flask jinja2==3.0.3 @@ -23,13 +26,15 @@ jinja2==3.0.3 markupsafe==2.0.1 # via jinja2 psycopg2-binary==2.9.2 - # via -r requirements.in + # via -r api/requirements.in python-dotenv==0.19.2 - # via -r requirements.in + # via -r api/requirements.in six==1.16.0 # via flask-cors sqlalchemy==1.4.27 - # via -r requirements.in + # via + # -r api/requirements.in + # flask-sqlalchemy werkzeug==2.0.2 # via flask diff --git a/tests/__init__.py b/api/tests/__init__.py similarity index 100% rename from tests/__init__.py rename to api/tests/__init__.py diff --git a/tests/unit/__init__.py b/api/tests/unit/__init__.py similarity index 100% rename from tests/unit/__init__.py rename to api/tests/unit/__init__.py diff --git a/tests/unit/backend/__init__.py b/api/tests/unit/api/__init__.py similarity index 100% rename from tests/unit/backend/__init__.py rename to api/tests/unit/api/__init__.py diff --git a/tests/unit/backend/test_settings.py b/api/tests/unit/api/test_settings.py similarity index 96% rename from tests/unit/backend/test_settings.py rename to api/tests/unit/api/test_settings.py index 282f303..e335d9a 100644 --- a/tests/unit/backend/test_settings.py +++ b/api/tests/unit/api/test_settings.py @@ -1,6 +1,6 @@ import pytest -from backend.settings import build_postgres_uri +from api.settings import build_postgres_uri @pytest.mark.parametrize( diff --git a/api/wsgi.py b/api/wsgi.py new file mode 100644 index 0000000..0b0468a --- /dev/null +++ b/api/wsgi.py @@ -0,0 +1,6 @@ +from api import create_app + +app = create_app() + +if __name__ == "__main__": + app.run() diff --git a/backend/src/app.py b/backend/src/app.py deleted file mode 100644 index bd5014c..0000000 --- a/backend/src/app.py +++ /dev/null @@ -1,21 +0,0 @@ -import os -from flask import Flask -from flask_cors import CORS - -app = Flask(__name__) -cors = CORS(app) - -__version__ = "1.0.0" - - -@app.route("/ping") -@app.route("/") -def pong(): - """A simple ping route.""" - return {"message": "pong"} - - -@app.route("/env") -def env(): - """Exposes the environment variables.""" - return dict(os.environ) diff --git a/backend/src/settings.py b/backend/src/settings.py deleted file mode 100644 index 71c400e..0000000 --- a/backend/src/settings.py +++ /dev/null @@ -1,27 +0,0 @@ -import os - - -def build_postgres_uri( - host, user, dbname, port, sslmode=None, passwd=None, params=None -): - """Builds a PostgreSQL uri. - - See https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING for additional details. - """ - - if passwd: - uri = f"postgresql://{user}:{passwd}@{host}:{port}/{dbname}" - else: - uri = f"postgresql://{user}@{host}:{port}/{dbname}" - if sslmode: - uri = f"{uri}?sslmode={sslmode}" - return uri - - -POSTGRES_HOST = os.getenv("POSTGRES_HOST") -POSTGRES_USER = os.getenv("POSTGRES_USER") -POSTGRES_APP_DATABASE = os.getenv("POSTGRES_APP_DATABASE") -POSTGRES_PASSWORD = os.getenv("POSTGRES_PASSWORD") -POSTGRES_PORT = os.getenv("POSTGRES_PORT", default="5432") -POSTGRES_SSL_MODE = os.getenv("POSTGRES_SSL_MODE", default="prefer") -POSTGRES_PARAMS = os.getenv("POSTGRES_PARAMS")