diff --git a/api/api/__init__.py b/api/api/__init__.py index 23d2b2d..21452f2 100644 --- a/api/api/__init__.py +++ b/api/api/__init__.py @@ -6,6 +6,7 @@ from flask_sqlalchemy import SQLAlchemy from api.settings import app_settings +from api.util import CustomJSONEncoder # instantiate the extensions db = SQLAlchemy() @@ -15,6 +16,7 @@ cors = CORS() def create_app(script_info=None): app = Flask(__name__) + app.json_encoder = CustomJSONEncoder # set config config_engine = app_settings[os.getenv("APP_SETTINGS", "dev")]() @@ -34,5 +36,9 @@ def create_app(script_info=None): # app.register_blueprint(users_blueprint) # shell context for flask cli - app.shell_context_processor({"app": app, "db": db}) + app.shell_context_processor(lambda: {"app": app, "db": db}) return app + + +# Model import occurs here to allow flask_sqlalchemy to import metadata +import api.models diff --git a/api/api/app.py b/api/api/app.py index f9dd01b..5038367 100644 --- a/api/api/app.py +++ b/api/api/app.py @@ -1,10 +1,10 @@ -import os -from flask import Blueprint +import datetime as dt +from flask import Blueprint, jsonify +from .models import Cookie main_blueprint = Blueprint("main", __name__) - __version__ = "1.2.0" @@ -15,7 +15,13 @@ def pong(): return {"message": "pong"} -@main_blueprint.route("/env") -def env(): - """Exposes the environment variables.""" - return dict(os.environ) +@main_blueprint.route("/date") +def current_date(): + """A route that simply returns the current datetime of the system""" + return {"current_date": dt.datetime.now(dt.timezone.utc).isoformat()} + + +@main_blueprint.route("/cookies") +def get_cookies(): + cookies = Cookie.query.all() + return jsonify([cookie.to_dict() for cookie in cookies]) diff --git a/api/api/models.py b/api/api/models.py new file mode 100644 index 0000000..533ae05 --- /dev/null +++ b/api/api/models.py @@ -0,0 +1,58 @@ +from sqlalchemy import ( + Column, + Identity, # Identity replaced Serial + Integer, + BigInteger, + Numeric, + String, + ForeignKey, + DateTime, + Boolean, + Enum, +) +from sqlalchemy.orm import relationship +from sqlalchemy.sql import expression +from sqlalchemy.ext.compiler import compiles +from sqlalchemy.types import DateTime as DatetimeType + +from . import db + + +class utcnow(expression.FunctionElement): + type = DatetimeType() + + +@compiles(utcnow, "postgresql") +def pg_utcnow(element, compiler, **kw): + return "TIMEZONE('utc', CURRENT_TIMESTAMP)" + + +def ReferenceCol(tablename, nullable=False, **kw): + """A utility function for declaring foreign key relationships.""" + return Column(ForeignKey("{}.id".format(tablename)), nullable=nullable, **kw) + + +class DBMixin: + id = Column(BigInteger, Identity(start=100000), primary_key=True) + created_date = Column(DateTime(timezone=True), server_default=utcnow()) + modified_date = Column( + DateTime(timezone=True), server_default=utcnow(), onupdate=utcnow() + ) + + def to_dict(self): + d = self.__dict__.copy() + if "_sa_instance_state" in d: + d.pop("_sa_instance_state") + return d + + +class Cookie(DBMixin, db.Model): + """A Cookie Class""" + + __tablename__ = "cookies" + + name = Column(String(50), index=True) + recipe_url = Column(String(255)) + sku = Column(String(55)) + qoh = Column(Integer) + unit_cost = Column(Numeric(12, 2)) diff --git a/api/api/settings.py b/api/api/settings.py index 4794b06..4530b3c 100644 --- a/api/api/settings.py +++ b/api/api/settings.py @@ -1,5 +1,15 @@ import os +# TODO Find an alternative to injection envvars into config. +if os.getenv("FLASK_ENV") == "development": + try: + import dotenv + + dotenv.load_dotenv() + except Exception: + print("Didn't load a .env file") + pass + # TODO Add postgres params option def build_postgres_uri(host, user, dbname, passwd=None, port="5432", sslmode=None): @@ -44,6 +54,7 @@ class BaseConfigEngine: class DevConfigEngine(BaseConfigEngine): DEBUG = True + SQLALCHEMY_ECHO = True class TestingConfigEngine(BaseConfigEngine): diff --git a/api/api/util.py b/api/api/util.py new file mode 100644 index 0000000..f7483eb --- /dev/null +++ b/api/api/util.py @@ -0,0 +1,19 @@ +import datetime as dt +from decimal import Decimal + +from flask.json import JSONEncoder + + +class CustomJSONEncoder(JSONEncoder): + def default(self, obj): + try: + if isinstance(obj, dt.datetime): + return obj.isoformat() + if isinstance(obj, Decimal): + return float(obj) + iterable = iter(obj) + except TypeError: + pass + else: + return list(iterable) + return JSONEncoder.default(self, obj)