diff --git a/Product_MicroService_Group3/app/__pycache__/config.cpython-311.pyc b/Product_MicroService_Group3/app/__pycache__/config.cpython-311.pyc index f5dbbfc7766e0722ceb9c91c99c512ea13f2f156..24ec9fb0533112f07ac3fe8e36eedc6a3f03e748 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 c00614d76e76bd4f038472b1cffe8ef34b2415d7..5ddee9138343724688ae8453ce6e8591c11b39c4 100644 --- a/Product_MicroService_Group3/app/config.py +++ b/Product_MicroService_Group3/app/config.py @@ -4,4 +4,6 @@ import os DEBUG = True SECRET_KEY = "Group3" -PORT = 5001 \ No newline at end of file +PORT = 5001 + +KAFKA_SERVER= "localhost:9092" \ No newline at end of file diff --git a/Product_MicroService_Group3/app/controllers/__pycache__/getReviews.cpython-311.pyc b/Product_MicroService_Group3/app/controllers/__pycache__/getReviews.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5ccdb50ab470f1f972531278c526c941297e5bd9 Binary files /dev/null and b/Product_MicroService_Group3/app/controllers/__pycache__/getReviews.cpython-311.pyc differ diff --git a/Product_MicroService_Group3/app/controllers/getReviews.py b/Product_MicroService_Group3/app/controllers/getReviews.py new file mode 100644 index 0000000000000000000000000000000000000000..87f4f38ae4126f9f2fa914984b3e65a4ee33d794 --- /dev/null +++ b/Product_MicroService_Group3/app/controllers/getReviews.py @@ -0,0 +1,44 @@ +from flask import Blueprint, jsonify, request, json, session +from models.getReviews import get_reviews + +from kafka import KafkaProducer + + +get_review_bp = Blueprint("getReview",__name__) + +@get_review_bp.route("/product/getReview", methods=["POST"]) +def get_review(): + + user_id = session.get("user_id") + + if user_id: + if request.method == 'POST': + + #data = request.get_json() + #review = data.get("review") + + get_reviews_by_user = get_reviews(user_id) + + send_review_message(get_reviews_by_user) + + return({"reviews" : get_reviews_by_user}) + + else: + return {"error" : "null"} + + else: + return {"error" : "You need to be logged in to add a review"} + + +producer = KafkaProducer(bootstrap_servers = "localhost:9092") + +def send_review_message(reviews): + metadata =producer.send("customer_reviews", json.dumps(reviews).encode("utf_8")) + try: + record_metadata = metadata.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) \ No newline at end of file diff --git a/Product_MicroService_Group3/app/events/__init__.py b/Product_MicroService_Group3/app/events/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/Product_MicroService_Group3/app/index.py b/Product_MicroService_Group3/app/index.py index 9f0267b580f95986bae3f617d8c23bbf4ddf994f..79afca3d4836e65a64c37d47fb3141e77acb26ea 100644 --- a/Product_MicroService_Group3/app/index.py +++ b/Product_MicroService_Group3/app/index.py @@ -6,10 +6,12 @@ from flask import jsonify from config import DEBUG, SECRET_KEY, PORT import os import requests +import subscribers from controllers.getProductController import display_product_bp from controllers.addReviewController import add_review_bp from controllers.productHomeController import product_home_bp +from controllers.getReviews import get_review_bp @@ -24,6 +26,9 @@ 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') + +#subscribers.consume_username_updated_event() + @app.route('/product', methods=['POST']) def get_session_id(): session_id = session.get('user_id') @@ -43,8 +48,14 @@ def index(): app.register_blueprint(display_product_bp) app.register_blueprint(add_review_bp) app.register_blueprint(product_home_bp) +app.register_blueprint(get_review_bp) + + if __name__ == '__main__': + + subscribers.start_kafka_consumer() + app.run(debug=DEBUG, port=PORT) \ No newline at end of file diff --git a/Product_MicroService_Group3/app/models/__pycache__/getReviews.cpython-311.pyc b/Product_MicroService_Group3/app/models/__pycache__/getReviews.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4ccf1bdd0dc2a3f16681b83ed686dd3e96875123 Binary files /dev/null and b/Product_MicroService_Group3/app/models/__pycache__/getReviews.cpython-311.pyc differ diff --git a/Product_MicroService_Group3/app/models/__pycache__/updateUsername.cpython-311.pyc b/Product_MicroService_Group3/app/models/__pycache__/updateUsername.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d55720a3e7872e81ac56a1facc02ba57c682325d Binary files /dev/null and b/Product_MicroService_Group3/app/models/__pycache__/updateUsername.cpython-311.pyc differ diff --git a/Product_MicroService_Group3/app/models/getReviews.py b/Product_MicroService_Group3/app/models/getReviews.py new file mode 100644 index 0000000000000000000000000000000000000000..ce8811ec24a858c8b20354711fd2837a1e8dc0eb --- /dev/null +++ b/Product_MicroService_Group3/app/models/getReviews.py @@ -0,0 +1,42 @@ +#from app import db +import pyodbc +from flask import jsonify +from models.database_connection import connect_db + +#Function to get user info +def get_reviews(data): + + try: #error handling + + connection = connect_db() + cursor = connection.cursor() + + user_id = data + + #Get reviews from database + reviews_query = "SELECT CustomerID, Username, Review, rating, ProductID FROM dbo.ReviewsAndRatings WHERE CustomerID= ?" + cursor.execute(reviews_query, user_id) + reviews = cursor.fetchall() + + reviews_data = [{"CustomerID": row[0], "Username": row[1], "Review": row[2], "rating" : row[3], "ProductID" : row[4]} for row in reviews] + + + + #connection.close() + + return (reviews_data) + + except pyodbc.Error as e: #error handling + print(f"Database error in get_review: {e}") + return None + + except Exception as e: #error handling + print(f"Unexpected error occured in get_review: {e}") + return None + + finally: + if cursor: + cursor.close() + if connection: + connection.close() + \ No newline at end of file diff --git a/Product_MicroService_Group3/app/models/updateUsername.py b/Product_MicroService_Group3/app/models/updateUsername.py new file mode 100644 index 0000000000000000000000000000000000000000..d673f8bcf96de129c3a5f95196c928bdff094b72 --- /dev/null +++ b/Product_MicroService_Group3/app/models/updateUsername.py @@ -0,0 +1,48 @@ +#from app import db +from flask import jsonify +import pyodbc +from models.database_connection import connect_db + + +def update_username(data): + + try: #error handling + + connection = connect_db() + cursor = connection.cursor() + + user_id = data["user_id"] + username = data["new_username"] + + #insert data into user table + update_user_query = '''UPDATE dbo.ReviewsAndRatings + SET + Username= ? + WHERE + CustomerID= ?''' + + cursor.execute(update_user_query, (username, user_id)) + + + #commit changes to database if no errors + connection.commit() + + return {"message" : "Username updated successfully"} + + + + except pyodbc.Error as e: #more error handling + print(f"Database error in update_username: {e}") + connection.rollback() + return {"Error" : "Database error"} + + except Exception as e: #more error handling + print(f"Unexpected error occured in update_username: {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/subscribers/__init__.py b/Product_MicroService_Group3/app/subscribers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..377b837184694cf1acc74fa09e655f651327ffb0 --- /dev/null +++ b/Product_MicroService_Group3/app/subscribers/__init__.py @@ -0,0 +1,5 @@ +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 diff --git a/Product_MicroService_Group3/app/subscribers/__pycache__/__init__.cpython-311.pyc b/Product_MicroService_Group3/app/subscribers/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3983da2bfc76957fedcc9a12c59acbeee1d68af6 Binary files /dev/null and b/Product_MicroService_Group3/app/subscribers/__pycache__/__init__.cpython-311.pyc differ diff --git a/Product_MicroService_Group3/app/subscribers/__pycache__/updateUsernameSubscriber.cpython-311.pyc b/Product_MicroService_Group3/app/subscribers/__pycache__/updateUsernameSubscriber.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ecca0f3a049b16bf2c1abebacb308cdcb80d4cc0 Binary files /dev/null and b/Product_MicroService_Group3/app/subscribers/__pycache__/updateUsernameSubscriber.cpython-311.pyc differ diff --git a/Product_MicroService_Group3/app/subscribers/updateUsernameSubscriber.py b/Product_MicroService_Group3/app/subscribers/updateUsernameSubscriber.py new file mode 100644 index 0000000000000000000000000000000000000000..4235a17f9f2c3b3673d1cc139fc1ebf9cb9001d4 --- /dev/null +++ b/Product_MicroService_Group3/app/subscribers/updateUsernameSubscriber.py @@ -0,0 +1,34 @@ +from kafka import KafkaConsumer + +from config import KAFKA_SERVER + +from models.updateUsername import update_username + +from threading import Thread + + +import json +import logging + +#This function is called in the "__init__.py" file in the "subscribers" folder +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: + profile_data_str = message.value.decode("utf-8") + profile_data = json.loads(profile_data_str) + print("I am here") + print(profile_data) + update_username(profile_data) + +def start_kafka_consumer(): + logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + 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 diff --git a/User_MicroService_Group3/app/__pycache__/config.cpython-311.pyc b/User_MicroService_Group3/app/__pycache__/config.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..105819a92eef07108a2fb4558af6e0b1ad972654 Binary files /dev/null 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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..358b5ce9540cb4c24a27e99d55fedb7cc47a936c 100644 --- a/User_MicroService_Group3/app/config.py +++ b/User_MicroService_Group3/app/config.py @@ -0,0 +1,7 @@ +import os + + +DEBUG = True +SECRET_KEY = "Group3" + +PORT = 5000 \ No newline at end of file diff --git a/User_MicroService_Group3/app/controllers/__pycache__/changePasswordController.cpython-311.pyc b/User_MicroService_Group3/app/controllers/__pycache__/changePasswordController.cpython-311.pyc index f2bccc2cdba24cef3aa5a38361740cb00752099f..34cc426ee500d1191ecb52b66c1c4517c902dad9 100644 Binary files a/User_MicroService_Group3/app/controllers/__pycache__/changePasswordController.cpython-311.pyc and b/User_MicroService_Group3/app/controllers/__pycache__/changePasswordController.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/controllers/__pycache__/deleteProfileController.cpython-311.pyc b/User_MicroService_Group3/app/controllers/__pycache__/deleteProfileController.cpython-311.pyc index 8e06212bf94edd278c153db45bf9c8cb26a0fc10..6ce448acfcb8142341af9ce1dbd5583428925b08 100644 Binary files a/User_MicroService_Group3/app/controllers/__pycache__/deleteProfileController.cpython-311.pyc and b/User_MicroService_Group3/app/controllers/__pycache__/deleteProfileController.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/controllers/__pycache__/fetchUsernameController.cpython-311.pyc b/User_MicroService_Group3/app/controllers/__pycache__/fetchUsernameController.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..993fb4379d90d57d8dccc87743e0bec879754e00 Binary files /dev/null and b/User_MicroService_Group3/app/controllers/__pycache__/fetchUsernameController.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/controllers/__pycache__/loginController.cpython-311.pyc b/User_MicroService_Group3/app/controllers/__pycache__/loginController.cpython-311.pyc index 0ae33a0fab843db2a609b147bf8a5d6eaea25f24..c0b17d9c4604c96ba994a28b64fd1eebc7061a71 100644 Binary files a/User_MicroService_Group3/app/controllers/__pycache__/loginController.cpython-311.pyc and b/User_MicroService_Group3/app/controllers/__pycache__/loginController.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/controllers/__pycache__/logoutController.cpython-311.pyc b/User_MicroService_Group3/app/controllers/__pycache__/logoutController.cpython-311.pyc index fef5e24b0dc447c671daa0656ba2d91c90e66b0a..63d12e36ae470889c341e2c26608db4c1b994b39 100644 Binary files a/User_MicroService_Group3/app/controllers/__pycache__/logoutController.cpython-311.pyc and b/User_MicroService_Group3/app/controllers/__pycache__/logoutController.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/controllers/__pycache__/showReviewsController.cpython-311.pyc b/User_MicroService_Group3/app/controllers/__pycache__/showReviewsController.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e94a298c93ec474867d52280eef3e0341f88aca2 Binary files /dev/null and b/User_MicroService_Group3/app/controllers/__pycache__/showReviewsController.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/controllers/__pycache__/signupController.cpython-311.pyc b/User_MicroService_Group3/app/controllers/__pycache__/signupController.cpython-311.pyc index d6315f975befa50cae416644dbce66638592358c..55cd6b76556ac83d7c5d814dc2a64f828492b907 100644 Binary files a/User_MicroService_Group3/app/controllers/__pycache__/signupController.cpython-311.pyc and b/User_MicroService_Group3/app/controllers/__pycache__/signupController.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/controllers/__pycache__/updateProfileController.cpython-311.pyc b/User_MicroService_Group3/app/controllers/__pycache__/updateProfileController.cpython-311.pyc index d12100ee3869119656505afbb8d537567f89b91e..8b7bc1569e5e102cdf2927d1001d7ef46f330135 100644 Binary files a/User_MicroService_Group3/app/controllers/__pycache__/updateProfileController.cpython-311.pyc and b/User_MicroService_Group3/app/controllers/__pycache__/updateProfileController.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/controllers/changePasswordController.py b/User_MicroService_Group3/app/controllers/changePasswordController.py index 516ca01a998aabcda4d2480ffa2b3bd2949d16c5..5fbb96dbca89b737017962cde091c6c2b72d7499 100644 --- a/User_MicroService_Group3/app/controllers/changePasswordController.py +++ b/User_MicroService_Group3/app/controllers/changePasswordController.py @@ -1,3 +1,6 @@ +#This page allows users to change their password +# +# from flask import Blueprint, jsonify, request, session from models.changePassword import check_old_password, set_new_password import hashlib @@ -9,9 +12,9 @@ change_password_bp = Blueprint("change_password",__name__) @change_password_bp.route("/user/change_password", methods=["POST"]) def change_password(): - user_id = session.get("user_id") + user_id = session.get("user_id") #Get user ID from session - if user_id: + if user_id:#checks if user is logges in if request.method == 'POST': @@ -26,18 +29,17 @@ def change_password(): new_password_salt = new_encoded_password["salt"] new_password_iterations = new_encoded_password["iterations"] - + #collect old password, user id and email into json old_auth = { "user_id" : user_id, "email" : email, "password": old_password } - #user_old_auth = check_old_password(old_auth) #Collect user data from database + #user_old_auth = check_old_password(old_auth) - - + #send old password to database old_auth_info, value = check_old_password(old_auth) #function returns certain columns collected from database if value == 1: #if user exists in database @@ -47,12 +49,13 @@ def change_password(): old_password_iterations = old_auth_info.get("Iterations") - #password authentication + #password authentication. Calls password fucntions old_password_info = generate_password_hash(old_password) is_correct = verify_password(old_password_info, old_password, old_password_salt, old_password_iterations, old_password_hash) - if is_correct == True: + if is_correct == True:#if password is correct + #passes new password information to json new_auth = { "user_id" : user_id, "email" : email, @@ -62,6 +65,7 @@ def change_password(): "iterations": new_password_iterations } + #sends new password to database new_auth_info = set_new_password(new_auth) @@ -86,7 +90,7 @@ def change_password(): - +#password encoding def generate_password_hash(password): # Generate a 16-byte salt salt = secrets.token_bytes(16) @@ -103,6 +107,7 @@ def generate_password_hash(password): } +#password verification def verify_password(stored_password_info, submitted_password, salt, iterations, user_hash): # Convert the stored salt back to bytes salt = bytes.fromhex(salt) diff --git a/User_MicroService_Group3/app/controllers/deleteProfileController.py b/User_MicroService_Group3/app/controllers/deleteProfileController.py index c74dfe1faf87415a2fd52292becc0ff06484e5bb..f29f3689923437d63695693ebaf81f1be31a2dcb 100644 --- a/User_MicroService_Group3/app/controllers/deleteProfileController.py +++ b/User_MicroService_Group3/app/controllers/deleteProfileController.py @@ -1,3 +1,6 @@ +#This section allows a user to delete their profile +# + from flask import Blueprint, jsonify, request, session from models.deleteProfile import delete @@ -6,9 +9,10 @@ deleteProfile_bp = Blueprint("deleteProfile",__name__) @deleteProfile_bp.route("/user/deleteProfile", methods=["POST"]) def deleteProfile(): + #collects user id from session user_id = session.get("user_id") - if user_id: + if user_id:#checks if user is logged in if request.method == 'POST': @@ -21,7 +25,7 @@ def deleteProfile(): "user_id" : user_id } - user = delete(user_info) #Collect user data from database + user = delete(user_info) #sends user data to database return jsonify(user, user_id) diff --git a/User_MicroService_Group3/app/controllers/fetchUsernameController.py b/User_MicroService_Group3/app/controllers/fetchUsernameController.py new file mode 100644 index 0000000000000000000000000000000000000000..311efa0a87efc3fd7c7979238b3604e9f264c537 --- /dev/null +++ b/User_MicroService_Group3/app/controllers/fetchUsernameController.py @@ -0,0 +1,28 @@ +from flask import Blueprint, jsonify, request, json, session +from models.fetchUsername import get_username + +fetch_username_bp = Blueprint("getUsername",__name__) + +@fetch_username_bp.route("/user/getUsername", methods=["POST"]) +def username(): + + user_id = session.get("user_id") #gets session data + + #if user_id: #if user is logged in + + if request.method == 'POST': + + #User data from front end + data = request.get_json() + userID = data.get("id") + + + + username= get_username(userID) #Send user info to database + + return jsonify({"username" : username}), 200 + + else: + return {"error" : "null"} + #else: + # return {"error" : "User not logged in"} \ No newline at end of file diff --git a/User_MicroService_Group3/app/controllers/getUsersController.py b/User_MicroService_Group3/app/controllers/getUsersController.py new file mode 100644 index 0000000000000000000000000000000000000000..8c51a05fd07bac7ea438572fc488a4c090fcc713 --- /dev/null +++ b/User_MicroService_Group3/app/controllers/getUsersController.py @@ -0,0 +1,18 @@ +from flask import Blueprint, jsonify, request, json, session, redirect + +from models.getUsers import fetch_user_info + +from kafka import KafkaConsumer + +consumer = KafkaConsumer("review_events", bootstrap_servers='localhost:9092') + +def getusers(user_id): + + pass + + +for message in consumer: + + event_data = json.loads(message.value.decode()) + user_id = event_data['user_id'] + username = getusers(user_id) \ No newline at end of file diff --git a/User_MicroService_Group3/app/controllers/loginController.py b/User_MicroService_Group3/app/controllers/loginController.py index 31313b3f1185ece1ffa16fc85b96b1344896dbb6..232350cda6d673061fd1a75a26a98ddcd07ab8de 100644 --- a/User_MicroService_Group3/app/controllers/loginController.py +++ b/User_MicroService_Group3/app/controllers/loginController.py @@ -1,3 +1,5 @@ +#This section allows a user to log in + from flask import Blueprint, jsonify, request, session from models.login import fetch_user from models.login import fetch_password @@ -40,7 +42,7 @@ def login(): password_info = generate_password_hash(password) is_correct = verify_password(password_info, password, user_salt, user_iterations, user_hash) - if is_correct == True: + if is_correct == True: #if password is correct session["user_id"] = user.get("UserID") response_data = {"message":"Login Sucessful", "email": email, "session" : session["user_id"]} return jsonify(response_data) @@ -60,7 +62,7 @@ def login(): return {"message" : "null"} - +#password encryption def generate_password_hash(password): # Generate a 16-byte salt salt = secrets.token_bytes(16) @@ -76,7 +78,7 @@ def generate_password_hash(password): 'hash': hash.hex() } - +#password verification def verify_password(stored_password_info, submitted_password, salt, iterations, user_hash): # Convert the stored salt back to bytes salt = bytes.fromhex(salt) diff --git a/User_MicroService_Group3/app/controllers/logoutController.py b/User_MicroService_Group3/app/controllers/logoutController.py index c7ff97bca6d52edf2fd05e0c15ca25a2728a277c..241e00d337582cf50f06867c0fb7aeb36c401486 100644 --- a/User_MicroService_Group3/app/controllers/logoutController.py +++ b/User_MicroService_Group3/app/controllers/logoutController.py @@ -1,3 +1,5 @@ +#This section allows a user to log out + from flask import Blueprint, jsonify, request, json, session, redirect @@ -6,13 +8,13 @@ logout_bp = Blueprint("logout",__name__) @logout_bp.route("/logout", methods=["POST"]) def logout(): - user_id = session.get("user_id") + user_id = session.get("user_id") #get session data - if user_id: + if user_id: #if user is logges in if request.method == 'POST': - session.pop("user_id", None) + session.pop("user_id", None) #deletes session return ({"message" : "Log out successful"}) else: diff --git a/User_MicroService_Group3/app/controllers/showReviewsController.py b/User_MicroService_Group3/app/controllers/showReviewsController.py new file mode 100644 index 0000000000000000000000000000000000000000..8ac832b31c124ef029910d93bc78c41c98762850 --- /dev/null +++ b/User_MicroService_Group3/app/controllers/showReviewsController.py @@ -0,0 +1,54 @@ +from flask import Blueprint, jsonify, request, session +from kafka import KafkaConsumer +import json + +show_reviews_bp = Blueprint("showReviews", __name__) + +# Kafka consumer configuration +consumer_conf = { + "bootstrap_servers": "localhost:9092", + "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 +} + +# Function to consume reviews from Kafka +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 +@show_reviews_bp.route("/user/showReviews", methods=["POST"]) +def show_reviews(): + # 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 + reviews = consume_reviews() + + return jsonify(reviews) + else: + return {"message": "null"} + else: + return {"error": "User not logged in"} diff --git a/User_MicroService_Group3/app/controllers/signupController.py b/User_MicroService_Group3/app/controllers/signupController.py index 3876e7a869e0fb3bb5013a04ee00d6b45d48c906..0ec066371a3283032f8f059a08a2e7eab3ea1d78 100644 --- a/User_MicroService_Group3/app/controllers/signupController.py +++ b/User_MicroService_Group3/app/controllers/signupController.py @@ -1,8 +1,9 @@ +#This section allows a user to create a profile + from flask import Blueprint, jsonify, request, json from models.signup import new_user import hashlib import secrets -import hmac signup_bp = Blueprint("signup",__name__) @@ -24,7 +25,7 @@ def signup(): salt = encoded_password["salt"] iterations = encoded_password["iterations"] - if email.strip() != "": + if email.strip() != "": #checks if email is not empty (email cannot be null) # Create a dictionary from user data user_data = { @@ -48,9 +49,9 @@ def signup(): return jsonify(update) else: - return {"message" : "email cannot be empty"} + return {"error" : "email cannot be empty"} - return {"message" : "null"} + return {"error" : "null"} #This function encrypts the user's password diff --git a/User_MicroService_Group3/app/controllers/updateProfileController.py b/User_MicroService_Group3/app/controllers/updateProfileController.py index faedd619b81621fbdef00903a2247b5812e348ff..bca3119885f245ad049dc58820fc7c92a1d5aceb 100644 --- a/User_MicroService_Group3/app/controllers/updateProfileController.py +++ b/User_MicroService_Group3/app/controllers/updateProfileController.py @@ -1,15 +1,20 @@ +#This section allows a user to update their profile + from flask import Blueprint, jsonify, request, json, session from models.updateProfile import update_user from models.updateProfile import fetch_user_info +from events.eventDefinitions import updated_username +from publishers.kafkaPublishers import publish_username_updated_event + update_profile_bp = Blueprint("update",__name__) @update_profile_bp.route("/user/update", methods=["POST"]) def update_profile(): - user_id = session.get("user_id") + user_id = session.get("user_id") #gets session data - if user_id: + if user_id: #if user is logged in user_info = fetch_user_info(user_id) print(jsonify(user_info)) @@ -25,7 +30,7 @@ def update_profile(): gender = data.get("gender") - # Create a dictionary from user data + # Create a json object from user data user_data = { "user_id" : user_id, "email": email, @@ -35,13 +40,15 @@ def update_profile(): "gender": gender, } - # Convert to JSON - json_user_data = json.dumps(user_data) + update = update_user(user_data) #Send user info to database + if "message" in update: - update = update_user(user_data) #Send user info to database + event_data = {"user_id" : user_id, "new_username" : user_data["first_name"]} + event_message = updated_username(user_id, user_data["first_name"]) + publish_username_updated_event(event_data) - return jsonify(update, user_id) + return jsonify({"Update Status": update, "Username" : user_data["first_name"], "User ID" : user_id, "Event message" : event_message}) else: return {"error" : "null"} diff --git a/User_MicroService_Group3/app/events/__init__.py b/User_MicroService_Group3/app/events/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/User_MicroService_Group3/app/events/__pycache__/__init__.cpython-311.pyc b/User_MicroService_Group3/app/events/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..95be18671d60d24a8dd5c89660bea97b034080d2 Binary files /dev/null and b/User_MicroService_Group3/app/events/__pycache__/__init__.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/events/__pycache__/eventDefinitions.cpython-311.pyc b/User_MicroService_Group3/app/events/__pycache__/eventDefinitions.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..26189d001a07adbc3c51f25578fd46235d1830b5 Binary files /dev/null and b/User_MicroService_Group3/app/events/__pycache__/eventDefinitions.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/events/eventDefinitions.py b/User_MicroService_Group3/app/events/eventDefinitions.py new file mode 100644 index 0000000000000000000000000000000000000000..bb88066408e7c588d1eeadaec115cac9b4c2f06a --- /dev/null +++ b/User_MicroService_Group3/app/events/eventDefinitions.py @@ -0,0 +1,5 @@ +def updated_username(user_id, new_username): + + return {"event_type": "profile_updated", + "user_id": user_id, + "new_username": new_username} \ No newline at end of file diff --git a/User_MicroService_Group3/app/index.py b/User_MicroService_Group3/app/index.py index 32fd79e7e8f7ebd21b299ee5237d86336e929f75..a22a973629796e6f93ba702e7f1c7aeeb60947fb 100644 --- a/User_MicroService_Group3/app/index.py +++ b/User_MicroService_Group3/app/index.py @@ -1,4 +1,4 @@ -from flask import Flask, redirect, url_for, request, render_template, make_response, session, abort +from flask import Flask, jsonify, redirect, json, url_for, request, render_template, make_response, session, abort from flask_cors import CORS from flask import jsonify from controllers.loginController import login_bp @@ -7,13 +7,20 @@ from controllers.updateProfileController import update_profile_bp from controllers.changePasswordController import change_password_bp from controllers.deleteProfileController import deleteProfile_bp 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 +import os +import requests +import publishers app = Flask(__name__) CORS(app) -app.secret_key = 'Group3' +app.secret_key = SECRET_KEY @app.route('/') def index(): @@ -37,6 +44,21 @@ app.register_blueprint(deleteProfile_bp) app.register_blueprint(logout_bp) +app.register_blueprint(fetch_username_bp) + +app.register_blueprint(show_reviews_bp) + +publishers.create_profile_updated_topic() + + + + +@app.route("/userIDs", methods=["POST"]) +def userIDs(): + + #ids = request.json + #print(ids) + return 'hi' if __name__ == '__main__': - app.run(debug=True, port=5000) \ No newline at end of file + app.run(debug=DEBUG, port=PORT) \ No newline at end of file diff --git a/User_MicroService_Group3/app/models/__pycache__/changePassword.cpython-311.pyc b/User_MicroService_Group3/app/models/__pycache__/changePassword.cpython-311.pyc index 3e2f696425d98b4acece898d09560e3b644ec81a..efbc1602a81cf43627230b17ff6be9b650f9cba8 100644 Binary files a/User_MicroService_Group3/app/models/__pycache__/changePassword.cpython-311.pyc and b/User_MicroService_Group3/app/models/__pycache__/changePassword.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/models/__pycache__/fetchUsername.cpython-311.pyc b/User_MicroService_Group3/app/models/__pycache__/fetchUsername.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ae95206848ec046c1557589c2b224c793de637e2 Binary files /dev/null and b/User_MicroService_Group3/app/models/__pycache__/fetchUsername.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/models/__pycache__/getUsers.cpython-311.pyc b/User_MicroService_Group3/app/models/__pycache__/getUsers.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dd54bdac2b3189ffb26e70c80dcb6d7a9d4b3f61 Binary files /dev/null and b/User_MicroService_Group3/app/models/__pycache__/getUsers.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/models/__pycache__/updateProfile.cpython-311.pyc b/User_MicroService_Group3/app/models/__pycache__/updateProfile.cpython-311.pyc index 43ec1e62b7e96678f2be3c7811088ea64c8e1774..a3cc094c4a2bb3f0c99ed37c439242aab85aaf1a 100644 Binary files a/User_MicroService_Group3/app/models/__pycache__/updateProfile.cpython-311.pyc and b/User_MicroService_Group3/app/models/__pycache__/updateProfile.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/models/changePassword.py b/User_MicroService_Group3/app/models/changePassword.py index 18d40df2c5a44a850a75212d058e6bee07d70a64..7cb162b6b303c4a857ba5c7e398741dd48aed1f3 100644 --- a/User_MicroService_Group3/app/models/changePassword.py +++ b/User_MicroService_Group3/app/models/changePassword.py @@ -18,17 +18,17 @@ def check_old_password(data): # Check if the email already exists email_check_query = "SELECT COUNT(*) FROM dbo.User_table WHERE UserID = ?" - cursor.execute(email_check_query, user_id) + cursor.execute(email_check_query, user_id) #executes the query count = cursor.fetchone()[0] if count == 0: return ({"Error" : "Email does not exist"}, 0) else: - + #selects password information from database query = "SELECT t1.Email, t1.UserID, t2.Iterations, t2.PasswordHash, t2.PasswordSalt FROM dbo.User_table as t1 INNER JOIN dbo.AuthInfo as t2 ON t1.UserID = t2.UserID where t1.UserID= ?" - cursor.execute(query, user_id) + cursor.execute(query, user_id) #executes query row = cursor.fetchone() @@ -67,13 +67,13 @@ def set_new_password(data): user_id = data["user_id"] - # + #selects user ID select_userID_query = "SELECT UserID from dbo.User_table where UserID = ?" cursor.execute(select_userID_query, user_id) UserID = cursor.fetchone()[0] - # + #updates password update_authinfo_query = '''UPDATE dbo.AuthInfo SET PasswordHash = ?, PasswordSalt = ?, Iterations = ?, TempPlainText = ? WHERE UserID = ?''' diff --git a/User_MicroService_Group3/app/models/fetchUsername.py b/User_MicroService_Group3/app/models/fetchUsername.py new file mode 100644 index 0000000000000000000000000000000000000000..437cac41ebc8b863c7bae101dfa15e2b60092046 --- /dev/null +++ b/User_MicroService_Group3/app/models/fetchUsername.py @@ -0,0 +1,39 @@ +from flask import jsonify +import pyodbc +from models.database_connection import connect_db + + +def get_username(id): + + try: #error handling + + connection = connect_db() + cursor = connection.cursor() + + query = "SELECT Username FROM dbo.User_table where UserID = ?" + cursor.execute(query, id) + + row = cursor.fetchone() #fetch data + + if row: + return row[0] # Assuming the username is in the first column + else: + return None + + #connection.close() + + return username + + except pyodbc.Error as e: #error handling + print(f"Database error in fetch_user_info: {e}") + return None + + except Exception as e: #error handling + print(f"Unexpected error occured in fetch_user_info: {e}") + return None + + finally: + if cursor: + cursor.close() + if connection: + connection.close() diff --git a/User_MicroService_Group3/app/models/getUsers.py b/User_MicroService_Group3/app/models/getUsers.py new file mode 100644 index 0000000000000000000000000000000000000000..f056e2dea7fe5579b85688e91003cd420b0dc591 --- /dev/null +++ b/User_MicroService_Group3/app/models/getUsers.py @@ -0,0 +1,38 @@ +#from app import db +from flask import jsonify +import pyodbc +from models.database_connection import connect_db + + +def fetch_user_info(id): + + try: #error handling + + connection = connect_db() + cursor = connection.cursor() + + query = "SELECT First_name, Last_name FROM dbo.User_table where UserID = ?" + cursor.execute(query, id) + + row = cursor.fetchone() #fetch data + + columns = [column[0] for column in cursor.description] + user_data = dict(zip(columns, row)) + + #connection.close() + + return user_data + + except pyodbc.Error as e: #error handling + print(f"Database error in fetch_user_info: {e}") + return None + + except Exception as e: #error handling + print(f"Unexpected error occured in fetch_user_info: {e}") + return None + + finally: + if cursor: + cursor.close() + if connection: + connection.close() \ No newline at end of file diff --git a/User_MicroService_Group3/app/publishers/__init__.py b/User_MicroService_Group3/app/publishers/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..beb65e5ef1e4ca89b1eb1f7148c7aa845f8a7348 --- /dev/null +++ b/User_MicroService_Group3/app/publishers/__init__.py @@ -0,0 +1 @@ +from publishers.kafkaPublishers import create_profile_updated_topic \ No newline at end of file diff --git a/User_MicroService_Group3/app/publishers/__pycache__/__init__.cpython-311.pyc b/User_MicroService_Group3/app/publishers/__pycache__/__init__.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3e5ad7960c3085c3269be00737e82ddc12a6c966 Binary files /dev/null and b/User_MicroService_Group3/app/publishers/__pycache__/__init__.cpython-311.pyc differ diff --git a/User_MicroService_Group3/app/publishers/__pycache__/kafkaPublishers.cpython-311.pyc b/User_MicroService_Group3/app/publishers/__pycache__/kafkaPublishers.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3847106fe73d1b4414eb89e2fd3d5401f6c225f7 Binary files /dev/null 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 new file mode 100644 index 0000000000000000000000000000000000000000..042a5c13ed0c05097f8941b26627a5a542f2ab2d --- /dev/null +++ b/User_MicroService_Group3/app/publishers/kafkaPublishers.py @@ -0,0 +1,44 @@ +from kafka import KafkaProducer +from kafka.admin import KafkaAdminClient, NewTopic + +import json + +producer = KafkaProducer(bootstrap_servers="localhost:9092") + + + +def create_profile_updated_topic(): + # Create KafkaAdminClient instance + admin_client = KafkaAdminClient(bootstrap_servers="localhost:9092") + + # Define the topic name and configuration + topic_name = "profile_updated_topic" + 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_username_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("profile_updated_topic", 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() \ No newline at end of file