From 613a1a53aa7d66c572fa4a824f837db3cb87ff7f Mon Sep 17 00:00:00 2001 From: Xx_DrkLeo_xX Date: Wed, 27 Mar 2024 16:42:43 +0100 Subject: [PATCH] Add medal api --- medal/Dockerfile | 9 +++ medal/main.py | 146 +++++++++++++++++++++++++++++++++++++++++ medal/requirements.txt | 3 + medal/start_debug.sh | 9 +++ medal/test.py | 68 +++++++++++++++++++ 5 files changed, 235 insertions(+) create mode 100644 medal/Dockerfile create mode 100644 medal/main.py create mode 100644 medal/requirements.txt create mode 100755 medal/start_debug.sh create mode 100755 medal/test.py diff --git a/medal/Dockerfile b/medal/Dockerfile new file mode 100644 index 0000000..bee527a --- /dev/null +++ b/medal/Dockerfile @@ -0,0 +1,9 @@ +FROM python:alpine3.19 + +COPY . /app +WORKDIR /app +RUN pip install --no-cache-dir --upgrade -r requirements.txt + +EXPOSE 80 + +CMD ["gunicorn", "--bind", "0.0.0.0:80", "main:app"] diff --git a/medal/main.py b/medal/main.py new file mode 100644 index 0000000..0572ce3 --- /dev/null +++ b/medal/main.py @@ -0,0 +1,146 @@ +import json +import os +from flask import Flask, request, jsonify +from pydantic import BaseModel + +app = Flask(__name__) +SAVE_FILE: str = "./data/medal.json" + + +class Medal(BaseModel): + current_idx: int = 0 + id: int + rank: str + athleteID: int + + def modify_from_key(self, key, value): + if key == "rank": + self.rank = value + if key == "athleteId": + self.athleteID = int(value) + + def serialize(self): + return { + 'id': self.id, + 'rank': self.rank, + 'athleteID': self.athleteID, + } + + +def load_medal_from_json(path: str) -> dict[int, Medal]: + if not os.path.isfile(path): + Medal.current_idx = 0 + return {} + with open(path, "r") as file: + content = file.read() + list_test = json.loads(content) + if len(list_test) != 0: + Medal.current_idx = list_test[-1]['id'] + else: + Medal.current_idx = 0 + print(Medal.current_idx) + obj: dict[int, Medal] = {} + for element in list_test: + obj[int(element['id'])] = Medal(id=int(element['id']), + rank=element['rank'], athleteID=element['athleteID']) + return obj + + +def save_medal_in_json(path: str, medal_dict: dict[int, Medal]): + medal_list = [] + for _, value in medal_dict.items(): + medal_list.append(value.serialize()) + with open(path, "w") as file: + file.write(json.dumps(medal_list)) + + +@ app.route('/') +def root(): + return jsonify({"name": "Jo API", "description": "An API to get information on 2024 olympic games"}), 200 + + +@ app.route('/medal', methods=['GET', 'POST']) +def handleMedal(): + if request.method == 'POST': + return handleMedalPOST() + else: + return handleMedalGET() + + +def handleMedalPOST(): + medal_list: dict[int, Medal] = load_medal_from_json(SAVE_FILE) + try: + post_data = request.get_json() + id = Medal.current_idx + 1 + rank = post_data['rank'] + if rank not in ['gold', 'silver', 'bronze']: + return jsonify({"Message": "Bad request"}), 400 + athleteID = post_data['athleteID'] + medal = Medal(id=id, rank=rank, athleteID=athleteID) + medal_list[medal.id] = medal + Medal.current_idx += 1 + save_medal_in_json(SAVE_FILE, medal_list) + except Exception: + return jsonify({"Message": "Bad request"}), 400 + return jsonify({'id': medal.id}), 200 + + +def handleMedalGET(): + medal_list: dict[int, Medal] = load_medal_from_json(SAVE_FILE) + ret: list(Medal) = [] + for _, val in medal_list.items(): + ret.append(val.serialize()) + return jsonify(ret) + + +@ app.route('/medal/', methods=['GET', 'PATCH', 'DELETE']) +def handleMedalId(id: int): + if request.method == 'GET': + return handleMedalIdGET(id) + elif request.method == 'PATCH': + return handleMedalIdPATCH(id) + else: + return handleMedalIdDELETE(id) + + +def handleMedalIdGET(id: int): + medal_list: dict[int, Medal] = load_medal_from_json(SAVE_FILE) + try: + medal = medal_list[int(id)] + return jsonify(medal.serialize()) + except Exception: + return jsonify({'Message': 'Not found'}), 404 + + +def handleMedalIdPATCH(id: int): + try: + medal_list: dict[int, Medal] = load_medal_from_json(SAVE_FILE) + patch_data = request.get_json() + key_list = ['rank', 'athleteID'] + _ = medal_list[int(id)] + for key, value in patch_data.items(): + if key in key_list: + if key == "rank": + if value in ['gold', 'bronze', 'silver']: + medal_list[int(id)].modify_from_key(key, value) + else: + return jsonify({'Message': 'Bad request'}), 400 + else: + medal_list[int(id)].modify_from_key(key, value) + else: + return jsonify({'Message': 'Bad request'}), 400 + save_medal_in_json(SAVE_FILE, medal_list) + return "OK", 200 + except Exception: + return jsonify({'Message': 'Not found'}), 404 + + +def handleMedalIdDELETE(id: int): + medal_list: dict[int, Medal] = load_medal_from_json(SAVE_FILE) + try: + medal_list.pop(int(id)) + save_medal_in_json(SAVE_FILE, medal_list) + except Exception: + return jsonify({"Message": "Not found"}) + + return "Ok" diff --git a/medal/requirements.txt b/medal/requirements.txt new file mode 100644 index 0000000..1ca9aac --- /dev/null +++ b/medal/requirements.txt @@ -0,0 +1,3 @@ +flask +pydantic +gunicorn diff --git a/medal/start_debug.sh b/medal/start_debug.sh new file mode 100755 index 0000000..4ae4d86 --- /dev/null +++ b/medal/start_debug.sh @@ -0,0 +1,9 @@ +if [ ! -d "./venv" ]; then + python3 -m venv ./venv +fi + +source ./venv/bin/activate + +pip install flask pydantic + +flask --app main run --reload --debug diff --git a/medal/test.py b/medal/test.py new file mode 100755 index 0000000..0cd019c --- /dev/null +++ b/medal/test.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +import requests +import json + +HOST = "http://localhost:5000" + +# Get root +assert 200 == requests.get(HOST + "/").status_code + +# Get list of medals as a json object +get_root = requests.get(HOST + "/medal").text + +# If the list is currently empty, it cannot interpreted a JSON +if get_root != "[]\n": + assert json.loads(get_root) + +# Post on '/medal' With no data +assert 400 == requests.post(HOST + "/medal").status_code + +# Post on '/medal' with uncorrect body +assert 400 == requests.post( + HOST + "/medal", json={"should": "fail"}).status_code + +# Post on '/medal' with uncorrect value of rank + +assert 400 == requests.post( + HOST + "/medal", json={"rank": "fake", "athleteID": 15}).status_code + +# Post on '/medal' with correct body +post_request = requests.post( + HOST + "/medal", json={"rank": "gold", "athleteID": 15}) +assert 200 == post_request.status_code +id = json.loads(post_request.text)['id'] + +# Get newly created object +expected_result = f"""{{ + "athleteID": 15, + "id": {id}, + "rank": "gold" +}} +""" +assert expected_result == requests.get(HOST + f"/medal/{id}").text + +assert 400 == requests.patch( + HOST + f"/medal/{id}", json={"should": "fail"}).status_code + +# Test to patch with incorrect value for rank +assert 400 == requests.patch( + HOST + f"/medal/{id}", json={"rank": "fkae"}).status_code + +# Test to patch object with correct data +assert 200 == requests.patch( + HOST + f"/medal/{id}", json={"rank": "silver"}).status_code + +expected_result = f"""{{ + "athleteID": 15, + "id": {id}, + "rank": "silver" +}} +""" +assert expected_result == requests.get(HOST + f"/medal/{id}").text + +# test delete methode on the newly created medal + +assert 200 == requests.delete(HOST + f"/medal/{id}").status_code + +# verify that the object don't exist anymore +assert 404 == requests.get(HOST + f"/medal/{id}").status_code