From 4b710c072d1df39efae71996ac0e175e546fd818 Mon Sep 17 00:00:00 2001 From: Atishay Jain Date: Sun, 30 Apr 2023 12:58:53 -0400 Subject: [PATCH 1/6] Added tests for extensions --- test.sh | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test.sh b/test.sh index 44a7054..ff20027 100644 --- a/test.sh +++ b/test.sh @@ -144,6 +144,42 @@ else echo "Message not updated." fi +#threaded_replies + +response=$(curl http://127.0.0.1:5000/post/3 -X POST -d '{"msg": "Test Reply"}') +status=$(echo $response | jq -r '.status') +msg=$(echo $response | jq -r '.msg') +reply_id=$(echo $response | jq -r '.id') + +if [ $status -ne 20 ] || [ "$msg" != "This is a reply" ]; then + echo "Threaded replies test failed" + exit 1 +fi + +db_reply=$(curl http://127.0.0.1:5000/post/3/thread/$reply_id) +db_reply_msg=$(echo $db_reply | jq -r '.msg') + +if [ "$db_reply_msg" != "This is a reply" ]; then + echo "Threaded replies data failed" + exit 1 +fi + +echo "Threaded replies test passed" + + + +#thread based range queries +curl http://localhost:5000/post -X POST -d '{"msg": "Test Post"}' +RESPONSE=$(curl http://localhost:5000/post/3/thread) +THREADS=$(echo $RESPONSE | jq -r '.[] | .msg') +if [ "$THREADS" != "Test Post" ]; then + echo "Failed to retrieve threads for post 3." + exit 1 +else + echo "Threads successfully retrieved for post 3." +fi + + # Clean up key=$(echo $New | jq -r '.key') curl -X DELETE http://127.0.0.1:5000/post/$id/delete/$key From edd5e3fc6566de70a1360e33e9b6af416cebbdc2 Mon Sep 17 00:00:00 2001 From: Atishay Jain Date: Sun, 30 Apr 2023 13:08:48 -0400 Subject: [PATCH 2/6] Added description of thread functions --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index bd2dd7e..12de89a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Name + Stevens Login Jason Rossi, jrossi3@stevens.edu Nouman Syed, nsyed1@stevens.edu +Atishay Jain, ajain70@stevens.edu # The URL of your public GitHub repo https://github.com/Jrossi3/WebForum @@ -40,3 +41,12 @@ We tested persistence in a very simple way. We ran the command "curl http://127. 3) Persistence We tested persistence through 2 different ways. Our first way was restarting the server through running two commands to end and start the server. There is a "kill $PID" command and a "flask run &" command to end and start the server resepctively. The second way of testing was giving it a bad request. The exact command is "curl http://127.0.0.1:5000/post/fulltext" and here this is a bad request because it is an unfinished request and therefore will return an error. Then there is a test to see if the post is still existing after the command is run. There are many comments in the testing to show the exact specifics of when these tests occur. + +4) Threaded replies + +We tested threaded replies we test the threaded replies, we have send a POST request to the URL with an 'id' parameter and a JSON object containing a 'msg' field. The exact command is "curl http://127.0.0.1:5000/post/3" in which after post is the postId in which we want to insert the thread into the thread array. Inside the thread it should generate a reply id, a msg, a key and timestamp when returning. + + +5) Thread based range queries + +We tested thread based range queries by running: curl "http://127.0.0.1:5000/post/3/thread" in order to retrieve all the threaded replies within that particular post whose id is based which is "3" in this case. The output should a return as a response providing all the threaded replies to that post and should ignore any other posts which aren't a part of that post's replies. \ No newline at end of file From 592c4e59384e517f489287c6a1b168c2798d3719 Mon Sep 17 00:00:00 2001 From: Atishay Jain Date: Sun, 30 Apr 2023 20:00:27 -0400 Subject: [PATCH 3/6] Added datetime and modified threaded replies --- README.md | 4 ++-- app.py | 71 ++++++++++++++++++++++++------------------------------- test.sh | 28 +++++++++------------- 3 files changed, 44 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index 12de89a..db888d1 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,6 @@ We tested persistence through 2 different ways. Our first way was restarting the We tested threaded replies we test the threaded replies, we have send a POST request to the URL with an 'id' parameter and a JSON object containing a 'msg' field. The exact command is "curl http://127.0.0.1:5000/post/3" in which after post is the postId in which we want to insert the thread into the thread array. Inside the thread it should generate a reply id, a msg, a key and timestamp when returning. -5) Thread based range queries +5) Date based range queries -We tested thread based range queries by running: curl "http://127.0.0.1:5000/post/3/thread" in order to retrieve all the threaded replies within that particular post whose id is based which is "3" in this case. The output should a return as a response providing all the threaded replies to that post and should ignore any other posts which aren't a part of that post's replies. \ No newline at end of file +We tested date based range queries by running: curl "http://127.0.0.1:5000/post/2023-04-30T21:13:52Z/2023-04-30T21:13:44Z" for example. Here the two parameters after /post are start and end timestamps. They result with posts in between the two timestamps. We have added three more cases for this. First case if the user wants to give the start timestamp as none, second in which the end time is none and the third one in which both are none. The program will handle the first two out of three but for the third one in which both are entered as none, then it would just give a 404 and throw an error. \ No newline at end of file diff --git a/app.py b/app.py index 321693a..7190467 100644 --- a/app.py +++ b/app.py @@ -67,69 +67,60 @@ def post_request(): @app.route("/post/", methods=["POST"]) def threaded_replies(id): - body = request.get_json(force=True) msg = body['msg'] - # key = body['key'] if not isinstance(msg, str) or msg is None: return "Post content should be of type string", 400 - - # Get the parent post object + parent_post = db["posts_collection"].find_one({"id": id}) if parent_post is None: return "Parent post not found", 404 - + key = secrets.token_hex(16) - # if parent_post["key"] != key: - # return "Key is invalid" - - # Generate a new UUID for the reply - max_id = 0 - for thread in parent_post["thread"]: - if thread["id"] > max_id: - max_id = thread["id"] - reply_id = max_id + 1 - timestamp = datetime.now() + # Generate a new reply id + max_reply_id = parent_post.get("max_reply_id", 0) + reply_id = max_reply_id + 1 + timestamp = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') reply = { "id": reply_id, "msg": msg, "key": key, - "timestamp": timestamp + "timestamp": timestamp, + "parent_id": parent_post["id"], + "thread": [] } with lock: posts_collection = db["posts_collection"] - posts_collection.update_one({"_id": ObjectId(parent_post["_id"])}, - {"$push": {"thread": reply}}) - - inserted_reply = posts_collection.find_one( - {"id": id, "thread.id": reply_id}, - {"_id": 0, "thread.$": 1} - ) - - return jsonify(inserted_reply["thread"][0]), 200 + posts_collection.update_one( + {"_id": ObjectId(parent_post["_id"])}, + {"$push": {"thread": reply}, "$set": {"max_reply_id": reply_id}} + ) + return jsonify(reply), 200 - -@app.route("/post//thread", methods=['GET']) -def get_thread_queries(id): - # Get the threads of a post from the database by ID +@app.route("/post//", methods=['GET']) +def date_time_queries(start, end): with lock: posts_collection = db["posts_collection"] - post = posts_collection.find_one({"id": id}) - threads_in_post = post["thread"] - - if post is None: - return f"Post with ID: {id} not found", 404 - - threads_list = list(threads_in_post) - for thread in threads_list: - thread.pop("_id", None) - - return jsonify(threads_list), 200 + if start is None and end is None: + return "Both Start and End cannot be None", 404 + elif start is None: + posts = posts_collection.find({"timestamp": {"$lte": end}}) + elif end is None: + posts = posts_collection.find({"timestamp": {"$gte": start}}) + else: + posts = posts_collection.find({"timestamp": {"$gte": start, "$lte": end}}) + + result = [] + for post in posts: + post_dict = dict(post) + post_dict.pop("_id", None) + result.append(post_dict) + return jsonify(result), 200 @app.route("/post/", methods=['GET']) def get_post(id): diff --git a/test.sh b/test.sh index ff20027..4fb921f 100644 --- a/test.sh +++ b/test.sh @@ -144,14 +144,14 @@ else echo "Message not updated." fi -#threaded_replies - -response=$(curl http://127.0.0.1:5000/post/3 -X POST -d '{"msg": "Test Reply"}') -status=$(echo $response | jq -r '.status') -msg=$(echo $response | jq -r '.msg') -reply_id=$(echo $response | jq -r '.id') +# Threaded replies +curl http://127.0.0.1:5000/post -X POST -d '{"msg": "hi my name is jason"}' +RESPONSE=$(curl http://127.0.0.1:5000/post/3 -X POST -d '{"msg": "Test Reply"}') +status_code=$(echo $RESPONSE | jq -r '.status') +msg=$(echo $RESPONSE | jq -r '.msg') +reply_id=$(echo $RESPONSE | jq -r '.id') -if [ $status -ne 20 ] || [ "$msg" != "This is a reply" ]; then +if [ $status_code -ne 200 ] || [ "$msg" != "This is a reply" ]; then echo "Threaded replies test failed" exit 1 fi @@ -166,18 +166,12 @@ fi echo "Threaded replies test passed" +#datetime range based queries +#here start and end are timestamps +curl -X GET http://127.0.0.1:5000/post// + -#thread based range queries -curl http://localhost:5000/post -X POST -d '{"msg": "Test Post"}' -RESPONSE=$(curl http://localhost:5000/post/3/thread) -THREADS=$(echo $RESPONSE | jq -r '.[] | .msg') -if [ "$THREADS" != "Test Post" ]; then - echo "Failed to retrieve threads for post 3." - exit 1 -else - echo "Threads successfully retrieved for post 3." -fi # Clean up From 47ee4bab0e2ef47330e1caae9215b821123982b6 Mon Sep 17 00:00:00 2001 From: Atishay Jain Date: Sun, 30 Apr 2023 20:42:25 -0400 Subject: [PATCH 4/6] Minor fix in thread and datetime functions --- app.py | 56 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/app.py b/app.py index 7190467..0efe4e7 100644 --- a/app.py +++ b/app.py @@ -72,44 +72,72 @@ def threaded_replies(id): if not isinstance(msg, str) or msg is None: return "Post content should be of type string", 400 - parent_post = db["posts_collection"].find_one({"id": id}) - if parent_post is None: - return "Parent post not found", 404 - key = secrets.token_hex(16) + # parent_post = db["posts_collection"].find_one({"id": id}) + # if parent_post is None: + # return "Parent post not found", 404 + + - # Generate a new reply id - max_reply_id = parent_post.get("max_reply_id", 0) - reply_id = max_reply_id + 1 + # # Generate a new reply id + # max_reply_id = parent_post.get("max_reply_id", 0) + # reply_id = max_reply_id + 1 timestamp = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') + # Generate a new UUID for the post + max_id_doc = db["posts_collection"].find_one(sort=[("id", -1)]) + if max_id_doc is None: + max_id = 0 + else: + max_id = max_id_doc["id"] + + # Generate a new post_id by incrementing the maximum post_id + reply_id = max_id + 1 + reply = { "id": reply_id, "msg": msg, "key": key, "timestamp": timestamp, - "parent_id": parent_post["id"], + # "parent_id": parent_post["id"], "thread": [] } + # Insert the new post object into the database + with lock: + posts_collection = db["posts_collection"] + posts_collection.insert_one(reply) with lock: posts_collection = db["posts_collection"] posts_collection.update_one( - {"_id": ObjectId(parent_post["_id"])}, - {"$push": {"thread": reply}, "$set": {"max_reply_id": reply_id}} + {"id": id}, + {"$push": {"thread": reply_id}} ) - return jsonify(reply), 200 + inserted_post = posts_collection.find_one({"id": reply_id}) + + post_dict = dict(inserted_post) + post_dict.pop("_id", None) + post_dict.pop("key", None) + post_dict.pop("thread", None) + return jsonify(post_dict), 200 + + # return jsonify(reply), 200 @app.route("/post//", methods=['GET']) def date_time_queries(start, end): + if start.lower() == "none": + start = start.lower() + if end.lower() == "none": + end = end.lower() + # print(end) with lock: posts_collection = db["posts_collection"] - if start is None and end is None: + if start == "none" and end == "none": return "Both Start and End cannot be None", 404 - elif start is None: + elif start == "none": posts = posts_collection.find({"timestamp": {"$lte": end}}) - elif end is None: + elif end == "none": posts = posts_collection.find({"timestamp": {"$gte": start}}) else: posts = posts_collection.find({"timestamp": {"$gte": start, "$lte": end}}) From abdd23f5f8babc6551031eb401211c72cfca247e Mon Sep 17 00:00:00 2001 From: Atishay Jain Date: Sun, 30 Apr 2023 21:31:26 -0400 Subject: [PATCH 5/6] Added tests for datetime and threaded replies --- app.py | 2 + test.sh | 246 +++++++++++++++++++++++++++++++++++++------------------- 2 files changed, 166 insertions(+), 82 deletions(-) diff --git a/app.py b/app.py index 0efe4e7..36735b7 100644 --- a/app.py +++ b/app.py @@ -131,6 +131,7 @@ def date_time_queries(start, end): if end.lower() == "none": end = end.lower() # print(end) + timestamp = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') with lock: posts_collection = db["posts_collection"] if start == "none" and end == "none": @@ -146,6 +147,7 @@ def date_time_queries(start, end): for post in posts: post_dict = dict(post) post_dict.pop("_id", None) + post_dict.pop("key", None) result.append(post_dict) return jsonify(result), 200 diff --git a/test.sh b/test.sh index 4fb921f..c0b6fad 100644 --- a/test.sh +++ b/test.sh @@ -1,8 +1,8 @@ #!/bin/bash # echo "├─ pymongo" -# pip3 install pymongo +pip3 install pymongo # echo "├─ secrets" -# pip3 install secrets +pip3 install secrets # Start the app in the background python3 app.py & @@ -15,14 +15,15 @@ sleep 2 # Test POST /post and GET /post/id # This test will exit immediately if the POST /post or GET /post/id fails. -curl http://127.0.0.1:5000/post -X POST -d '{"msg": "hi my name is jason"}' -RESPONSE=$(curl http://localhost:5000/post/1) +RESPONSE=$(curl http://127.0.0.1:5000/post -X POST -d '{"msg": "hi my name is jason"}') +echo "$RESPONSE" key=$(echo $RESPONSE | jq -r '.key') timestamp=$(echo $RESPONSE | jq -r '.timestamp') id=$(echo $RESPONSE | jq -r '.id') +TEST=$(curl http://127.0.0.1:5000/post/1) # GET /post/id test -if [ "$RESPONSE" == "Post with ID: 1 not found" ]; then +if [ "$TEST" == "Post with ID: {$id} not found" ]; then echo "Get /post/id failed." exit 1 else @@ -31,7 +32,7 @@ fi # Check if the response matches the expected output # POST /post test -EXPECTED={'"id"':$id,'"key"':'"'$key'"','"msg"':'"hi my name is jason"','"thread"':[],'"timestamp"':'"'$timestamp'"'} +EXPECTED={'"id"':$id,'"key"':'"'$key'"','"timestamp"':'"'$timestamp'"'} if [[ "$RESPONSE" != *"$EXPECTED"* ]]; then echo "ERROR: POST /post failed" echo "Expected: $EXPECTED" @@ -77,22 +78,16 @@ else fi # Testing the delete function by creating 4 more posts and then deleting all 5 of the posts -# This test will exit immediately if the deletion fails. -counter=2 -while [ $counter -le 5 ] -do - curl http://127.0.0.1:5000/post -X POST -d '{"msg": "hi my name is jason"}' - ((counter++)) -done - +# This test will exit immediately if the deletion fails. counter=1 while [ $counter -le 5 ] do - RESPONSE=$(curl http://localhost:5000/post/$counter) + RESPONSE=$(curl http://127.0.0.1:5000/post -X POST -d '{"msg": "hi my name is jason"}') key=$(echo $RESPONSE | jq -r '.key') - curl -X DELETE http://127.0.0.1:5000/post/$counter/delete/$key - EXISTS=$(curl http://localhost:5000/post/$counter) - if [ "$EXISTS" = "Post with ID: $counter not found" ]; then + id=$(echo $RESPONSE | jq -r '.id') + curl -X DELETE http://127.0.0.1:5000/post/$id/delete/$key + EXISTS=$(curl http://localhost:5000/post/$id) + if [ "$EXISTS" = "Post with ID: $id not found" ]; then echo "Delete passed." else echo "Delete failed." @@ -103,15 +98,14 @@ done # Test the update function # This should return "Message updated." Otherwise, the test will exit immediately. - -curl http://127.0.0.1:5000/post -X POST -d '{"msg": "hi my name is jason"}' -RESPONSE=$(curl http://localhost:5000/post/1) +RESPONSE=$(curl http://127.0.0.1:5000/post -X POST -d '{"msg": "hi my name is jason"}') +Old_post=$(curl http://localhost:5000/post/$id) key=$(echo $RESPONSE | jq -r '.key') id=$(echo $RESPONSE | jq -r '.id') -msg=$(echo $RESPONSE | jq -r '.msg') +msg=$(echo $Old_post | jq -r '.msg') curl -X PUT -d '{"msg": "hello I am update"}' http://127.0.0.1:5000/post/$id/update/$key -New=$(curl http://localhost:5000/post/1) -newMsg=$(echo $New | jq -r '.msg') +New_post=$(curl http://localhost:5000/post/$id) +newMsg=$(echo $New_post | jq -r '.msg') if [ "$msg" == "$newMsg" ]; then echo "Message update failed." @@ -120,90 +114,178 @@ else echo "Message update passed." fi -# Clean up -key=$(echo $New | jq -r '.key') curl -X DELETE http://127.0.0.1:5000/post/$id/delete/$key # Running the same test as before except commenting out the update function to show that the update will not happen # This is to show that the update function is actually updating the message -# This should return "Message not updated." Otherwise, the test will exit immediately. +# This should return "Message update failed." Otherwise, the test will exit immediately. -curl http://127.0.0.1:5000/post -X POST -d '{"msg": "hi my name is jason"}' -RESPONSE=$(curl http://localhost:5000/post/1) +RESPONSE=$(curl http://127.0.0.1:5000/post -X POST -d '{"msg": "hi my name is jason"}') +Old_post=$(curl http://localhost:5000/post/$id) key=$(echo $RESPONSE | jq -r '.key') id=$(echo $RESPONSE | jq -r '.id') -msg=$(echo $RESPONSE | jq -r '.msg') +msg=$(echo $Old_post | jq -r '.msg') # curl -X PUT -d '{"msg": "hello I am update"}' http://127.0.0.1:5000/post/$id/update/$key -New=$(curl http://localhost:5000/post/1) -newMsg=$(echo $New | jq -r '.msg') +New_post=$(curl http://localhost:5000/post/$id) +newMsg=$(echo $New_post | jq -r '.msg') -if [ "$msg" != "$newMsg" ]; then - echo "Message updated." - exit 1 +if [ "$msg" == "$newMsg" ]; then + echo "Message update failed." else - echo "Message not updated." + echo "Message update passed." + exit 1 fi -# Threaded replies +curl -X DELETE http://127.0.0.1:5000/post/$id/delete/$key + +# Testing for fulltext search +# Creating multiple posts and then checking if the fulltext search works on each post +# This will exit immediately if the fulltext search fails curl http://127.0.0.1:5000/post -X POST -d '{"msg": "hi my name is jason"}' -RESPONSE=$(curl http://127.0.0.1:5000/post/3 -X POST -d '{"msg": "Test Reply"}') -status_code=$(echo $RESPONSE | jq -r '.status') -msg=$(echo $RESPONSE | jq -r '.msg') -reply_id=$(echo $RESPONSE | jq -r '.id') +curl http://127.0.0.1:5000/post -X POST -d '{"msg": "hi my name is jason"}' +RESPONSE=$(curl http://127.0.0.1:5000/post/fulltext/"hi%20my%20name%20is%20jason") +counter=0 +while [ 1 ] +do + msg=$(echo $RESPONSE | jq -r '.[0].msg') + if [ "$msg" != "hi my name is jason" ]; then + echo "Fulltext search failed." + exit 1 + fi + RESPONSE=$(echo "$RESPONSE" | jq 'del(.[0])') + if [ "$RESPONSE" == [] ]; then + echo "Fulltext search passed." + break + fi +done -if [ $status_code -ne 200 ] || [ "$msg" != "This is a reply" ]; then - echo "Threaded replies test failed" +# Clean up +DB_NAME="web_forum_database" +mongo </ - - - - - # Clean up -key=$(echo $New | jq -r '.key') -curl -X DELETE http://127.0.0.1:5000/post/$id/delete/$key +DB_NAME="web_forum_database" +mongo < Date: Mon, 1 May 2023 16:01:45 -0400 Subject: [PATCH 6/6] Edited readme for threaded replies and date based fn --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index db888d1..42a5f66 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,9 @@ We tested persistence through 2 different ways. Our first way was restarting the 4) Threaded replies -We tested threaded replies we test the threaded replies, we have send a POST request to the URL with an 'id' parameter and a JSON object containing a 'msg' field. The exact command is "curl http://127.0.0.1:5000/post/3" in which after post is the postId in which we want to insert the thread into the thread array. Inside the thread it should generate a reply id, a msg, a key and timestamp when returning. +We tested threaded replies for which we have send a POST request to the URL with an 'id' parameter and a JSON object containing a 'msg' field. For testing we first created a command just to generate a new post, using the command: "curl http://127.0.0.1:5000/post" and gave the msg as "First". Now that we have the first post available, our job is to now add threaded replies inside of this 1st post. The exact command is "curl http://127.0.0.1:5000/post/1" in which we gave a msg "First Reply".When then check if the threaded reply gets inserted inside of the thread array or not.it should finally generate a reply id, a msg, and timestamp when returning. + +We tested the threaded replies 5) Date based range queries