diff --git a/Kafka/docker-compose.yml b/Kafka/docker-compose.yml index ac67edd7f1578e2ecd771e04f017a55d135c183a..f0490b975bf467484029471bbf6a46d5681733e3 100644 --- a/Kafka/docker-compose.yml +++ b/Kafka/docker-compose.yml @@ -1,11 +1,11 @@ -version: "3" +version: "3.2" services: zookeeper: image: wurstmeister/zookeeper container_name: zookeeper ports: - - "2182:2182" + - "2181:2181" networks: - kafka_network @@ -14,31 +14,16 @@ services: container_name: kafka ports: - "9092:9092" + depends_on: + - zookeeper environment: KAFKA_ADVERTISED_HOST_NAME: kafka KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 KAFKA_BOOTSTRAP_SEVERS: kafka:9092 networks: - kafka_network - - user-microservice: - image: user-microservice:1.0 - container_name: user-microservice - depends_on: - - kafka - - zookeeper - networks: - - kafka_network - - product-microservice: - image: product-microservice:1.0 - container_name: product-microservice - depends_on: - - kafka - - zookeeper - networks: - - kafka_network networks: kafka_network: - driver: bridge \ No newline at end of file + external: true + #driver: bridge diff --git a/Orders_Microservice_Group3/0.0.0.0 b/Orders_Microservice_Group3/0.0.0.0 new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Orders_Microservice_Group3/Dockerfile b/Orders_Microservice_Group3/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..1091fe8833907601a28e831b25bab0c4a9455afe --- /dev/null +++ b/Orders_Microservice_Group3/Dockerfile @@ -0,0 +1,64 @@ +FROM python:3.11.4 + +#Copy requirements +COPY requirements.txt /order_ms/ + +COPY app /order_ms/ + +#Set working direcroty +WORKDIR /order_ms + +#Install dependencies +RUN pip install --no-cache-dir -r requirements.txt + +#Install other libraries in container +RUN apt-get update && apt-get install -y \ + unixodbc \ + unixodbc-dev \ + iputils-ping\ + apt-transport-https \ + gcc\ + curl \ + gnupg \ + freetds-dev \ + tdsodbc \ + && rm -rf /var/lib/apt/lists/* + +#Install ODBC Driver for Microsoft SQL Server Section... +#Source: https://learn.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver16&tabs=debian18-install%2Calpine17-install%2Cdebian8-install%2Credhat7-13-install%2Crhel7-offline + +#Add Microsoft repository GPG key +RUN curl https://packages.microsoft.com/keys/microsoft.asc | tee /etc/apt/trusted.gpg.d/microsoft.asc + +#Add the Microsoft SQL Server repository for Debian 12 +RUN curl https://packages.microsoft.com/config/debian/12/prod.list | tee /etc/apt/sources.list.d/mssql-release.list + + +#Add Microsoft GPG key +RUN curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft-archive-keyring.gpg + +#Add the Microsoft SQL Server repository for Debian 12 +RUN echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/debian/12/prod bookworm main" > /etc/apt/sources.list.d/mssql-release.list + +#Update package list +RUN apt-get update + +#Install ODBC Driver 17 for SQL Server +RUN ACCEPT_EULA=Y apt-get install -y msodbcsql17 + +#Install mssql-tools +RUN ACCEPT_EULA=Y apt-get install -y mssql-tools +RUN bash -c "echo 'export PATH=\"$PATH:/opt/mssql-tools/bin\"' >> ~/.bashrc && source ~/.bashrc" + + +#RUN echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc && source ~/.bashrc + +#Install unixodbc-dev and kerberos library +RUN apt-get install -y libgssapi-krb5-2 + + +#Set environment variables for ODBC configuration +ENV ODBCINI=/etc/odbc.ini +ENV ODBCSYSINI=/etc + +CMD ["python", "index.py"] \ No newline at end of file diff --git a/Orders_Microservice_Group3/__pycache__/config.cpython-311.pyc b/Orders_Microservice_Group3/__pycache__/config.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..101ebab0c3bf83308915c60456759b6a5076dd5b Binary files /dev/null and b/Orders_Microservice_Group3/__pycache__/config.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/__init__.py b/Orders_Microservice_Group3/app/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..8ffbc3af6baf3cdf9034964801c376d9ae17c7b6 --- /dev/null +++ b/Orders_Microservice_Group3/app/__init__.py @@ -0,0 +1,44 @@ +# from flask import Flask + +# from .controllers.AddToCartController import AddToCart_bp +# from .controllers.CheckOutController import checkout_bp +# from .controllers.DeleteFromItemController import DeleteCart_bp +# from .controllers.FetchCartController import cart_bp + + +# def create_app(): +# app = Flask(__name__) +# app.register_blueprint(AddToCart_bp) +# app.register_blueprint(checkout_bp) +# app.register_blueprint(DeleteCart_bp) +# app.register_blueprint(cart_bp) + + +# return app + +from flask import Flask +from flask_cors import CORS +import os + +from app.controllers.AddToCartController import AddToCart_bp +from app.controllers.CheckOutController import checkout_bp +from .controllers.DeleteFromItemController import DeleteCart_bp +from .controllers.FetchCartController import cart_bp +from .subscriber.UpdateProductSubscriber import start_kafka_consumer + +def create_app(): + app = Flask(__name__) + CORS(app) + + app.config.from_object('config') + + app.register_blueprint(AddToCart_bp) + app.register_blueprint(checkout_bp) + app.register_blueprint(DeleteCart_bp) + app.register_blueprint(cart_bp) + + # Optionally start Kafka consumer based on configuration + if app.config.get('START_KAFKA_CONSUMER', False): + start_kafka_consumer() + + return app \ No newline at end of file diff --git a/Orders_Microservice_Group3/app/__pycache__/__init__.cpython-311.pyc b/Orders_Microservice_Group3/app/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..45f09827829da5b30358373d313d7bf931794085 Binary files /dev/null and b/Orders_Microservice_Group3/app/__pycache__/__init__.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/__pycache__/__init__.cpython-312.pyc b/Orders_Microservice_Group3/app/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..58ee95c249f77ea8b7a423614344f4c1dee19f9e Binary files /dev/null and b/Orders_Microservice_Group3/app/__pycache__/__init__.cpython-312.pyc differ diff --git a/Orders_Microservice_Group3/app/__pycache__/config.cpython-311.pyc b/Orders_Microservice_Group3/app/__pycache__/config.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..86c246b8cffc2ff3f0cfd64293963826bce4eba9 Binary files /dev/null and b/Orders_Microservice_Group3/app/__pycache__/config.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/config.py b/Orders_Microservice_Group3/app/config.py new file mode 100644 index 0000000000000000000000000000000000000000..14dd59325bb1f27ad20bc68b4e9a1f18dfa9d4f8 --- /dev/null +++ b/Orders_Microservice_Group3/app/config.py @@ -0,0 +1,6 @@ +DEBUG = True +SECRET_KEY = 'Group3' +PORT = 5003 +START_KAFKA_CONSUMER = True +HOST = '0.0.0.0' +KAFKA_SERVER = "kafka:9092" \ No newline at end of file diff --git a/Orders_Microservice_Group3/app/controllers/AddToCartController.py b/Orders_Microservice_Group3/app/controllers/AddToCartController.py new file mode 100644 index 0000000000000000000000000000000000000000..16ff26afba66251af41977d07baa3d7f6158553f --- /dev/null +++ b/Orders_Microservice_Group3/app/controllers/AddToCartController.py @@ -0,0 +1,51 @@ +from flask import Blueprint, jsonify, request, json, session + +from models.AddToCart import add_item_shopping_cart +from models.CheckPriceAndQuantity import check_availability_and_price + +import requests + +AddToCart_bp = Blueprint("AddToCart", __name__) + + +@AddToCart_bp.route("/cart/AddToCart", methods=["POST"]) +def add_item(): + #user_id = 1 # Temporarily bypass authentication for testing + + user_id = session.get("user_id") + if not user_id: + return jsonify({"error": "User not logged in"}), 401 + + data = request.get_json() + ProductID = data.get("ProductID") + CartQuantity = data.get("CartQuantity") + UnitPrice = data.get("UnitPrice") + + # Check for required data + if not ProductID or not CartQuantity or UnitPrice is None: + return jsonify({"error": "Product ID, quantity, and unit price are required."}), 400 + + # Call the CheckPriceAndQuantity function + inventory_check = check_availability_and_price(ProductID, CartQuantity, UnitPrice, user_id) + + # If there's an issue with availability or price, return the message from the inventory check + if not inventory_check.get("available") or not inventory_check.get("price_correct"): + return jsonify({ + "error": inventory_check.get("message"), # Message from inventory check + "available_quantity": inventory_check.get("available_quantity"), + "current_price": inventory_check.get("current_price") + }), 400 + + cart_item_data = { + "UserID": user_id, + "ProductID": ProductID, + "UnitPrice": UnitPrice, + "CartQuantity": CartQuantity + } + + # Update cart item data with the current price + cart_item_data["UnitPrice"] = inventory_check["current_price"] + + # If the checks pass, add the item to the shopping cart + add_cart_item_message = add_item_shopping_cart(cart_item_data) + return jsonify(add_cart_item_message) diff --git a/Orders_Microservice_Group3/app/controllers/CheckOutController.py b/Orders_Microservice_Group3/app/controllers/CheckOutController.py new file mode 100644 index 0000000000000000000000000000000000000000..1f2d33b14dfd5ab6e2de330d476dc995b5f53a89 --- /dev/null +++ b/Orders_Microservice_Group3/app/controllers/CheckOutController.py @@ -0,0 +1,41 @@ +from flask import Blueprint, request, jsonify, session +from models.CheckOut import checkout_cart +from publishers.kafkaPublishers import publish_product_updated_event + +checkout_bp = Blueprint('checkout', __name__) + +@checkout_bp.route('/cart/checkout', methods=['POST']) +def handle_checkout(): + user_id = session.get("user_id") # Retrieve the user ID from session; ensure the user is logged in + #user_id = 1 + if not user_id: + return jsonify({"error": "User not logged in", "UserID" : user_id}), 401 + + data = request.get_json() + if not data or 'address' not in data: + return jsonify({"error": "Missing required fields: address"}), 400 + + address = data['address'] + + # Call the checkout function with the provided data + checkout_result = checkout_cart(user_id, address) + + # Check for errors in the result and return appropriate responses + if 'error' in checkout_result: + return jsonify({"error": checkout_result["error"]}), 500 # or another appropriate status code + + # If checkout is successful, publish an event to Kafka for each item purchased + for item in checkout_result.get('item_details', []): # Ensure that item_details are included in checkout_result + event_data = { + "ProductID": item['ProductID'], + "QuantityPurchased": item['Quantity'] + } + publish_product_updated_event(event_data) + + return jsonify({ + "message": "Checkout completed successfully", + "TransactionID": checkout_result.get("TransactionID", "Unknown") + }), 200 + + + diff --git a/Orders_Microservice_Group3/app/controllers/DeleteFromItemController.py b/Orders_Microservice_Group3/app/controllers/DeleteFromItemController.py new file mode 100644 index 0000000000000000000000000000000000000000..5520377bdab4b3877269318d6f601eb15869d77e --- /dev/null +++ b/Orders_Microservice_Group3/app/controllers/DeleteFromItemController.py @@ -0,0 +1,21 @@ +from models.DeleteItemCartModel import delete_item_from_cart +from flask import Blueprint, jsonify, request, json, session + +DeleteCart_bp = Blueprint("delete", __name__) + + + +@DeleteCart_bp.route("/cart/delete/<int:cart_item_id>", methods=['DELETE']) +def delete_item(cart_item_id): + #user_id = 1 + user_id = session.get("user_id") + + if not user_id: + return jsonify({"error": "You need to be logged in to delete items from the cart."}), 401 + + # Call the function to delete the item from the cart + result = delete_item_from_cart(user_id, cart_item_id) + + if "error" in result: + return jsonify(result), 404 # Or another appropriate status code based on the error + return jsonify(result), 200 \ No newline at end of file diff --git a/Orders_Microservice_Group3/app/controllers/FetchCartController.py b/Orders_Microservice_Group3/app/controllers/FetchCartController.py new file mode 100644 index 0000000000000000000000000000000000000000..2c6e905b798388ada7b3eda8c80d969e66062e2c --- /dev/null +++ b/Orders_Microservice_Group3/app/controllers/FetchCartController.py @@ -0,0 +1,21 @@ +from flask import Blueprint, jsonify, request, json, session +from models.FetchKart import get_user_cart_items +import requests + +cart_bp = Blueprint("cart", __name__) + +@cart_bp.route("/cart", methods=["GET"]) +def fetch_user_cart(): + + #user_id = 5 + + user_id = session.get("user_id") + + if not user_id: + return jsonify({"error": "User ID is required."}), 400 + + cart_data = get_user_cart_items(user_id) + if "error" in cart_data: + return jsonify({"error": cart_data["error"]}), 500 + + return jsonify(cart_data) \ No newline at end of file diff --git a/Orders_Microservice_Group3/app/controllers/OrderHistoryController.py b/Orders_Microservice_Group3/app/controllers/OrderHistoryController.py new file mode 100644 index 0000000000000000000000000000000000000000..804480e4714e705551f5353d4be59d5020f3d041 --- /dev/null +++ b/Orders_Microservice_Group3/app/controllers/OrderHistoryController.py @@ -0,0 +1,18 @@ +from flask import Blueprint, jsonify, session +from models.order_history import fetch_order_history + +order_history_bp = Blueprint('order_history', __name__) + +@order_history_bp.route('/order/history', methods=['GET']) +def order_history(): + user_id = session.get('user_id') + #user_id = 1 + if not user_id: + return jsonify({"error": "Authentication required to view order history."}), 401 + + order_history_data = fetch_order_history(user_id) + + if 'error' in order_history_data: + return jsonify({"error": order_history_data["error"]}), 500 + + return jsonify({"order_history": order_history_data}), 200 \ No newline at end of file diff --git a/Orders_Microservice_Group3/app/controllers/UpdateProductController.py b/Orders_Microservice_Group3/app/controllers/UpdateProductController.py new file mode 100644 index 0000000000000000000000000000000000000000..ba1a39398981f74841fda90072bb5ef0f32408b1 --- /dev/null +++ b/Orders_Microservice_Group3/app/controllers/UpdateProductController.py @@ -0,0 +1,7 @@ +from flask import Blueprint, jsonify, request, json, session +from models.UpdateProduct import UpdateProduct +from subscriber.UpdateProductSubscriber import consume_product_updated_event + +import requests + + diff --git a/Orders_Microservice_Group3/app/controllers/__pycache__/AddToCart.cpython-312.pyc b/Orders_Microservice_Group3/app/controllers/__pycache__/AddToCart.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..61f056c0c6c1d44e3037c3caa6a159cbe1c2d4dd Binary files /dev/null and b/Orders_Microservice_Group3/app/controllers/__pycache__/AddToCart.cpython-312.pyc differ diff --git a/Orders_Microservice_Group3/app/controllers/__pycache__/AddToCartController.cpython-311.pyc b/Orders_Microservice_Group3/app/controllers/__pycache__/AddToCartController.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..97c0db86ef890aa773e3911eb8cb923b45158d13 Binary files /dev/null and b/Orders_Microservice_Group3/app/controllers/__pycache__/AddToCartController.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/controllers/__pycache__/AddToCartController.cpython-312.pyc b/Orders_Microservice_Group3/app/controllers/__pycache__/AddToCartController.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0f2978378b85343616505d320886ec0171822f64 Binary files /dev/null and b/Orders_Microservice_Group3/app/controllers/__pycache__/AddToCartController.cpython-312.pyc differ diff --git a/Orders_Microservice_Group3/app/controllers/__pycache__/CheckOutController.cpython-311.pyc b/Orders_Microservice_Group3/app/controllers/__pycache__/CheckOutController.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..510d3c9c3eb0884b1e6afd398e7694964bd0042f Binary files /dev/null and b/Orders_Microservice_Group3/app/controllers/__pycache__/CheckOutController.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/controllers/__pycache__/CheckOutController.cpython-312.pyc b/Orders_Microservice_Group3/app/controllers/__pycache__/CheckOutController.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e365f5bde611cb80404546ffa04313ef6241fc51 Binary files /dev/null and b/Orders_Microservice_Group3/app/controllers/__pycache__/CheckOutController.cpython-312.pyc differ diff --git a/Orders_Microservice_Group3/app/controllers/__pycache__/DeleteFromItemController.cpython-311.pyc b/Orders_Microservice_Group3/app/controllers/__pycache__/DeleteFromItemController.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..27cfe415a7050583d7a3462ce0a183d05dd71968 Binary files /dev/null and b/Orders_Microservice_Group3/app/controllers/__pycache__/DeleteFromItemController.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/controllers/__pycache__/DeleteFromItemController.cpython-312.pyc b/Orders_Microservice_Group3/app/controllers/__pycache__/DeleteFromItemController.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..da076fa047163edc36e0bf3b2339a6b74d0b7478 Binary files /dev/null and b/Orders_Microservice_Group3/app/controllers/__pycache__/DeleteFromItemController.cpython-312.pyc differ diff --git a/Orders_Microservice_Group3/app/controllers/__pycache__/FetchCartController.cpython-311.pyc b/Orders_Microservice_Group3/app/controllers/__pycache__/FetchCartController.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..53d9a1b05291e8a728ade7d7d25a829e4c7ba8fa Binary files /dev/null and b/Orders_Microservice_Group3/app/controllers/__pycache__/FetchCartController.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/controllers/__pycache__/FetchCartController.cpython-312.pyc b/Orders_Microservice_Group3/app/controllers/__pycache__/FetchCartController.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dfc13a9f5c15cb3c4f0eba233881f4a60cc6c9a3 Binary files /dev/null and b/Orders_Microservice_Group3/app/controllers/__pycache__/FetchCartController.cpython-312.pyc differ diff --git a/Orders_Microservice_Group3/app/controllers/__pycache__/OrderHistoryController.cpython-311.pyc b/Orders_Microservice_Group3/app/controllers/__pycache__/OrderHistoryController.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b860e83af0ea0d5e99f82ca0488580230806fdbb Binary files /dev/null and b/Orders_Microservice_Group3/app/controllers/__pycache__/OrderHistoryController.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/controllers/__pycache__/UpdateProductController.cpython-311.pyc b/Orders_Microservice_Group3/app/controllers/__pycache__/UpdateProductController.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..23e96840e525d88b7815e326927bd300b7099792 Binary files /dev/null and b/Orders_Microservice_Group3/app/controllers/__pycache__/UpdateProductController.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/hello_world.py b/Orders_Microservice_Group3/app/hello_world.py new file mode 100644 index 0000000000000000000000000000000000000000..df9ab9f16ad0c309cd6fbe9a539a12b1a821d2fb --- /dev/null +++ b/Orders_Microservice_Group3/app/hello_world.py @@ -0,0 +1,9 @@ +from flask import Flask +app = Flask(__name__) + +@app.route('/') +def hello_world(): + return 'Hello World' + +if __name__ == '__main__': + app.run() \ No newline at end of file diff --git a/Orders_Microservice_Group3/app/index.py b/Orders_Microservice_Group3/app/index.py new file mode 100644 index 0000000000000000000000000000000000000000..c42090f982e2bb0fa249815b4100e57fd5c6357d --- /dev/null +++ b/Orders_Microservice_Group3/app/index.py @@ -0,0 +1,127 @@ +from flask import Flask, jsonify, request, render_template, session, abort, redirect, url_for, make_response +import requests +import subscriber +from flask_cors import CORS +from config import DEBUG, SECRET_KEY, PORT, HOST +import os + +# Importing blueprints from the controllers +from controllers.AddToCartController import AddToCart_bp +from controllers.CheckOutController import checkout_bp +from controllers.DeleteFromItemController import DeleteCart_bp +from controllers.FetchCartController import cart_bp +from controllers.OrderHistoryController import order_history_bp + +# Setting up Flask application +app = Flask(__name__) +CORS(app) + + +@app.route('/cart', methods=['POST']) +def get_session_id(): + session_id = session.get('user_id') + if session_id: + return jsonify({'session_id': session_id}) + else: + return jsonify({'message': 'Session ID not found'}) + +# @app.route('/') +# def index(): + +# return render_template("index.html") + + +# Register blueprints for different parts of the application +app.register_blueprint(AddToCart_bp) +app.register_blueprint(checkout_bp) +app.register_blueprint(DeleteCart_bp) +app.register_blueprint(cart_bp) +app.register_blueprint(order_history_bp) + + + +try: + + # Check for the existence of the /proc/self/cgroup file + with open("/proc/self/cgroup", "r") as cgroup_file: + cgroup_info = cgroup_file.read() + + # Check if the cgroup information contains 'docker' keyword + if 'docker' in cgroup_info: + print("Running inside Docker container") + app.secret_key = os.environ.get('SECRET_KEY') + +except FileNotFoundError: + # If the file doesn't exist + print("Running on a local Windows machine") + + app.secret_key = SECRET_KEY + + +if __name__ == '__main__': + + subscriber.start_kafka_consumer() + + # Running the Flask application + app.run(host=HOST, debug=DEBUG, port=PORT) + + + + + + + + + +# from flask import Flask, redirect, url_for, request, render_template, make_response, session, abort +# from flask_cors import CORS +# from flask import jsonify +# from config import DEBUG, SECRET_KEY, PORT +# import os +# import requests +# import subscribers + +# from controllers.AddToCartController import AddToCart_bp +# from controllers.CheckOutController import checkout_bp +# from controllers.DeleteFromItemController import DeleteCart_bp +# from controllers.FetchCartController import cart_bp +# from controllers.UpdateProductController import cart_bp + + +# app = Flask(__name__) +# CORS(app) + + +# app.secret_key = SECRET_KEY + + +# # Read user microservice URL from environment variable +# USER_MICROSERVICE_URL = os.getenv('USER_MICROSERVICE_URL', 'http://127.0.0.1:5000') + + +# @app.route('/cart', methods=['POST']) +# def get_session_id(): +# session_id = session.get('user_id') +# if session_id: +# return jsonify({'session_id': session_id}) +# else: +# return jsonify({'message': 'Session ID not found'}) + +# @app.route('/') +# def index(): +# return render_template("index.html") + + + +# # app.register_blueprint(AddToCart_bp) +# # app.register_blueprint(checkout_bp) +# # app.register_blueprint(DeleteCart_bp) +# # app.register_blueprint(cart_bp) + + + +# if __name__ == '__main__': + +# subscribers.start_kafka_consumer() + +# app.run(debug=DEBUG, port=PORT) \ No newline at end of file diff --git a/Orders_Microservice_Group3/app/models/AddToCart.py b/Orders_Microservice_Group3/app/models/AddToCart.py new file mode 100644 index 0000000000000000000000000000000000000000..b5df717131fa80ac5f91a8de62fa7a1deb48d9ec --- /dev/null +++ b/Orders_Microservice_Group3/app/models/AddToCart.py @@ -0,0 +1,35 @@ +import pyodbc +from flask import jsonify +from decimal import Decimal +from models.database_connection import connect_db + +def add_item_shopping_cart(data): + + try: #error handling + connection = connect_db() + cursor = connection.cursor() + + insert_cart_item_query = '''INSERT INTO dbo.ShoppingCart (UserID, ProductID, UnitPrice, CartQuantity) +VALUES (?, ?, ?, ?);''' + cursor.execute(insert_cart_item_query, (data["UserID"], data["ProductID"], data["UnitPrice"], data["CartQuantity"])) + + connection.commit() + + return {"message": "Item added to cart successfully"} + + except pyodbc.Error as e: + print(f"Database error in add_item_to_cart: {e}") + connection.rollback() + return {"Error": f"Unexpected error: {str(e)}"} + + except Exception as e: + print(f"Unexpected error occurred in add_item_to_cart: {e}") + connection.rollback() + return {"Error": "Unexpected error"} + + finally: + if cursor: + cursor.close() + if connection: + connection.close() + diff --git a/Orders_Microservice_Group3/app/models/CheckOut.py b/Orders_Microservice_Group3/app/models/CheckOut.py new file mode 100644 index 0000000000000000000000000000000000000000..d047f9f222fb12d24401bdaaf45f4d5decb620c7 --- /dev/null +++ b/Orders_Microservice_Group3/app/models/CheckOut.py @@ -0,0 +1,111 @@ +from models.database_connection import connect_db +from flask import Blueprint, request, jsonify, session + +from decimal import Decimal +import pyodbc +from datetime import datetime + + + +def checkout_cart(user_id, address): + try: + connection = connect_db() + connection.autocommit = False + cursor = connection.cursor() + + # Fetch cart items for the user + cursor.execute(""" + SELECT ProductID, CartQuantity, FORMAT(UnitPrice, 'N2') AS UnitPrice + FROM ShoppingCart + WHERE UserID = ? + """, (user_id,)) + cart_items = cursor.fetchall() + + if not cart_items: + return {"error": "No items in the cart to checkout."} + + total_price = 0 + item_details = [] + + # Verify availability and price for each cart item + for item in cart_items: + ProductID, CartQuantity, UnitPrice = item + UnitPrice_decimal = Decimal(UnitPrice) # Convert string to Decimal for precise calculations + + # Fetch the current availability and unit price from the product data + cursor.execute(""" + SELECT QuantityAvailable, FORMAT(UnitPrice, 'N2') AS UnitPrice + FROM ProductData + WHERE ProductID = ? + """, (ProductID,)) + row = cursor.fetchone() + if row: + available_quantity, current_price = row + current_price_decimal = Decimal(current_price) + + if available_quantity < CartQuantity: + connection.rollback() + return {"error": f"Not enough inventory for ProductID {ProductID}."} + + if current_price_decimal != UnitPrice_decimal: + cursor.execute(""" + UPDATE ShoppingCart + SET UnitPrice = ? + WHERE ProductID = ? AND UserID = ? + """, (current_price, ProductID, user_id)) + connection.commit() + return {"error": f"Price has changed for ProductID {ProductID}. Updated price in cart."} + + + new_quantity = available_quantity - CartQuantity + cursor.execute(""" + UPDATE ProductData + SET QuantityAvailable = ? + WHERE ProductID = ? + """, (new_quantity, ProductID)) + + + TotalItemPrice = CartQuantity * current_price_decimal + total_price += TotalItemPrice + item_details.append({ + "ProductID": ProductID, + "Quantity": CartQuantity, + "UnitPrice": current_price_decimal, + "TotalItemPrice": TotalItemPrice + }) + + # Insert a new order group + cursor.execute(""" + INSERT INTO OrderGroup (CustomerID, TotalPrice, TransactionDate, Location, OrdersStatus) + OUTPUT INSERTED.OrderGroupID + VALUES (?, ?, ?, ?, 'Processing') + """, (user_id, total_price, datetime.now(), address)) + OrderGroupID = cursor.fetchone()[0] + + # Insert cart items into OrderItem table + for item in item_details: + cursor.execute(""" + INSERT INTO OrderItem (OrderGroupID, ProductID, Quantity, TotalItemPrice) + VALUES (?, ?, ?, ?) + """, (OrderGroupID, item["ProductID"], item["Quantity"], item["TotalItemPrice"])) + + # Delete the cart items from the ShoppingCart + cursor.execute("DELETE FROM ShoppingCart WHERE UserID = ?", (user_id,)) + connection.commit() + + return { + "success": "Checkout completed successfully.", + "TransactionID": OrderGroupID, + "item_details": item_details # Include item details for post-processing + } + + except pyodbc.Error as e: + print(f"Database error during checkout: {e}") + connection.rollback() + return {"error": "Database error during checkout"} + + finally: + if cursor: + cursor.close() + if connection: + connection.close() \ No newline at end of file diff --git a/Orders_Microservice_Group3/app/models/CheckPriceAndQuantity.py b/Orders_Microservice_Group3/app/models/CheckPriceAndQuantity.py new file mode 100644 index 0000000000000000000000000000000000000000..ebb2581fde514c083ccabe2ebca1d59246247249 --- /dev/null +++ b/Orders_Microservice_Group3/app/models/CheckPriceAndQuantity.py @@ -0,0 +1,66 @@ +from models.database_connection import connect_db +import pyodbc + +from decimal import Decimal + + + +def check_availability_and_price(product_id, requested_quantity, expected_price, user_id): + connection = connect_db() + try: + cursor = connection.cursor() + # Fetch the current available quantity and price + cursor.execute(""" + SELECT QuantityAvailable, FORMAT(UnitPrice, 'N2') AS UnitPrice + FROM ProductData + WHERE ProductID = ? + """, (product_id,)) + + row = cursor.fetchone() + if row: + available_quantity, current_price = row + current_pricefloat = float(current_price) # Convert formatted price back to float if necessary for comparisons + + response_data = { + "available_quantity": available_quantity, + "current_price": current_price, + "available": available_quantity >= requested_quantity, + "price_correct": current_pricefloat == expected_price + } + + messages = [] + if not response_data["available"]: + messages.append(f"We apologize, but there are only {available_quantity} items available.") + + # Check if the price has changed + if not response_data["price_correct"]: + messages.append(f"The price has changed. The current price is now {current_pricefloat}.") + # Update the ShoppingCart with the new price + cursor.execute(""" + UPDATE ShoppingCart + SET UnitPrice = ? + WHERE ProductID = ? AND UserID = ? + """, (current_price, product_id, user_id)) + connection.commit() + + if messages: + response_data["message"] = " ".join(messages) + return response_data + else: + # All checks passed + return response_data + + else: + return {"available": False, "message": "Product not found."} + + except pyodbc.Error as e: + print(f"Database error in check_availability_and_price: {e}") + connection.rollback() + return {"available": False, "message": f"Database error: {str(e)}"} + + finally: + if cursor: + cursor.close() + if connection: + connection.close() + diff --git a/Orders_Microservice_Group3/app/models/DeleteItemCartModel.py b/Orders_Microservice_Group3/app/models/DeleteItemCartModel.py new file mode 100644 index 0000000000000000000000000000000000000000..2ca2a67930c83e98fd019405659321f84bb1a0d3 --- /dev/null +++ b/Orders_Microservice_Group3/app/models/DeleteItemCartModel.py @@ -0,0 +1,40 @@ +from flask import jsonify +from models.database_connection import connect_db +import pyodbc + +def delete_item_from_cart(user_id, cart_item_id): + + try: # error handling + connection = connect_db() + cursor = connection.cursor() + + # SQL query to delete the item from the cart based on cart_item_id and UserID + delete_cart_item_query = '''DELETE FROM dbo.ShoppingCart WHERE UserID = ? AND CartItemID = ?;''' + cursor.execute(delete_cart_item_query, (user_id, cart_item_id)) + + # Committing the transaction to the database + connection.commit() + + # Check if the delete operation was successful + if cursor.rowcount == 0: + return {"error": "Item not found or could not be deleted"} + + return {"message": "Item deleted successfully"} + + except pyodbc.Error as e: + print(f"Database error in delete_item_from_cart: {e}") + # Rollback the transaction in case of an error + connection.rollback() + return {"error": "Database error during item deletion"} + + except Exception as e: + print(f"Unexpected error occurred in delete_item_from_cart: {e}") + # Rollback the transaction in case of an unexpected error + connection.rollback() + return {"error": "Unexpected error during item deletion"} + + finally: + if cursor: + cursor.close() + if connection: + connection.close() \ No newline at end of file diff --git a/Orders_Microservice_Group3/app/models/FetchKart.py b/Orders_Microservice_Group3/app/models/FetchKart.py new file mode 100644 index 0000000000000000000000000000000000000000..b9bd2df5c1a4e1bf7e88703f3630617a51609a1e --- /dev/null +++ b/Orders_Microservice_Group3/app/models/FetchKart.py @@ -0,0 +1,48 @@ +import pyodbc +from datetime import datetime +from models.database_connection import connect_db + + +from models.database_connection import connect_db +from flask import jsonify +import pyodbc + +def get_user_cart_items(user_id): + try: + connection = connect_db() + cursor = connection.cursor() + + query = "SELECT * FROM ShoppingCart WHERE UserID = ?" + cursor.execute(query, (user_id,)) + + # Fetch all rows for the user_id + rows = cursor.fetchall() + + # Prepare a list of dictionaries to hold cart item data + cart_items = [] + Total_Price = 0 + for row in rows: + Total_item_price = row.CartQuantity * row.UnitPrice + cart_item = { + "CartItemID": row.CartItemID, + "UserID": row.UserID, + "ProductID": row.ProductID, + "UnitPrice": row.UnitPrice, + "CartQuantity": row.CartQuantity, + "AddedDate": row.AddedDate, + "TotalItemPrice": Total_item_price + } + cart_items.append(cart_item) + Total_Price += Total_item_price + + return {"CartItems": cart_items, "TotalPrice": Total_Price} + + except pyodbc.Error as e: + print(f"Database error in get_user_cart_items: {e}") + return {"error": "Database error"} + + finally: + if cursor: + cursor.close() + if connection: + connection.close() \ No newline at end of file diff --git a/Orders_Microservice_Group3/app/models/UpdateProduct.py b/Orders_Microservice_Group3/app/models/UpdateProduct.py new file mode 100644 index 0000000000000000000000000000000000000000..a472d4bae015dec5be5f841f0ceac0da88228f62 --- /dev/null +++ b/Orders_Microservice_Group3/app/models/UpdateProduct.py @@ -0,0 +1,55 @@ +#from confluent_kafka import Producer +import json +import pyodbc +from flask import jsonify +#from flask import jsonify +from models.database_connection import connect_db + +#subscriber + +def UpdateProduct(product_id, quantity, price): + try: + connection = connect_db() + connection.autocommit = False + cursor = connection.cursor() + + # SQL MERGE statement to insert or update + sql_product_data = """ + MERGE INTO ProductData AS target + USING (SELECT ? AS ProductID, ? AS QuantityAvailable, ? AS UnitPrice) AS source + ON target.ProductID = source.ProductID + WHEN MATCHED THEN + UPDATE SET QuantityAvailable = source.QuantityAvailable, UnitPrice = source.UnitPrice + WHEN NOT MATCHED BY TARGET THEN + INSERT (ProductID, QuantityAvailable, UnitPrice) + VALUES (source.ProductID, source.QuantityAvailable, source.UnitPrice); + """ + cursor.execute(sql_product_data, (product_id, quantity, price)) + + # Update ShoppingCart to reflect new prices + sql_shopping_cart = """ + UPDATE ShoppingCart + SET UnitPrice = ? + WHERE ProductID = ? + """ + cursor.execute(sql_shopping_cart, (price, product_id)) + + connection.commit() + return {"success": "Product information updated successfully."} + + except pyodbc.Error as e: + print(f"Database error during product update: {e}") + connection.rollback() + return {"error": f"Database error during product update: {e}"} + + except Exception as e: + print(f"Unexpected error occurred: {e}") + connection.rollback() + return {"error": f"Unexpected error: {e}"} + + finally: + if cursor: + cursor.close() + if connection: + connection.close() + diff --git a/Orders_Microservice_Group3/app/models/__init__.py b/Orders_Microservice_Group3/app/models/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..7cf5b4c542f1443c20eb27c463b645ba727a4ad8 --- /dev/null +++ b/Orders_Microservice_Group3/app/models/__init__.py @@ -0,0 +1,15 @@ +from flask import Flask +from flask_cors import CORS + + +#from app.models import models + +app = Flask(__name__) +CORS(app) + +#db = sqlAlchemy + +#from app import routes + +if __name__ == '__main__': + app.run(debug=True) diff --git a/Orders_Microservice_Group3/app/models/__pycache__/AddToCart.cpython-311.pyc b/Orders_Microservice_Group3/app/models/__pycache__/AddToCart.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ddd270469158b1323dbea0108fe0f694fa8263d9 Binary files /dev/null and b/Orders_Microservice_Group3/app/models/__pycache__/AddToCart.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/models/__pycache__/AddToCart.cpython-312.pyc b/Orders_Microservice_Group3/app/models/__pycache__/AddToCart.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bd1c0a1653bfb72c78ee6c043d7d6b69b87afbd3 Binary files /dev/null and b/Orders_Microservice_Group3/app/models/__pycache__/AddToCart.cpython-312.pyc differ diff --git a/Orders_Microservice_Group3/app/models/__pycache__/CheckOut.cpython-311.pyc b/Orders_Microservice_Group3/app/models/__pycache__/CheckOut.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ab02bef1ebefdfcc2317bcec2dd48f95f799223b Binary files /dev/null and b/Orders_Microservice_Group3/app/models/__pycache__/CheckOut.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/models/__pycache__/CheckOut.cpython-312.pyc b/Orders_Microservice_Group3/app/models/__pycache__/CheckOut.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aa7bdb081e740a0063e28ae61d22f4e633ced122 Binary files /dev/null and b/Orders_Microservice_Group3/app/models/__pycache__/CheckOut.cpython-312.pyc differ diff --git a/Orders_Microservice_Group3/app/models/__pycache__/CheckPriceAndQuantity.cpython-311.pyc b/Orders_Microservice_Group3/app/models/__pycache__/CheckPriceAndQuantity.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c79d5db4c1eefa4213fb9c763d66403f25f4861c Binary files /dev/null and b/Orders_Microservice_Group3/app/models/__pycache__/CheckPriceAndQuantity.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/models/__pycache__/CheckPriceAndQuantity.cpython-312.pyc b/Orders_Microservice_Group3/app/models/__pycache__/CheckPriceAndQuantity.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..83a41ba36819661015f476b957f0f91566c55176 Binary files /dev/null and b/Orders_Microservice_Group3/app/models/__pycache__/CheckPriceAndQuantity.cpython-312.pyc differ diff --git a/Orders_Microservice_Group3/app/models/__pycache__/DeleteItemCartModel.cpython-311.pyc b/Orders_Microservice_Group3/app/models/__pycache__/DeleteItemCartModel.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7084c58d824a608c98d1ce39a68872d6a9dc1284 Binary files /dev/null and b/Orders_Microservice_Group3/app/models/__pycache__/DeleteItemCartModel.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/models/__pycache__/DeleteItemCartModel.cpython-312.pyc b/Orders_Microservice_Group3/app/models/__pycache__/DeleteItemCartModel.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a2ea66674acb226f5185fc19100366ff091f702e Binary files /dev/null and b/Orders_Microservice_Group3/app/models/__pycache__/DeleteItemCartModel.cpython-312.pyc differ diff --git a/Orders_Microservice_Group3/app/models/__pycache__/FetchKart.cpython-311.pyc b/Orders_Microservice_Group3/app/models/__pycache__/FetchKart.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bed234c22c7b1ebd9c17c3faf94d2966bb327360 Binary files /dev/null and b/Orders_Microservice_Group3/app/models/__pycache__/FetchKart.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/models/__pycache__/FetchKart.cpython-312.pyc b/Orders_Microservice_Group3/app/models/__pycache__/FetchKart.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8566e4c71881890ccfe8d200cc358919b41a2a62 Binary files /dev/null and b/Orders_Microservice_Group3/app/models/__pycache__/FetchKart.cpython-312.pyc differ diff --git a/Orders_Microservice_Group3/app/models/__pycache__/UpdateProduct.cpython-311.pyc b/Orders_Microservice_Group3/app/models/__pycache__/UpdateProduct.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5411ca5c9766993a6b7cd19baecd897ec1b7db06 Binary files /dev/null and b/Orders_Microservice_Group3/app/models/__pycache__/UpdateProduct.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/models/__pycache__/__init__.cpython-311.pyc b/Orders_Microservice_Group3/app/models/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5e9bc8be7b53872e499459cc9155650cec52f4f2 Binary files /dev/null and b/Orders_Microservice_Group3/app/models/__pycache__/__init__.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/models/__pycache__/__init__.cpython-312.pyc b/Orders_Microservice_Group3/app/models/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1a6cdeed180b8b57206cfa994f5e6fd4dc67512a Binary files /dev/null and b/Orders_Microservice_Group3/app/models/__pycache__/__init__.cpython-312.pyc differ diff --git a/Orders_Microservice_Group3/app/models/__pycache__/database_connection.cpython-311.pyc b/Orders_Microservice_Group3/app/models/__pycache__/database_connection.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a76c64f94ba987629cbf450158caf7e238505000 Binary files /dev/null and b/Orders_Microservice_Group3/app/models/__pycache__/database_connection.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/models/__pycache__/database_connection.cpython-312.pyc b/Orders_Microservice_Group3/app/models/__pycache__/database_connection.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f93efc54c77937369eb7735c897e3beaf7600747 Binary files /dev/null and b/Orders_Microservice_Group3/app/models/__pycache__/database_connection.cpython-312.pyc differ diff --git a/Orders_Microservice_Group3/app/models/__pycache__/order_history.cpython-311.pyc b/Orders_Microservice_Group3/app/models/__pycache__/order_history.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..793a28e25ca43b12911abbe33cac99eae7e28750 Binary files /dev/null and b/Orders_Microservice_Group3/app/models/__pycache__/order_history.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/models/database_connection.py b/Orders_Microservice_Group3/app/models/database_connection.py new file mode 100644 index 0000000000000000000000000000000000000000..3d1c7c3be68df935bc40493fefbc35adaf05e1c1 --- /dev/null +++ b/Orders_Microservice_Group3/app/models/database_connection.py @@ -0,0 +1,25 @@ +import pyodbc +import os + + +print("Outside db connection function") +#Connect to database +def connect_db(): + print("In CONNECT DB") + try: + + server = '34.39.6.180' + database = 'ordermanagementdatabase' + username = 'sqlserver' + password = 'WebTechGroup3' + driver = 'ODBC Driver 17 for SQL Server' + + connection_string = f'DRIVER={driver};SERVER={server};DATABASE={database};UID={username};PWD={password};Trusted_Connection=no;' + + return pyodbc.connect(connection_string) + + except Exception as e: + # If the file doesn't exist + print("Unexpected error occured {e}") + + return False diff --git a/Orders_Microservice_Group3/app/models/order_history.py b/Orders_Microservice_Group3/app/models/order_history.py new file mode 100644 index 0000000000000000000000000000000000000000..7337ee09987d039d6af53d4ff22d0838ca109329 --- /dev/null +++ b/Orders_Microservice_Group3/app/models/order_history.py @@ -0,0 +1,59 @@ +import pyodbc +from models.database_connection import connect_db + +def fetch_order_history(user_id): + try: + # Open database connection + connection = connect_db() + cursor = connection.cursor() + + # Fetch the order history for the user + cursor.execute(""" + SELECT og.OrderGroupID, og.CustomerID, og.TransactionDate, og.Location, og.OrdersStatus, og.TotalPrice, oi.OrderItemID, oi.ProductID, oi.UnitPrice, oi.Quantity, oi.TotalItemPrice + FROM OrderGroup og + INNER JOIN OrderItem oi ON og.OrderGroupID = oi.OrderGroupID + WHERE og.CustomerID = ? + ORDER BY og.TransactionDate DESC;""", (user_id,)) + + # Fetch all records and close cursor and connection + records = cursor.fetchall() + cursor.close() + connection.close() + + # Process the fetched data into a structured format for the API response + order_history = {} + for record in records: + # Unpack the fetched row + order_group_id, customer_id, transaction_date, location, orders_status, total_price, order_item_id, product_id, unit_price, quantity, total_item_price = record + + + if order_group_id not in order_history: + order_history[order_group_id] = { + "CustomerID": customer_id, + "TransactionDate": transaction_date, + "Location": location, + "OrdersStatus": orders_status, + "TotalPrice": total_price, + "Items": [] + } + + # Append this item to the items list for the order group + order_history[order_group_id]["Items"].append({ + "OrderItemID": order_item_id, + "ProductID": product_id, + "UnitPrice": unit_price, + "Quantity": quantity, + "TotalItemPrice": total_item_price + }) + + # Convert to list of order histories + order_history_list = list(order_history.values()) + return order_history_list + + except pyodbc.Error as e: + # Close cursor and connection in case of error + if cursor: + cursor.close() + if connection: + connection.close() + return {"error": str(e)} diff --git a/Orders_Microservice_Group3/app/publishers/__pycache__/kafkaPublishers.cpython-311.pyc b/Orders_Microservice_Group3/app/publishers/__pycache__/kafkaPublishers.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e2b0a2c94e95fffa6a67092d66f9fe3958cd432c Binary files /dev/null and b/Orders_Microservice_Group3/app/publishers/__pycache__/kafkaPublishers.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/publishers/__pycache__/kafkaPublishers.cpython-312.pyc b/Orders_Microservice_Group3/app/publishers/__pycache__/kafkaPublishers.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..432486e9bcb7f12b1184b584251c58df46243066 Binary files /dev/null and b/Orders_Microservice_Group3/app/publishers/__pycache__/kafkaPublishers.cpython-312.pyc differ diff --git a/Orders_Microservice_Group3/app/publishers/kafkaPublishers.py b/Orders_Microservice_Group3/app/publishers/kafkaPublishers.py new file mode 100644 index 0000000000000000000000000000000000000000..0e198a82934f6d9032e5cb5ee8a610b5cc1da03a --- /dev/null +++ b/Orders_Microservice_Group3/app/publishers/kafkaPublishers.py @@ -0,0 +1,44 @@ +from kafka import KafkaProducer +from kafka.admin import KafkaAdminClient, NewTopic +from config import KAFKA_SERVER + +import json + +producer = KafkaProducer(bootstrap_servers=KAFKA_SERVER) + + +#Creates the topic +def create_Quantity_updated_topic(): + + admin_client = KafkaAdminClient(bootstrap_servers=KAFKA_SERVER) + + # Define the topic name + topic_name = "QuantityUpdateFromOrders" + num_partitions = 1 + replication_factor = 1 + + # Retrieve the list of existing topics + topic_metadata = admin_client.list_topics() + + # Check if the topic already exists + if topic_name not in topic_metadata: + # Define the configuration for the new topic + new_topic = NewTopic(name=topic_name, num_partitions=num_partitions, replication_factor=replication_factor) + # Create the new topic + admin_client.create_topics(new_topics=[new_topic], validate_only=False) + + +def publish_product_updated_event(event_data): + # Serialize the event data to JSON + event_json = json.dumps(event_data) + # Publish the event to the Kafka topic + data_to_send = producer.send("QuantityUpdateFromOrders", value=event_json.encode("utf-8")) + try: + record_metadata = data_to_send.get(timeout=10) + print("Message sent successfully!") + print("Topic:", record_metadata.topic) + print("Partition:", record_metadata.partition) + print("Offset:", record_metadata.offset) + except Exception as e: + print("Failed to send message:", e) + producer.flush() diff --git a/Orders_Microservice_Group3/app/run.py b/Orders_Microservice_Group3/app/run.py new file mode 100644 index 0000000000000000000000000000000000000000..507f9d423bdba63ec48425668bdca4f4a8e28c05 --- /dev/null +++ b/Orders_Microservice_Group3/app/run.py @@ -0,0 +1,7 @@ + +from app import create_app + +app = create_app() + +if __name__ == '__main__': + app.run(debug=app.config['DEBUG'], port=app.config['PORT']) \ No newline at end of file diff --git a/Orders_Microservice_Group3/app/subscriber/UpdateProductSubscriber.py b/Orders_Microservice_Group3/app/subscriber/UpdateProductSubscriber.py new file mode 100644 index 0000000000000000000000000000000000000000..1de212e9d7060e9fb32be1b8071e27beed83abf7 --- /dev/null +++ b/Orders_Microservice_Group3/app/subscriber/UpdateProductSubscriber.py @@ -0,0 +1,39 @@ +import sys +print(sys.path) +from threading import Thread +from kafka import KafkaConsumer +import json +import logging +from config import KAFKA_SERVER + +#KAFKA_SERVER= "localhost:9092" +TOPIC_NAME = "product_updated_topic" + + +from models.UpdateProduct import UpdateProduct + +def consume_product_updated_event(): + logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + logging.info("Consuming product updated event...") + + # Create KafkaConsumer listening on the product_updated_topic + consumer = KafkaConsumer("product_updated_topic", bootstrap_servers=KAFKA_SERVER) + + for message in consumer: + product_data_str = message.value.decode("utf-8") + product_data = json.loads(product_data_str) + logging.info("Received product update: {}".format(product_data)) + + # Call your UpdateProduct function passing the dictionary containing product details + UpdateProduct(product_id=product_data['product_id'], + quantity=product_data['quantity'], + price=product_data['price']) + +def start_kafka_consumer(): + logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + logging.info("Starting Kafka consumer...") + + # Starting the consumer in a separate thread + kafka_thread = Thread(target=consume_product_updated_event) + kafka_thread.daemon = True + kafka_thread.start() \ No newline at end of file diff --git a/Orders_Microservice_Group3/app/subscriber/__init__.py b/Orders_Microservice_Group3/app/subscriber/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4006e98f1130d14171b31be230321054cc4fca01 --- /dev/null +++ b/Orders_Microservice_Group3/app/subscriber/__init__.py @@ -0,0 +1,5 @@ +print("Subscribers package initialized") + +from subscriber.UpdateProductSubscriber import consume_product_updated_event + +from subscriber.UpdateProductSubscriber import start_kafka_consumer diff --git a/Orders_Microservice_Group3/app/subscriber/__pycache__/UpdateProductSubscriber.cpython-311.pyc b/Orders_Microservice_Group3/app/subscriber/__pycache__/UpdateProductSubscriber.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..347dfdcb2d1cb3528ea4579548142eb62d88f351 Binary files /dev/null and b/Orders_Microservice_Group3/app/subscriber/__pycache__/UpdateProductSubscriber.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/subscriber/__pycache__/__init__.cpython-311.pyc b/Orders_Microservice_Group3/app/subscriber/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6f5f6208ebc6c55c800f135380842c5ebd5009ab Binary files /dev/null and b/Orders_Microservice_Group3/app/subscriber/__pycache__/__init__.cpython-311.pyc differ diff --git a/Orders_Microservice_Group3/app/subscriber/import pyodbc.txt b/Orders_Microservice_Group3/app/subscriber/import pyodbc.txt new file mode 100644 index 0000000000000000000000000000000000000000..192c306ea69eb14b1066fa4500d4abba9ad0ca09 --- /dev/null +++ b/Orders_Microservice_Group3/app/subscriber/import pyodbc.txt @@ -0,0 +1,45 @@ +import pyodbc +from datetime import datetime +from app.models.database_connection import connect_db +from your_kafka_module import kafka_produce # Import the Kafka producer function + +def order_service_consumer(): + consumer = Consumer({ + 'bootstrap.servers': 'localhost:9092', + 'group.id': 'orders-group', + 'auto.offset.reset': 'earliest' + }) + consumer.subscribe(['product_changes']) + + while True: + message = consumer.poll(1.0) + if message is None: + continue + if message.error(): + print("Consumer error: {}".format(message.error())) + continue + + data = json.loads(message.value().decode('utf-8')) + if data['type'] == 'quantity_update': + # Update orders database based on quantity change + update_orders_database(data['product_id'], data['quantity_sold']) + +def update_orders_database(product_id, quantity_sold): + connection = connect_db() + try: + cursor = connection.cursor() + # Update logic here, e.g., flagging orders as fulfilled + cursor.execute("UPDATE Orders SET Status = 'Updated' WHERE product_id = ?", (product_id,)) + connection.commit() + finally: + cursor.close() + connection.close() + + + + + + + + +kafka-console-producer.bat --broker-list localhost:9092 --topic product_update_topic diff --git a/Orders_Microservice_Group3/config.py b/Orders_Microservice_Group3/config.py new file mode 100644 index 0000000000000000000000000000000000000000..cdd27f126d1eef015410d18492f2803aa62304d9 --- /dev/null +++ b/Orders_Microservice_Group3/config.py @@ -0,0 +1,10 @@ +import os + +DEBUG = True +SECRET_KEY = "Group3" + +PORT = 5003 + +KAFKA_SERVER= "kafka:9092" + +HOST = "0.0.0.0" diff --git a/Orders_Microservice_Group3/debug_consumer.py b/Orders_Microservice_Group3/debug_consumer.py new file mode 100644 index 0000000000000000000000000000000000000000..d89bad3946a34b5cc074c4c4336dab727d293f02 --- /dev/null +++ b/Orders_Microservice_Group3/debug_consumer.py @@ -0,0 +1,6 @@ +import logging +from .app.subscriber.UpdateProductSubscriber import consume_product_updated_event + +if __name__ == '__main__': + logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + consume_product_updated_event() \ No newline at end of file diff --git a/Orders_Microservice_Group3/docker-compose.yml b/Orders_Microservice_Group3/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..7184b10fa8c5bdc804ec5350f1931f28f1ae6a1e --- /dev/null +++ b/Orders_Microservice_Group3/docker-compose.yml @@ -0,0 +1,24 @@ +version: "3.2" + +services: + + orders-microservice: + build: + context: . + dockerfile: Dockerfile + no_cache: true + image: orders-microservice:3.0 + container_name: orders-microservice + ports: + - "5003:5003" + environment: + #DATABASE_URL: "DRIVER={ODBC Driver 17 for SQL Server};SERVER=orders-database,1433;DATABASE=Orders;UID=sa;PWD=WebTechGroup3;" + KAFKA_SERVER: "kafka:9092" + SECRET_KEY: Group3 + networks: + - kafka_network + +networks: + kafka_network: + external: true + #driver: bridge \ No newline at end of file diff --git a/Orders_Microservice_Group3/requirements.txt b/Orders_Microservice_Group3/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..ed705a7c38f2b94cff3180d09f4c4af1e946bc93 Binary files /dev/null and b/Orders_Microservice_Group3/requirements.txt differ diff --git a/Orders_Microservice_Group3/run.py b/Orders_Microservice_Group3/run.py new file mode 100644 index 0000000000000000000000000000000000000000..b26a5df89d454bbe8e2b02f9de58accf3f4b2e9e --- /dev/null +++ b/Orders_Microservice_Group3/run.py @@ -0,0 +1,36 @@ +# from flask import Flask +# from app.__init__ import create_app +# app = Flask(__name__) + +# # Define the route for the root URL "/" +# @app.route('/') +# def home(): +# # This is the view function that returns a response +# return "Welcome to the Flask App!" + +# # Check if this is the script being run as the main program and start the app +# if __name__ == '__main__': +# app.run(debug=True) + +# from flask import Flask +# from flask_cors import CORS + + +# #from app.models import models + +# app = Flask(__name__) +# CORS(app) + +# #db = sqlAlchemy + +# #from app import routes + +# if __name__ == '__main__': +# app.run(debug=True) + +from app import create_app # Adjusted import to reference the app package + +app = create_app() + +if __name__ == '__main__': + app.run(debug=True) \ No newline at end of file diff --git a/Product_MicroService_Group3/Dockerfile b/Product_MicroService_Group3/Dockerfile index e51666e85b66d04d93dd8ed47ffa291991a71152..ae9cf4767b918df94c41cc792e0cbe9f44e04abe 100644 --- a/Product_MicroService_Group3/Dockerfile +++ b/Product_MicroService_Group3/Dockerfile @@ -1,22 +1,65 @@ FROM python:3.11.4 +#Copy requirements COPY requirements.txt /product_ms/ COPY app /product_ms/ +#Set working direcroty WORKDIR /product_ms +#Install dependencies RUN pip install --no-cache-dir -r requirements.txt - +#Install other libraries in container RUN apt-get update && apt-get install -y \ unixodbc \ unixodbc-dev \ + iputils-ping\ + apt-transport-https \ + gcc\ + curl \ + gnupg \ freetds-dev \ tdsodbc \ && rm -rf /var/lib/apt/lists/* -# Set environment variables for ODBC configuration if needed + +#Install ODBC Driver for Microsoft SQL Server Section... +#Source: https://learn.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver16&tabs=debian18-install%2Calpine17-install%2Cdebian8-install%2Credhat7-13-install%2Crhel7-offline + + +#Add Microsoft repository GPG key +RUN curl https://packages.microsoft.com/keys/microsoft.asc | tee /etc/apt/trusted.gpg.d/microsoft.asc + +#Add the Microsoft SQL Server repository for Debian 12 +RUN curl https://packages.microsoft.com/config/debian/12/prod.list | tee /etc/apt/sources.list.d/mssql-release.list + + +#Add Microsoft GPG key +RUN curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft-archive-keyring.gpg + +#Add the Microsoft SQL Server repository for Debian 12 +RUN echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/debian/12/prod bookworm main" > /etc/apt/sources.list.d/mssql-release.list + +#Update package list +RUN apt-get update + +#Install ODBC Driver 17 for SQL Server +RUN ACCEPT_EULA=Y apt-get install -y msodbcsql17 + +#Install mssql-tools +RUN ACCEPT_EULA=Y apt-get install -y mssql-tools +RUN bash -c "echo 'export PATH=\"$PATH:/opt/mssql-tools/bin\"' >> ~/.bashrc && source ~/.bashrc" + + +#RUN echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc && source ~/.bashrc + + +RUN apt-get install -y libgssapi-krb5-2 + + +#Set environment variables for ODBC configuration ENV ODBCINI=/etc/odbc.ini ENV ODBCSYSINI=/etc diff --git a/Product_MicroService_Group3/app/__pycache__/config.cpython-311.pyc b/Product_MicroService_Group3/app/__pycache__/config.cpython-311.pyc index 24ec9fb0533112f07ac3fe8e36eedc6a3f03e748..333b2f6a931f7163b6335d840c3370775f00b552 100644 Binary files a/Product_MicroService_Group3/app/__pycache__/config.cpython-311.pyc and b/Product_MicroService_Group3/app/__pycache__/config.cpython-311.pyc differ diff --git a/Product_MicroService_Group3/app/config.py b/Product_MicroService_Group3/app/config.py index 96b983721bde59aa5a40f74aee24fe6a8c0075ff..dd7fdaa8b3471768c35dcdffd85bc653981bbe2f 100644 --- a/Product_MicroService_Group3/app/config.py +++ b/Product_MicroService_Group3/app/config.py @@ -6,4 +6,6 @@ SECRET_KEY = "Group3" PORT = 5001 -KAFKA_SERVER= "kafka:9092" \ No newline at end of file +KAFKA_SERVER= "kafka:9092" + +HOST = "0.0.0.0" \ No newline at end of file diff --git a/Product_MicroService_Group3/app/controllers/__pycache__/getReviewsController.cpython-311.pyc b/Product_MicroService_Group3/app/controllers/__pycache__/getReviewsController.cpython-311.pyc index 6c78e2bcbd74045dce7a609453092db0728bf0ef..935fb47d5ddd11cd0ad7865e16dcd71b61d40f11 100644 Binary files a/Product_MicroService_Group3/app/controllers/__pycache__/getReviewsController.cpython-311.pyc and b/Product_MicroService_Group3/app/controllers/__pycache__/getReviewsController.cpython-311.pyc differ diff --git a/Product_MicroService_Group3/app/controllers/addReviewController.py b/Product_MicroService_Group3/app/controllers/addReviewController.py index 4f85e35e1ded97b201011a4e17ba815135224f7e..eec24c9468060b0dafb642a881d078867717610b 100644 --- a/Product_MicroService_Group3/app/controllers/addReviewController.py +++ b/Product_MicroService_Group3/app/controllers/addReviewController.py @@ -1,5 +1,6 @@ from flask import Blueprint, jsonify, request, json, session from models.addReview import add_user_review +from config import HOST, PORT import requests @@ -13,20 +14,20 @@ def get_username_from_user_microservice(productID): user_id = session.get("user_id") - if user_id: - if request.method == 'GET': + if user_id: #Check if session is set + if request.method == "GET": product_id = productID - response = requests.post('http://localhost:5000/user/getUsername', json={'id': user_id}) + response = requests.post(f"{HOST}:{PORT}/user/getUsername", json={"id": user_id}) #Send http request to user microservice if response.status_code == 200: - username = response.json()['username'] + username = response.json()["username"] userID = user_id - session['username'] = username - session['productID'] = product_id - + session["username"] = username + session["productID"] = product_id + #Data in json format rating_info = { "UserID" : userID, "ProductID" : product_id, @@ -47,14 +48,14 @@ def get_username_from_user_microservice(productID): - +#Add a product review with the username gotten @add_review_bp.route("/product/<int:productID>/addReview", methods=["POST"]) def add_review(productID): user_id = session.get("user_id") if user_id: - if request.method == 'POST': + if request.method == "POST": data = request.get_json() review = data.get("review") @@ -63,7 +64,7 @@ def add_review(productID): username = session.get('username') product_id = session.get('productID') - if review.strip() != "": + if review.strip() != "": #Check if review is empty if username is None: return {"error": "Username is not available"} @@ -72,6 +73,7 @@ def add_review(productID): if product_id is None: return {"error": "Product ID is not available"} + #Check if rating is valid if isinstance(rating, int) and 1 <= rating <= 5: review_info = { @@ -82,7 +84,7 @@ def add_review(productID): "Username" : username } - user_review_message = add_user_review(review_info) + user_review_message = add_user_review(review_info) #Send data to database return user_review_message else: diff --git a/Product_MicroService_Group3/app/controllers/getProductController.py b/Product_MicroService_Group3/app/controllers/getProductController.py index 88c9f2c6a960befdd847ab73ef534d0bc63b3582..4ae868d3564db74a68ad51b6d1353b49b7196b93 100644 --- a/Product_MicroService_Group3/app/controllers/getProductController.py +++ b/Product_MicroService_Group3/app/controllers/getProductController.py @@ -4,7 +4,7 @@ from models.getProduct import get_product display_product_bp = Blueprint("product",__name__) - +#Display product with a given product ID @display_product_bp.route("/product/<int:productID>", methods=["GET"]) def display_product(productID): @@ -20,7 +20,7 @@ def display_product(productID): # Convert to JSON #json_user_data = json.dumps(user_data) - product, images, reviews = get_product(product_id) #Send user info to database + product, images, reviews = get_product(product_id) #Retrieve product data from database customers = [review_data["CustomerID"] for review_data in reviews] diff --git a/Product_MicroService_Group3/app/controllers/getReviewsController.py b/Product_MicroService_Group3/app/controllers/getReviewsController.py index 3ecdc45c326f26eba4710d3f4a5ef70e696db4a8..f55b5c96337d8eee801f909d605eaf8047b1cb50 100644 --- a/Product_MicroService_Group3/app/controllers/getReviewsController.py +++ b/Product_MicroService_Group3/app/controllers/getReviewsController.py @@ -34,9 +34,11 @@ def get_review(): producer = KafkaProducer(bootstrap_servers = KAFKA_SERVER) def send_review_message(reviews): + #Publish message metadata =producer.send("customer_reviews", json.dumps(reviews).encode("utf_8")) try: record_metadata = metadata.get(timeout=10) + #Print out message for confirmation in logs print("Message sent successfully!") print("Topic:", record_metadata.topic) print("Partition:", record_metadata.partition) diff --git a/Product_MicroService_Group3/app/controllers/productHomeController.py b/Product_MicroService_Group3/app/controllers/productHomeController.py index 4acfaf5e5c3babe95574864bb08424664a0ca925..3ca6998d79233496ea604b30b80a696faacd5bb8 100644 --- a/Product_MicroService_Group3/app/controllers/productHomeController.py +++ b/Product_MicroService_Group3/app/controllers/productHomeController.py @@ -4,7 +4,7 @@ from models.productHome import get_product_by_section product_home_bp = Blueprint("home",__name__) - +#Display products on home page @product_home_bp.route("/product/home", methods=["POST"]) def product_section(): @@ -20,8 +20,7 @@ def product_section(): # Convert to JSON #json_user_data = json.dumps(user_data) - products = get_product_by_section(category_id) #Send user info to database - #customers = [review_data["CustomerID"] for review_data in reviews] + products = get_product_by_section(category_id) #Get products return jsonify({"products" : products, "session" : user_id}) diff --git a/Product_MicroService_Group3/app/controllers/updateProductController.py b/Product_MicroService_Group3/app/controllers/updateProductController.py index 70d31a74e24364a5ac7a0d7f9e90ede23b20d0b3..3b499c406c509602dc438c23fcac63c8c16a293f 100644 --- a/Product_MicroService_Group3/app/controllers/updateProductController.py +++ b/Product_MicroService_Group3/app/controllers/updateProductController.py @@ -7,7 +7,7 @@ import requests update_product_bp = Blueprint("updateProduct",__name__) - +#Update a product @update_product_bp.route("/product/updateProduct", methods=["POST"]) def update_product(): @@ -22,8 +22,10 @@ def update_product(): product_id = data.get("product_id") #username = session.get('username') + #Check if price is int or float if isinstance(data.get("price"), (int, float)): - + + #Check if quantity is int if isinstance(data.get("quantity"), int): info = { "quantity" : quantity, @@ -31,7 +33,7 @@ def update_product(): "product_id" : product_id } - update = update_product_info(info) + update = update_product_info(info) #Send updated info to database if "message" in update: event_data = {"quantity" : quantity, "price" : price, "product_id" : product_id} publish_product_updated_event(event_data) @@ -51,4 +53,4 @@ def update_product(): return {"error" : "null"} else: - return {"error" : "You need to be logged in to add a review"} \ No newline at end of file + return {"error" : "You need to be logged in to update a product"} \ No newline at end of file diff --git a/Product_MicroService_Group3/app/index.py b/Product_MicroService_Group3/app/index.py index 676b86d8db8a3ab0bfc301378d4f1af0abc75a89..fa5047e51ad6fc19afe452700d11fbb17cad79d1 100644 --- a/Product_MicroService_Group3/app/index.py +++ b/Product_MicroService_Group3/app/index.py @@ -3,7 +3,7 @@ from flask_cors import CORS from flask import jsonify -from config import DEBUG, SECRET_KEY, PORT +from config import DEBUG, SECRET_KEY, PORT, HOST import os import requests import subscribers @@ -21,11 +21,24 @@ app = Flask(__name__) CORS(app) -app.secret_key = SECRET_KEY +#Check if applcation is running in docker to get or set app secret key +try: -# Read user microservice URL from environment variable -USER_MICROSERVICE_URL = os.getenv('USER_MICROSERVICE_URL', 'http://127.0.0.1:5000') + #Check for the existence of the /proc/self/cgroup file + with open("/proc/self/cgroup", "r") as cgroup_file: + cgroup_info = cgroup_file.read() + + #Check if the cgroup information contains 'docker' keyword + if 'docker' in cgroup_info: + print("Running inside Docker container") + app.secret_key = os.environ.get('SECRET_KEY') + +except FileNotFoundError: + #If the file doesn't exist + print("Running on a local Windows machine") + + app.secret_key = SECRET_KEY #subscribers.consume_username_updated_event() @@ -55,9 +68,9 @@ app.register_blueprint(update_product_bp) - if __name__ == '__main__': subscribers.start_kafka_consumer() + subscribers.start_price_updated_consumer() - app.run(debug=DEBUG, port=PORT) \ No newline at end of file + app.run(host=HOST, debug=DEBUG, port=PORT) \ No newline at end of file diff --git a/Product_MicroService_Group3/app/models/__pycache__/database_connection.cpython-311.pyc b/Product_MicroService_Group3/app/models/__pycache__/database_connection.cpython-311.pyc index 005839d68435047d092e0b3a8b7c4d965cb7ff3d..faa64fe46341a4279d6dc734d086cc8516d4bdde 100644 Binary files a/Product_MicroService_Group3/app/models/__pycache__/database_connection.cpython-311.pyc and b/Product_MicroService_Group3/app/models/__pycache__/database_connection.cpython-311.pyc differ diff --git a/Product_MicroService_Group3/app/models/__pycache__/updateProductQuantity.cpython-311.pyc b/Product_MicroService_Group3/app/models/__pycache__/updateProductQuantity.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2a2b40c54d9e58ea7ca34020c7460ff6c144c331 Binary files /dev/null and b/Product_MicroService_Group3/app/models/__pycache__/updateProductQuantity.cpython-311.pyc differ diff --git a/Product_MicroService_Group3/app/models/addReview.py b/Product_MicroService_Group3/app/models/addReview.py index 8f67372fa8e1e80281e735675913bf514855c42c..7946a66465f9ac2a21e335ce3831eac071149b76 100644 --- a/Product_MicroService_Group3/app/models/addReview.py +++ b/Product_MicroService_Group3/app/models/addReview.py @@ -6,20 +6,20 @@ from models.database_connection import connect_db def add_user_review(data): - try: #error handling + try: #Error handling connection = connect_db() cursor = connection.cursor() - #insert data into reviews and ratings table + #Insert data into reviews and ratings table insert_user_query = '''INSERT INTO dbo.ReviewsAndRatings (CustomerID, ProductID, Review, rating, Username) VALUES (?, ?, ?, ?, ?);''' cursor.execute(insert_user_query, (data["UserID"], data["ProductID"], data["Review"], data["Rating"], data["Username"])) - #commit changes to database if no errors + #Commit changes to database if no errors connection.commit() return {"message" : "Review added successfully"} diff --git a/Product_MicroService_Group3/app/models/database_connection.py b/Product_MicroService_Group3/app/models/database_connection.py index 0986cea0aafc84365dceb7ca9170ffa9bbcdeb02..4179c446883e7a9a1fd0a3225aec2a2fe30dd538 100644 --- a/Product_MicroService_Group3/app/models/database_connection.py +++ b/Product_MicroService_Group3/app/models/database_connection.py @@ -1,14 +1,25 @@ import pyodbc +import os - +#Code to connect to the database +print("Outside db connection function") #Connect to database def connect_db(): - - server = 'Chiamaka' - database = 'Products' - username = 'CHIAMAKA\amych' - password = '' - - connection_string = f'DRIVER={{SQL Server}};SERVER={server};DATABASE={database};UID={username};PWD={password};Trusted_Connection=yes;' - - return pyodbc.connect(connection_string) \ No newline at end of file + print("In CONNECT DB") + try: + + server = '34.89.83.33' + database = 'productsmanagement' + username = 'sqlserver' + password = 'WebTechGroup3' + driver = 'ODBC Driver 17 for SQL Server' + + connection_string = f'DRIVER={driver};SERVER={server};DATABASE={database};UID={username};PWD={password};Trusted_Connection=no;' + + return pyodbc.connect(connection_string) + + except Exception as e: + # If the file doesn't exist + print("Unexpected error occured {e}") + + return False diff --git a/Product_MicroService_Group3/app/models/updateProduct.py b/Product_MicroService_Group3/app/models/updateProduct.py index 4ad88367ed92d9a0bcd10de2e7b7cc8eab2053c8..90ab9981829572229252560a74a780a0dbe5c820 100644 --- a/Product_MicroService_Group3/app/models/updateProduct.py +++ b/Product_MicroService_Group3/app/models/updateProduct.py @@ -23,7 +23,7 @@ WHERE - #commit changes to database if no errors + #Commit changes to database if no errors connection.commit() return {"message" : "Product updated successfully"} @@ -35,7 +35,7 @@ WHERE except Exception as e: #more error handling print(f"Unexpected error occured in add_review: {e}") - connection.rollback() + connection.rollback() #Rollback if errors return {"Error" : "Unexpected error"} finally: diff --git a/Product_MicroService_Group3/app/models/updateProductQuantity.py b/Product_MicroService_Group3/app/models/updateProductQuantity.py new file mode 100644 index 0000000000000000000000000000000000000000..ff9022bcd01b83ce03f2ac84cad90c4e0fa79159 --- /dev/null +++ b/Product_MicroService_Group3/app/models/updateProductQuantity.py @@ -0,0 +1,47 @@ +import pyodbc +from flask import jsonify +from models.database_connection import connect_db +import logging + + + +def update_product_quantity(data): + + try: #error handling + + connection = connect_db() + cursor = connection.cursor() + print("At the database...") + + #Collect image information from database + #stock_query = "SELECT StockQuantity FROM dbo.ProductCatalog WHERE ProductID = ?" + #cursor.execute(stock_query, data["ProductID"]) + #stock = cursor.fetchall() + + #insert data into reviews and ratings table + update_product_query = '''UPDATE dbo.ProductCatalog SET StockQuantity = StockQuantity - ? WHERE ProductID= ?;''' + cursor.execute(update_product_query, (data["QuantityPurchased"], data["ProductID"])) + + + + #commit changes to database if no errors + connection.commit() + + return {"message" : "Quantity updated successfully"} + #return {"message" : stock} + + except pyodbc.Error as e: #more error handling + print(f"Database error in update_product_quantity: {e}") + connection.rollback() + return {"Error" : "Database error"} + + except Exception as e: #more error handling + print(f"Unexpected error occured in update_product_quantity: {e}") + connection.rollback() + return {"Error" : "Unexpected error"} + + finally: + if cursor: + cursor.close() + if connection: + connection.close() \ No newline at end of file diff --git a/Product_MicroService_Group3/app/publishers/__pycache__/kafkaPublishers.cpython-311.pyc b/Product_MicroService_Group3/app/publishers/__pycache__/kafkaPublishers.cpython-311.pyc index 0dc8ca0740bb9e53a5d54a2c55ad96fb36f17dbe..ffc53d552abe70db16ea1ef55d9e215d78d8c986 100644 Binary files a/Product_MicroService_Group3/app/publishers/__pycache__/kafkaPublishers.cpython-311.pyc and b/Product_MicroService_Group3/app/publishers/__pycache__/kafkaPublishers.cpython-311.pyc differ diff --git a/Product_MicroService_Group3/app/publishers/kafkaPublishers.py b/Product_MicroService_Group3/app/publishers/kafkaPublishers.py index d03504a620a6397896bf554fed530ac58c48af2e..f316dd3b5217e3f5b459f2bab1cdbbac3d1fc549 100644 --- a/Product_MicroService_Group3/app/publishers/kafkaPublishers.py +++ b/Product_MicroService_Group3/app/publishers/kafkaPublishers.py @@ -9,30 +9,30 @@ producer = KafkaProducer(bootstrap_servers=KAFKA_SERVER) #Creates the topic def create_product_updated_topic(): - # Create KafkaAdminClient instance + admin_client = KafkaAdminClient(bootstrap_servers=KAFKA_SERVER) - # Define the topic name and configuration + #Set topic name topic_name = "product_updated_topic" num_partitions = 1 replication_factor = 1 - # Retrieve the list of existing topics + #Get topics topic_metadata = admin_client.list_topics() - # Check if the topic already exists + #Check if the topic already exists if topic_name not in topic_metadata: - # Define the configuration for the new topic + new_topic = NewTopic(name=topic_name, num_partitions=num_partitions, replication_factor=replication_factor) - # Create the new topic + admin_client.create_topics(new_topics=[new_topic], validate_only=False) #Function is called in updateProfileControllers.py def publish_product_updated_event(event_data): - # Serialize the event data to JSON + event_json = json.dumps(event_data) - # Publish the event to the Kafka topic + #Publish the event data_to_send = producer.send("product_updated_topic", value=event_json.encode("utf-8")) try: record_metadata = data_to_send.get(timeout=10) diff --git a/Product_MicroService_Group3/app/subscribers/UpdateProductQuantitySubscriber.py b/Product_MicroService_Group3/app/subscribers/UpdateProductQuantitySubscriber.py new file mode 100644 index 0000000000000000000000000000000000000000..f8c86deed5697e9a9bcb047c7bd9f09648f5eb44 --- /dev/null +++ b/Product_MicroService_Group3/app/subscribers/UpdateProductQuantitySubscriber.py @@ -0,0 +1,32 @@ +from kafka import KafkaConsumer + +from config import KAFKA_SERVER + +from models.updateProductQuantity import update_product_quantity + +from threading import Thread + + +import json +import logging + +#This function is called in the "__init__.py" file in the "subscribers" folder +def consume_product_price_updated_event(): + #Logging information + logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + logging.info("Consuming product price updated event...") + consumer = KafkaConsumer("QuantityUpdateFromOrders", bootstrap_servers=KAFKA_SERVER) + for message in consumer: + product_data_str = message.value.decode("utf-8") + product_data = json.loads(product_data_str) + print("I am here") + print(product_data) + update_product_quantity(product_data) + +def start_price_updated_consumer(): + logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + logging.info("Starting Kafka price updated consumer...") + print("Hello from kafka consumer") + kafka_thread = Thread(target=consume_product_price_updated_event) + kafka_thread.daemon = True #Threading to avoid blocking of the Flask server logs + kafka_thread.start() \ No newline at end of file diff --git a/Product_MicroService_Group3/app/subscribers/__init__.py b/Product_MicroService_Group3/app/subscribers/__init__.py index 377b837184694cf1acc74fa09e655f651327ffb0..2e03bd03782b463fc105c86b11404eb2a07fdcdf 100644 --- a/Product_MicroService_Group3/app/subscribers/__init__.py +++ b/Product_MicroService_Group3/app/subscribers/__init__.py @@ -1,5 +1,10 @@ print("Subscribers package initialized") -from subscribers.updateUsernameSubscriber import consume_username_updated_event -from subscribers.updateUsernameSubscriber import start_kafka_consumer \ No newline at end of file +from subscribers.updateUsernameSubscriber import start_kafka_consumer + +from subscribers.UpdateProductQuantitySubscriber import start_price_updated_consumer + +from subscribers.UpdateProductQuantitySubscriber import consume_product_price_updated_event + +from subscribers.updateUsernameSubscriber import consume_username_updated_event \ No newline at end of file diff --git a/Product_MicroService_Group3/app/subscribers/__pycache__/UpdateProductQuantitySubscriber.cpython-311.pyc b/Product_MicroService_Group3/app/subscribers/__pycache__/UpdateProductQuantitySubscriber.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4071d3b04bf8e013fb60c7483a77dbdb9b06a94d Binary files /dev/null and b/Product_MicroService_Group3/app/subscribers/__pycache__/UpdateProductQuantitySubscriber.cpython-311.pyc differ diff --git a/Product_MicroService_Group3/app/subscribers/__pycache__/__init__.cpython-311.pyc b/Product_MicroService_Group3/app/subscribers/__pycache__/__init__.cpython-311.pyc index 3983da2bfc76957fedcc9a12c59acbeee1d68af6..64318b1c819099d356bc3ebd32e9e922a9efef7f 100644 Binary files a/Product_MicroService_Group3/app/subscribers/__pycache__/__init__.cpython-311.pyc and b/Product_MicroService_Group3/app/subscribers/__pycache__/__init__.cpython-311.pyc differ diff --git a/Product_MicroService_Group3/app/subscribers/updateUsernameSubscriber.py b/Product_MicroService_Group3/app/subscribers/updateUsernameSubscriber.py index 4235a17f9f2c3b3673d1cc139fc1ebf9cb9001d4..7f020bb2081ecac43296af10a4bc896bc5cebf90 100644 --- a/Product_MicroService_Group3/app/subscribers/updateUsernameSubscriber.py +++ b/Product_MicroService_Group3/app/subscribers/updateUsernameSubscriber.py @@ -15,7 +15,7 @@ def consume_username_updated_event(): logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') logging.info("Consuming username updated event...") consumer = KafkaConsumer("profile_updated_topic", bootstrap_servers=KAFKA_SERVER) - for message in consumer: + for message in consumer: #Consume data profile_data_str = message.value.decode("utf-8") profile_data = json.loads(profile_data_str) print("I am here") @@ -27,8 +27,5 @@ def start_kafka_consumer(): logging.info("Starting Kafka consumer...") print("Hello from kafka consumer") kafka_thread = Thread(target=consume_username_updated_event) - kafka_thread.daemon = True # Daemonize the thread so it will be automatically killed when the main thread exits - kafka_thread.start() - -# Call the start_kafka_consumer function to start the Kafka consumer thread - \ No newline at end of file + kafka_thread.daemon = True #Threading to avoid blocking of the Flask server logs + kafka_thread.start() \ No newline at end of file diff --git a/Product_MicroService_Group3/docker-compose.yml b/Product_MicroService_Group3/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..84a862bed0a07e7bedadf9f87de44260253ffeab --- /dev/null +++ b/Product_MicroService_Group3/docker-compose.yml @@ -0,0 +1,24 @@ +version: "3.2" + +services: + + product-microservice: + build: + context: . + dockerfile: Dockerfile + no_cache: true + image: product-microservice:3.0 + container_name: product-microservice + ports: + - "5001:5001" + environment: + #DATABASE_URL: "DRIVER={ODBC Driver 17 for SQL Server};SERVER=product-database,1433;DATABASE=Products;UID=sa;PWD=WebTechGroup3;" + KAFKA_SERVER: "kafka:9092" + SECRET_KEY: Group3 + networks: + - kafka_network + +networks: + kafka_network: + external: true + #driver: bridge \ No newline at end of file diff --git a/User_MicroService_Group3/Dockerfile b/User_MicroService_Group3/Dockerfile index 3e7d584f9207328c2b4ae5b319bc506fcd1394da..7148b367d2da835dedfe208b5aec5590b27bc048 100644 --- a/User_MicroService_Group3/Dockerfile +++ b/User_MicroService_Group3/Dockerfile @@ -1,22 +1,65 @@ FROM python:3.11.4 +#Copy requirements COPY requirements.txt /user_ms/ COPY app /user_ms/ +#Set working direcroty WORKDIR /user_ms +#Install dependencies RUN pip install --no-cache-dir -r requirements.txt +#Install other libraries in container RUN apt-get update && apt-get install -y \ unixodbc \ unixodbc-dev \ + iputils-ping\ + apt-transport-https \ + gcc\ + curl \ + gnupg \ freetds-dev \ tdsodbc \ && rm -rf /var/lib/apt/lists/* -# Set environment variables for ODBC configuration if needed + +#Install ODBC Driver for Microsoft SQL Server Section... +#Source: https://learn.microsoft.com/en-us/sql/connect/odbc/linux-mac/installing-the-microsoft-odbc-driver-for-sql-server?view=sql-server-ver16&tabs=debian18-install%2Calpine17-install%2Cdebian8-install%2Credhat7-13-install%2Crhel7-offline + + +#Add Microsoft repository GPG key +RUN curl https://packages.microsoft.com/keys/microsoft.asc | tee /etc/apt/trusted.gpg.d/microsoft.asc + +#Add the Microsoft SQL Server repository for Debian 12 +RUN curl https://packages.microsoft.com/config/debian/12/prod.list | tee /etc/apt/sources.list.d/mssql-release.list + + +#Add Microsoft GPG key +RUN curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft-archive-keyring.gpg + +#Add the Microsoft SQL Server repository for Debian 12 +RUN echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/debian/12/prod bookworm main" > /etc/apt/sources.list.d/mssql-release.list + +#Update package list +RUN apt-get update + +#Install ODBC Driver 17 for SQL Server +RUN ACCEPT_EULA=Y apt-get install -y msodbcsql17 + +#Install mssql-tools +RUN ACCEPT_EULA=Y apt-get install -y mssql-tools +RUN bash -c "echo 'export PATH=\"$PATH:/opt/mssql-tools/bin\"' >> ~/.bashrc && source ~/.bashrc" + + +#RUN echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc && source ~/.bashrc + +#Install unixodbc-dev and kerberos library +RUN apt-get install -y libgssapi-krb5-2 + +#Set environment variables for ODBC configuration ENV ODBCINI=/etc/odbc.ini ENV ODBCSYSINI=/etc diff --git a/User_MicroService_Group3/Dockerfile.userdb b/User_MicroService_Group3/Dockerfile.userdb new file mode 100644 index 0000000000000000000000000000000000000000..c7d3ab70d82a45909512cf4bc2a09936ade8c4ad --- /dev/null +++ b/User_MicroService_Group3/Dockerfile.userdb @@ -0,0 +1,27 @@ +FROM mcr.microsoft.com/mssql/server:2022-latest + +# Set environment variables +ENV SA_PASSWORD WebTechGroup3 +ENV ACCEPT_EULA=Y + +COPY User_Management.bak /var/opt/mssql/data/ + + + + +# Change user permissions +USER root + +RUN chown -R mssql:mssql /var/opt/mssql/data + +COPY entrypoint.sh /usr/local/bin/entrypoint.sh + + +# Expose the default SQL Server port +EXPOSE 1433 + +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] + + +# Define the default command to run when the container starts +CMD ["/opt/mssql/bin/sqlservr"] \ No newline at end of file diff --git a/User_MicroService_Group3/Dockerfile.userms b/User_MicroService_Group3/Dockerfile.userms new file mode 100644 index 0000000000000000000000000000000000000000..3e73e5fab859fa6eaf160356db11b952ca22c20a --- /dev/null +++ b/User_MicroService_Group3/Dockerfile.userms @@ -0,0 +1,62 @@ +FROM python:3.11.4 + +#Copy requirements +COPY requirements.txt /user_ms/ + +COPY app /user_ms/ + +#Set working direcroty +WORKDIR /user_ms + +#Install dependencies +RUN pip install --no-cache-dir -r requirements.txt + + +#Install other libraries in container +RUN apt-get update && apt-get install -y \ + unixodbc \ + unixodbc-dev \ + iputils-ping\ + apt-transport-https \ + gcc\ + curl \ + gnupg \ + freetds-dev \ + tdsodbc \ + && rm -rf /var/lib/apt/lists/* + + +#Add Microsoft repository GPG key +RUN curl https://packages.microsoft.com/keys/microsoft.asc | tee /etc/apt/trusted.gpg.d/microsoft.asc + +#Add the Microsoft SQL Server repository for Debian 12 +RUN curl https://packages.microsoft.com/config/debian/12/prod.list | tee /etc/apt/sources.list.d/mssql-release.list + + +#Add Microsoft GPG key +RUN curl -fsSL https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /usr/share/keyrings/microsoft-archive-keyring.gpg + +#Add the Microsoft SQL Server repository for Debian 12 +RUN echo "deb [arch=amd64 signed-by=/usr/share/keyrings/microsoft-archive-keyring.gpg] https://packages.microsoft.com/debian/12/prod bookworm main" > /etc/apt/sources.list.d/mssql-release.list + +#Update package list +RUN apt-get update + +#Install ODBC Driver 17 for SQL Server +RUN ACCEPT_EULA=Y apt-get install -y msodbcsql17 + +#Optional: Install mssql-tools and add to PATH +RUN ACCEPT_EULA=Y apt-get install -y mssql-tools +RUN bash -c "echo 'export PATH=\"$PATH:/opt/mssql-tools/bin\"' >> ~/.bashrc && source ~/.bashrc" + + +#RUN echo 'export PATH="$PATH:/opt/mssql-tools/bin"' >> ~/.bashrc && source ~/.bashrc + +#Optional: Install unixodbc-dev and kerberos library +RUN apt-get install -y libgssapi-krb5-2 + +#Set environment variables for ODBC configuration if needed +ENV ODBCINI=/etc/odbc.ini +ENV ODBCSYSINI=/etc + +CMD ["python", "index.py"] \ No newline at end of file diff --git a/User_MicroService_Group3/app/__pycache__/config.cpython-311.pyc b/User_MicroService_Group3/app/__pycache__/config.cpython-311.pyc index 105819a92eef07108a2fb4558af6e0b1ad972654..091813a29718206a89d166b99bce03396b05d34c 100644 Binary files a/User_MicroService_Group3/app/__pycache__/config.cpython-311.pyc and b/User_MicroService_Group3/app/__pycache__/config.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/config.py b/User_MicroService_Group3/app/config.py index 3217efec4a6a96ac59bc14a1aef3f74470c83e1a..0405b913851c12c81c205bd6e66277fba2a0278a 100644 --- a/User_MicroService_Group3/app/config.py +++ b/User_MicroService_Group3/app/config.py @@ -6,4 +6,6 @@ SECRET_KEY = "Group3" PORT = 5000 -KAFKA_SERVER = "kafka:9092" \ No newline at end of file +KAFKA_SERVER = "kafka:9092" + +HOST = "0.0.0.0" \ No newline at end of file diff --git a/User_MicroService_Group3/app/controllers/__pycache__/showReviewsController.cpython-311.pyc b/User_MicroService_Group3/app/controllers/__pycache__/showReviewsController.cpython-311.pyc index d12a61ce3d85861ca4a71b737865e07616283d1c..51d9ad122d7784716c744cb4a8122c6254d70f40 100644 Binary files a/User_MicroService_Group3/app/controllers/__pycache__/showReviewsController.cpython-311.pyc and b/User_MicroService_Group3/app/controllers/__pycache__/showReviewsController.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/controllers/logoutController.py b/User_MicroService_Group3/app/controllers/logoutController.py index 241e00d337582cf50f06867c0fb7aeb36c401486..e296c4dae0172fed0af189b5005cfbef8d1a021c 100644 --- a/User_MicroService_Group3/app/controllers/logoutController.py +++ b/User_MicroService_Group3/app/controllers/logoutController.py @@ -10,7 +10,7 @@ def logout(): user_id = session.get("user_id") #get session data - if user_id: #if user is logges in + if user_id: #if user is logged in if request.method == 'POST': diff --git a/User_MicroService_Group3/app/controllers/showReviewsController.py b/User_MicroService_Group3/app/controllers/showReviewsController.py index 924ede66f969d1ac4ce0e76b9d1f78e2de506249..42d853475f2622e476bcb9ca5ed87e0c8d58e472 100644 --- a/User_MicroService_Group3/app/controllers/showReviewsController.py +++ b/User_MicroService_Group3/app/controllers/showReviewsController.py @@ -5,47 +5,48 @@ from config import KAFKA_SERVER show_reviews_bp = Blueprint("showReviews", __name__) -# Kafka consumer configuration + consumer_conf = { "bootstrap_servers": KAFKA_SERVER, - "group_id": "show_reviews_group", # Specify a unique group ID for this consumer - "auto_offset_reset": "earliest" # Start consuming from the beginning of the topic + "group_id": "show_reviews_group", + "auto_offset_reset": "earliest" } -# Function to consume reviews published by product microservice +#Function to consume reviews published by product microservice def consume_reviews(num_reviews=1): consumer = KafkaConsumer("customer_reviews", **consumer_conf) reviews = [] - # Iterate over messages received by the consumer + for message in consumer: - # Decode the message from bytes to a string using UTF-8 encoding + review_data_str = message.value.decode("utf-8") - # Parse the JSON-encoded message to extract the review data + review_data = json.loads(review_data_str) - # Append the review data to the list of reviews + reviews.append(review_data) - # Exit the loop after consuming the specified number of reviews + if len(reviews) >= num_reviews: break - # Close the consumer to release resources + consumer.close() return reviews -# Route to show reviews for a user + +#Route to show reviews for a user @show_reviews_bp.route("/user/showReviews", methods=["POST"]) def show_reviews(): - # Collect user id from session + #Collect user id from session user_id = session.get("user_id") if user_id: # Check if user is logged in if request.method == 'POST': - # Call function to consume reviews + #Call function to consume reviews reviews = consume_reviews() return jsonify(reviews) diff --git a/User_MicroService_Group3/app/index.py b/User_MicroService_Group3/app/index.py index a22a973629796e6f93ba702e7f1c7aeeb60947fb..2ee61fb8d8d7cd0d715eb4c479b74b65da147209 100644 --- a/User_MicroService_Group3/app/index.py +++ b/User_MicroService_Group3/app/index.py @@ -10,7 +10,7 @@ from controllers.logoutController import logout_bp from controllers.fetchUsernameController import fetch_username_bp from controllers.showReviewsController import show_reviews_bp -from config import DEBUG, SECRET_KEY, PORT +from config import DEBUG, SECRET_KEY, PORT, HOST import os import requests import publishers @@ -20,7 +20,6 @@ import publishers app = Flask(__name__) CORS(app) -app.secret_key = SECRET_KEY @app.route('/') def index(): @@ -51,6 +50,23 @@ app.register_blueprint(show_reviews_bp) publishers.create_profile_updated_topic() +#Check if application is running in docker to collect or set secret key +try: + + #Check for the existence of the /proc/self/cgroup file + with open("/proc/self/cgroup", "r") as cgroup_file: + cgroup_info = cgroup_file.read() + + #Check if the cgroup information contains 'docker' keyword + if 'docker' in cgroup_info: + print("Running inside Docker container") + app.secret_key = os.environ.get('SECRET_KEY') + +except FileNotFoundError: + # If the file doesn't exist + print("Running on a local Windows machine") + + app.secret_key = SECRET_KEY @app.route("/userIDs", methods=["POST"]) @@ -61,4 +77,4 @@ def userIDs(): return 'hi' if __name__ == '__main__': - app.run(debug=DEBUG, port=PORT) \ No newline at end of file + app.run(host=HOST, debug=DEBUG, port=PORT) \ No newline at end of file diff --git a/User_MicroService_Group3/app/models/__pycache__/database_connection.cpython-311.pyc b/User_MicroService_Group3/app/models/__pycache__/database_connection.cpython-311.pyc index ee658c793e4a505d7b70e54b3c82b148e4f6561e..d54224fd06a5210d5b85dad1790380d510110bd7 100644 Binary files a/User_MicroService_Group3/app/models/__pycache__/database_connection.cpython-311.pyc and b/User_MicroService_Group3/app/models/__pycache__/database_connection.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/models/__pycache__/login.cpython-311.pyc b/User_MicroService_Group3/app/models/__pycache__/login.cpython-311.pyc index 54f0eb17fc5e099f4eaf6dcc17f1775c07a610e1..b77602b227dc0fa7d1be7ce1a7e1b0b2398dca15 100644 Binary files a/User_MicroService_Group3/app/models/__pycache__/login.cpython-311.pyc and b/User_MicroService_Group3/app/models/__pycache__/login.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/models/changePassword.py b/User_MicroService_Group3/app/models/changePassword.py index 7cb162b6b303c4a857ba5c7e398741dd48aed1f3..3e7403d667fa12de9e122853552fb14da27210a1 100644 --- a/User_MicroService_Group3/app/models/changePassword.py +++ b/User_MicroService_Group3/app/models/changePassword.py @@ -16,7 +16,7 @@ def check_old_password(data): - # Check if the email already exists + #Check if the email already exists email_check_query = "SELECT COUNT(*) FROM dbo.User_table WHERE UserID = ?" cursor.execute(email_check_query, user_id) #executes the query count = cursor.fetchone()[0] diff --git a/User_MicroService_Group3/app/models/database_connection.py b/User_MicroService_Group3/app/models/database_connection.py index 8bad5678aab1e99f1324fa173b69a9a281ee1c74..945742875f3b666e8427edc3e662536a8e6ace70 100644 --- a/User_MicroService_Group3/app/models/database_connection.py +++ b/User_MicroService_Group3/app/models/database_connection.py @@ -1,14 +1,24 @@ import pyodbc +import os - +print("Outside db connection function") #Connect to database def connect_db(): - - server = 'Chiamaka' - database = 'User_Management' - username = 'CHIAMAKA\amych' - password = '' - - connection_string = f'DRIVER={{SQL Server}};SERVER={server};DATABASE={database};UID={username};PWD={password};Trusted_Connection=yes;' - - return pyodbc.connect(connection_string) \ No newline at end of file + print("In CONNECT DB") + try: + + server = "35.197.217.212" + database = "userdatabase" + username = "sqlserver" + password = "WebTechGroup3" + driver = "ODBC Driver 17 for SQL Server" + + connection_string = f"DRIVER={driver};SERVER={server};DATABASE={database};UID={username};PWD={password};Trusted_Connection=no;" + + return pyodbc.connect(connection_string) + + except Exception as e: + # If the file doesn't exist + print("Unexpected error occured {e}") + + return False diff --git a/User_MicroService_Group3/app/models/fetchUsername.py b/User_MicroService_Group3/app/models/fetchUsername.py index 437cac41ebc8b863c7bae101dfa15e2b60092046..98c4cb135760391159dec56757fa7d2ddf267c88 100644 --- a/User_MicroService_Group3/app/models/fetchUsername.py +++ b/User_MicroService_Group3/app/models/fetchUsername.py @@ -16,7 +16,7 @@ def get_username(id): row = cursor.fetchone() #fetch data if row: - return row[0] # Assuming the username is in the first column + return row[0] else: return None @@ -29,7 +29,7 @@ def get_username(id): return None except Exception as e: #error handling - print(f"Unexpected error occured in fetch_user_info: {e}") + print(f"Unexpected error occured in get_username: {e}") return None finally: diff --git a/User_MicroService_Group3/app/models/login.py b/User_MicroService_Group3/app/models/login.py index 6e344a768e8205c00e6d40e93df07c27e1deb941..f790b89365c7182f6c5345ca70a2a2436fa36c95 100644 --- a/User_MicroService_Group3/app/models/login.py +++ b/User_MicroService_Group3/app/models/login.py @@ -7,7 +7,7 @@ from models.database_connection import connect_db def fetch_user(email): try: #error handling - + print("In FETCH USER") connection = connect_db() cursor = connection.cursor() @@ -31,16 +31,12 @@ def fetch_user(email): print(f"Unexpected error occured in fetch_user: {e}") return None - finally: - if cursor: - cursor.close() - if connection: - connection.close() - + +#Fetch user's password def fetch_password(email): try: @@ -73,8 +69,4 @@ def fetch_password(email): print(f"Unexpected error occured in fetch_user: {e}") return None - finally: - if cursor: - cursor.close() - if connection: - connection.close() \ No newline at end of file + \ No newline at end of file diff --git a/User_MicroService_Group3/app/models/signup.py b/User_MicroService_Group3/app/models/signup.py index 714741338ac7dfc19add5029d5813f105dbeae6d..63031efd240aead253b9dfcab8b2ac556cd55b36 100644 --- a/User_MicroService_Group3/app/models/signup.py +++ b/User_MicroService_Group3/app/models/signup.py @@ -4,7 +4,7 @@ import pyodbc from models.database_connection import connect_db - +#Create a new user def new_user(data): try: #error handling @@ -38,12 +38,6 @@ VALUES (?, ?, ?, ?, ?, ?, ?);''' insert_authinfo_query = '''INSERT INTO dbo.AuthInfo (PasswordHash, PasswordSalt, Iterations, TempPlainText, UserID) VALUES (?, ?, ?, ?, ?);''' cursor.execute(insert_authinfo_query, (data["hash"], data["salt"], data["iterations"], data["password"], UserID)) - - - - - - diff --git a/User_MicroService_Group3/app/publishers/__pycache__/kafkaPublishers.cpython-311.pyc b/User_MicroService_Group3/app/publishers/__pycache__/kafkaPublishers.cpython-311.pyc index f298ced069dad7e7a1242f27879c71831e59724d..ab88574606876c33991d2d1f9cf5c27a3e73f26b 100644 Binary files a/User_MicroService_Group3/app/publishers/__pycache__/kafkaPublishers.cpython-311.pyc and b/User_MicroService_Group3/app/publishers/__pycache__/kafkaPublishers.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/publishers/kafkaPublishers.py b/User_MicroService_Group3/app/publishers/kafkaPublishers.py index 7d41460ea71816937142c157fa6180cc964b4c25..279b149bd37ce8f3c30a1c38c406d8eb1bf06048 100644 --- a/User_MicroService_Group3/app/publishers/kafkaPublishers.py +++ b/User_MicroService_Group3/app/publishers/kafkaPublishers.py @@ -10,31 +10,31 @@ producer = KafkaProducer(bootstrap_servers=KAFKA_SERVER) #Creates the topic def create_profile_updated_topic(): - # Create KafkaAdminClient instance + admin_client = KafkaAdminClient(bootstrap_servers=KAFKA_SERVER) - # Define the topic name and configuration + # Define the topic name topic_name = "profile_updated_topic" num_partitions = 1 replication_factor = 1 - # Retrieve the list of existing topics + #Get topics topic_metadata = admin_client.list_topics() - # Check if the topic already exists + #Check if the topic exists if topic_name not in topic_metadata: - # Define the configuration for the new topic + new_topic = NewTopic(name=topic_name, num_partitions=num_partitions, replication_factor=replication_factor) - # Create the new topic + admin_client.create_topics(new_topics=[new_topic], validate_only=False) #Function is called in updateProfileControllers.py #Topic message is collected from ProductMicroservice/Subsribers/updateUsernameSubscriber.py def publish_username_updated_event(event_data): - # Serialize the event data to JSON + event_json = json.dumps(event_data) - # Publish the event to the Kafka topic + #Publish the event to the Kafka topic data_to_send = producer.send("profile_updated_topic", value=event_json.encode("utf-8")) try: record_metadata = data_to_send.get(timeout=10) diff --git a/User_MicroService_Group3/docker-compose.yml b/User_MicroService_Group3/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..8ff3f2bcf3113452668a674fa0aea07c9c7f5cb7 --- /dev/null +++ b/User_MicroService_Group3/docker-compose.yml @@ -0,0 +1,24 @@ +version: "3.2" + +services: + + user-microservice: + build: + context: . + dockerfile: Dockerfile + no_cache: true + image: user-microservice:3.0 + container_name: user-microservice + ports: + - "5000:5000" + environment: + #DATABASE_URL: "DRIVER={ODBC Driver 17 for SQL Server};SERVER=user-database,1433;DATABASE=User_Management;UID=sa;PWD=WebTechGroup3;" + KAFKA_SERVER: "kafka:9092" + SECRET_KEY: Group3 + networks: + - kafka_network + +networks: + kafka_network: + external: true + #driver: bridge \ No newline at end of file diff --git a/User_MicroService_Group3/entrypoint.sh b/User_MicroService_Group3/entrypoint.sh new file mode 100644 index 0000000000000000000000000000000000000000..268cd4a00310bd1e5a014d92a84e48a52a1cb1ad --- /dev/null +++ b/User_MicroService_Group3/entrypoint.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# Start SQL Server in the background +/opt/mssql/bin/sqlservr & + +# Wait for 5 minutes (300 seconds) +sleep 300 + +# Log in and restore database +/opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P "$SA_PASSWORD" -Q "RESTORE DATABASE User_Management FROM DISK = '/var/opt/mssql/data/User_Management.bak' WITH MOVE 'User_Management.mdf' TO '/var/opt/mssql/data/User_Management.mdf', MOVE 'User_Management_log.ldf' TO '/var/opt/mssql/data/User_Management_log.ldf';" + +# Keep the script running indefinitely +wait %1 diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000000000000000000000000000000000..ed16d574aa7b320109a1b0ae8e05aea21cc7e514 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,84 @@ +version: "3.2" + +services: + + # Orders microservice + orders-microservice: + image: amychude/group3:orders-microservice-3.0 + container_name: orders-microservice-3.0 + ports: + - "5003:5003" + environment: + #DATABASE_URL: "DRIVER={ODBC Driver 17 for SQL Server};SERVER=orders-database-2.0,1433;DATABASE=Orders;UID=sa;PWD=WebTechGroup3;" + KAFKA_SERVER: "kafka:9092" + SECRET_KEY: "Group3" + depends_on: + - kafka + networks: + - kafka_network + command: > + /bin/bash -c "sleep 60 && python index.py" + + + # Product microservice + product-microservice: + image: amychude/group3:product-microservice-3.0 + container_name: product-microservice-3.0 + ports: + - "5001:5001" + environment: + #DATABASE_URL: "DRIVER={ODBC Driver 17 for SQL Server};SERVER=product-database-2.0,1433;DATABASE=Products;UID=sa;PWD=WebTechGroup3;" + KAFKA_SERVER: "kafka:9092" + SECRET_KEY: "Group3" + depends_on: + - kafka + networks: + - kafka_network + command: > + /bin/bash -c "sleep 60 && python index.py" + + + # User microservice + user-microservice: + image: amychude/group3:user-microservice-3.0 + container_name: user-microservice-3.0 + ports: + - "5000:5000" + environment: + #DATABASE_URL: "DRIVER={ODBC Driver 17 for SQL Server};SERVER=user-database-2.0,1433;DATABASE=User_Management;UID=sa;PWD=WebTechGroup3;" + KAFKA_SERVER: "kafka:9092" + SECRET_KEY: "Group3" + depends_on: + - kafka + networks: + - kafka_network + command: > + /bin/bash -c "sleep 60 && python index.py" + + # Kafka service + kafka: + image: amychude/group3:kafka-2.0 + container_name: kafka-2.0 + ports: + - "9092:9092" + environment: + KAFKA_ADVERTISED_HOST_NAME: kafka + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_BOOTSTRAP_SEVERS: kafka:9092 + depends_on: + - zookeeper + networks: + - kafka_network + + # Zookeeper service + zookeeper: + image: amychude/group3:zookeeper-2.0 + container_name: zookeeper-2.0 + ports: + - "2181:2181" + networks: + - kafka_network + +networks: + kafka_network: + driver: bridge