diff --git a/sport/Dockerfile b/sport/Dockerfile new file mode 100644 index 0000000..bee527a --- /dev/null +++ b/sport/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/sport/main.py b/sport/main.py new file mode 100644 index 0000000..3b48927 --- /dev/null +++ b/sport/main.py @@ -0,0 +1,140 @@ +import json +import os +from flask import Flask, request, jsonify +from pydantic import BaseModel + +app = Flask(__name__) +SAVE_FILE: str = "./data/sport.json" + + +class Sport(BaseModel): + current_idx: int = 0 + id: int + name: str + category: str + + def modify_from_key(self, key, value): + if key == "name": + self.name = value + if key == "category": + self.category = value + + def serialize(self): + return { + 'id': self.id, + 'name': self.name, + 'category': self.category, + } + + +def load_sport_from_json(path: str) -> dict[int, Sport]: + if not os.path.isfile(path): + Sport.current_idx = 0 + return {} + with open(path, "r") as file: + content = file.read() + list_test = json.loads(content) + if len(list_test) != 0: + Sport.current_idx = list_test[-1]['id'] + else: + Sport.current_idx = 0 + print(Sport.current_idx) + obj: dict[int, Sport] = {} + for element in list_test: + obj[int(element['id'])] = Sport(id=int(element['id']), + name=element['name'], category=element['category']) + return obj + + +def save_sport_in_json(path: str, sport_dict: dict[int, Sport]): + sport_list = [] + for _, value in sport_dict.items(): + sport_list.append(value.serialize()) + with open(path, "w") as file: + file.write(json.dumps(sport_list)) + + +@ app.route('/') +def root(): + return jsonify({"name": "Jo API", "description": "An API to get information on 2024 olympic games"}), 200 + + +@ app.route('/sport', methods=['GET', 'POST']) +def handleSport(): + if request.method == 'POST': + return handleSportPOST() + else: + return handleSportGET() + + +def handleSportPOST(): + sport_list: dict[int, Sport] = load_sport_from_json(SAVE_FILE) + try: + post_data = request.get_json() + id = Sport.current_idx + 1 + name = post_data['name'] + category = post_data['category'] + sport = Sport(id=id, name=name, category=category) + sport_list[sport.id] = sport + Sport.current_idx += 1 + save_sport_in_json(SAVE_FILE, sport_list) + except Exception: + return jsonify({"Message": "Bad request"}), 400 + return jsonify({'id': sport.id}), 200 + + +def handleSportGET(): + sport_list: dict[int, Sport] = load_sport_from_json(SAVE_FILE) + ret: list(Sport) = [] + for _, val in sport_list.items(): + ret.append(val.serialize()) + return jsonify(ret) + + +@ app.route('/sport/', methods=['GET', 'PATCH', 'DELETE']) +def handleSportId(id: int): + if request.method == 'GET': + return handleSportIdGET(id) + elif request.method == 'PATCH': + return handleSportIdPATCH(id) + else: + return handleSportIdDELETE(id) + + +def handleSportIdGET(id: int): + sport_list: dict[int, Sport] = load_sport_from_json(SAVE_FILE) + try: + sport = sport_list[int(id)] + return jsonify(sport.serialize()) + except Exception: + return jsonify({'Message': 'Not found'}), 404 + + +def handleSportIdPATCH(id: int): + try: + sport_list: dict[int, Sport] = load_sport_from_json( + SAVE_FILE) + patch_data = request.get_json() + key_list = ['name', 'category'] + print(patch_data) + _ = sport_list[int(id)] + for key, value in patch_data.items(): + if key in key_list: + sport_list[int(id)].modify_from_key(key, value) + else: + return jsonify({'Message': 'Bad request'}), 400 + save_sport_in_json(SAVE_FILE, sport_list) + return "OK", 200 + except Exception: + return jsonify({'Message': 'Not found'}), 404 + + +def handleSportIdDELETE(id: int): + sport_list: dict[int, Sport] = load_sport_from_json(SAVE_FILE) + try: + sport_list.pop(int(id)) + save_sport_in_json(SAVE_FILE, sport_list) + except Exception: + return jsonify({"Message": "Not found"}) + + return "Ok" diff --git a/sport/requirements.txt b/sport/requirements.txt new file mode 100644 index 0000000..1ca9aac --- /dev/null +++ b/sport/requirements.txt @@ -0,0 +1,3 @@ +flask +pydantic +gunicorn diff --git a/sport/start_debug.sh b/sport/start_debug.sh new file mode 100755 index 0000000..4ae4d86 --- /dev/null +++ b/sport/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/sport/test.py b/sport/test.py new file mode 100755 index 0000000..3b4007d --- /dev/null +++ b/sport/test.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +import requests +import json + +HOST = "http://localhost:5000" + +# Get root +assert 200 == requests.get(HOST + "/").status_code + +# Get list of sports as a json object +get_root = requests.get(HOST + "/sport").text +if get_root != "[]\n": + assert json.loads(get_root) + +# Post on '/sport' With no data +assert 400 == requests.post(HOST + "/sport").status_code + +# Post on '/sport' with uncorrect body +assert 400 == requests.post( + HOST + "/sport", json={"should": "fail"}).status_code + + +# Post on '/sport' with correct body +post_request = requests.post( + HOST + "/sport", json={"name": "test", "category": "aquatic"}) +assert 200 == post_request.status_code +id = json.loads(post_request.text)['id'] + +# Get newly created object +expected_result = f"""{{ + "category": "aquatic", + "id": {id}, + "name": "test" +}} +""" + +assert expected_result == requests.get(HOST + f"/sport/{id}").text + +assert 400 == requests.patch( + HOST + f"/sport/{id}", json={"should": "fail"}).status_code + +# Test to patch object +assert 200 == requests.patch( + HOST + f"/sport/{id}", json={"name": "modify"}).status_code + +expected_result = f"""{{ + "category": "aquatic", + "id": {id}, + "name": "modify" +}} +""" +assert expected_result == requests.get(HOST + f"/sport/{id}").text + +# test delete methode on the newly created sport + +assert 200 == requests.delete(HOST + f"/sport/{id}").status_code + +# verify that the object don't exist anymore +assert 404 == requests.get(HOST + f"/sport/{id}").status_code