jsonplaceholder-clone 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ from .main import app
2
+ from .schemas import Post, UpdatePost
3
+ from . import crud
@@ -0,0 +1,15 @@
1
+ import uvicorn
2
+
3
+
4
+ # Démarrage du serveur (commande jsonplaceholder-clone ou bouton Play ▷ de VS Code)
5
+ def main():
6
+ uvicorn.run(
7
+ "jsonplaceholder_clone.main:app",
8
+ host="127.0.0.1",
9
+ port=8000,
10
+ reload=True,
11
+ )
12
+
13
+
14
+ if __name__ == "__main__":
15
+ main()
@@ -0,0 +1,54 @@
1
+ from typing import Optional
2
+
3
+ from .database import db_posts
4
+ from .schemas import Post, UpdatePost
5
+
6
+
7
+ # Récupérer tous les posts (avec filtre optionnel par userId)
8
+ def list_posts(user_id: Optional[int] = None) -> list[dict]:
9
+ if user_id is not None:
10
+ return [p for p in db_posts if p["userId"] == user_id]
11
+ return db_posts
12
+
13
+
14
+ # Récupérer UN SEUL post par son ID (None s'il n'existe pas)
15
+ def get_post(post_id: int) -> Optional[dict]:
16
+ return next((p for p in db_posts if p["id"] == post_id), None)
17
+
18
+
19
+ # Créer un nouveau post (None si l'ID existe déjà)
20
+ def create_post(post: Post) -> Optional[dict]:
21
+ # On vérifie si l'ID existe déjà pour éviter les doublons
22
+ if get_post(post.id) is not None:
23
+ return None
24
+ new_post = post.model_dump()
25
+ db_posts.append(new_post)
26
+ return new_post
27
+
28
+
29
+ # Remplacer complètement un post existant (None s'il n'existe pas)
30
+ def replace_post(post_id: int, post: Post) -> Optional[dict]:
31
+ for index, existing in enumerate(db_posts):
32
+ if existing["id"] == post_id:
33
+ db_posts[index] = post.model_dump()
34
+ return db_posts[index]
35
+ return None
36
+
37
+
38
+ # Modifier partiellement un post existant (None s'il n'existe pas)
39
+ def update_post(post_id: int, data: UpdatePost) -> Optional[dict]:
40
+ post = get_post(post_id)
41
+ if post is None:
42
+ return None
43
+ # On ne met à jour que les champs envoyés
44
+ post.update(data.model_dump(exclude_unset=True))
45
+ return post
46
+
47
+
48
+ # Supprimer un post (False s'il n'existe pas)
49
+ def delete_post(post_id: int) -> bool:
50
+ post = get_post(post_id)
51
+ if post is None:
52
+ return False
53
+ db_posts.remove(post)
54
+ return True
@@ -0,0 +1,6 @@
1
+ # Base de données fictive en mémoire
2
+ db_posts: list[dict] = [
3
+ {"userId": 1, "id": 1, "title": "Premier Post", "body": "Ceci est le contenu du premier post."},
4
+ {"userId": 1, "id": 2, "title": "Deuxième Post", "body": "Ceci est le contenu du deuxième post."},
5
+ {"userId": 2, "id": 3, "title": "Troisième Post", "body": "Un post écrit par l'utilisateur 2."},
6
+ ]
@@ -0,0 +1,11 @@
1
+ from fastapi import FastAPI
2
+
3
+ from .routers import posts_router
4
+
5
+ app = FastAPI(
6
+ title="JSONPlaceholder Clone",
7
+ description="Reconstruction de l'API JSONPlaceholder avec FastAPI",
8
+ version="1.0.0",
9
+ )
10
+
11
+ app.include_router(posts_router)
@@ -0,0 +1 @@
1
+ from .posts import router as posts_router
@@ -0,0 +1,61 @@
1
+ from typing import List, Optional
2
+
3
+ from fastapi import APIRouter, HTTPException, status
4
+
5
+ from .. import crud
6
+ from ..schemas import Post, UpdatePost
7
+
8
+ router = APIRouter(prefix="/posts", tags=["posts"])
9
+
10
+ NOT_FOUND = "Post non trouvé"
11
+
12
+
13
+ # [GET] Récupérer TOUS les posts (avec filtre optionnel par userId)
14
+ @router.get("", response_model=List[Post])
15
+ def get_posts(userId: Optional[int] = None):
16
+ return crud.list_posts(userId)
17
+
18
+
19
+ # [GET] Récupérer UN SEUL post par son ID
20
+ @router.get("/{post_id}", response_model=Post)
21
+ def get_post_by_id(post_id: int):
22
+ post = crud.get_post(post_id)
23
+ if post is None:
24
+ raise HTTPException(status_code=404, detail=NOT_FOUND)
25
+ return post
26
+
27
+
28
+ # [POST] Créer un nouveau post
29
+ @router.post("", status_code=status.HTTP_201_CREATED, response_model=Post)
30
+ def create_post(post: Post):
31
+ created = crud.create_post(post)
32
+ # crud renvoie None si l'ID existe déjà
33
+ if created is None:
34
+ raise HTTPException(status_code=400, detail="Un post avec cet ID existe déjà")
35
+ return created
36
+
37
+
38
+ # [PUT] Remplacer complètement un post existant
39
+ @router.put("/{post_id}", response_model=Post)
40
+ def update_post_complete(post_id: int, updated_post: Post):
41
+ result = crud.replace_post(post_id, updated_post)
42
+ if result is None:
43
+ raise HTTPException(status_code=404, detail=NOT_FOUND)
44
+ return result
45
+
46
+
47
+ # [PATCH] Modifier partiellement un post existant
48
+ @router.patch("/{post_id}", response_model=Post)
49
+ def update_post_partial(post_id: int, partial_data: UpdatePost):
50
+ result = crud.update_post(post_id, partial_data)
51
+ if result is None:
52
+ raise HTTPException(status_code=404, detail=NOT_FOUND)
53
+ return result
54
+
55
+
56
+ # [DELETE] Supprimer un post
57
+ @router.delete("/{post_id}")
58
+ def delete_post(post_id: int):
59
+ if not crud.delete_post(post_id):
60
+ raise HTTPException(status_code=404, detail=NOT_FOUND)
61
+ return {"message": f"Le post {post_id} a été supprimé avec succès"}
@@ -0,0 +1,15 @@
1
+ from typing import Optional
2
+ from pydantic import BaseModel
3
+
4
+
5
+ class Post(BaseModel):
6
+ id: int
7
+ userId: int
8
+ title: str
9
+ body: str
10
+
11
+
12
+ class UpdatePost(BaseModel):
13
+ userId: Optional[int] = None
14
+ title: Optional[str] = None
15
+ body: Optional[str] = None
@@ -0,0 +1,111 @@
1
+ Metadata-Version: 2.4
2
+ Name: jsonplaceholder-clone
3
+ Version: 1.0.0
4
+ Summary: Reconstruction de l'API JSONPlaceholder avec FastAPI
5
+ Author-email: Tom Doizon <tom.doizon1@gmail.com>
6
+ Classifier: Programming Language :: Python :: 3
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Framework :: FastAPI
9
+ Requires-Python: >=3.9
10
+ Description-Content-Type: text/markdown
11
+ License-File: Licence
12
+ Requires-Dist: fastapi
13
+ Requires-Dist: uvicorn[standard]
14
+ Requires-Dist: pydantic>=2
15
+ Dynamic: license-file
16
+
17
+ # JSONPlaceholder Clone
18
+
19
+ Reconstruction de l'API [JSONPlaceholder](https://jsonplaceholder.typicode.com) avec FastAPI.
20
+ L'API gère des posts (CRUD complet) avec une base de données fictive en mémoire.
21
+
22
+ ## Installation
23
+
24
+ Depuis la racine du projet (là où se trouve `pyproject.toml`) :
25
+
26
+ ```bash
27
+ py -m venv .venv
28
+ .\.venv\Scripts\Activate.ps1 # Windows (PowerShell)
29
+ # source .venv/bin/activate # macOS / Linux
30
+ pip install -e .
31
+ ```
32
+
33
+ Le mode `-e` (éditable) prend en compte vos modifications sans réinstaller.
34
+
35
+ ## Lancer l'API
36
+
37
+ ```bash
38
+ jsonplaceholder-clone
39
+ ```
40
+
41
+ ou :
42
+
43
+ ```bash
44
+ python -m jsonplaceholder_clone
45
+ ```
46
+
47
+ L'API est disponible sur http://127.0.0.1:8000 et la documentation interactive sur http://127.0.0.1:8000/docs.
48
+
49
+ ## Routes
50
+
51
+ | Méthode | Route | Description |
52
+ |---------|----------------|-----------------------------------------------------|
53
+ | GET | `/posts` | Liste tous les posts (filtre optionnel `?userId=1`) |
54
+ | GET | `/posts/{id}` | Récupère un post par son ID |
55
+ | POST | `/posts` | Crée un nouveau post |
56
+ | PUT | `/posts/{id}` | Remplace complètement un post |
57
+ | PATCH | `/posts/{id}` | Modifie partiellement un post |
58
+ | DELETE | `/posts/{id}` | Supprime un post |
59
+
60
+ Exemple de post :
61
+
62
+ ```json
63
+ {
64
+ "userId": 1,
65
+ "id": 1,
66
+ "title": "Premier Post",
67
+ "body": "Ceci est le contenu du premier post."
68
+ }
69
+ ```
70
+
71
+ ## Utiliser le package dans un autre script
72
+
73
+ ```python
74
+ from fastapi import FastAPI
75
+ from jsonplaceholder_clone import crud
76
+ from jsonplaceholder_clone.routers import posts_router
77
+
78
+ app = FastAPI()
79
+ app.include_router(posts_router)
80
+
81
+ print(crud.list_posts())
82
+ ```
83
+
84
+ ## Structure du projet
85
+
86
+ ```
87
+ jsonplaceholder_clone/
88
+ ├── pyproject.toml
89
+ ├── README.md
90
+ ├── LICENSE
91
+ ├── MANIFEST.in
92
+ └── src/
93
+ └── jsonplaceholder_clone/
94
+ ├── __init__.py # expose app, Post, UpdatePost, crud
95
+ ├── __main__.py # démarrage du serveur
96
+ ├── main.py # création de l'application FastAPI
97
+ ├── schemas.py # modèles Pydantic
98
+ ├── database.py # base de données fictive en mémoire
99
+ ├── crud.py # logique métier
100
+ └── routers/
101
+ ├── __init__.py # expose posts_router
102
+ └── posts.py # routes /posts
103
+ ```
104
+
105
+ ## Remarque
106
+
107
+ Les données sont stockées en mémoire : elles sont réinitialisées à chaque redémarrage du serveur.
108
+
109
+ ## Licence
110
+
111
+ Ce projet est distribué sous licence MIT. Voir le fichier [LICENSE](LICENSE).
@@ -0,0 +1,14 @@
1
+ jsonplaceholder_clone/__init__.py,sha256=y2edlRqDkjJcqk0N-Or__UBqoJ0eP9KKR4NBud6qw2I,78
2
+ jsonplaceholder_clone/__main__.py,sha256=oxQq-dULN0pQp_xpqSQX3_ck39-Q_c-3dsHgal1dzZo,286
3
+ jsonplaceholder_clone/crud.py,sha256=GiLVCFpQXz3Fu7RKEUg80tNonXiu8Vq8wDJ2pxy5xrc,1663
4
+ jsonplaceholder_clone/database.py,sha256=BUvax0CNCRVdWK17bGUXnDHKVX8c0I4WE4APHiNtABU,375
5
+ jsonplaceholder_clone/main.py,sha256=1isf_B-_7DTashq14biX299jJPcV1lubW17a2LEk2O0,242
6
+ jsonplaceholder_clone/schemas.py,sha256=epUdBs55QV_KkibG2WHiLDKxAw277c_gFJiN6hC7rP8,268
7
+ jsonplaceholder_clone/routers/__init__.py,sha256=1BIv7wzIL5wml7j2FF8EcClxYqFELctD5rinO_GLoGI,41
8
+ jsonplaceholder_clone/routers/posts.py,sha256=HHpHNVjoNItLF04wvz7vRthm4W4UeZteUCLOUdWhoVk,1948
9
+ jsonplaceholder_clone-1.0.0.dist-info/licenses/Licence,sha256=N4EwQxjuar6nuK9T0hS8AIoomg52p_MbeCW2xufou8w,1086
10
+ jsonplaceholder_clone-1.0.0.dist-info/METADATA,sha256=ZHWLLO5M9QWOTSOYhKqXmKeE0uSpWgFAQKyKuMW73F0,3346
11
+ jsonplaceholder_clone-1.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
12
+ jsonplaceholder_clone-1.0.0.dist-info/entry_points.txt,sha256=8fiMaLzqrdk20K5BBA7cCnrU3LLIqLBBmHtZ6cmZTpI,78
13
+ jsonplaceholder_clone-1.0.0.dist-info/top_level.txt,sha256=BCznWgu92u0v3sLmNvyVQMZIxn6f77kSePUBToDSCVQ,22
14
+ jsonplaceholder_clone-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ jsonplaceholder-clone = jsonplaceholder_clone.__main__:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Tom Doizon
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ jsonplaceholder_clone