From b914f9ed86f3850a79239a4965d5aeb7019f6d63 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Tue, 24 Oct 2023 19:58:34 +0200 Subject: [PATCH 01/16] build app --- app.py | 20 ++++++++++++++++++++ main.py | 9 +++++++++ 2 files changed, 29 insertions(+) create mode 100644 app.py create mode 100644 main.py diff --git a/app.py b/app.py new file mode 100644 index 0000000..79793b3 --- /dev/null +++ b/app.py @@ -0,0 +1,20 @@ +from enum import Enum +from fastapi import FastAPI +from pydantic import BaseModel + +app = FastAPI() + + +class Category(Enum): + TOOLS = 'tools' + CONSUMABLES = 'consumables' + + +class Item(BaseModel): + name: str + price: float + count: int + id: int + category: Category + + diff --git a/main.py b/main.py new file mode 100644 index 0000000..94f3703 --- /dev/null +++ b/main.py @@ -0,0 +1,9 @@ +items = { + 0: Item(name="Hammer", price=9.99, count=20, id=0, category=Category.TOOLS), + 1: Item(name="Pliers", price=5.99, count=20, id=1, category=Category.TOOLS), + 2: Item(name="Nails", price=1.99, count=100, id=2, category=Category.CONSUMABLES), +} + +@app.get("/") +def index() -> dict[str, dict[int, Item]]: + return {"items": items} \ No newline at end of file From 276649451e906f459b8e98df3778d1d305a1885c Mon Sep 17 00:00:00 2001 From: Matthieu Date: Tue, 24 Oct 2023 20:36:01 +0200 Subject: [PATCH 02/16] build decorator --- app.py | 18 +++--------------- decorator.py | 14 ++++++++++++++ main.py | 35 ++++++++++++++++++++++++++--------- 3 files changed, 43 insertions(+), 24 deletions(-) create mode 100644 decorator.py diff --git a/app.py b/app.py index 79793b3..406b689 100644 --- a/app.py +++ b/app.py @@ -1,20 +1,8 @@ -from enum import Enum from fastapi import FastAPI -from pydantic import BaseModel app = FastAPI() -class Category(Enum): - TOOLS = 'tools' - CONSUMABLES = 'consumables' - - -class Item(BaseModel): - name: str - price: float - count: int - id: int - category: Category - - +@app.get("/") +def index(): + return {"items": 1} diff --git a/decorator.py b/decorator.py new file mode 100644 index 0000000..2a584f2 --- /dev/null +++ b/decorator.py @@ -0,0 +1,14 @@ +# In this script we build the decorator and the function we want to transform into an API + +def fast_api_decorator(func): + def example(*param, **param2): + print("Action avant") + func(*param, **param2) + print("Action après") + + return example + + +@fast_api_decorator +def function_to_transform(x, a): + return x**a diff --git a/main.py b/main.py index 94f3703..8fecc3b 100644 --- a/main.py +++ b/main.py @@ -1,9 +1,26 @@ -items = { - 0: Item(name="Hammer", price=9.99, count=20, id=0, category=Category.TOOLS), - 1: Item(name="Pliers", price=5.99, count=20, id=1, category=Category.TOOLS), - 2: Item(name="Nails", price=1.99, count=100, id=2, category=Category.CONSUMABLES), -} - -@app.get("/") -def index() -> dict[str, dict[int, Item]]: - return {"items": items} \ No newline at end of file +""" +FastApiDecoratorBuilder + +Description du Projet : +Votre défi dans le projet "FastApiDecoratorBuilder" est de concevoir un décorateur Python qui +transforme une fonction Python en une API FastAPI basée sur la fonction et des configurations +définies. + +Objectifs du Projet : +- Création de Décorateur: Construire un décorateur qui transforme une fonction Python en API FastAPI. +- Gestion de Configurations: Implanter un mécanisme de configuration pour l’API. + +Consignes : +1) Développement du Décorateur : Elaborez un décorateur qui, appliqué à une fonction, génère une API FastAPI +correspondante. +2) Configuration de l'API : Intégrez une méthode pour configurer les propriétés de l’API générée, telles que +les routes et les méthodes HTTP acceptées. +""" + +from decorator import fast_api_decorator, function_to_transform +from app import app +import requests + + +if __name__ == "__main__": + print(requests.get("http://127.0.0.1:8000/").json()) From 50c7ef3d50c4835faaa41496b8cd8f73795d7f06 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Tue, 24 Oct 2023 23:20:10 +0200 Subject: [PATCH 03/16] build decorator --- decorator.py | 24 +++++++++++++++--------- main.py | 5 +++-- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/decorator.py b/decorator.py index 2a584f2..ff4ed82 100644 --- a/decorator.py +++ b/decorator.py @@ -1,14 +1,20 @@ # In this script we build the decorator and the function we want to transform into an API +from app import app -def fast_api_decorator(func): - def example(*param, **param2): - print("Action avant") - func(*param, **param2) - print("Action après") - return example +def fast_api_decorator(route, methods): + def decorator(func): + def wrapper(x, a): + print("before func") + func(x, a) + print("after func") + # add root and function return to the API + app.add_api_route(route, wrapper, methods=methods) + return wrapper + return decorator -@fast_api_decorator -def function_to_transform(x, a): - return x**a + +@fast_api_decorator(route="/power", methods=["GET"]) +def power_function(x, a): + return {f"{x} to the power of {a}": x**a} diff --git a/main.py b/main.py index 8fecc3b..2b9c3f3 100644 --- a/main.py +++ b/main.py @@ -17,10 +17,11 @@ les routes et les méthodes HTTP acceptées. """ -from decorator import fast_api_decorator, function_to_transform +from decorator import power_function from app import app import requests if __name__ == "__main__": - print(requests.get("http://127.0.0.1:8000/").json()) + power_function(x=9, a=2) + print(requests.get("http://127.0.0.1:8000/power").json()) From 7cfe61b2e6b38236bf6feb8a0f23bc3ceb8a1617 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Wed, 25 Oct 2023 10:45:53 +0200 Subject: [PATCH 04/16] custom app --- app.py | 16 ++++++++++++++++ decorator.py | 5 +++-- main.py | 4 +++- 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/app.py b/app.py index 406b689..b735617 100644 --- a/app.py +++ b/app.py @@ -6,3 +6,19 @@ @app.get("/") def index(): return {"items": 1} + + +class CustomApp(FastAPI): + def get_func_result(self, func): + def wrapper(*args, **kwargs): + result = func(*args, **kwargs) + return result + return wrapper + + +custom_app = CustomApp() + + +@custom_app.get_func_result("/") +def example(): + return {"items": 2} diff --git a/decorator.py b/decorator.py index ff4ed82..a6400e4 100644 --- a/decorator.py +++ b/decorator.py @@ -4,10 +4,11 @@ def fast_api_decorator(route, methods): def decorator(func): - def wrapper(x, a): + def wrapper(*args, **kwargs): print("before func") - func(x, a) + func(*args, **kwargs) print("after func") + return func(*args, **kwargs) # add root and function return to the API app.add_api_route(route, wrapper, methods=methods) diff --git a/main.py b/main.py index 2b9c3f3..f870325 100644 --- a/main.py +++ b/main.py @@ -17,8 +17,10 @@ les routes et les méthodes HTTP acceptées. """ +# Use command : "python -m uvicorn main:app --reload" to lauch server and be able to request the "app" API. + from decorator import power_function -from app import app +from app import app, custom_app import requests From f2640fa62676966dbb7ce27b5ce60d2d4df16fda Mon Sep 17 00:00:00 2001 From: Matthieu Date: Sun, 29 Oct 2023 13:37:02 +0100 Subject: [PATCH 05/16] power function ok --- app.py | 38 ++++++++++++++++++++++++-------------- decorator.py | 19 ++++++++----------- main.py | 12 ++++++++---- 3 files changed, 40 insertions(+), 29 deletions(-) diff --git a/app.py b/app.py index b735617..40f1535 100644 --- a/app.py +++ b/app.py @@ -1,24 +1,34 @@ -from fastapi import FastAPI +from fastapi import FastAPI, APIRouter +my_router = APIRouter() app = FastAPI() +""" +def square_function(x: str): + return {f"{x} square equals": int(x)**2} -@app.get("/") -def index(): - return {"items": 1} +def power_function(x: str, a: str): + return {f"{x} to the power of {a}": int(x)**int(a)} -class CustomApp(FastAPI): - def get_func_result(self, func): - def wrapper(*args, **kwargs): - result = func(*args, **kwargs) - return result - return wrapper +route_path = '/power_function/{x}/{a}' +my_router.add_api_route(route_path, endpoint=power_function) +app.include_router(my_router) -custom_app = CustomApp() +""" -@custom_app.get_func_result("/") -def example(): - return {"items": 2} +def fast_api_decorator(func): + def wrapper(x, a): + my_router.add_api_route(path='/{func}/{x}/{a}', endpoint=func) + app.include_router(my_router) + return wrapper + + +@fast_api_decorator +def power_function(x: str, a: str): + return {f"{x} to the power of {a}": int(x)**int(a)} + + +power_function(x=0, a=0) diff --git a/decorator.py b/decorator.py index a6400e4..3e40809 100644 --- a/decorator.py +++ b/decorator.py @@ -1,21 +1,18 @@ # In this script we build the decorator and the function we want to transform into an API -from app import app +from app import app, my_router -def fast_api_decorator(route, methods): +def fast_api_decorator(route): def decorator(func): - def wrapper(*args, **kwargs): - print("before func") - func(*args, **kwargs) - print("after func") - return func(*args, **kwargs) - - # add root and function return to the API - app.add_api_route(route, wrapper, methods=methods) + def wrapper(x, a): + func(x, a) + return func(x, a) + my_router.add_api_route(path='/{func}/{x}/{a}', endpoint=wrapper) + app.include_router(my_router) return wrapper return decorator -@fast_api_decorator(route="/power", methods=["GET"]) +@fast_api_decorator(route="") def power_function(x, a): return {f"{x} to the power of {a}": x**a} diff --git a/main.py b/main.py index f870325..360823d 100644 --- a/main.py +++ b/main.py @@ -19,11 +19,15 @@ # Use command : "python -m uvicorn main:app --reload" to lauch server and be able to request the "app" API. -from decorator import power_function -from app import app, custom_app +from app import app, my_router, power_function import requests +def power(x, a): + print(requests.get(f"http://127.0.0.1:8000/power_function/{x}/{a}").json()) + + if __name__ == "__main__": - power_function(x=9, a=2) - print(requests.get("http://127.0.0.1:8000/power").json()) + power(x="9", a="2") + # power_function(x="9", a="2") + # print(requests.get("http://127.0.0.1:8000/power_function/10/2").json()) From 792b8890135418d675e32c09edaaedcc803edaf2 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Wed, 1 Nov 2023 13:32:41 +0100 Subject: [PATCH 06/16] add query params and specific route for each function --- app.py | 39 ++++++++++++++++++++------------------- main.py | 21 ++++++++++++++++----- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/app.py b/app.py index 40f1535..5356f99 100644 --- a/app.py +++ b/app.py @@ -3,32 +3,33 @@ my_router = APIRouter() app = FastAPI() -""" -def square_function(x: str): - return {f"{x} square equals": int(x)**2} +def fast_api_decorator(route, method): + def decorator(func): + def wrapper(*args, **kwargs): + my_router.add_api_route(path=route, endpoint=func, methods=method) + app.include_router(my_router) + return func(*args, **kwargs) + return wrapper + return decorator + +@fast_api_decorator(route="/power/", method=["GET"]) def power_function(x: str, a: str): return {f"{x} to the power of {a}": int(x)**int(a)} -route_path = '/power_function/{x}/{a}' -my_router.add_api_route(route_path, endpoint=power_function) -app.include_router(my_router) - -""" - +@fast_api_decorator(route="/add/", method=["GET"]) +def add_function(x: str, a: str): + return {f"{x} + {a} equals": int(x) + int(a)} -def fast_api_decorator(func): - def wrapper(x, a): - my_router.add_api_route(path='/{func}/{x}/{a}', endpoint=func) - app.include_router(my_router) - return wrapper - -@fast_api_decorator -def power_function(x: str, a: str): - return {f"{x} to the power of {a}": int(x)**int(a)} +@fast_api_decorator(route="/sous/", method=["POST"]) +def sous_function(x: str, a: str, b: str): + return {f"{x} - {a} - {b} equals": int(x) - int(a) - int(b)} -power_function(x=0, a=0) +# On "lance" les fonctions pour qu'elles soient lisibles par l'app FastAPI +power_function(x="0", a="0") +add_function(x="0", a="0") +sous_function(x="0", a="0", b="0") diff --git a/main.py b/main.py index 360823d..79dc920 100644 --- a/main.py +++ b/main.py @@ -19,15 +19,26 @@ # Use command : "python -m uvicorn main:app --reload" to lauch server and be able to request the "app" API. -from app import app, my_router, power_function +from app import app, power_function, add_function, sous_function import requests def power(x, a): - print(requests.get(f"http://127.0.0.1:8000/power_function/{x}/{a}").json()) + print(requests.get(f"http://127.0.0.1:8000/power/?x={x}&a={a}").json()) + + +def add(x, a): + print(requests.get(f"http://127.0.0.1:8000/add/?x={x}&a={a}").json()) + + +def sous(x, a, b): + print(requests.post(f"http://127.0.0.1:8000/sous/?x={x}&a={a}&b={b}").json()) if __name__ == "__main__": - power(x="9", a="2") - # power_function(x="9", a="2") - # print(requests.get("http://127.0.0.1:8000/power_function/10/2").json()) + power(x=9, a=2) + add(x=9, a=2) + sous(x=9, a=2, b=1) + print(power_function(x="9", a="2")) + print(add_function(x="9", a="2")) + print(sous_function(x="9", a="2", b="1")) From 3436c5b3591516eda47ce0cd3c0d30ce4f544264 Mon Sep 17 00:00:00 2001 From: Spydie78 <61595590+Spydie78@users.noreply.github.com> Date: Wed, 1 Nov 2023 19:21:30 +0100 Subject: [PATCH 07/16] Update README.md --- README.md | 46 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1edc188..43b978f 100644 --- a/README.md +++ b/README.md @@ -1 +1,45 @@ -# Python_OOP_Project \ No newline at end of file +# Python_OOP_Project + +**FastApiDecoratorBuilder** + +"FastApiDecoratorBuilder" est un algorithme permettant de concevoir un décorateur Python qui transforme une fonction Python en une API FastAPI. + +**Installation** + +1. Télécharger les fichiers Python +2. Installer l'application FastAPI + pip install fastapi +3. Installer le serveur ASGI + pip install "uvicorn[standard]" +4. Lancer le serveur dans le terminal de l'application Python + python -m uvicorn main:app --reload +5. Le serveur API démarrera et sera accessible à http://127.0.0.1:8000. + +**Utilisation** +Pour utiliser les 3 fonctions comprisent dans l'API, il suffit d'entrer les points de terminaison suivants après le lien http://127.0.0.1:8000 + +1. Power + Description : Calcul de la puissance d'un nombre + Paramètres : + - 'x': Nombre (integer) + - 'a': La puissance (integer) + Exemple : '/power/?x=2&a=3' retournera le résultat de 2 à la puissance 3 + +2. Add + Description : Calcul l'addition de deux nombres + Paramètres : + - 'x': Nombre (integer) + - 'a': Nombre (integer) + Exemple : '/add/?x=2&a=3' retournera la somme de 2 et 3 + +3. Sous + Description : Calcul la soustraction de trois nombres + Paramètres : + - 'x': Nombre à soustraire(integer) + - 'a': Nombre (integer) + - 'b': Nombre (integer) + Exemple : '/sous/?x=10&a=3&b=2' pour soustraire 3 et 2 à 10 + + + + From a3e405f15686cbcb5e9a6be444778a28283f5629 Mon Sep 17 00:00:00 2001 From: Spydie78 <61595590+Spydie78@users.noreply.github.com> Date: Wed, 1 Nov 2023 19:22:44 +0100 Subject: [PATCH 08/16] Update README.md --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 43b978f..81d1d19 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ Pour utiliser les 3 fonctions comprisent dans l'API, il suffit d'entrer les poin Paramètres : - 'x': Nombre (integer) - 'a': La puissance (integer) + Exemple : '/power/?x=2&a=3' retournera le résultat de 2 à la puissance 3 2. Add @@ -30,6 +31,7 @@ Pour utiliser les 3 fonctions comprisent dans l'API, il suffit d'entrer les poin Paramètres : - 'x': Nombre (integer) - 'a': Nombre (integer) + Exemple : '/add/?x=2&a=3' retournera la somme de 2 et 3 3. Sous @@ -38,6 +40,7 @@ Pour utiliser les 3 fonctions comprisent dans l'API, il suffit d'entrer les poin - 'x': Nombre à soustraire(integer) - 'a': Nombre (integer) - 'b': Nombre (integer) + Exemple : '/sous/?x=10&a=3&b=2' pour soustraire 3 et 2 à 10 From fb9a9f6f7b3253e130adf415d065014c62d7ad37 Mon Sep 17 00:00:00 2001 From: Spydie78 <61595590+Spydie78@users.noreply.github.com> Date: Wed, 1 Nov 2023 19:45:26 +0100 Subject: [PATCH 09/16] Update README.md --- README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 81d1d19..d53b588 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,24 @@ -# Python_OOP_Project - -**FastApiDecoratorBuilder** +# FastApiDecoratorBuilder "FastApiDecoratorBuilder" est un algorithme permettant de concevoir un décorateur Python qui transforme une fonction Python en une API FastAPI. -**Installation** +## Installation 1. Télécharger les fichiers Python 2. Installer l'application FastAPI - pip install fastapi + $ pip install fastapi 3. Installer le serveur ASGI - pip install "uvicorn[standard]" + $ pip install "uvicorn[standard]" 4. Lancer le serveur dans le terminal de l'application Python - python -m uvicorn main:app --reload + $ python -m uvicorn main:app --reload 5. Le serveur API démarrera et sera accessible à http://127.0.0.1:8000. -**Utilisation** -Pour utiliser les 3 fonctions comprisent dans l'API, il suffit d'entrer les points de terminaison suivants après le lien http://127.0.0.1:8000 +## Utilisation +Le décorateur 'Fast_api_decorator' ajoute une route avec un endpoint correspondant aux paramètres de la requête (paramètres de la fonction). Le décorateur appliqué à une fonction puis "lancée" sur le même script de l'instance FastAPI (app) permet de requêter l'API avec une route qui dépend de la fonction et de ses paramètres. +L'API est configurée directement grâce aux paramètres du décorateur avec les routes ("/power/", "/Add/" et "/Sous/") et les méthode HTTP ("GET" et "Post"). + +## Test +Dans le code suivant, il y a trois fonctions qui ont été implémenté dans l'API. Il suffit d'entrer les points de terminaison suivants après le lien 'http://127.0.0.1:8000'. 1. Power Description : Calcul de la puissance d'un nombre From fa6c6c7f4671c995686117fcef699335e336ba81 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Wed, 1 Nov 2023 19:49:15 +0100 Subject: [PATCH 10/16] readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d53b588..001a724 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,8 @@ 5. Le serveur API démarrera et sera accessible à http://127.0.0.1:8000. ## Utilisation -Le décorateur 'Fast_api_decorator' ajoute une route avec un endpoint correspondant aux paramètres de la requête (paramètres de la fonction). Le décorateur appliqué à une fonction puis "lancée" sur le même script de l'instance FastAPI (app) permet de requêter l'API avec une route qui dépend de la fonction et de ses paramètres. -L'API est configurée directement grâce aux paramètres du décorateur avec les routes ("/power/", "/Add/" et "/Sous/") et les méthode HTTP ("GET" et "Post"). +Le décorateur 'fast_api_decorator' ajoute une route avec un endpoint correspondant aux paramètres de la requête (paramètres de la fonction). Le décorateur appliqué à une fonction puis "lancée" sur le même script de l'instance FastAPI (app) permet de requêter l'API avec une route qui dépend de la fonction et de ses propres paramètres. +L'API est configurée directement grâce aux paramètres du décorateur avec les routes ("/power/", "/add/" et "/sous/") et les méthodes HTTP ("GET", "POST", "PUT", "DELETE"). ## Test Dans le code suivant, il y a trois fonctions qui ont été implémenté dans l'API. Il suffit d'entrer les points de terminaison suivants après le lien 'http://127.0.0.1:8000'. From 6b79daaf5aa9d204b031a1080c0d455c81ee7d1a Mon Sep 17 00:00:00 2001 From: Matthieu Date: Fri, 10 Nov 2023 12:15:06 +0100 Subject: [PATCH 11/16] change struct --- app.py | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/app.py b/app.py index 5356f99..d201419 100644 --- a/app.py +++ b/app.py @@ -1,5 +1,6 @@ from fastapi import FastAPI, APIRouter + my_router = APIRouter() app = FastAPI() @@ -24,12 +25,45 @@ def add_function(x: str, a: str): return {f"{x} + {a} equals": int(x) + int(a)} -@fast_api_decorator(route="/sous/", method=["POST"]) -def sous_function(x: str, a: str, b: str): - return {f"{x} - {a} - {b} equals": int(x) - int(a) - int(b)} +@fast_api_decorator(route="/sous/", method=["GET"]) +def sous_function(x: str, lst): + return {f"{x} - {lst[0]} - {lst[1]} equals": int(x) - int(lst[0]) - int(lst[1])} # On "lance" les fonctions pour qu'elles soient lisibles par l'app FastAPI power_function(x="0", a="0") add_function(x="0", a="0") -sous_function(x="0", a="0", b="0") +sous_function(x="0", lst=[0, 0]) + +# résolution pb de lancement des fonctions +""" +from fastapi import FastAPI, APIRouter + +app = FastAPI() + +class PowerEndpoint: + router = APIRouter() + + @router.get("/power/") + async def power_function(self, x: str, a: str): + return {f"{x} to the power of {a}": int(x)**int(a)} + +class AddEndpoint: + router = APIRouter() + + @router.get("/add/") + async def add_function(self, x: str, a: str): + return {f"{x} + {a} equals": int(x) + int(a)} + +class SousEndpoint: + router = APIRouter() + + @router.get("/sous/") + async def sous_function(self, x: str, lst): + return {f"{x} - {lst[0]} - {lst[1]} equals": int(x) - int(lst[0]) - int(lst[1])} + +# Including the routers directly in the main app +app.include_router(PowerEndpoint.router, tags=["power"]) +app.include_router(AddEndpoint.router, tags=["add"]) +app.include_router(SousEndpoint.router, tags=["sous"]) +""" \ No newline at end of file From b64dbfc655d15073f5f3e28057c9668787b8965f Mon Sep 17 00:00:00 2001 From: Spydie78 <61595590+Spydie78@users.noreply.github.com> Date: Sun, 12 Nov 2023 15:53:46 +0100 Subject: [PATCH 12/16] Authentification implementation --- Authentification.py | 80 +++++++++++++++++++++++++++++++++++++++++++++ app.py | 57 +++++++++++++++++++++++++++----- main.py | 2 +- 3 files changed, 130 insertions(+), 9 deletions(-) create mode 100644 Authentification.py diff --git a/Authentification.py b/Authentification.py new file mode 100644 index 0000000..87a26ff --- /dev/null +++ b/Authentification.py @@ -0,0 +1,80 @@ +from __future__ import annotations +from typing import Annotated +from fastapi import Depends, FastAPI, HTTPException, status +from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm +from pydantic import BaseModel + + + +fake_users_db = { + "john": { + "username": "john", + "full_name": "John Doe", + "email": "johndoe@example.com", + "hashed_password": "fakehashedsecret", + "disabled": False, + }, + "alice": { + "username": "alice", + "full_name": "Alice Wonderson", + "email": "alice@example.com", + "hashed_password": "fakehashedsecret2", + "disabled": True, + }, +} + + +def fake_hash_password(password: str): + return "fakehashed" + password + + +oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") + + +class User(BaseModel): + username: str + email: str = None + full_name: str = None + disabled: bool = None + + +class UserInDB(User): + hashed_password: str + + +def get_user(db, username: str): + if username in db: + user_dict = db[username] + return UserInDB(**user_dict) + + +def fake_decode_token(token): + # This doesn't provide any security at all + # Check the next version + user = get_user(fake_users_db, token) + return user + + +async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]): + user = fake_decode_token(token) + if not user: + raise HTTPException( + status_code=status.HTTP_401_UNAUTHORIZED, + detail="Invalid authentication credentials", + headers={"WWW-Authenticate": "Bearer"}, + ) + return user + + +async def get_current_active_user(current_user: Annotated[User, Depends(get_current_user)]): + if current_user.disabled: + raise HTTPException(status_code=400, detail="Function not available because you are an Inactive user") + return current_user + +async def get_current_inactive_user(current_user: Annotated[User, Depends(get_current_user)]): + if not current_user.disabled: + raise HTTPException(status_code=400, detail="Function not available because you are an Active user") + return current_user + + + diff --git a/app.py b/app.py index d201419..c8df22f 100644 --- a/app.py +++ b/app.py @@ -1,5 +1,14 @@ -from fastapi import FastAPI, APIRouter - +from typing import Annotated +from functools import lru_cache +from fastapi import FastAPI, APIRouter, Depends, HTTPException +from fastapi.security import OAuth2PasswordRequestForm +from Authentification import User, get_current_active_user, fake_users_db, UserInDB, fake_hash_password, \ + get_current_inactive_user + +from config import Settings +@lru_cache +def get_settings(): + return Settings() my_router = APIRouter() app = FastAPI() @@ -8,6 +17,7 @@ def fast_api_decorator(route, method): def decorator(func): def wrapper(*args, **kwargs): + #if current_user in kwargs.values(): my_router.add_api_route(path=route, endpoint=func, methods=method) app.include_router(my_router) return func(*args, **kwargs) @@ -15,22 +25,53 @@ def wrapper(*args, **kwargs): return decorator -@fast_api_decorator(route="/power/", method=["GET"]) -def power_function(x: str, a: str): - return {f"{x} to the power of {a}": int(x)**int(a)} - @fast_api_decorator(route="/add/", method=["GET"]) def add_function(x: str, a: str): return {f"{x} + {a} equals": int(x) + int(a)} - @fast_api_decorator(route="/sous/", method=["GET"]) def sous_function(x: str, lst): return {f"{x} - {lst[0]} - {lst[1]} equals": int(x) - int(lst[0]) - int(lst[1])} -# On "lance" les fonctions pour qu'elles soient lisibles par l'app FastAPI + +@fast_api_decorator(route="/users/me", method=["GET"]) +async def read_users_me(current_user: User = Depends(get_current_active_user)): + return current_user + +@fast_api_decorator(route="/power/", method=["POST"]) +async def power_function(x: float, a: float, current_user: User = Depends(get_current_inactive_user)): + return {f"{x} to the power of {a}": float(x)**float(a)} + +@fast_api_decorator(route="/rendement/", method=["POST"]) +async def rendement(x: int, r: float, current_user: User = Depends(get_current_active_user)): + return {f"{x} * (1 + {r}) equals": int(x) * (1 + float(r))} + +@app.post("/token") +async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]): + user_dict = fake_users_db.get(form_data.username) + if not user_dict: + raise HTTPException(status_code=400, detail="Incorrect username or password") + user = UserInDB(**user_dict) + hashed_password = fake_hash_password(form_data.password) + if not hashed_password == user.hashed_password: + raise HTTPException(status_code=400, detail="Incorrect username or password") + + return {"access_token": user.username, "token_type": "bearer"} + +@app.get("/env") +async def info(settings: Annotated[Settings, Depends(get_settings)]): + return { + "default variable": settings.DEFAULT_VAR, + "api key": settings.API_KEY, + "app max integer": settings.APP_MAX, + } + + +# On "lance" les fonctions pour qu'elles soient visibles par l'app FastAPI +read_users_me() +rendement(x="0", r="0") power_function(x="0", a="0") add_function(x="0", a="0") sous_function(x="0", lst=[0, 0]) diff --git a/main.py b/main.py index 79dc920..d656bd3 100644 --- a/main.py +++ b/main.py @@ -41,4 +41,4 @@ def sous(x, a, b): sous(x=9, a=2, b=1) print(power_function(x="9", a="2")) print(add_function(x="9", a="2")) - print(sous_function(x="9", a="2", b="1")) + print(sous_function(x="9", lst = "[2, 1]")) From ea33057d05c21ee1eb904aae136637d74f081e1e Mon Sep 17 00:00:00 2001 From: Spydie78 <61595590+Spydie78@users.noreply.github.com> Date: Sat, 18 Nov 2023 14:59:08 +0100 Subject: [PATCH 13/16] .env implementation --- .env | 3 +++ app.py | 65 ++++++++++++++++++++++++++++++++++++++++++++--------- settings.py | 6 +++++ 3 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 .env create mode 100644 settings.py diff --git a/.env b/.env new file mode 100644 index 0000000..c42a131 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +ADMIN_EMAIL="deadpool@example.com" +APP_NAME="ChimichangApp" + diff --git a/app.py b/app.py index c8df22f..948f9e7 100644 --- a/app.py +++ b/app.py @@ -5,15 +5,65 @@ from Authentification import User, get_current_active_user, fake_users_db, UserInDB, fake_hash_password, \ get_current_inactive_user -from config import Settings + +from settings import Settings + + +my_router = APIRouter() +app = FastAPI() + + @lru_cache def get_settings(): return Settings() -my_router = APIRouter() -app = FastAPI() + +@app.get("/info") +async def info(settings: Annotated[Settings, Depends(get_settings)]): + return { + "app_name": settings.app_name, + "admin_email": settings.admin_email, + "items_per_user": settings.items_per_user, + } + + + + +# @app.get("/info") +# async def info(): +# return { +# "app_name": settings.app_name, +# "admin_email": settings.admin_email, +# "items_per_user": settings.items_per_user, +# } + +# class Settings(BaseSettings): +# DEFAULT_VAR: str = "some default string value" # default value if env variable does not exist +# API_KEY: str = "ma key" +# APP_MAX: int = 100 # default value if env variable does not exist +# +# model_config = SettingsConfigDict(env_file=".env") + + + +#def get_settings(): +# return Settings() + + +# async def info(settings: Settings = Depends(get_settings)): +# return { +# "default variable": settings.DEFAULT_VAR, +# "api key": settings.API_KEY, +# "app max integer": settings.APP_MAX, +# } +# +# @app.get("/") +# @app.get("/env") +# async def root(): +# return {"settings": settings} + def fast_api_decorator(route, method): def decorator(func): def wrapper(*args, **kwargs): @@ -60,13 +110,7 @@ async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]): return {"access_token": user.username, "token_type": "bearer"} -@app.get("/env") -async def info(settings: Annotated[Settings, Depends(get_settings)]): - return { - "default variable": settings.DEFAULT_VAR, - "api key": settings.API_KEY, - "app max integer": settings.APP_MAX, - } + # On "lance" les fonctions pour qu'elles soient visibles par l'app FastAPI @@ -76,6 +120,7 @@ async def info(settings: Annotated[Settings, Depends(get_settings)]): add_function(x="0", a="0") sous_function(x="0", lst=[0, 0]) + # résolution pb de lancement des fonctions """ from fastapi import FastAPI, APIRouter diff --git a/settings.py b/settings.py new file mode 100644 index 0000000..a40fc45 --- /dev/null +++ b/settings.py @@ -0,0 +1,6 @@ +from pydantic_settings import BaseSettings, SettingsConfigDict +class Settings(BaseSettings): + app_name: str = "Awesome" + admin_email: str = "Et" + items_per_user: int = 50 + model_config = SettingsConfigDict(env_file=".env") From c6d805d2509628e9b57a0c5f2e42fda9f7f65f3b Mon Sep 17 00:00:00 2001 From: Spydie78 <61595590+Spydie78@users.noreply.github.com> Date: Sat, 18 Nov 2023 15:21:21 +0100 Subject: [PATCH 14/16] .env implementation --- .env | 7 ++++++- app.py | 51 +++++++-------------------------------------------- 2 files changed, 13 insertions(+), 45 deletions(-) diff --git a/.env b/.env index c42a131..b539844 100644 --- a/.env +++ b/.env @@ -1,3 +1,8 @@ ADMIN_EMAIL="deadpool@example.com" -APP_NAME="ChimichangApp" +APP_NAME="ChimichangAppAAAAAA" +ITEMS_PER_USER=2 + +#ENV="development" +#DATABASE_PASSWORD="motDePasseSecret" +#THIRD_API_PRIVATE_KEY="cleSecrete" diff --git a/app.py b/app.py index 948f9e7..ed016c9 100644 --- a/app.py +++ b/app.py @@ -18,51 +18,7 @@ def get_settings(): return Settings() -@app.get("/info") -async def info(settings: Annotated[Settings, Depends(get_settings)]): - return { - "app_name": settings.app_name, - "admin_email": settings.admin_email, - "items_per_user": settings.items_per_user, - } - - - - - - -# @app.get("/info") -# async def info(): -# return { -# "app_name": settings.app_name, -# "admin_email": settings.admin_email, -# "items_per_user": settings.items_per_user, -# } -# class Settings(BaseSettings): -# DEFAULT_VAR: str = "some default string value" # default value if env variable does not exist -# API_KEY: str = "ma key" -# APP_MAX: int = 100 # default value if env variable does not exist -# -# model_config = SettingsConfigDict(env_file=".env") - - - -#def get_settings(): -# return Settings() - - -# async def info(settings: Settings = Depends(get_settings)): -# return { -# "default variable": settings.DEFAULT_VAR, -# "api key": settings.API_KEY, -# "app max integer": settings.APP_MAX, -# } -# -# @app.get("/") -# @app.get("/env") -# async def root(): -# return {"settings": settings} def fast_api_decorator(route, method): def decorator(func): @@ -111,6 +67,13 @@ async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]): return {"access_token": user.username, "token_type": "bearer"} +@app.get("/info") +async def info(settings: Annotated[Settings, Depends(get_settings)]): + return { + "app_name": settings.app_name, + "admin_email": settings.admin_email, + "items_per_user": settings.items_per_user, + } # On "lance" les fonctions pour qu'elles soient visibles par l'app FastAPI From ed94d684035b0e30a1b80a7e6b2fb74051f742e3 Mon Sep 17 00:00:00 2001 From: Spydie78 <61595590+Spydie78@users.noreply.github.com> Date: Sat, 18 Nov 2023 16:36:12 +0100 Subject: [PATCH 15/16] add Matthieu work --- app.py | 109 ++++++++++++++++++++++++++++++++++++++++++-------------- main.py | 17 +++++---- 2 files changed, 92 insertions(+), 34 deletions(-) diff --git a/app.py b/app.py index ed016c9..59f99e4 100644 --- a/app.py +++ b/app.py @@ -1,10 +1,11 @@ +import asyncio from typing import Annotated from functools import lru_cache -from fastapi import FastAPI, APIRouter, Depends, HTTPException +from fastapi import FastAPI, APIRouter, Depends, HTTPException, Query from fastapi.security import OAuth2PasswordRequestForm from Authentification import User, get_current_active_user, fake_users_db, UserInDB, fake_hash_password, \ get_current_inactive_user - +from pydantic import BaseModel from settings import Settings @@ -17,42 +18,78 @@ def get_settings(): return Settings() +def get_saved_value(): + try: + with open("saved_count.txt", "r") as file: + value = int(file.read()) + except FileNotFoundError: + with open("saved_count.txt", 'w') as file: + file.write('0') + value = 0 + return value + + +request_count = get_saved_value() + +def save_value(value): + with open("saved_count.txt", "w") as file: + file.write(str(value)) -def fast_api_decorator(route, method): +# def fast_api_decorator(route, method): +# def decorator(func): +# def wrapper(*args, **kwargs): +# #if current_user in kwargs.values(): +# my_router.add_api_route(path=route, endpoint=func, methods=method) +# app.include_router(my_router) +# return func(*args, **kwargs) +# return wrapper +# return decorator + +def fast_api_decorator(route, method, type_args): def decorator(func): - def wrapper(*args, **kwargs): - #if current_user in kwargs.values(): + def wrapper(**kwargs): + # Handle argument type error if type_args is not None + if type_args is not None: + for value, expected_type in zip(kwargs.values(), type_args): + if not isinstance(value, expected_type): + raise TypeError(f"Type d'argument incorrect. Attendu : {expected_type.__name__}, Reçu : {type(value).__name__}") + + # Count the number of request + global request_count + request_count += 1 + save_value(request_count) + + # add endpoint to the API my_router.add_api_route(path=route, endpoint=func, methods=method) app.include_router(my_router) - return func(*args, **kwargs) + return func(**kwargs) return wrapper return decorator +@fast_api_decorator(route="/add/", method=["GET"], type_args=[int, int]) +def add_function(x: Annotated[int, Query(description="Int we'll add something")], a: Annotated[int, Query(description="Int added")]): + return {f"{x} + {a} equals": x + a} -@fast_api_decorator(route="/add/", method=["GET"]) -def add_function(x: str, a: str): - return {f"{x} + {a} equals": int(x) + int(a)} - -@fast_api_decorator(route="/sous/", method=["GET"]) -def sous_function(x: str, lst): - return {f"{x} - {lst[0]} - {lst[1]} equals": int(x) - int(lst[0]) - int(lst[1])} +@fast_api_decorator(route="/sous/", method=["GET"], type_args=[int, list]) +def sous_function(x: Annotated[int, Query(description="Int we'll substract something")], lst: Annotated[list[int], Query(description="List of 2 int that will be substracted")]): + return {f"{x} - {lst[0]} - {lst[1]} equals": x - lst[0] - lst[1]} -@fast_api_decorator(route="/users/me", method=["GET"]) -async def read_users_me(current_user: User = Depends(get_current_active_user)): - return current_user +@fast_api_decorator(route="/users/me", method=["GET"],type_args=None) +def read_users_me(current_user: User = Depends(get_current_active_user)): + return current_user -@fast_api_decorator(route="/power/", method=["POST"]) -async def power_function(x: float, a: float, current_user: User = Depends(get_current_inactive_user)): - return {f"{x} to the power of {a}": float(x)**float(a)} +@fast_api_decorator(route="/power/", method=["POST"],type_args=[int, int]) +def power_function(x: Annotated[int, Query(description="Int we'll add something")], a: Annotated[int, Query(description="Int added")], current_user: User = Depends(get_current_inactive_user)): + return {f"{x} to the power of {a}": int(x)**int(a)} -@fast_api_decorator(route="/rendement/", method=["POST"]) -async def rendement(x: int, r: float, current_user: User = Depends(get_current_active_user)): - return {f"{x} * (1 + {r}) equals": int(x) * (1 + float(r))} +@fast_api_decorator(route="/rendement/", method=["POST"],type_args=[int, float]) +def rendement(x: Annotated[int, Query(description="Int we'll add something")], r: Annotated[float, Query(description="float added")], current_user: User = Depends(get_current_active_user)): + return {f"{x} * (1 + {r}) equals": int(x) * (1 + float(r))} @app.post("/token") async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]): @@ -76,13 +113,31 @@ async def info(settings: Annotated[Settings, Depends(get_settings)]): } +class InputDiv(BaseModel): + div: int + + +# Pour faire une requête avec un argument "Body" ou un json avec des arguments il faut passer +# par une méthode "POST" et pas "GET" +@fast_api_decorator(route="/div/", method=["POST"], type_args=[int, InputDiv]) +def div_function(x: Annotated[int, Query(description="Int we will divide something")], item: InputDiv): + return {f"{x} / {item.div} equals": item.div} + + +@app.get("/stats") +async def get_stats(): + request_count = get_saved_value() + return {"request_count": request_count} + + # On "lance" les fonctions pour qu'elles soient visibles par l'app FastAPI read_users_me() -rendement(x="0", r="0") -power_function(x="0", a="0") -add_function(x="0", a="0") -sous_function(x="0", lst=[0, 0]) - +rendement(x=0, r=0.0) +power_function(x=0, a=0) +add_function(x=0, a=0) +sous_function(x=0, lst=[0, 0]) +input_item = InputDiv(div=10) +div_function(x=100, item=input_item) # résolution pb de lancement des fonctions """ diff --git a/main.py b/main.py index d656bd3..1fbe5b0 100644 --- a/main.py +++ b/main.py @@ -19,8 +19,9 @@ # Use command : "python -m uvicorn main:app --reload" to lauch server and be able to request the "app" API. -from app import app, power_function, add_function, sous_function +from app import app, power_function, add_function, sous_function, rendement import requests +import asyncio def power(x, a): @@ -31,14 +32,16 @@ def add(x, a): print(requests.get(f"http://127.0.0.1:8000/add/?x={x}&a={a}").json()) -def sous(x, a, b): - print(requests.post(f"http://127.0.0.1:8000/sous/?x={x}&a={a}&b={b}").json()) +def sous(x, lst): + print(requests.get(f"http://127.0.0.1:8000/sous/?x={x}&lst={lst[0]}&lst={lst[1]}").json()) + if __name__ == "__main__": power(x=9, a=2) add(x=9, a=2) - sous(x=9, a=2, b=1) - print(power_function(x="9", a="2")) - print(add_function(x="9", a="2")) - print(sous_function(x="9", lst = "[2, 1]")) + sous(x=9, lst=[2, 1]) + print(rendement(x=0, r=0.0)) + print(power_function(x=9, a=2)) + print(add_function(x=9, a=2)) + print(sous_function(x=9, lst=[2, 1])) From a87b733dd9085f273ce0707d564473c7f21f4807 Mon Sep 17 00:00:00 2001 From: Spydie78 <61595590+Spydie78@users.noreply.github.com> Date: Sat, 18 Nov 2023 16:36:59 +0100 Subject: [PATCH 16/16] delete comments --- app.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app.py b/app.py index 59f99e4..f7c7351 100644 --- a/app.py +++ b/app.py @@ -37,16 +37,6 @@ def save_value(value): file.write(str(value)) -# def fast_api_decorator(route, method): -# def decorator(func): -# def wrapper(*args, **kwargs): -# #if current_user in kwargs.values(): -# my_router.add_api_route(path=route, endpoint=func, methods=method) -# app.include_router(my_router) -# return func(*args, **kwargs) -# return wrapper -# return decorator - def fast_api_decorator(route, method, type_args): def decorator(func): def wrapper(**kwargs):