From 6833cba08b8f164e2c22ebd752ba938c75a15094 Mon Sep 17 00:00:00 2001 From: Anrab35 Date: Wed, 27 Mar 2024 17:04:51 +0100 Subject: [PATCH] First commit :tada: --- Barnabe_DUPONT_TP1_R410.zip | Bin 0 -> 3794 bytes README.md | 0 .../APItest.cpython-310-pytest-6.2.5.pyc | Bin 0 -> 938 bytes app/__pycache__/medals.cpython-310.pyc | Bin 0 -> 2819 bytes app/athletes.py | 84 +++++++ app/data/athletes.json | 5 + app/data/medals.json | 5 + app/data/sports.json | 5 + app/docs.py | 25 ++ app/medals.py | 88 +++++++ app/sports.py | 80 ++++++ docker-compose.yml | 71 ++++++ docker/athletes/Dockerfile | 29 +++ docker/docs/Dockerfile | 26 ++ docker/docs/requirements.txt | 2 + docker/medals/Dockerfile | 29 +++ docker/requirements.txt | 1 + docker/sports/Dockerfile | 29 +++ .../APItest.cpython-310-pytest-6.2.5.pyc | Bin 0 -> 1413 bytes .../conftest.cpython-310-pytest-6.2.5.pyc | Bin 0 -> 443 bytes ...test_athletes.cpython-310-pytest-6.2.5.pyc | Bin 0 -> 5023 bytes .../test_medals.cpython-310-pytest-6.2.5.pyc | Bin 0 -> 4944 bytes .../test_sports.cpython-310-pytest-6.2.5.pyc | Bin 0 -> 5145 bytes pytest/test_athletes.py | 27 +++ pytest/test_medals.py | 27 +++ pytest/test_sports.py | 28 +++ swagger.yml | 227 ++++++++++++++++++ 27 files changed, 788 insertions(+) create mode 100644 Barnabe_DUPONT_TP1_R410.zip create mode 100644 README.md create mode 100644 __pycache__/APItest.cpython-310-pytest-6.2.5.pyc create mode 100644 app/__pycache__/medals.cpython-310.pyc create mode 100644 app/athletes.py create mode 100644 app/data/athletes.json create mode 100644 app/data/medals.json create mode 100644 app/data/sports.json create mode 100644 app/docs.py create mode 100644 app/medals.py create mode 100644 app/sports.py create mode 100644 docker-compose.yml create mode 100644 docker/athletes/Dockerfile create mode 100644 docker/docs/Dockerfile create mode 100644 docker/docs/requirements.txt create mode 100644 docker/medals/Dockerfile create mode 100644 docker/requirements.txt create mode 100644 docker/sports/Dockerfile create mode 100644 pytest/__pycache__/APItest.cpython-310-pytest-6.2.5.pyc create mode 100644 pytest/__pycache__/conftest.cpython-310-pytest-6.2.5.pyc create mode 100644 pytest/__pycache__/test_athletes.cpython-310-pytest-6.2.5.pyc create mode 100644 pytest/__pycache__/test_medals.cpython-310-pytest-6.2.5.pyc create mode 100644 pytest/__pycache__/test_sports.cpython-310-pytest-6.2.5.pyc create mode 100644 pytest/test_athletes.py create mode 100644 pytest/test_medals.py create mode 100644 pytest/test_sports.py create mode 100644 swagger.yml diff --git a/Barnabe_DUPONT_TP1_R410.zip b/Barnabe_DUPONT_TP1_R410.zip new file mode 100644 index 0000000000000000000000000000000000000000..b7f4d101f151cf81805248faaefe51b29ad6e2d5 GIT binary patch literal 3794 zcmb7{c{r49`^U#JG9&vk_HC#bO?YHqLdkBBi457sE<%GAYnJRKldXst)KJU_Ew&O- zQeg|d7#LNc*R^u1XG$&iftx6U{Kp-|+PQQv66tP*tk%ndL7%hN($8r6~x=sx#ay|Tru%D8I zSbM;XW2DPd1kzRvE;A5qv)XWsRifH3{yMOHw!UWW2;%$n7#5!h|a|k`SE?eFK%@=I(eVh9Ngz{AYn7 zT*eEc%!vFQrG56fFM=PzFR{XQUlh8LFz%|^U+f1no)>kG4}MGb6MwMykW(-to0Ky+ z=hf_8xt8P+&Fgkn{Yz77`?`w4pjm_JZK3DaVj0AaJ(5#IUpmX&;$$V_duR6gp#(eE;?zqFMGq2oLm8~$u%esxLc>Xqk9QihR@ zp_SjNY?_CXRD#=txJ*@5-lv{#di<~%D*e?{QGGghUF+qD-ur&poIy1Puk6!26`42O zeKKwn-Y?{ftDmS&y*!?&-jV(u%&6K<$i^S&n-qZ?2rImUqLWT*s>_P?*DtHDFOc6b z3F=99j9AoYyn#9)c^@B1jx+K;v-dNk@r zI7-Spi#+5j{owL7H55Uy*fLoR40_U-AQ!=d%R6MEJ639y9cT>)MO}8y(zFYRWlOpdE?Y8Ufr3B~nv8y`_NR!hwJu|GX#wp2 zM?_9mLyCMCDAn}{PRql_`ffcJ-}v-StCm4BrycF=kdJ+wc(iJ-3ncuQtDS$15;n?N zRBq~P(9x}?6WW$nJ6@ecJ_s z?R^hT_Si4u9)*X~FNKff<%2tR$Iq>E#O&h%^>Y=5yFBZe9nZ9umYbhwo)>H{qCyE( zpV%<4tfGr^cZv7kzDp`0*Bo77L*og#BbgVQ!G^9Z&-7ne5^&Qv&iZ1Am+g_KX>}WE zotQnTZ^?Ok5ywuc+S;rdD1Xt;!wT3Y`GbQs9b&ZSN{B~ICl_;W6-t*}s{3d|@>JFl z%&KnV&Sj40gA8~okxln{qgJvxpcYk!XYf&@y$N}k@0LY``Hy~mjW7dBgqK-YB8Q{1 zGbUj1R_e?r?>;AZGXKIog~95tybWS;?5WQ?<>*t*ouTck5mz#eFy^ zgg1YxkK^Wdh$!tyXqn`#M?TTM#yE7m{+{T3r{K(bznfN+fTJhj1ouPG(5o3n$CX;~ z;gtn&==bv)#T#W*t32$OWego$#Ex3Aeu1Y|EYw#EFMB%u_|yjPoni|2yDRB*Hfzx!t4= zAg~rS=hYbI%+hV64=&vcR~=^`5LtIAt?K`_(D2RhHLhD9TanegpWE}eRjE?LBgAZ$?`5J!5 zNZG98gC90$tPkOXnOU74OrpX~L=>f>hkI`kkGhw1FIJ3yx*67HEKHui)}(rUo?#aT z%oC>QdVBnEMQbWd+b?+WB%)hhe}Bh?Z>8r1PjMu-SSu+i)Zw27Nw&e^qQVGU%t`_z zvM?mf0~gjcif{CLIya4lNsX4AyA_Sy_;dKR)wrJ?A3Q21XJT?GrnUU*0cD|1!ch`ZR7qA8 z*=oe9GrQ-a!#dB8jZIHGuCX7sMjMQPc2H&vjC@T0zuo`K`GP=e`oP<7n*YC$bo;;E zq67AyzDO~KbmT7|`#XOI=vM~#bfZtV=ztNYFH%eco&OJP|8_cH^S9sr?{p!Cp9T2` zrhhvfF!b9!3Sj8zixiVaqyH+)|F-wr!GOit-jadFOJAgzK4#GG8=mc0dOxSv`tMj> pR?zQv_w87qn{WR`{=E#>0POGgc9a>I1xOY6ZBS?zonZq${RcUt#mE2v literal 0 HcmV?d00001 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/__pycache__/APItest.cpython-310-pytest-6.2.5.pyc b/__pycache__/APItest.cpython-310-pytest-6.2.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ec3c20dbcaf7254531ab2d24a5b50f88f94ed8fd GIT binary patch literal 938 zcmY*YOK%e~5VpOKY|{rqNT>*qDkM->m9j}Fg(8Al0SAPr%Hx#F%84Bk*cY|~r0iZ= zeg=o;$hE(M-{32!{)L_x@1|(6G&A4KcxLAtYtz-$CW7;7%N`wr{bQW^A>izR$QCGu z7>-aMmzKm9K^Vn$-@#SJ?YoRH>j&yL7=DL*`y6V}!O-Z-(*WRsJOhzupcpQYLOD9Y z1c z+QIw7Zm@s&((Is>&Gc$CG0Rvyr`eY@|G*g*x54inkZvquVg+i-S18++)78{1)6shpr)(PWrx-kGCb)xEtiOSR4$%Mr literal 0 HcmV?d00001 diff --git a/app/__pycache__/medals.cpython-310.pyc b/app/__pycache__/medals.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..24931bc00acb56bb9c53a2afcf4ae24d07c05058 GIT binary patch literal 2819 zcmZ`*&2Jk;6rY*>@cJW;YyokMJMNl~XR95Qhi|@6B!;ClYJT+nKlX`Fp?L8;1h}83NA_ zzghJ^p!Pd9`X3J(_n<`QDIuJ2+91{VO{)~ztihUA)oR*RyXjONO1LG+y1Sn;))VPh z^cD0QCu4-$W2Bl9S&`w+9{XvaniF(?K;&N0YW^FT*(NsGvnXkKw@5U-MJd7Gw@|pt zQ_o3taFuZHHLVteC5HBBwJ3_~!#vG1tCVM7FrMQB&k6WP5`RAN5AuTXpELfUUjHH= zHvZAXKa%u6$48BSEb)&e{_}j?_|JoXoKNt{7qCZ87fE^Q05O{{TiW@o;YW{kN{Wq+ zh?GvPM`0_dZ|^wVSAJ#3xq%K%ERRJ$R%Ra$tq8~3a+J@2jU8<~orwcLIW&L9~StNk+o&HhiD?9U=ZXMXLg)-G{ z(Bh({q=QXY+hJR@v|SGxLOWYBP(s`0G!E}H+mW^#q0a#WuO+r>@iFU~uD>C&2ooH> zQ&|g}qT;utzg(FOS2|6YJF0wj`_|3M(!zU{rr>@fy3yX&>B!#{W`-OLjL_uhD4jaW zu^hd0Bu8*RmbO>J2G?#BG&Y6Q?y?M9J7Oo5Or)*P=a%3QsishCA&*vI(5VLxX#med zML{3|a`uw#VL+{Uor*duDCFjzwQssjha7?zpDk#YzP9<0v{LsRD)L8R7dzFKPt zC8AOAMGQ=y$&2{iUU;;)bZR&F6=i64^D9(#rWM(zimh7^I0AvE<3!n;RE{YJNx*H~ zCwJ-))J38|Zp!8EIN>QM-dHjM#-+!xo&j(6rH#Z4M+%VLYF#HHd4uAV&_po&rYkdy8(7a$#QE(r-Q1j*v2xhu4 zFko_yj!>KCscU}T-=3R?mPapA?-~17-iB^FxrEJ?y8)j$l{1huC`?0|hfq;umV7@B ziieDWguxKTHO0Endd%dcvN!{uh4f*NSr^F$1*;S#A5xEXsjKDnrq(F<5 z9Tl1U2nRX~pDx}1;?$X4@b`p*H_}d{Pbd`b2l^T^owAj)M*)q+@7S)jYj4|OS=yA_CRhkDt#Io#0(3HCh6yNEfAS=m#BM`~xBb*82uyH|UaZezsH1~KKg?Bv9#-LdxlSCjJiy9=E+x`(G33sZIbltkW7V6;r$YbNYg-mh&|s&bCOm~ zM$HKx^caJ(h;Vw8kO!P8fJNEl=#**YD0}81ll~gX_Q~lU4AJiF+?R7pbEim!$LZY# z%h15<)6nNK&UA2N(oqgXjMQb^^Kyi9sH9 zW#BKT', methods=['GET']) +def get_athlete(id): + for a in j: + print(a['id']) + if a['id'] == int(id): + return jsonify({"status": "success", "data": a}) + return jsonify({"status": "error", "message": "Id not found"}), 404 + +@app.route('/athletes/', methods=['PATCH']) +def edit_athlete(id): + try: + update = json.loads(request.data) + entries = ['name', 'surname', 'email', 'country', 'isDisabled'] + for i, a in enumerate(j): + if a['id'] == int(id): + for e in entries: + if update.get(e) is not None: + j[i][e] = update[e] + save_json(j) + return jsonify({"status": "success"}) + return jsonify({"status": "error", "message": "Id not found"}), 404 + except Exception as e: + print(e) + return jsonify({"status": "error", "message": "Please provide a valid JSON"}), 500 + +@app.route('/athletes/', methods=['DELETE']) +def rm_athlete(id): + for i, a in enumerate(j): + print(a['id']) + if a['id'] == int(id): + j.pop(i) + save_json(j) + return jsonify({"status": "success"}) + return jsonify({"status": "error", "message": "Id not found"}), 404 + + +app.run(host="0.0.0.0", debug=False) \ No newline at end of file diff --git a/app/data/athletes.json b/app/data/athletes.json new file mode 100644 index 0000000..db98f51 --- /dev/null +++ b/app/data/athletes.json @@ -0,0 +1,5 @@ +[ + { + "id": 0 + } +] \ No newline at end of file diff --git a/app/data/medals.json b/app/data/medals.json new file mode 100644 index 0000000..db98f51 --- /dev/null +++ b/app/data/medals.json @@ -0,0 +1,5 @@ +[ + { + "id": 0 + } +] \ No newline at end of file diff --git a/app/data/sports.json b/app/data/sports.json new file mode 100644 index 0000000..db98f51 --- /dev/null +++ b/app/data/sports.json @@ -0,0 +1,5 @@ +[ + { + "id": 0 + } +] \ No newline at end of file diff --git a/app/docs.py b/app/docs.py new file mode 100644 index 0000000..12b4bf0 --- /dev/null +++ b/app/docs.py @@ -0,0 +1,25 @@ +from flask import Flask, send_file +from flask_swagger_ui import get_swaggerui_blueprint + +app = Flask(__name__) + +SWAGGER_URL = '/docs' # URL for exposing Swagger UI (without trailing '/') +API_URL = '/docs/api/swagger.yml' # Our API url (can of course be a local resource) + +@app.route(API_URL, methods=['GET']) +def swagger_yml(): + # Read before use: http://flask.pocoo.org/docs/0.12/api/#flask.send_file + return send_file('/app/swagger.yml') + +# Call factory function to create our blueprint +swaggerui_blueprint = get_swaggerui_blueprint( + SWAGGER_URL, # Swagger UI static files will be mapped to '{SWAGGER_URL}/dist/' + API_URL, + config={ # Swagger UI config overrides + 'app_name': "JO" + }, +) + +app.register_blueprint(swaggerui_blueprint) + +app.run(host="0.0.0.0", debug=False) \ No newline at end of file diff --git a/app/medals.py b/app/medals.py new file mode 100644 index 0000000..753b751 --- /dev/null +++ b/app/medals.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# encoding: utf-8 +import os +import json +from flask import Flask, request, jsonify + +file = 'data/'+os.path.splitext(os.path.basename(__file__))[0]+'.json' + +with open(file, 'r') as f: + j = json.load(f) + +def save_json(new_json): + with open(file, 'w') as f: + f.write(json.dumps(new_json, indent=4)) + with open(file, 'r') as f: + j = json.load(f) + +valid_ranks = ['gold', 'silver', 'bronze'] + +app = Flask(__name__) + +@app.route('/healthcheck', methods=['GET']) +def ping(): + return jsonify({"conatiner_status": "healthy"}) + +@app.route('/medals', methods=['GET']) +def list_medals(): + return jsonify({"status": "success", "data": j}) + +@app.route('/medals', methods=['POST']) +def add_medal(): + try: + new = json.loads(request.data) + if new['rank'] not in valid_ranks: + return jsonify({"status": "error", "message": "The valid ranks are gold, silver and bronze"}), 400 + next_id = j[-1]['id']+1 + j.append({ + "id": next_id, + "rank": new['rank'], + "sportID": new['sportID'], + "athleteID": new['athleteID'] + }) + save_json(j) + return jsonify({"status": "success", "id": next_id}) + except Exception as e: + print(e) + return jsonify({"status": "error", "message": "Please provide a valid JSON"}), 500 + +@app.route('/medals/', methods=['GET']) +def get_medal(id): + for m in j: + print(m['id']) + if m['id'] == int(id): + return jsonify({"status": "success", "data": m}) + return jsonify({"status": "error", "message": "Id not found"}), 404 + +@app.route('/medals/', methods=['PATCH']) +def edit_medal(id): + try: + update = json.loads(request.data) + entries = ['rank', 'sportID', 'athleteID'] + for i, m in enumerate(j): + if m['id'] == int(id): + for e in entries: + if update.get(e) is not None: + if e == "rank": + if update[e] not in valid_ranks: + return jsonify({"status": "error", "message": "The valid ranks are gold, silver and bronze"}), 400 + j[i][e] = update[e] + save_json(j) + return jsonify({"status": "success"}) + return jsonify({"status": "error", "message": "Id not found"}), 404 + except Exception as e: + print(e) + return jsonify({"status": "error", "message": "Please provide a valid JSON"}), 500 + +@app.route('/medals/', methods=['DELETE']) +def rm_medal(id): + for i, m in enumerate(j): + print(m['id']) + if m['id'] == int(id): + j.pop(i) + save_json(j) + return jsonify({"status": "success"}) + return jsonify({"status": "error", "message": "Id not found"}), 404 + + +app.run(host="0.0.0.0", debug=False) \ No newline at end of file diff --git a/app/sports.py b/app/sports.py new file mode 100644 index 0000000..0712941 --- /dev/null +++ b/app/sports.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +# encoding: utf-8 +import os +import json +from flask import Flask, request, jsonify + +file = 'data/'+os.path.splitext(os.path.basename(__file__))[0]+'.json' + + +with open(file, 'r') as f: + j = json.load(f) + +def save_json(new_json): + with open(file, 'w') as f: + f.write(json.dumps(new_json, indent=4)) + with open(file, 'r') as f: + j = json.load(f) + +app = Flask(__name__) + +@app.route('/healthcheck', methods=['GET']) +def ping(): + return jsonify({"conatiner_status": "healthy"}) + +@app.route('/sports', methods=['GET']) +def list_sports(): + return jsonify({"status": "success", "data": j}) + +@app.route('/sports', methods=['POST']) +def add_sport(): + try: + new = json.loads(request.data) + next_id = j[-1]['id']+1 + j.append({ + "id": next_id, + "name": new['name'], + "place": new['place'], + "category": new['category'], + }) + save_json(j) + return jsonify({"status": "success", "id": next_id}) + except Exception as e: + print(e) + return jsonify({"status": "error", "message": "Please provide a valid JSON"}), 500 + +@app.route('/sports/', methods=['GET']) +def get_sport(id): + for s in j: + if s['id'] == int(id): + return jsonify({"status": "success", "data": s}) + return jsonify({"status": "error", "message": "Id not found"}), 404 + +@app.route('/sports/', methods=['PATCH']) +def edit_sport(id): + try: + update = json.loads(request.data) + entries = ['name', 'place', 'category'] + for i, s in enumerate(j): + if s['id'] == int(id): + for e in entries: + if update.get(e) is not None: + j[i][e] = update[e] + save_json(j) + return jsonify({"status": "success"}) + return jsonify({"status": "error", "message": "Id not found"}), 404 + except Exception as e: + print(e) + return jsonify({"status": "error", "message": "Please provide a valid JSON"}), 500 + +@app.route('/sports/', methods=['DELETE']) +def rm_sport(id): + for i, s in enumerate(j): + if s['id'] == int(id): + j.pop(i) + save_json(j) + return jsonify({"status": "success"}) + return jsonify({"status": "error", "message": "Id not found"}), 404 + + +app.run(host="0.0.0.0", debug=False) \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..2a38fbc --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,71 @@ +version: "3.3" + +services: + traefik: + image: "traefik:v2.11" + container_name: "traefik" + command: + - "--log.level=DEBUG" + - "--api.insecure=true" + - "--providers.docker=true" + - "--providers.docker.exposedbydefault=false" + - "--entrypoints.web.address=:80" + ports: + - "80:80" + - "8080:8080" + volumes: + - "/var/run/docker.sock:/var/run/docker.sock:ro" + networks: + - backend + + docs: + image: "git.gnous.eu/anrab35/sae410_docs:latest" + container_name: "JO_docs" + networks: + - backend + labels: + - "traefik.enable=true" + - "traefik.http.routers.docs.rule=PathPrefix(`/docs`)" + + athletes: + image: "git.gnous.eu/anrab35/sae410_athletes:latest" + container_name: "JO_athletes" + networks: + - backend + volumes: + - "JO_athletes_data:/app/data:rw" + labels: + - "traefik.enable=true" + - "traefik.http.routers.athletes.rule=PathPrefix(`/athletes`)" + + medals: + image: "git.gnous.eu/anrab35/sae410_medals:latest" + container_name: "JO_medals" + networks: + - backend + volumes: + - "JO_medals_data:/app/data:rw" + labels: + - "traefik.enable=true" + - "traefik.http.routers.medals.rule=PathPrefix(`/medals`)" + + sports: + image: "git.gnous.eu/anrab35/sae410_sports:latest" + container_name: "JO_sports" + networks: + - backend + volumes: + - "JO_sports_data:/app/data:rw" + labels: + - "traefik.enable=true" + - "traefik.http.routers.sports.rule=PathPrefix(`/sports`)" + +networks: + backend: + name: backend + external: false + +volumes: + JO_athletes_data: + JO_medals_data: + JO_sports_data: \ No newline at end of file diff --git a/docker/athletes/Dockerfile b/docker/athletes/Dockerfile new file mode 100644 index 0000000..a9fe3ec --- /dev/null +++ b/docker/athletes/Dockerfile @@ -0,0 +1,29 @@ +FROM python:3-alpine + +MAINTAINER Barnabé Dupont + +LABEL version="1.0.0" + +RUN apk add curl + +RUN adduser -D worker + +USER worker + +WORKDIR /home/worker + +COPY --chown=worker:worker docker/requirements.txt /home/worker + +RUN pip install --user -r requirements.txt + +WORKDIR /app + +COPY --chown=worker:worker app/athletes.py /app/ + +COPY --chown=worker:worker app/data/athletes.json /app/data/ + +EXPOSE 5000 + +HEALTHCHECK --interval=15s --timeout=30s --start-period=10s --retries=3 CMD curl --fail http://localhost:5000/healthcheck || exit 1 + +CMD ["python3", "athletes.py"] \ No newline at end of file diff --git a/docker/docs/Dockerfile b/docker/docs/Dockerfile new file mode 100644 index 0000000..2d7ca83 --- /dev/null +++ b/docker/docs/Dockerfile @@ -0,0 +1,26 @@ +FROM python:3-alpine + +MAINTAINER Barnabé Dupont + +LABEL version="1.0.0" + +RUN adduser -D worker + +USER worker + +WORKDIR /home/worker + +COPY --chown=worker:worker docker/docs/requirements.txt /home/worker + +RUN pip install --user -r requirements.txt + +WORKDIR /app + +COPY --chown=worker:worker app/docs.py /app/ + +COPY --chown=worker:worker swagger.yml /app/ + +EXPOSE 5000 + + +CMD ["python3", "docs.py"] \ No newline at end of file diff --git a/docker/docs/requirements.txt b/docker/docs/requirements.txt new file mode 100644 index 0000000..87945d1 --- /dev/null +++ b/docker/docs/requirements.txt @@ -0,0 +1,2 @@ +flask==3.0.0 +flask-swagger-ui==4.11.1 \ No newline at end of file diff --git a/docker/medals/Dockerfile b/docker/medals/Dockerfile new file mode 100644 index 0000000..8d9c775 --- /dev/null +++ b/docker/medals/Dockerfile @@ -0,0 +1,29 @@ +FROM python:3-alpine + +MAINTAINER Barnabé Dupont + +LABEL version="1.0.0" + +RUN apk add curl + +RUN adduser -D worker + +USER worker + +WORKDIR /home/worker + +COPY --chown=worker:worker docker/requirements.txt /home/worker + +RUN pip install --user -r requirements.txt + +WORKDIR /app + +COPY --chown=worker:worker app/medals.py /app/ + +COPY --chown=worker:worker app/data/medals.json /app/data/ + +EXPOSE 5000 + +HEALTHCHECK --interval=15s --timeout=30s --start-period=10s --retries=3 CMD curl --fail http://localhost:5000/healthcheck || exit 1 + +CMD ["python3", "medals.py"] \ No newline at end of file diff --git a/docker/requirements.txt b/docker/requirements.txt new file mode 100644 index 0000000..0f800fc --- /dev/null +++ b/docker/requirements.txt @@ -0,0 +1 @@ +flask==3.0.0 \ No newline at end of file diff --git a/docker/sports/Dockerfile b/docker/sports/Dockerfile new file mode 100644 index 0000000..4042d77 --- /dev/null +++ b/docker/sports/Dockerfile @@ -0,0 +1,29 @@ +FROM python:3-alpine + +MAINTAINER Barnabé Dupont + +LABEL version="1.0.0" + +RUN apk add curl + +RUN adduser -D worker + +USER worker + +WORKDIR /home/worker + +COPY --chown=worker:worker docker/requirements.txt /home/worker + +RUN pip install --user -r requirements.txt + +WORKDIR /app + +COPY --chown=worker:worker app/sports.py /app/ + +COPY --chown=worker:worker app/data/sports.json /app/data/ + +EXPOSE 5000 + +HEALTHCHECK --interval=15s --timeout=30s --start-period=10s --retries=3 CMD curl --fail http://localhost:5000/healthcheck || exit 1 + +CMD ["python3", "sports.py"] \ No newline at end of file diff --git a/pytest/__pycache__/APItest.cpython-310-pytest-6.2.5.pyc b/pytest/__pycache__/APItest.cpython-310-pytest-6.2.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..36b2e3742d0277bdc7f0796089f8f6baee96f97a GIT binary patch literal 1413 zcmah}Pj4GV6rY*>iOj{5^i2xxkQPt+;%V^^n*KXI|?aUZ( z*u9V*`3M~9qrXQFe1^Gl>XqJd;=SDs7?p~V-tYa}H}B1RGn;K~bqK6qZ@HsCf&YQZ z`f@-y0@8f|K?F_7fL5GM9Y!dbx&x2a9)I9}M+6hjQzD!THfRa@fQ0S~h$JUN#P!8M zIRerj0LW_B0TWcPr)1y=N4UUU;lU2R-8z-p@MAMhrIC6_A>wMaK|uN%=;d<&LmttR z3R<#)7VLtSPT@Rar7KwJ6>i}@W~E>F`qVhjT;UY{hzb|&BesNe#(U;3$qz(_;BOhS zq^b*2fYOYDALN2AX~kK?orb#&_m))nzxnTyvL#v~C|VN=a$wpJ)hU7{y`aB96J1hL zAO_?Bxj=sD2?9rMpPXMG8JQ|@6*Kh$BT>}r#kx#q)6$WFO?Snn6dp|*jN zPv$TSz4EP-FM0_32z`Xx2zRZUFXH&}%|vUdOjS#K0I_I3SZ$~Ps<#d#wHs@RZ09In zz;F#ul-=fV=9DX$t6?_H6D92?xcS+9lA6g(Tfgdxw(oI0&gQA8KIIS7>?}$7ESXB% z<~kWk+`+zaTvam3X5T86Df>1bWonujE`Q3?WR@84tlHMqG9B3s%u|;huPX=Z%4h4! zow~Lrrnm0;ymr;;;{AVV{S{H4Kr1eL(KwsRD4D6`EczxJ&Zlx_bacNT_oCB#`%&GN z==k3E*q&~_uscr5YbU_!Qv$qyMSlm3%8`J o>;@$Q-;841rSz>H{!uK=^_T zx&@dc7(-UdJ%D!qQFI!7l(kBU+9U=4Mv6l5Z_e&zC0JUkbj>bg)>cs0?CNxW%x*4E zSkoDy4a;PmyI|IIuvW)Yle%lQn?YWE5@8iOuCWc*ZBd${)^=~%bjdb5OomajRBNG3 zA?ukEPpUA&j<=8vHdqABjxb4;r9)_gym&UP5&a`P|gB|4?T1PeD|Ko7S=%f~Q(8zpX-fxbQM$5#v?M)QL|T?5Sw>os6*+-)LQcvlq?1y-r&TX~ zfqQG~+ayQ-Xl$?Jid+aOq#jahV?Fh@50whqaWj-kpzcgl29lw8x>Vci90WDLt^BRp z8=dXL12m6n?=IBmY8ww5weI6Mh~gTFrXTOM0%)_{$LVBN(QNw%LDcoP1Dozo19642 zkckY2$bUnLwOE|!G}Kf5iRfvuv2RL4nu#VYS{msdTe9$x-V=#9u}(GV&1kXR zbP}=e_H|; zM%7A9TFcal4izg_aNvin)Y>QEZC z%cfgTi^I&qXl6dkWagGeGb=-y6gCbmoo4I4l+AYVK_8j|Y8cN+aYIzZUxk`Q$rvO> z6qIohBwyYJuCcZcG^FT>Sl>5#dTb^Hi;;-LxQ3T7(GnARE3x*io-OsBlh}y^DBQ%2 z{u0|Kjx-V%P?%KCP*|~hQeY?+Q18VWpjd@gBn?nlq$kih1t`)U^!PdVFwx^(?g0%w z0PCQ|@3;k!Qrp}D8d^A{Gia0oG|pRj3bZ)y;9l;r2j~j2Aibo(0D5r=wdKSc0gxaR zIfw?qh|9ud<8pi0aRFv={ppQo;#gBtSYmYv;!GUlWEsK--rYoI0?}twPHUz&DNKYtCm3NaL4v~Rs7?+nA|%0sTXJ>GbFxE zv+>jw(yu}gu?y;2t}Rh^1$aKL&#|0mxyW+m>BI=_4Gfiv8*^tBwx1f99NTA>v)g@# z?sJpGcS*cR;(H{%PvRvKKOljPQ9CQDD(Npnj54)6npw@b&pE7*L*=CpK_(qzz1+#09Q}q!4|K*W8g#QUI=8&bSr^XuX2^4A;AaOO9)S^f6q$L5~gY zLAd^gdt|sG%owx?xCP<5&n+@si=;mfuH4QAwx#1Du=QkFRuYe~E#}x3pA%aL=oWe| zqdT##=CHu2_4fS-4}S4xRadlkD%u$pt^N>R_`A;-UO1>6GiZHyFCzLG@OJ%pd+(dU z_eVF$QLphZv57pBW}@yK&DG+NW;IVUCz{m)iA54iB$i36KrqV7>J_L*V?1Nh%yXlf z^K9{azYfvW(vWEN6B@x?68A{FMuHGg_aVmU9A|t*y)|f%BRYQ`(J4kWkl}#QN%V;_ zMt`lTUm`cgDD8g~Z8D?(Vn$Ddgaf4r^NF|y*VfY#jbloE299JG5nZC~+lc54xKtxC zK_px%IJMMj0_TZVj>-x) z*!ez0J#(vm1+7E`!%=#ylY^u(@G6TOk@BG+DKlA!Sm6#Ur$T5L-`~-a?nApqg?1CV z5BG*U<&^lGIM%@@A0?6rwlc|9CfQ11V5I~rmuMyfD{#AP1su!EY-LJL%PYwgTe-wm zE|C?q!pP*+Q$5shugmh9yq;X}aa7#9Fs>_V|ya0pm6*kxvrU5DKx zhk#wp;@(x5o@Rhys2?{|m{8T3xd!8R(x<(!h68KMtVCkc0JUQ4(K?Pc*fQlCgbeG+0+P)FR|$zcuEb z{>kseMV{#Yi7jP7$L?>&KRweST@YTX2Ii@5lOQkCcPDl>zOUm?)T?CF7zjQ`UFPSL z8Sg(Cifq+SNrVv3_e8>v;Rbk*P<84#9y6btl)6JrE*egDtjmTocvEPz$i}mOemuzI zq>_6gx+fjy=!=ImLU@&r^nG1gk97KEgaeDcuILL&YCpXH+Uxh;+2Bwo{2NA&&51|+ zEmUWa57&t`UaTv;Saj&z@gD{K<+_1B{88owp<2*!f_M8TYmV8|nPzqtpUsHI`FK_Z zAE+=6(!!t!C;hA}i}-msWIB7~^tFdivm7zAOJ-j95h+?1zW2!E9S+;Q1NIJE!K;|4 X7SYpv5g(B7Gw`!q9Y536OZtBS2x{7x literal 0 HcmV?d00001 diff --git a/pytest/__pycache__/test_medals.cpython-310-pytest-6.2.5.pyc b/pytest/__pycache__/test_medals.cpython-310-pytest-6.2.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..142e28c594cc268d5e34709ea574b14d0d89e115 GIT binary patch literal 4944 zcmcJT&vP6{700LN_s*_XKW*6%z=U6361>`#WJ|_M?7{(D6-3GjMM?*@#_Ex^@@i-4 z89A|-xezWKps0eQ4-z+>IZ~D4m;-;oT)E(LZa$>=zSpzATvnB-R4mWy*FWcVPk;Ko z-gssvF!1mAj=lHTHjMvJX7Znj%xyf$V+d(T(KB`gr{+#Q!|yd95I zf5(@mwEkf1RHQ8(q=9s$hqNkvSwUKpfvh5(ku^Dkv@Yv%7U`^{CPfmyOivxS2Tca#L1v zD>F0ek(t|>o%}lWPbxCV>^&i?RK91P+On1gC)HEqv5{<`zLtqop|+sSkk%KdC*3?1 zoLZ+s&OEg)8o5IYo;h6s-A(JLoz2`+@rn37KC*4(CLTMtq=AK<+wT4s%h;X$fnhvf z-cQrP&1SO~?}oko7;B}Msjwf$J^74Qyl1Qc8`3Bmc@J3^>+I1o9Q%mr=q+)VAv{6t_Qi_BWamdcFGM&8Ms11EQ- znR}U=dD!B9<|ltl-4jn*nU5`QQ#o&O2b;6PTYL@mL26)&Z$hh*hAr-po3roHhSFW9?;MOXVIv|Z@2{jQM=p%8d`XyGd7e08_qjr3Q#!j;a&dI00>nip;=*^ zg0zO(nJk#ViI5T{Ru-X!$HC*`@pGJ-z^uCc{L(paHPtK@Sj|D4iQ|6~7AyNN@J~|?HkNX3$&G^LMsQr_}pw`SO=x7x(+zUXKs);@E_01 zfo-9mx{Ns#7I0Hvr`ZH*k!H5UrLaU@D7EXv?FP_%(pq7;%5t6M#`D<;$lEZLirXt^ z^>Tbnj>l(?Rg+%v8i{X^_$G;Ok+?$Q+az8m@f{N1g*dCK?~{HNVv?sDlbKD;e92&I zD%YOMtxe@_Oy#cYoJO=&j;V9)){BtV8B$D616qf9pA)@bh+BsGDU>NVYbc;51ZQ&p z72qVN-6c2yY34{9p2^Kzfa*yHfO?q^P%Bv_`71WKC*7<9P(3PVQ2h~9H&L%aeVafj zLG?+W0<|*g@jmw;P(S1z8dN|vY9YA=f!gI38q|RFR{@pV`2e>1v%boei(lA^=ZOsxaB#$`CN+{la*2l1eJ8E)etqOr_4a1o7JaG+LZ zgF-k^u<5AH46YKbULX{86|Qqp7f3I!Wi1ZZTk;+#!{LQP4!uB!}_Y4AWH4OgO*#sSpP2`v+Rmd+4O74^Im2 z4d=-zF**Cx#3&s6XR|!Y9FH=`qf|zt)G*2d&4jGw-f*&Yg@-`w-c~K5aSzOlFjkT-ANr7>e`j ze6yOy7ml6lHX8H#fcP;@nB6X&650z>ocIb}xNyV4AnMDaJ`95&#BnbkjJ|@3!U2_@gYS+9zS+k-K0Aha%`&ISs3q|cI9}Zvq!%>6MS+7jF?rBH9FRd@pnT-D49BGCWCYJCTbC4YHu}) zCb4Ngh2iM<)z*IjY@etzGzDM_Oa07fksB6@dV&rqDq;68}%Z$-iIBiV1OD<7cwq_sN zlr_lXR#HZX6dcpljeez|7?5JW15-MWO!|ojmlJne(Qh7#4z|L1H&)$*Bc1MFU^-4F zHu-DRg5&J*GO@;Mwa9BlH_W~8QKVOkdhqWkuL*Sky)j?-!%H-W=*8uZzII>JFyktG zO+_Ce28{5l!tmaVOT8}B0X`eEOvdo(LqE{))s;hJ_Q(tie<4K+!|$H>wqv9HTZgec c>P2s1nnpq&_jUZ{gFg#@j&I`6_RX64KeA}K?EnA( literal 0 HcmV?d00001 diff --git a/pytest/__pycache__/test_sports.cpython-310-pytest-6.2.5.pyc b/pytest/__pycache__/test_sports.cpython-310-pytest-6.2.5.pyc new file mode 100644 index 0000000000000000000000000000000000000000..e8263c3c21781e6e0db1a9ce0ebe6aac37efe98a GIT binary patch literal 5145 zcmc(jO>Z1Y8OOW&eO_%RAx?NT3oA0p;_=u{?1XqX*-cojL@bm<2)trPovF4x&UkvZ zy1lzLJ(q+FheZgGkWdbuV?F~C2TmLiAE2*X$a!x$toT1q^}JaQNEHis;;hI z{h#WnQYjkvJ-%gc|J^o>e^6rbXCiSESNsW#G^FSlTY^J#-!cth?Au$8&?WAc%O&2H z$DzOFOH*3EG`0%TmJY(Abft%|Bz;*xSe8XuLRgVySwUEpRXKxjM$XDPgtJoIH)>~| zqiV|x!*?88VQ%{JAxHnU>uK&h~sG}}@Mlsz+)gF%!HmkFn-C#eAdqFF7sdpNNFO-8| zYKR^(=;9&%1(C!^#IZ>O9hgr<-$<-ITUydi4e3btsU^M7&Av#*u`@8Ff6++Xrk9F6 zzi-My-%8EYdSv$P)Q*3a6po9snA+PymMDMQ9N4m)l#a^-;}avkj`B(>2131qSe0U3 zfpUtQ1Hqv+5VG>rI&btHn)K9Z3&h=I2Bov9I}lIAW4vhF=$p9gz9kJz?(Am!pP0(_ z%=ZlA#r$rP^sd+Iov0Obb|cJ}o~q1#aOeJmtaSIFleBxC;4oA*E3;|VG6(5anE9UL18Zvn`+h6_fN#yf8O5YT{_+%}c$*IYknJY)tAdW+o@Xc$egUq$sTxP|I%3Kw6Wwf7p6t=rb z=Bx1I1HAP(vwL_VRl$%lSHE{fZ#pYYbeBe`mGrxQI4I=rT`LNgAwLuw(b-r`ZWqwXE0B9)zOZbxcA zNSfhin8I$5prqzy9xqX>^D{g)>Q6szBP)#)tCNWpozTr)olLBc2~pS#jO+}z9!S~j zhM#H>3dm>RX2p3?6~7khE#yos{1P%~-gx2v2mmIaGZBfoXFU<~#uFio#NKoI$nA4( z-%LbGNLZ;zt$8fB)JScloz&TL`>r(mUh1YEmcO6+@oy9F*q2u7WBJ>Z&&%Jz!Y%Uh z-#~dOv9bK$MXXFQEPsdMse$EBA&`*n=#HQ89R$pWe1`@Gd)27M4%YxYs>3x9Lz^DO z87T^Z6sMg$1Vo(n(3k(T2)qii0Bj44S~01hw3-$tP$S$#j+{l<;c{@fxcokLRUlZ} zd~x9=;MLU(rd`d#JQGKMB3c@tLK8%>9HGX_C=;qXr<% zTSY8y0PSh7ClzC~t%%(uzB zLFPMTzDwp!m}e!0?a)v)m`TvqCKK26W{|_#m`W{ArBYg4JKI;GidNZPH|gC232Bi?=iP~kf{ku?P*F$-P=SdXiXhyL%!V9 z1=*f-K&qGeAhwVe;@>6iu_xWM0AfALXJY-tKQ82g(yLL!zzB9=lB%b|}xOHWH6w@4mJ zT4ZudNg1V;wDgMPdZ4$^_nF@6rusgnSkWGtdAB}3z((FuH1lJ2A%1^jcA-`|Wrz*g zJq_rWu-glg*6#m?-nSM=wKsT}*h5|lGeLI(=2~eCvs$7jCzw@(%rco3GOJ{+z%a>c zY8}y|DV*`pjPr7>s=;;md3nRoIP?DTIwaT1W0KVk8o>scx5>Oi<|dhUVWy~@W_w-T z9#zQETsev645Jy!cFyJ;?ubcpzZ4G)br+E-GHFL2xssa9V*yPy$%Q0M9e~r+V8_wC z2o2eR+m;&8PD@%)Q!BMWBGeRoI_h!?-9)VuloGq~AD~G-NOhQ0+?SI&)K*edW>US+ z+BPJ0QvblBEU;?{ClwBAPEBfRW`uz;JkI-}9icqj5x7NSk6I)4Do;(YS7ryb%@ zNRX-@lA#wc^hqIuno80`?RsJw~wqD{eW7w zMutyebG)u~<9bCC-(P6CfFt*Fq+Yt3)rTm>p@H8?`}bJdyqT#*>SNl=KK-#2?NDw+rGBOy(~H!_M`W5XQxAl+L7wY-xZ(wv#Mp!Wk^f0wmhkDs{oLF$B~FO+ zEefY3e^b#7nY|NrWc!aexj(1`VE8rAmnJ@SvkTAO0`F+N5P0&q?}=qJ9<+Z0d*eYT ae4Az?CM9|UAFuFd;m^Tk`nGSD&3^$q>D(s( literal 0 HcmV?d00001 diff --git a/pytest/test_athletes.py b/pytest/test_athletes.py new file mode 100644 index 0000000..ffd9a76 --- /dev/null +++ b/pytest/test_athletes.py @@ -0,0 +1,27 @@ +import requests +import random +import pytest + + +def pytest_namespace(): + return {'current_id': 0} + +def test_add_new_athlete(): + test_id = str(random.randint(1000,9999)) + x = requests.post('http://localhost/athletes', json={"name": "TEST", "surname": test_id, "email": "test@test.org", "country": "Listenbourg", "isDisabled": True}) + pytest.current_id = x.json()['id'] + assert x.status_code == 200 +def test_get_athlete(): + assert requests.get("http://localhost/athletes/"+str(pytest.current_id)).status_code == 200 +def test_edit_athlete(): + assert requests.patch("http://localhost/athletes/"+str(pytest.current_id), json={"name": "HELLO", "isDisabled": False}).status_code == 200 +def test_get_modified_athlete(): + x = requests.get("http://localhost/athletes/"+str(pytest.current_id)) + json = x.json()['data'] + assert x.status_code == 200 and json['name'] == "HELLO" and json['isDisabled'] == False +def test_delete_athlete(): + assert requests.delete("http://localhost/athletes/"+str(pytest.current_id)).status_code == 200 +def test_deleted_athlete(): + assert requests.get("http://localhost/athletes/"+str(pytest.current_id)).status_code == 404 +def test_add_fake_athlete(): + requests.post('http://localhost/athletes', json={"PEBCAK": True}).status_code == 400 \ No newline at end of file diff --git a/pytest/test_medals.py b/pytest/test_medals.py new file mode 100644 index 0000000..6f3efd7 --- /dev/null +++ b/pytest/test_medals.py @@ -0,0 +1,27 @@ +import requests +import random +import pytest + + +def pytest_namespace(): + return {'current_id': 0} + +def test_add_new_medal(): + test_id = random.randint(1000,9999) + x = requests.post('http://localhost/medals', json={"rank": "gold", "sportID": test_id, "athleteID": 1}) + pytest.current_id = x.json()['id'] + assert x.status_code == 200 +def test_get_medal(): + assert requests.get("http://localhost/medals/"+str(pytest.current_id)).status_code == 200 +def test_edit_medal(): + assert requests.patch("http://localhost/medals/"+str(pytest.current_id), json={"athleteID": 88}).status_code == 200 +def test_get_modified_medal(): + x = requests.get("http://localhost/medals/"+str(pytest.current_id)) + json = x.json()['data'] + assert x.status_code == 200 and json['rank'] == "gold" and json['athleteID'] == 88 +def test_delete_medal(): + assert requests.delete("http://localhost/medals/"+str(pytest.current_id)).status_code == 200 +def test_deleted_medal(): + assert requests.get("http://localhost/medals/"+str(pytest.current_id)).status_code == 404 +def test_add_fake_medal(): + requests.post('http://localhost/medals', json={"rank": "Loris"}).status_code == 400 \ No newline at end of file diff --git a/pytest/test_sports.py b/pytest/test_sports.py new file mode 100644 index 0000000..cacdb64 --- /dev/null +++ b/pytest/test_sports.py @@ -0,0 +1,28 @@ +import requests +import random +import pytest + + +def pytest_namespace(): + return {'current_id': 0} + +def test_add_new_sport(): + test_id = str(random.randint(1000,9999)) + x = requests.post('http://localhost/sports', json={"name": "TEST", "place": test_id, "category": "Multiplayer"}) + pytest.current_id = x.json()['id'] + assert x.status_code == 200 +def test_get_sport(): + print("http://localhost/sports/"+str(pytest.current_id)) + assert requests.get("http://localhost/sports/"+str(pytest.current_id)).status_code == 200 +def test_edit_sport(): + assert requests.patch("http://localhost/sports/"+str(pytest.current_id), json={"category": "Aquatic"}).status_code == 200 +def test_get_modified_sport(): + x = requests.get("http://localhost/sports/"+str(pytest.current_id)) + json = x.json()['data'] + assert x.status_code == 200 and json['category'] == "Aquatic" +def test_delete_sport(): + assert requests.delete("http://localhost/sports/"+str(pytest.current_id)).status_code == 200 +def test_deleted_sport(): + assert requests.get("http://localhost/sports/"+str(pytest.current_id)).status_code == 404 +def test_add_fake_sport(): + requests.post('http://localhost/sports', json={"rank": "gold", "sportID": 666, "athleteID": 1}).status_code == 400 \ No newline at end of file diff --git a/swagger.yml b/swagger.yml new file mode 100644 index 0000000..54ba976 --- /dev/null +++ b/swagger.yml @@ -0,0 +1,227 @@ +openapi: '3.0.2' +info: + title: API Jo + version: '1.0' +servers: + - url: http://localhost/ + +components: + schemas: + Athlete: + type: object + properties: + name: + type: string + surname: + type: string + email: + type: string + country: + type: string + isDisabled: + type: boolean + default: false + Sport: + type: object + properties: + name: + type: string + place: + type: string + category: + type: string + Medal: + type: object + properties: + rank: + type: string + enum: + - gold + - silver + - bronze + sportID: + type: integer + athleteID: + type: integer + + + + +paths: + /athletes: + get: + tags: + - Athletes + summary: Display athletes list + responses: + '200': + description: OK + post: + tags: + - Athletes + summary: Add a new athlete + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Athlete' + + /athletes/{id}: + parameters: + - name: id + in: path + required: true + schema: + type: integer + example: 1 + get: + tags: + - Athletes + summary: Display one athlete using the ID + responses: + '200': + description: 'OK' + content: + application/json: + schema: + $ref: '#/components/schemas/Athlete' + '404': + description: 'Not found' + patch: + tags: + - Athletes + summary: Modify a athlete using the ID + responses: + '200': + description: 'OK' + delete: + tags: + - Athletes + summary: Delete an athlete using the ID + responses: + '200': + description: 'OK' + '404': + description: 'Athete not found' + /sports: + get: + tags: + - Sports + summary: List all available sports + responses: + '200': + description: OK + post: + tags: + - Sports + summary: Add a sport + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Sport' + /sports/{id}: + get: + tags: + - Sports + summary: Display a specific sport using the ID + responses: + '200': + description: 'OK' + content: + application/json: + schema: + $ref: '#/components/schemas/Sport' + '404': + description: 'Not found' + parameters: + - name: id + in: path + required: true + schema: + type: integer + example: 1 + patch: + tags: + - Sports + summary: Edit a specific sport using the ID + responses: + '200': + description: 'OK' + delete: + tags: + - Sports + summary: Delete a specific sport using the ID + responses: + '200': + description: 'OK' + '404': + description: 'Sport not found' + /medal: + get: + tags: + - Medal + summary: Display medals list + responses: + '200': + description: OK + post: + tags: + - Medal + summary: Add a new medal + responses: + '200': + description: OK + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/Medal' + /medals/{id}: + get: + tags: + - Medal + summary: Display a medal + responses: + '200': + description: 'OK' + content: + application/json: + schema: + $ref: '#/components/schemas/Medal' + '404': + description: 'Not found' + parameters: + - name: id + in: path + required: true + schema: + type: integer + example: 1 + patch: + tags: + - Medal + summary: Modify element from medal + responses: + '200': + description: 'OK' + '404': + description: 'Medal not found' + delete: + tags: + - Medal + summary: Delete medal from Id + responses: + '200': + description: 'OK' + '404': + description: 'Medal not found' \ No newline at end of file