Skip to content

Commit a5a377f

Browse files
committed
init
0 parents  commit a5a377f

File tree

12 files changed

+386
-0
lines changed

12 files changed

+386
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.idea
2+
__pycache__

code/backend/__init__.py

Whitespace-only changes.

code/backend/database/__init__.py

Whitespace-only changes.

code/backend/database/db.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import os
2+
3+
from sqlalchemy import create_engine
4+
from sqlalchemy.ext.declarative import declarative_base
5+
from sqlalchemy.orm import scoped_session, sessionmaker
6+
7+
user = os.environ['POSTGRES_USER']
8+
pwd = os.environ['POSTGRES_PASSWORD']
9+
db = os.environ['POSTGRES_DB']
10+
# Docker creates hostnames from the service names defined in docker-compose
11+
host = os.environ['DB_DOCKER_SERVICE_NAME']
12+
port = os.environ['DB_PORT']
13+
14+
engine = create_engine("postgres://{0}:{1}@{2}:{3}/{4}".format(user, pwd, host, port, db))
15+
16+
db_session = scoped_session(sessionmaker(autocommit=False,
17+
autoflush=False,
18+
bind=engine))
19+
Base = declarative_base()
20+
Base.query = db_session.query_property()
21+
22+
23+
def init_db():
24+
Base.metadata.create_all(bind=engine)

code/backend/database/db_models.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
from sqlalchemy import Column, Integer, String, BigInteger, ForeignKey, Boolean
2+
from sqlalchemy.orm import relationship
3+
from sqlalchemy.types import DateTime
4+
5+
from database import db
6+
7+
8+
class ApiKey(db.Base):
9+
__tablename__ = "api_key"
10+
11+
# Auto incrementing ID
12+
id = Column(Integer, primary_key=True)
13+
api_key = Column(String(256), unique=True)
14+
date_created = Column(DateTime())
15+
date_last_used = Column(DateTime())
16+
active = Column(Boolean, default=True)
17+
18+
19+
class User(db.Base):
20+
__tablename__ = "user"
21+
22+
# Auto incrementing ID
23+
id = Column(Integer, primary_key=True)
24+
first_name = Column(String(256))
25+
last_name = Column(String(256))
26+
email = Column(String(256), unique=True)
27+
date_signed_up = Column(DateTime())
28+
available_units = Column(BigInteger)
29+
used_units = Column(BigInteger)
30+
31+
# Foreign Key
32+
api_key_id = Column(Integer, ForeignKey(ApiKey.id))
33+
api_key = relationship(ApiKey)

code/backend/database/db_repo.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import os
2+
from binascii import hexlify
3+
from datetime import datetime
4+
5+
from database import db_models, db
6+
7+
8+
def generate_api_key() -> str:
9+
key = hexlify(os.urandom(16)).decode("utf-8")
10+
return key
11+
12+
13+
def init_api_key_object(api_key: str = None) -> db_models.ApiKey:
14+
if api_key is None:
15+
api_key = generate_api_key()
16+
new_api_key = db_models.ApiKey(api_key=api_key,
17+
date_created=datetime.now(),
18+
date_last_used=datetime.now(),
19+
active=True)
20+
return new_api_key
21+
22+
23+
def create_user(first_name: str, last_name: str, email: str, api_key: str = None) -> None:
24+
new_api_key = init_api_key_object(api_key)
25+
new_user = db_models.User(first_name=first_name,
26+
last_name=last_name,
27+
email=email,
28+
date_signed_up=datetime.now(),
29+
available_units=0,
30+
used_units=0,
31+
api_key=new_api_key, )
32+
db.db_session.add(new_user)
33+
db.db_session.commit()
34+
35+
36+
def find_user_by_api_key(api_key: str) -> db_models.User:
37+
user = db.db_session.query(db_models.User).join(db_models.ApiKey).filter(
38+
db_models.ApiKey.api_key == api_key).first()
39+
return user
40+
41+
42+
def update_after_service_usage(user: db_models.User, unit_used: int) -> None:
43+
user.used_units = user.used_units + unit_used
44+
user.available_units = user.available_units - unit_used
45+
user.api_key.date_last_used = datetime.now()
46+
47+
db.db_session.commit()

code/backend/my_service/__init__.py

Whitespace-only changes.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import random
2+
import string
3+
4+
5+
def generate_random_string(size: int):
6+
chars = string.ascii_uppercase + string.digits
7+
return ''.join(random.choice(chars) for _ in range(size))

code/backend/rest_api.py

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
import logging
2+
import os
3+
4+
from flask import Flask, request, jsonify
5+
6+
from database import db, db_repo
7+
from my_service import super_service
8+
9+
app = Flask(__name__)
10+
app.secret_key = os.environ['APP_SECRET_KEY']
11+
12+
file_handler = logging.FileHandler('rest_api.log')
13+
logger_formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
14+
file_handler.setFormatter(logger_formatter)
15+
app.logger.addHandler(file_handler)
16+
app.logger.setLevel(logging.INFO)
17+
18+
19+
def not_found_error(msg):
20+
status_code = 404
21+
message = {
22+
'status': status_code,
23+
'message': msg
24+
}
25+
resp = jsonify(message)
26+
resp.status_code = status_code
27+
return resp
28+
29+
30+
def sample_response(msg):
31+
status_code = 200
32+
message = {
33+
'status': status_code,
34+
'message': msg
35+
}
36+
resp = jsonify(message)
37+
resp.status_code = status_code
38+
return resp
39+
40+
41+
@app.route("/microservice/", methods=["GET", "POST"])
42+
def microservice_index():
43+
app.logger.info("Is this the real life? Or is this just fantasy?")
44+
return "Please use my service, this is how I get money"
45+
46+
47+
@app.route("/microservice/add_test_users/<nb_of_users>", methods=["GET"])
48+
def add_test_users(nb_of_users: int):
49+
nb_of_users = int(nb_of_users)
50+
51+
for i in range(nb_of_users):
52+
db_repo.create_user("Test", "User{0}".format(i), "just{0}@test.com".format(i))
53+
54+
app.logger.info("Test users are created")
55+
56+
return sample_response("Created {0} test users".format(nb_of_users))
57+
58+
59+
@app.route("/microservice/sample_service", methods=["POST"])
60+
def use_sample_service():
61+
req_json = request.get_json()
62+
63+
try:
64+
# For authentication
65+
api_key_str = req_json["api_key"]
66+
# For the service
67+
size = int(req_json["size"])
68+
except KeyError:
69+
app.logger.error("Could not find the necessary keys in the request's body")
70+
return not_found_error("Required keys are not found in POST's body")
71+
72+
user = db_repo.find_user_by_api_key(api_key_str)
73+
74+
if user is None:
75+
app.logger.error("No user found with {0} api key".format(api_key_str))
76+
return not_found_error("No associated user with this api key")
77+
78+
if user.available_units < size:
79+
app.logger.error("User with api key {0} has no units to use the service".format(api_key_str))
80+
return not_found_error("Not enough unit to use the service")
81+
82+
# Using the Sample Service
83+
random_str = super_service.generate_random_string(size)
84+
85+
app.logger.info("Successful service usage by {0}".format(api_key_str))
86+
87+
db_repo.update_after_service_usage(user, size)
88+
89+
response_dict = {"service_result": random_str}
90+
91+
return jsonify(response_dict)
92+
93+
94+
if __name__ == '__main__':
95+
db.init_db()
96+
app.run(host='0.0.0.0', port=5000, debug=True, threaded=True)

code/frontend/css/my.css

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
body {
2+
height: 100%;
3+
background-image: linear-gradient(135deg, #ffffff 60%, #f2f2f2 100%);
4+
}
5+
6+
.btn-dark {
7+
background-color: #333;
8+
color: #fff;
9+
}
10+
11+
.card-with-shadow {
12+
box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
13+
}
14+
15+
.bloody-mary-gradient {
16+
background-image: linear-gradient(120deg, #ff512f 0%, #dd2476 100%);
17+
}
18+
19+
svg path,
20+
svg rect {
21+
fill: #333;
22+
}

0 commit comments

Comments
 (0)