diff --git a/.env.template b/.env.template index aa202871..3729f4e8 100644 --- a/.env.template +++ b/.env.template @@ -1,6 +1,7 @@ ADS_PORT=7676 DISCOUNTS_PORT=2814 AUTH_PORT=7578 +DBM_PORT=7595 POSTGRES_USER=postgres POSTGRES_PASSWORD=postgres DD_API_KEY= @@ -48,4 +49,6 @@ NEXT_PUBLIC_DD_SERVICE="frontend" NEXT_PUBLIC_DD_VERSION="1.0.0" NEXT_PUBLIC_DD_ENV="development" NEXT_PUBLIC_AUTH_ROUTE="http://localhost" -NEXT_PUBLIC_AUTH_PORT="7578" \ No newline at end of file +NEXT_PUBLIC_AUTH_PORT="7578" +NEXT_PUBLIC_DBM_ROUTE="http://localhost" +NEXT_PUBLIC_DBM_PORT="7595" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 06d15bbe..c86a5bad 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -160,6 +160,32 @@ services: - "${AUTH_PORT}:${AUTH_PORT}" networks: - storedog-net + dbm: + depends_on: + - postgres + profiles: + - dbm + environment: + - FLASK_APP=dbm.py + - FLASK_DEBUG=1 + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_HOST=postgres + - DD_SERVICE=storedog-dbm + - DD_AGENT_HOST=dd-agent + - DD_LOGS_INJECTION=true + - DD_TRACE_ANALYTICS_ENABLED=true + - DD_PROFILING_ENABLED=true + - DD_APPSEC_ENABLED=true + build: + context: ./services/dbm + command: gunicorn --bind 0.0.0.0:${DBM_PORT} dbm:app # If using any other port besides the default 8282, overriding the CMD is required + volumes: + - "./services/dbm:/app" + ports: + - "${DBM_PORT}:${DBM_PORT}" + networks: + - storedog-net dd-agent: image: gcr.io/datadoghq/agent:latest environment: diff --git a/services/backend/storage/n8/aa/n8aao8d8v8ene7vn4d14zdqxntna b/services/backend/storage/n8/aa/n8aao8d8v8ene7vn4d14zdqxntna new file mode 100644 index 00000000..5b763cba Binary files /dev/null and b/services/backend/storage/n8/aa/n8aao8d8v8ene7vn4d14zdqxntna differ diff --git a/services/dbm/.dockerignore b/services/dbm/.dockerignore new file mode 100644 index 00000000..d96d4988 --- /dev/null +++ b/services/dbm/.dockerignore @@ -0,0 +1,37 @@ +# Version control +.git + +# Compiled Python bytecode +**/__pycache__ +**/*.pyc +**/*.pyo + +# Compiled extensions +**/*.pyd +**/*.so + +# coverage.py +.coverage +.coverage.* +htmlcov + +# Cached files +.cache +.mypy_cache +.hypothesis +.pytest_cache + +# Virtualenvs and builds +build/ +dist/ +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Docker +Dockerfile* +.dockerignore diff --git a/services/dbm/Dockerfile b/services/dbm/Dockerfile new file mode 100644 index 00000000..fc84a5f5 --- /dev/null +++ b/services/dbm/Dockerfile @@ -0,0 +1,27 @@ +# syntax = docker/dockerfile:1.2 +# ^ This enables the new BuildKit stable syntax which can be +# run with the DOCKER_BUILDKIT=1 environment variable in your +# docker build command (see build.sh) +FROM python:3.9.6-slim-buster + +# Update, upgrade, and cleanup debian packages +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get upgrade --yes && \ + apt-get install --yes build-essential libpq-dev && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +# Copy over app +WORKDIR /app +COPY . . + +# Install dependencies via pip and avoid caching build artifacts +RUN pip install --no-cache-dir -r requirements.txt + +# Set default Flask app and development environment +ENV FLASK_APP=dbm.py + +# Start the app using ddtrace so we have profiling and tracing +ENTRYPOINT ["ddtrace-run"] +CMD gunicorn --bind 0.0.0.0:7578 dbm:app diff --git a/services/dbm/bootstrap.py b/services/dbm/bootstrap.py new file mode 100644 index 00000000..9d8f0f12 --- /dev/null +++ b/services/dbm/bootstrap.py @@ -0,0 +1,40 @@ +from flask import Flask +from models import Items, db +from faker import Faker +import random +import os + +fake = Faker() +DB_USERNAME = os.environ['POSTGRES_USER'] +DB_PASSWORD = os.environ['POSTGRES_PASSWORD'] +DB_HOST = os.environ['POSTGRES_HOST'] + + +def create_app(): + """Create a Flask application""" + app = Flask(__name__) + app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://' + \ + DB_USERNAME + ':' + DB_PASSWORD + '@' + DB_HOST + '/' + DB_USERNAME + app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False + + db.init_app(app) + initialize_database(app, db) + return app + + +def initialize_database(app, db): + """Drop and restore database in a consistent state""" + app.logger.info('Running DB Init for DBM') + with app.app_context(): + db.drop_all() + db.create_all() + for i in range(15000): + newItem = Items( + fake.sentence(), + random.randint(1, 7000), + fake.image_url(), + random.randint(1, 10) + ) + db.session.add(newItem) + i+1 + db.session.commit() diff --git a/services/dbm/dbm.py b/services/dbm/dbm.py new file mode 100644 index 00000000..413c9ff9 --- /dev/null +++ b/services/dbm/dbm.py @@ -0,0 +1,50 @@ +from flask import jsonify +from flask_cors import CORS +from bootstrap import create_app +from models import db +import random +import os + +DB_USERNAME = os.environ['POSTGRES_USER'] +DB_PASSWORD = os.environ['POSTGRES_PASSWORD'] +DB_HOST = os.environ['POSTGRES_HOST'] + +DB_URL = 'postgresql://' + \ + DB_USERNAME + ':' + DB_PASSWORD + '@' + DB_HOST + '/' + DB_USERNAME + +app = create_app() +app.config.update( + DEBUG=True, + SECRET_KEY="secret_sauce", +) + +CORS(app) +engine = db.create_engine(DB_URL) + +@app.route("/get-item", methods=["GET"]) +def product_ticker(): + query = db.text(f'SELECT * FROM items WHERE order_count::int > {random.randint(1, 7000)};') + app.logger.info(engine) + try: + app.logger.info('Connecting to db') + with engine.begin() as conn: + results = conn.execute(query).fetchall() + if results: + app.logger.info('Results found, parsing single item') + result = random.choice(results) + item_response = { + 'id': result.id, + 'description': result.description, + 'last_hour': result.last_hour, + 'order_count': result.order_count, + 'image_url': result.image_url + } + return jsonify(item_response) + except: + app.logger.error("An error occurred while getting items.") + err = jsonify({'error': 'Internal Server Error'}) + err.status_code = 500 + return err + +if __name__ == "__main__": + app.run(debug=True) diff --git a/services/dbm/models.py b/services/dbm/models.py new file mode 100644 index 00000000..58797d19 --- /dev/null +++ b/services/dbm/models.py @@ -0,0 +1,27 @@ +from flask_sqlalchemy import SQLAlchemy + +db = SQLAlchemy() + + +class Items(db.Model): + __tablename__ = 'items' + id = db.Column(db.Integer, primary_key=True) + description = db.Column(db.String(128)) + order_count = db.Column(db.String(64)) + last_hour = db.Column(db.String(64)) + image_url = db.Column(db.String(64)) + + def __init__(self, description, order_count, image_url, last_hour): + self.description = description + self.order_count = order_count + self.last_hour = last_hour + self.image_url = image_url + + def serialize(self): + return { + 'id': self.id, + 'description': self.description, + 'order_count': self.order_count, + 'last_hour': self.last_hour, + 'image_url': self.image_url + } diff --git a/services/dbm/requirements.txt b/services/dbm/requirements.txt new file mode 100644 index 00000000..1c66c1ac --- /dev/null +++ b/services/dbm/requirements.txt @@ -0,0 +1,25 @@ +certifi==2020.11.8 +chardet==3.0.4 +click==8.0 +ddtrace==1.7.2 +Flask==2.2.2 +Flask-Login==0.6.2 +Flask-WTF==1.0.1 +Flask-Cors==3.0.10 +Flask-SQLAlchemy==3.0.2 +idna==2.10 +intervaltree==3.1.0 +itsdangerous==2.0 +Jinja2==3.0 +MarkupSafe==2.1.1 +nose==1.3.7 +protobuf==3.14.0 +requests==2.25.1 +six==1.15.0 +sortedcontainers==2.3.0 +SQLAlchemy==1.4.42 +psycopg2-binary +tenacity==6.2.0 +urllib3==1.26.5 +gunicorn==20.1.0 +Faker==18.3.2 \ No newline at end of file