From ceb346505f15a1c75475527ce99ab7d329f57914 Mon Sep 17 00:00:00 2001 From: pankaj gosavi Date: Tue, 6 Feb 2024 14:32:33 +0530 Subject: [PATCH 1/4] data validation and update in requirements, dockerfile --- source/RewardsService/Dockerfile | 5 ++- source/RewardsService/requirements.txt | 2 + .../handlers/rewards_handler.py | 43 +++++++++++++++++-- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/source/RewardsService/Dockerfile b/source/RewardsService/Dockerfile index 99c06781..018329c6 100644 --- a/source/RewardsService/Dockerfile +++ b/source/RewardsService/Dockerfile @@ -1,8 +1,9 @@ -FROM python:3.5 +FROM python:3.9 ENV PYTHONUNBUFFERED 1 ENV PYTHONPATH=$PYTHONPATH:/code/ RUN mkdir /code WORKDIR /code ADD requirements.txt /code/ -RUN pip install -r requirements.txt +RUN pip install --upgrade pip +RUN pip install --no-cache-dir -r requirements.txt ADD ./ /code/ diff --git a/source/RewardsService/requirements.txt b/source/RewardsService/requirements.txt index c6db96e9..ff9fba8c 100644 --- a/source/RewardsService/requirements.txt +++ b/source/RewardsService/requirements.txt @@ -1,3 +1,5 @@ fabric==1.10.2 motor==1.1.0 tornado==3.2 +jsonschema==4.21.1 +black==24.1.1 \ No newline at end of file diff --git a/source/RewardsService/rewardsservice/handlers/rewards_handler.py b/source/RewardsService/rewardsservice/handlers/rewards_handler.py index 4f2aea6e..f782fb9d 100644 --- a/source/RewardsService/rewardsservice/handlers/rewards_handler.py +++ b/source/RewardsService/rewardsservice/handlers/rewards_handler.py @@ -1,15 +1,52 @@ +import re import json -import tornado.web +import tornado.web +from jsonschema import validate +from jsonschema.exceptions import ValidationError from pymongo import MongoClient from tornado.gen import coroutine +regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b' + class RewardsHandler(tornado.web.RequestHandler): + client = MongoClient("mongodb", 27017) + schema = { + "type": "object", + "properties": { + "email": {"type": "string"}, + "order_total": {"type": "number"}, + }, + "required": ["email", "order_total"], + } + @coroutine def get(self): - client = MongoClient("mongodb", 27017) - db = client["Rewards"] + db = self.client["Rewards"] rewards = list(db.rewards.find({}, {"_id": 0})) self.write(json.dumps(rewards)) + + @coroutine + def post(self): + try: + data = json.loads(self.request.body.decode("utf-8")) + + try: + validate(data, schema=self.schema) + except ValidationError as ve: + self.set_status(400) + self.write(json.dumps({"error_message": ve.message})) + self.finish() + + if re.fullmatch(regex, data.get("email")): + pass + else: + self.set_status(422) + self.write(json.dumps({"error_message": "Invalid Email ID"})) + self.finish() + self.write(json.dumps({"status": "success"})) + except Exception as e: + self.set_status(400) + self.write(json.dumps({"error_message": f"{str(e)}"})) From 25196efc237a34a23b2a39462a6c2f316282cafd Mon Sep 17 00:00:00 2001 From: pankaj gosavi Date: Tue, 6 Feb 2024 14:58:54 +0530 Subject: [PATCH 2/4] reward match found --- .../handlers/rewards_handler.py | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/source/RewardsService/rewardsservice/handlers/rewards_handler.py b/source/RewardsService/rewardsservice/handlers/rewards_handler.py index f782fb9d..b56af858 100644 --- a/source/RewardsService/rewardsservice/handlers/rewards_handler.py +++ b/source/RewardsService/rewardsservice/handlers/rewards_handler.py @@ -1,3 +1,5 @@ +import datetime +import operator import re import json @@ -13,6 +15,7 @@ class RewardsHandler(tornado.web.RequestHandler): client = MongoClient("mongodb", 27017) + db = client["Rewards"] schema = { "type": "object", "properties": { @@ -24,8 +27,7 @@ class RewardsHandler(tornado.web.RequestHandler): @coroutine def get(self): - db = self.client["Rewards"] - rewards = list(db.rewards.find({}, {"_id": 0})) + rewards = list(self.db.rewards.find({}, {"_id": 0})) self.write(json.dumps(rewards)) @coroutine @@ -46,7 +48,25 @@ def post(self): self.set_status(422) self.write(json.dumps({"error_message": "Invalid Email ID"})) self.finish() - self.write(json.dumps({"status": "success"})) + + record = { + "email": data.get("email"), + "order_total": data.get("order_total", 0.0) + } + + rewards_data = list(self.db.rewards.find({}, {"_id": 0})) + rewards_data.sort(key=operator.itemgetter("points"), reverse=True) + + order_total = data.get('order_total', 0.0) + try: + matching_reward = next(( + item for item in rewards_data if order_total >= item["points"] + )) + except StopIteration: + matching_reward = None + self.write(json.dumps(matching_reward)) + # self.write(json.dumps({"status": "Order Created"})) + self.finish() except Exception as e: self.set_status(400) self.write(json.dumps({"error_message": f"{str(e)}"})) From ebfc3dea164a4ebf07b0908ba104826228271e17 Mon Sep 17 00:00:00 2001 From: pankaj gosavi Date: Tue, 6 Feb 2024 15:07:41 +0530 Subject: [PATCH 3/4] next reward match and order creation --- .../handlers/rewards_handler.py | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/source/RewardsService/rewardsservice/handlers/rewards_handler.py b/source/RewardsService/rewardsservice/handlers/rewards_handler.py index b56af858..bad92b2c 100644 --- a/source/RewardsService/rewardsservice/handlers/rewards_handler.py +++ b/source/RewardsService/rewardsservice/handlers/rewards_handler.py @@ -35,6 +35,7 @@ def post(self): try: data = json.loads(self.request.body.decode("utf-8")) + # input body validation try: validate(data, schema=self.schema) except ValidationError as ve: @@ -42,6 +43,9 @@ def post(self): self.write(json.dumps({"error_message": ve.message})) self.finish() + # email validation + # tried using external package was getting installation + # error in docker so moved to regex if re.fullmatch(regex, data.get("email")): pass else: @@ -53,10 +57,11 @@ def post(self): "email": data.get("email"), "order_total": data.get("order_total", 0.0) } - rewards_data = list(self.db.rewards.find({}, {"_id": 0})) + # sort by points descending true rewards_data.sort(key=operator.itemgetter("points"), reverse=True) + # fetch reward match order_total = data.get('order_total', 0.0) try: matching_reward = next(( @@ -64,8 +69,32 @@ def post(self): )) except StopIteration: matching_reward = None - self.write(json.dumps(matching_reward)) - # self.write(json.dumps({"status": "Order Created"})) + + # update record + if matching_reward: + record.update({ + "reward_points": matching_reward["points"], + "reward_tier": matching_reward["tier"], + "reward_tier_name": matching_reward["rewardName"] + }) + + # finding the next tier + if matching_reward != rewards_data[0]: + next_tier = rewards_data[rewards_data.index(matching_reward) - 1] + record.update({ + "next_reward_tier": next_tier["tier"], + "next_reward_tier_name": next_tier["rewardName"], + "next_reward_tier_progress": (order_total / next_tier["points"]) * 100 + }) + + # metadata + record["created"] = datetime.datetime.now() + record["updated"] = datetime.datetime.now() + + self.db.orders.insert(record) + + self.set_status(201) + self.write(json.dumps({"status": "Order Created"})) self.finish() except Exception as e: self.set_status(400) From 7445c1534da5aa193fdd61a144370bda328b65f4 Mon Sep 17 00:00:00 2001 From: pankaj gosavi Date: Tue, 6 Feb 2024 16:20:20 +0530 Subject: [PATCH 4/4] get order, get all orders, new OrdersHandler --- .../handlers/orders_handlers.py | 126 ++++++++++++++++++ .../handlers/rewards_handler.py | 85 ------------ .../rewardsservice/url_patterns.py | 5 +- 3 files changed, 130 insertions(+), 86 deletions(-) create mode 100644 source/RewardsService/rewardsservice/handlers/orders_handlers.py diff --git a/source/RewardsService/rewardsservice/handlers/orders_handlers.py b/source/RewardsService/rewardsservice/handlers/orders_handlers.py new file mode 100644 index 00000000..b60c1ae8 --- /dev/null +++ b/source/RewardsService/rewardsservice/handlers/orders_handlers.py @@ -0,0 +1,126 @@ +import datetime +import operator +import re +import json + +import tornado.web +from jsonschema import validate +from jsonschema.exceptions import ValidationError +from pymongo import MongoClient +from tornado.gen import coroutine + +regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b' +dt_format = "%Y/%m/%d %H:%M:%S" + + +class OrdersHandler(tornado.web.RequestHandler): + + client = MongoClient("mongodb", 27017) + db = client["Rewards"] + schema = { + "type": "object", + "properties": { + "email": {"type": "string"}, + "order_total": {"type": "number"}, + }, + "required": ["email", "order_total"], + } + + @staticmethod + def _serialize_data(data): + for item in data: + item["created"] = item["created"].strftime(dt_format) + item["updated"] = item["updated"].strftime(dt_format) + + @coroutine + def get(self): + orders = list(self.db.orders.find({}, {"_id": 0})) + self._serialize_data(orders) + self.write(json.dumps(orders)) + + @coroutine + def post(self): + try: + data = json.loads(self.request.body.decode("utf-8")) + + # input body validation + try: + validate(data, schema=self.schema) + except ValidationError as ve: + self.set_status(400) + self.write(json.dumps({"error_message": ve.message})) + self.finish() + + # email validation + # tried using external package was getting installation + # error in docker so moved to regex + if re.fullmatch(regex, data.get("email")): + pass + else: + self.set_status(422) + self.write(json.dumps({"error_message": "Invalid Email ID"})) + self.finish() + + record = { + "email": data.get("email"), + "order_total": data.get("order_total", 0.0) + } + rewards_data = list(self.db.rewards.find({}, {"_id": 0})) + # sort by points descending true + rewards_data.sort(key=operator.itemgetter("points"), reverse=True) + + # fetch reward match + order_total = data.get('order_total', 0.0) + try: + matching_reward = next(( + item for item in rewards_data if order_total >= item["points"] + )) + except StopIteration: + matching_reward = None + + # update record + if matching_reward: + record.update({ + "reward_points": matching_reward["points"], + "reward_tier": matching_reward["tier"], + "reward_tier_name": matching_reward["rewardName"] + }) + + # finding the next tier + if matching_reward != rewards_data[0]: + next_tier = rewards_data[rewards_data.index(matching_reward) - 1] + record.update({ + "next_reward_tier": next_tier["tier"], + "next_reward_tier_name": next_tier["rewardName"], + "next_reward_tier_progress": (order_total / next_tier["points"]) * 100 + }) + + # metadata + record["created"] = datetime.datetime.now() + record["updated"] = datetime.datetime.now() + + self.db.orders.insert(record) + + self.set_status(201) + self.write(json.dumps({"status": "Order Created"})) + self.finish() + except Exception as e: + self.set_status(400) + self.write(json.dumps({"error_message": f"{str(e)}"})) + + +class OrderHandler(tornado.web.RequestHandler): + client = MongoClient("mongodb", 27017) + db = client["Rewards"] + + def get(self): + query = {} + email = self.get_query_argument("email", default=None) + if not email: + self.write(json.dumps({"error_message": "Please provide email as query params"})) + self.finish() + query["email"] = email + order = self.db.orders.find_one(query, {"_id": 0}) + order["created"] = order["created"].strftime(dt_format) + order["updated"] = order["updated"].strftime(dt_format) + self.write(json.dumps(order)) diff --git a/source/RewardsService/rewardsservice/handlers/rewards_handler.py b/source/RewardsService/rewardsservice/handlers/rewards_handler.py index bad92b2c..6a51165f 100644 --- a/source/RewardsService/rewardsservice/handlers/rewards_handler.py +++ b/source/RewardsService/rewardsservice/handlers/rewards_handler.py @@ -1,101 +1,16 @@ -import datetime -import operator -import re import json import tornado.web -from jsonschema import validate -from jsonschema.exceptions import ValidationError from pymongo import MongoClient from tornado.gen import coroutine -regex = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,7}\b' - class RewardsHandler(tornado.web.RequestHandler): client = MongoClient("mongodb", 27017) db = client["Rewards"] - schema = { - "type": "object", - "properties": { - "email": {"type": "string"}, - "order_total": {"type": "number"}, - }, - "required": ["email", "order_total"], - } @coroutine def get(self): rewards = list(self.db.rewards.find({}, {"_id": 0})) self.write(json.dumps(rewards)) - - @coroutine - def post(self): - try: - data = json.loads(self.request.body.decode("utf-8")) - - # input body validation - try: - validate(data, schema=self.schema) - except ValidationError as ve: - self.set_status(400) - self.write(json.dumps({"error_message": ve.message})) - self.finish() - - # email validation - # tried using external package was getting installation - # error in docker so moved to regex - if re.fullmatch(regex, data.get("email")): - pass - else: - self.set_status(422) - self.write(json.dumps({"error_message": "Invalid Email ID"})) - self.finish() - - record = { - "email": data.get("email"), - "order_total": data.get("order_total", 0.0) - } - rewards_data = list(self.db.rewards.find({}, {"_id": 0})) - # sort by points descending true - rewards_data.sort(key=operator.itemgetter("points"), reverse=True) - - # fetch reward match - order_total = data.get('order_total', 0.0) - try: - matching_reward = next(( - item for item in rewards_data if order_total >= item["points"] - )) - except StopIteration: - matching_reward = None - - # update record - if matching_reward: - record.update({ - "reward_points": matching_reward["points"], - "reward_tier": matching_reward["tier"], - "reward_tier_name": matching_reward["rewardName"] - }) - - # finding the next tier - if matching_reward != rewards_data[0]: - next_tier = rewards_data[rewards_data.index(matching_reward) - 1] - record.update({ - "next_reward_tier": next_tier["tier"], - "next_reward_tier_name": next_tier["rewardName"], - "next_reward_tier_progress": (order_total / next_tier["points"]) * 100 - }) - - # metadata - record["created"] = datetime.datetime.now() - record["updated"] = datetime.datetime.now() - - self.db.orders.insert(record) - - self.set_status(201) - self.write(json.dumps({"status": "Order Created"})) - self.finish() - except Exception as e: - self.set_status(400) - self.write(json.dumps({"error_message": f"{str(e)}"})) diff --git a/source/RewardsService/rewardsservice/url_patterns.py b/source/RewardsService/rewardsservice/url_patterns.py index 55e471d6..d5803844 100644 --- a/source/RewardsService/rewardsservice/url_patterns.py +++ b/source/RewardsService/rewardsservice/url_patterns.py @@ -1,5 +1,8 @@ from handlers.rewards_handler import RewardsHandler +from handlers.orders_handlers import OrdersHandler, OrderHandler url_patterns = [ - (r'/rewards', RewardsHandler), + (r"/rewards", RewardsHandler), + (r"/orders", OrdersHandler), + (r"/order", OrderHandler), ]