Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
6da3ffe
Merge pull request #1 from Matthieu-Monnot/dev-matthieu
GuillaumeSima Nov 11, 2023
b64dbfc
Authentification implementation
GuillaumeSima Nov 12, 2023
208d105
handle list, objects and json parameters
Matthieu-Monnot Nov 12, 2023
99b6a1e
count in func
Matthieu-Monnot Nov 12, 2023
0bdd5a9
count ok
Matthieu-Monnot Nov 13, 2023
6ffdd07
readme update
Matthieu-Monnot Nov 17, 2023
ea33057
.env implementation
GuillaumeSima Nov 18, 2023
c6d805d
.env implementation
GuillaumeSima Nov 18, 2023
ed94d68
add Matthieu work
GuillaumeSima Nov 18, 2023
a87b733
delete comments
GuillaumeSima Nov 18, 2023
5276413
Merge pull request #3 from Matthieu-Monnot/dev-guillaume
GuillaumeSima Nov 18, 2023
e232cd0
indentation et espace
Matthieu-Monnot Nov 18, 2023
8dfe8db
update readme
Matthieu-Monnot Nov 18, 2023
f32fa5a
finish sur les stats
Matthieu-Monnot Nov 18, 2023
15cdb61
finish sur les stats de compte de requêtes
Matthieu-Monnot Nov 19, 2023
b209a7f
finish temps moyen d'API
Matthieu-Monnot Nov 19, 2023
7d4eb21
commentaires
Matthieu-Monnot Nov 19, 2023
3199f99
add .env and admin
GuillaumeSima Nov 19, 2023
95e3137
Merge branch 'main' into dev-guillaume
GuillaumeSima Nov 19, 2023
f0909de
Merge pull request #4 from Matthieu-Monnot/dev-guillaume
GuillaumeSima Nov 19, 2023
f868e70
Merge branch 'dev-matthieu'
Matthieu-Monnot Nov 19, 2023
d226442
merge
Matthieu-Monnot Nov 19, 2023
1215650
merge
Matthieu-Monnot Nov 19, 2023
c08794b
update
Matthieu-Monnot Nov 19, 2023
d3dd569
round ms time
Matthieu-Monnot Nov 19, 2023
55fc6ee
Merge pull request #5 from Matthieu-Monnot/main
GuillaumeSima Nov 19, 2023
7874dba
Readme et commentaire
GuillaumeSima Nov 19, 2023
792fa96
Merge pull request #6 from Matthieu-Monnot/dev-guillaume
GuillaumeSima Nov 19, 2023
18268ac
Merge branch 'dev-martin' into main
martin-bod Nov 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# TITLE: The title of the FastAPI application
TITLE="FastApi Decorator Builder"
# DESCRIPTION: A description of the API
DESCRIPTION="FastApi Decorator Builder est un algorithme permettant de concevoir un décorateur Python qui transforme
une fonction Python en une API FastAPI. Les fonctionnalités sont accessible suivant une authentification."
# ADMIN_EMAIL: The email address for the admin user of the application
ADMIN_EMAIL="admin@fastApi.com"
# COMMAND_LOAD: The command to start the FastAPI application using uvicorn with automatic reloading
COMMAND_LOAD="python -m uvicorn main:app --reload"
# URL: The base URL of the FastAPI application
URL="http://127.0.0.1:8000"
# SAVE_COUNT_FILE: The file name to save the count data
SAVE_COUNT_FILE="saved_count.pki"
# HASH_PASSWORD: A fake hashed password
HASH_PASSWORD="fakehashed"


117 changes: 117 additions & 0 deletions Authentification.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
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

# Users database with 2 regular users and 1 administrator
fake_users_db = {
"admin": {
"username": "admin",
"full_name": "admin",
"email": "admin@fastApi.com",
"hashed_password": "fakehashedsecret",
"disabled": False,
"admin": True,
},
"alice": {
"username": "alice",
"full_name": "Alice Wonderson",
"email": "alice@example.com",
"hashed_password": "fakehashedsecret2",
"disabled": True,
"admin": False,
},
"bod": {
"username": "bod",
"full_name": "bod Wonderson",
"email": "bod@example.com",
"hashed_password": "fakehashedsecret1",
"disabled": False,
"admin": False,
},
}


def fake_hash_password(password: str):
"""
Password hashing function.
:param password: The input password to be hashed.
:return: The hashed password.
"""
return "fakehashed" + password

# OAuth2 password bearer scheme
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

# Pydantic class to represent the structure of a user
class User(BaseModel):
username: str
email: str = None
full_name: str = None
disabled: bool = None
admin: bool = None


class UserInDB(User):
hashed_password: str


def get_user(db, username: str):
"""
Get user from the database.
:param db: The user database.
:param username: The username of the user to retrieve.
:return: An instance of UserInDB if the user exists, otherwise None.
"""
if username in db:
user_dict = db[username]
return UserInDB(**user_dict)


def fake_decode_token(token):
"""
function to decode a token.
:param token: The token to be decoded.
:return: An instance of UserInDB if the user exists, otherwise None.
"""
user = get_user(fake_users_db, token)
return user


async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):
"""
Get the current user based on the provided token.
:param token: The OAuth2 token.
:return: An instance of UserInDB if the user exists, otherwise raise HTTPException.
"""
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)]):
"""
Get the current active user based on the current user.
:param current_user: The current user.
:return: The current user if active, otherwise raise HTTPException.
"""
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_admin_user(current_user: Annotated[User, Depends(get_current_user)]):
"""
Get the current admin user based on the current user.
:param current_user: The current user.
:return:The current user if an admin, otherwise raise HTTPException.
"""
if not current_user.admin:
raise HTTPException(status_code=400, detail="Function not available because you are not an Administrator user")
return current_user
107 changes: 90 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# FastApiDecoratorBuilder
# FastApi Decorator Builder

"FastApiDecoratorBuilder" est un algorithme permettant de concevoir un décorateur Python qui transforme une fonction Python en une API FastAPI.
"FastApi Decorator Builder" est un algorithme permettant de concevoir un décorateur Python qui transforme
une fonction Python en une API FastAPI. Les fonctionnalités sont accessible suivant une authentification.
L'API permet de collecter des données statisques.

## Installation

Expand All @@ -11,40 +13,111 @@
$ 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.
5. Le serveur API démarrera et sera accessible à l'adresse suivante : http://127.0.0.1:8000.
6. Un aperçu des requêtes implémentées est disponible à l'adresse suivante : http://127.0.0.1:8000/docs.

## 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 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").
1. Le décorateur
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 exécutée sur le même script de l'instance
FastAPI (app) permet de rendre utilisable l'API avec une route qui dépend de la fonction et de ses propres paramètres.
Ainsi, une fois cette étape réalisée, il est possible de requêter l'API de la fonction à laquelle on a appliqué le
décorateur avec n'importe quels arguments. La réponse de l'API est évidemment l'output de cette fonction.
L'API est configurée directement grâce aux paramètres du décorateur avec les routes (ex: "/power/", "/add/" ou "/sous/"),
les méthodes HTTP (ex: "GET", "POST", "PUT", "DELETE") et une liste de type correspondants aux types des arguments de la
fonction décorée.

## 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'.

2. L'authentification
Cet API utilise une authentification OAuth2 Password Bearer pour assurer la sécurité des endpoints.
Trois types d'utilisateurs sont définis: administrateur (username : "admin" & password : "secret"), utilisateur régulier
(username : "bod" & password : "secret1") et utilisateur inactif (username : "alice" & password : "secret2").
L'administrateur a accès à toutes les fonctionnalitées proposées par l'API dont les statistiques.
L'utilisateur actif peut utiliser les fonctions mathématiques alors que l'utilisateur inactif ne peut qu'accèder qu'à la
fonctionnalité 'info' de l'API.
Si un token invalide ou si une fonctionnalité réservée à un type d'utilisateur spécifique est renseigné
sans les permissions nécessaires, l'API renverra une erreur :
- 401 Unauthorized: Le token fourni est invalide.
- 400 Bad Request: Vous n'avez pas les permissions nécessaires pour accéder à cette fonctionnalité.


3. Suivi des Statistiques
Les informations telles que le nombre d'appels par route, le temps moyen d'exécution par route sont collectées
automatiquement à chaque appel d'API. -> enregistré dans un fichier .pki

## Fonctionnalités
Dans le code suivant, il y a plusieurs fonctions qui ont été implémenté dans l'API. Certaines fonctions nécéssitent des authentifications.

### Information API
Info
Description : Obtenir des informations sur l'API
Route: '/info/'
Accès : Pas d'accès spécifique

### Opérations Mathématiques
1. Power
Description : Calcul de la puissance d'un nombre
Route: '/power/'
Accès via : Un token d'authentification
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
Route: '/add/'
Accès via : Un token d'authentification
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
Route: '/sous/'
Accès via : Un token d'authentification
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
- 'x': Nombre à soustraire (integer)
- [a, b] : liste de nombre (List[int])


4. Div
Description : Calcul la division d'un nombre
Route: '/div/'
Accès via : Un token d'authentification
Paramètres:
- 'x': Numérateur(integer)
- item: Modèle Pydantic contenant le diviseur

### Gestion des Utilisateurs
User
Description : Obtenir des informations sur l'utilisateur actuel
Route: '/users/me'
Accès via : Un token d'authentification

### Obtention d'un token
Token
Description : Obtenir le jeton d'accès
Route: '/token/'
Paramètres:
- 'username': Nom d'utilisateur
- 'password': Mot de passe

### Statistiques
Stats
Description : Obtenir le jeton d'accès
Route: '/stats/'
Accès via : Un token d'authentification d'administrateur


## Compte rendu du projet


Dans FastAPI, les routes doivent être enregistrées auprès de l'instance d'application pour qu'elles soient
accessibles lorsque le serveur est en cours d'exécution. Dans notre code, les routes sont définies à l'aide de
décorateurs qui configurent essentiellement le comportement de routage. Cependant, jusqu'à ce qu'on invoque
les fonctions décorées avec le décorateur @fast_api_decorator, les routes ne sont pas ajoutées à l'application
FastAPI. En effet, lors du lancement de l'application, le programme app.py est exécuté mais les routes ne sont pas
ajoutées tant que le décorateur n'est pas appelé, c'est-à-dire que les fonctions décorées ne sont pas exécutées une
première fois. Il est alors nécessaire d'appeler chaque fonction dans le programme app afin de rendre utilisable
leur route.
3 changes: 2 additions & 1 deletion app.py
Original file line number Diff line number Diff line change
Expand Up @@ -243,4 +243,5 @@ async def info():
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)
div_function(x=100, item=input_item)

35 changes: 9 additions & 26 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,4 @@
"""
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.
"""

# Use command : "python -m uvicorn main:app --reload" to lauch server and be able to request the "app" API.
import app
from app import power_function, add_function, sous_function
from app import app, power_function, add_function, sous_function
import requests


Expand All @@ -30,13 +9,17 @@ def add(x, a):
print(requests.get(f"http://127.0.0.1:8000/add/?x={x}&a={a}").json())

def sous(x, lst):
print(requests.get(f"http://127.0.0.1:8000/sous/?x={x}&lst=[{lst[0]},{lst[1]}]").json())
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, lst=[2, 1])
print(power_function(x="9", a="2"))
print(add_function(x="9", a="2"))
print(sous_function(x="9", lst=[2, 1]))
print(power_function(x=9, a=2))
print(add_function(x=9, a=2))
print(sous_function(x=9, lst=[2, 1]))


12 changes: 12 additions & 0 deletions settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from pydantic_settings import BaseSettings, SettingsConfigDict

# This Pydantic settings class is used to load the configuration parameters from the .env file
class Settings(BaseSettings):
title: str = None
description: str = None
admin_email: str = None
command_load:str = None
url:str = None
save_count_file:str =None
hash_password:str =None