commiter-cli 0.3.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.
Files changed (96) hide show
  1. commiter/__init__.py +3 -0
  2. commiter/adapters/__init__.py +0 -0
  3. commiter/adapters/base.py +96 -0
  4. commiter/adapters/django_rest.py +247 -0
  5. commiter/adapters/express.py +204 -0
  6. commiter/adapters/fastapi.py +170 -0
  7. commiter/adapters/flask.py +169 -0
  8. commiter/adapters/nextjs.py +180 -0
  9. commiter/adapters/prisma.py +76 -0
  10. commiter/adapters/raw_sql.py +191 -0
  11. commiter/adapters/react.py +129 -0
  12. commiter/adapters/sqlalchemy.py +99 -0
  13. commiter/adapters/supabase.py +68 -0
  14. commiter/auth.py +130 -0
  15. commiter/cli.py +667 -0
  16. commiter/correlator.py +208 -0
  17. commiter/extractors/__init__.py +0 -0
  18. commiter/extractors/api_calls.py +91 -0
  19. commiter/extractors/api_endpoints.py +354 -0
  20. commiter/extractors/backend_files.py +33 -0
  21. commiter/extractors/base.py +40 -0
  22. commiter/extractors/db_operations.py +69 -0
  23. commiter/extractors/dependencies.py +219 -0
  24. commiter/generic_resolver.py +204 -0
  25. commiter/handler_index.py +97 -0
  26. commiter/lib.py +63 -0
  27. commiter/middleware_index.py +350 -0
  28. commiter/models.py +117 -0
  29. commiter/parser.py +1283 -0
  30. commiter/prefix_index.py +211 -0
  31. commiter/report/__init__.py +0 -0
  32. commiter/report/ai.py +120 -0
  33. commiter/report/api_guide.py +217 -0
  34. commiter/report/architecture.py +930 -0
  35. commiter/report/console.py +254 -0
  36. commiter/report/json_output.py +122 -0
  37. commiter/report/markdown.py +163 -0
  38. commiter/scanner.py +383 -0
  39. commiter/type_index.py +304 -0
  40. commiter/uploader.py +46 -0
  41. commiter/utils/__init__.py +0 -0
  42. commiter/utils/env_reader.py +78 -0
  43. commiter/utils/file_classifier.py +187 -0
  44. commiter/utils/path_helpers.py +73 -0
  45. commiter/utils/tsconfig_resolver.py +281 -0
  46. commiter/wrapper_index.py +288 -0
  47. commiter_cli-0.3.0.dist-info/METADATA +14 -0
  48. commiter_cli-0.3.0.dist-info/RECORD +96 -0
  49. commiter_cli-0.3.0.dist-info/WHEEL +5 -0
  50. commiter_cli-0.3.0.dist-info/entry_points.txt +2 -0
  51. commiter_cli-0.3.0.dist-info/top_level.txt +2 -0
  52. tests/__init__.py +0 -0
  53. tests/fixtures/arch_backend/app.py +22 -0
  54. tests/fixtures/arch_backend/middleware/__init__.py +0 -0
  55. tests/fixtures/arch_backend/middleware/rate_limit.py +4 -0
  56. tests/fixtures/arch_backend/routes/__init__.py +0 -0
  57. tests/fixtures/arch_backend/routes/analytics.py +20 -0
  58. tests/fixtures/arch_backend/routes/auth.py +29 -0
  59. tests/fixtures/arch_backend/routes/projects.py +60 -0
  60. tests/fixtures/arch_backend/routes/users.py +55 -0
  61. tests/fixtures/arch_monorepo/apps/api/app.py +30 -0
  62. tests/fixtures/arch_monorepo/apps/api/middleware/__init__.py +0 -0
  63. tests/fixtures/arch_monorepo/apps/api/middleware/auth.py +17 -0
  64. tests/fixtures/arch_monorepo/apps/api/middleware/rate_limit.py +10 -0
  65. tests/fixtures/arch_monorepo/apps/api/routes/__init__.py +0 -0
  66. tests/fixtures/arch_monorepo/apps/api/routes/auth.py +46 -0
  67. tests/fixtures/arch_monorepo/apps/api/routes/invites.py +30 -0
  68. tests/fixtures/arch_monorepo/apps/api/routes/notifications.py +25 -0
  69. tests/fixtures/arch_monorepo/apps/api/routes/projects.py +80 -0
  70. tests/fixtures/arch_monorepo/apps/api/routes/tasks.py +91 -0
  71. tests/fixtures/arch_monorepo/apps/api/routes/users.py +48 -0
  72. tests/fixtures/arch_monorepo/apps/api/services/__init__.py +0 -0
  73. tests/fixtures/arch_monorepo/apps/api/services/email.py +11 -0
  74. tests/fixtures/backend_b/app.py +17 -0
  75. tests/fixtures/fastapi_app/app.py +48 -0
  76. tests/fixtures/fastapi_crossfile/routes.py +18 -0
  77. tests/fixtures/fastapi_crossfile/schemas.py +21 -0
  78. tests/fixtures/flask_app/app.py +33 -0
  79. tests/fixtures/flask_blueprint/app.py +7 -0
  80. tests/fixtures/flask_blueprint/routes/items.py +13 -0
  81. tests/fixtures/flask_blueprint/routes/users.py +20 -0
  82. tests/fixtures/middleware_test_flask/routes/public.py +8 -0
  83. tests/fixtures/middleware_test_flask/routes/users.py +26 -0
  84. tests/fixtures/python_deep_imports/app/__init__.py +0 -0
  85. tests/fixtures/python_deep_imports/app/api/__init__.py +0 -0
  86. tests/fixtures/python_deep_imports/app/api/health.py +11 -0
  87. tests/fixtures/python_deep_imports/app/api/v1/__init__.py +0 -0
  88. tests/fixtures/python_deep_imports/app/api/v1/items.py +18 -0
  89. tests/fixtures/python_deep_imports/app/api/v1/users.py +27 -0
  90. tests/fixtures/python_deep_imports/app/schemas/__init__.py +0 -0
  91. tests/fixtures/python_deep_imports/app/schemas/item.py +13 -0
  92. tests/fixtures/python_deep_imports/app/schemas/user.py +15 -0
  93. tests/fixtures/python_deep_imports/app/shared/__init__.py +0 -0
  94. tests/fixtures/python_deep_imports/app/shared/models.py +7 -0
  95. tests/fixtures/raw_sql_test/app.py +54 -0
  96. tests/test_architecture.py +757 -0
@@ -0,0 +1,17 @@
1
+ import jwt
2
+ from functools import wraps
3
+ from flask import request, jsonify
4
+
5
+ def require_auth(f):
6
+ @wraps(f)
7
+ def decorated(*args, **kwargs):
8
+ token = request.headers.get("Authorization", "").replace("Bearer ", "")
9
+ if not token:
10
+ return jsonify(error="Missing token"), 401
11
+ try:
12
+ payload = jwt.decode(token, "secret", algorithms=["HS256"])
13
+ request.user_id = payload["sub"]
14
+ except jwt.ExpiredSignatureError:
15
+ return jsonify(error="Token expired"), 401
16
+ return f(*args, **kwargs)
17
+ return decorated
@@ -0,0 +1,10 @@
1
+ from functools import wraps
2
+ from flask import request, jsonify
3
+
4
+ def rate_limit(max_requests=60, window=60):
5
+ def decorator(f):
6
+ @wraps(f)
7
+ def decorated(*args, **kwargs):
8
+ return f(*args, **kwargs)
9
+ return decorated
10
+ return decorator
@@ -0,0 +1,46 @@
1
+ from flask import Blueprint, request, jsonify
2
+
3
+ auth_bp = Blueprint("auth", __name__)
4
+
5
+
6
+ @auth_bp.route("/login", methods=["POST"])
7
+ def login():
8
+ email = request.json["email"]
9
+ password = request.json["password"]
10
+ user = db.table("users").select("*").eq("email", email).single().execute()
11
+ session = db.table("sessions").insert({
12
+ "user_id": user.data["id"],
13
+ "ip_address": request.remote_addr,
14
+ }).execute()
15
+ return jsonify(access_token="jwt...", refresh_token="rt...", expires_in=3600)
16
+
17
+
18
+ @auth_bp.route("/refresh", methods=["POST"])
19
+ def refresh_token():
20
+ refresh = request.cookies.get("refresh_token")
21
+ session = db.table("sessions").select("*").eq("refresh_token", refresh).single().execute()
22
+ db.table("sessions").update({"refreshed_at": "now()"}).eq("id", session.data["id"]).execute()
23
+ return jsonify(access_token="jwt...", expires_in=3600)
24
+
25
+
26
+ @auth_bp.route("/logout", methods=["POST"])
27
+ @login_required
28
+ def logout():
29
+ db.table("sessions").delete().eq("user_id", request.user_id).execute()
30
+ return jsonify(success=True)
31
+
32
+
33
+ @auth_bp.route("/signup", methods=["POST"])
34
+ def signup():
35
+ email = request.json["email"]
36
+ password = request.json["password"]
37
+ display_name = request.json["display_name"]
38
+ user = db.table("users").insert({
39
+ "email": email,
40
+ "display_name": display_name,
41
+ }).execute()
42
+ db.table("workspaces").insert({
43
+ "name": f"{display_name}'s Workspace",
44
+ "owner_id": user.data[0]["id"],
45
+ }).execute()
46
+ return jsonify(user=user.data[0], access_token="jwt...")
@@ -0,0 +1,30 @@
1
+ from flask import Blueprint, request, jsonify
2
+
3
+ invites_bp = Blueprint("invites", __name__)
4
+
5
+
6
+ @invites_bp.route("/", methods=["POST"])
7
+ @login_required
8
+ def create_invite():
9
+ email = request.json["email"]
10
+ workspace_id = request.json["workspace_id"]
11
+ invite = db.table("invites").insert({
12
+ "email": email,
13
+ "workspace_id": workspace_id,
14
+ "invited_by": request.user_id,
15
+ "status": "pending",
16
+ }).execute()
17
+ return jsonify(invite=invite.data[0])
18
+
19
+
20
+ @invites_bp.route("/<invite_id>/accept", methods=["POST"])
21
+ @login_required
22
+ def accept_invite(invite_id):
23
+ invite = db.table("invites").select("*").eq("id", invite_id).single().execute()
24
+ db.table("invites").update({"status": "accepted"}).eq("id", invite_id).execute()
25
+ db.table("workspace_members").insert({
26
+ "workspace_id": invite.data["workspace_id"],
27
+ "user_id": request.user_id,
28
+ "role": "member",
29
+ }).execute()
30
+ return jsonify(success=True)
@@ -0,0 +1,25 @@
1
+ from flask import Blueprint, request, jsonify
2
+
3
+ notifications_bp = Blueprint("notifications", __name__)
4
+
5
+
6
+ @notifications_bp.route("/", methods=["GET"])
7
+ @login_required
8
+ def list_notifications():
9
+ notifications = db.table("notifications").select("*").eq("user_id", request.user_id).order("created_at", desc=True).limit(50).execute()
10
+ return jsonify(notifications=notifications.data)
11
+
12
+
13
+ @notifications_bp.route("/read", methods=["POST"])
14
+ @login_required
15
+ def mark_read():
16
+ notification_ids = request.json["ids"]
17
+ db.table("notifications").update({"read": True}).in_("id", notification_ids).execute()
18
+ return jsonify(success=True)
19
+
20
+
21
+ @notifications_bp.route("/unread-count", methods=["GET"])
22
+ @login_required
23
+ def unread_count():
24
+ result = db.table("notifications").select("id", count="exact").eq("user_id", request.user_id).eq("read", False).execute()
25
+ return jsonify(count=result.count)
@@ -0,0 +1,80 @@
1
+ from flask import Blueprint, request, jsonify
2
+
3
+ projects_bp = Blueprint("projects", __name__)
4
+
5
+
6
+ @projects_bp.route("/", methods=["GET"])
7
+ @login_required
8
+ def list_projects():
9
+ workspace_id = request.args.get("workspace_id")
10
+ projects = db.table("projects").select("*").eq("workspace_id", workspace_id).execute()
11
+ return jsonify(projects=projects.data)
12
+
13
+
14
+ @projects_bp.route("/", methods=["POST"])
15
+ @login_required
16
+ def create_project():
17
+ name = request.json["name"]
18
+ description = request.json.get("description", "")
19
+ workspace_id = request.json["workspace_id"]
20
+ color = request.json.get("color", "#3b82f6")
21
+ project = db.table("projects").insert({
22
+ "name": name,
23
+ "description": description,
24
+ "workspace_id": workspace_id,
25
+ "color": color,
26
+ "owner_id": request.user_id,
27
+ }).execute()
28
+ # Create default columns for kanban
29
+ for col_name in ["To Do", "In Progress", "Done"]:
30
+ db.table("columns").insert({
31
+ "project_id": project.data[0]["id"],
32
+ "name": col_name,
33
+ }).execute()
34
+ return jsonify(project=project.data[0])
35
+
36
+
37
+ @projects_bp.route("/<project_id>", methods=["GET"])
38
+ @login_required
39
+ def get_project(project_id):
40
+ project = db.table("projects").select("*").eq("id", project_id).single().execute()
41
+ members = db.table("project_members").select("*, users(display_name, avatar_url)").eq("project_id", project_id).execute()
42
+ columns = db.table("columns").select("*").eq("project_id", project_id).order("position").execute()
43
+ return jsonify(project=project.data, members=members.data, columns=columns.data)
44
+
45
+
46
+ @projects_bp.route("/<project_id>", methods=["PUT"])
47
+ @login_required
48
+ def update_project(project_id):
49
+ updates = {k: v for k, v in request.json.items() if k in ("name", "description", "color", "archived")}
50
+ project = db.table("projects").update(updates).eq("id", project_id).execute()
51
+ return jsonify(project=project.data[0])
52
+
53
+
54
+ @projects_bp.route("/<project_id>", methods=["DELETE"])
55
+ @login_required
56
+ def delete_project(project_id):
57
+ db.table("tasks").delete().eq("project_id", project_id).execute()
58
+ db.table("columns").delete().eq("project_id", project_id).execute()
59
+ db.table("project_members").delete().eq("project_id", project_id).execute()
60
+ db.table("projects").delete().eq("id", project_id).execute()
61
+ return jsonify(success=True)
62
+
63
+
64
+ @projects_bp.route("/<project_id>/members", methods=["POST"])
65
+ @login_required
66
+ def add_member(project_id):
67
+ user_id = request.json["user_id"]
68
+ role = request.json.get("role", "member")
69
+ member = db.table("project_members").insert({
70
+ "project_id": project_id,
71
+ "user_id": user_id,
72
+ "role": role,
73
+ }).execute()
74
+ # Send notification to the invited user
75
+ db.table("notifications").insert({
76
+ "user_id": user_id,
77
+ "type": "project_invite",
78
+ "data": {"project_id": project_id, "invited_by": request.user_id},
79
+ }).execute()
80
+ return jsonify(member=member.data[0])
@@ -0,0 +1,91 @@
1
+ from flask import Blueprint, request, jsonify
2
+
3
+ tasks_bp = Blueprint("tasks", __name__)
4
+
5
+
6
+ @tasks_bp.route("/", methods=["GET"])
7
+ @login_required
8
+ def list_tasks():
9
+ project_id = request.args.get("project_id")
10
+ column_id = request.args.get("column_id")
11
+ query = db.table("tasks").select("*, assignee:users(display_name, avatar_url)")
12
+ if project_id:
13
+ query = query.eq("project_id", project_id)
14
+ if column_id:
15
+ query = query.eq("column_id", column_id)
16
+ tasks = query.order("position").execute()
17
+ return jsonify(tasks=tasks.data)
18
+
19
+
20
+ @tasks_bp.route("/", methods=["POST"])
21
+ @login_required
22
+ def create_task():
23
+ title = request.json["title"]
24
+ description = request.json.get("description", "")
25
+ project_id = request.json["project_id"]
26
+ column_id = request.json["column_id"]
27
+ assignee_id = request.json.get("assignee_id")
28
+ priority = request.json.get("priority", "medium")
29
+ due_date = request.json.get("due_date")
30
+ task = db.table("tasks").insert({
31
+ "title": title,
32
+ "description": description,
33
+ "project_id": project_id,
34
+ "column_id": column_id,
35
+ "assignee_id": assignee_id,
36
+ "priority": priority,
37
+ "due_date": due_date,
38
+ "creator_id": request.user_id,
39
+ }).execute()
40
+ # Notify assignee
41
+ if assignee_id and assignee_id != request.user_id:
42
+ db.table("notifications").insert({
43
+ "user_id": assignee_id,
44
+ "type": "task_assigned",
45
+ "data": {"task_id": task.data[0]["id"], "assigned_by": request.user_id},
46
+ }).execute()
47
+ return jsonify(task=task.data[0])
48
+
49
+
50
+ @tasks_bp.route("/<task_id>", methods=["PUT"])
51
+ @login_required
52
+ def update_task(task_id):
53
+ allowed = ("title", "description", "column_id", "assignee_id", "priority", "due_date", "position", "completed")
54
+ updates = {k: v for k, v in request.json.items() if k in allowed}
55
+ task = db.table("tasks").update(updates).eq("id", task_id).execute()
56
+ # Log activity
57
+ db.table("activity_log").insert({
58
+ "user_id": request.user_id,
59
+ "action": "task_updated",
60
+ "entity_type": "task",
61
+ "entity_id": task_id,
62
+ "changes": updates,
63
+ }).execute()
64
+ return jsonify(task=task.data[0])
65
+
66
+
67
+ @tasks_bp.route("/<task_id>", methods=["DELETE"])
68
+ @login_required
69
+ def delete_task(task_id):
70
+ db.table("comments").delete().eq("task_id", task_id).execute()
71
+ db.table("tasks").delete().eq("id", task_id).execute()
72
+ return jsonify(success=True)
73
+
74
+
75
+ @tasks_bp.route("/<task_id>/comments", methods=["GET"])
76
+ @login_required
77
+ def list_comments(task_id):
78
+ comments = db.table("comments").select("*, author:users(display_name, avatar_url)").eq("task_id", task_id).order("created_at").execute()
79
+ return jsonify(comments=comments.data)
80
+
81
+
82
+ @tasks_bp.route("/<task_id>/comments", methods=["POST"])
83
+ @login_required
84
+ def create_comment(task_id):
85
+ body = request.json["body"]
86
+ comment = db.table("comments").insert({
87
+ "task_id": task_id,
88
+ "author_id": request.user_id,
89
+ "body": body,
90
+ }).execute()
91
+ return jsonify(comment=comment.data[0])
@@ -0,0 +1,48 @@
1
+ from flask import Blueprint, request, jsonify
2
+
3
+ users_bp = Blueprint("users", __name__)
4
+
5
+
6
+ @users_bp.route("/me", methods=["GET"])
7
+ @login_required
8
+ def get_profile():
9
+ user = db.table("users").select("*").eq("id", request.user_id).single().execute()
10
+ return jsonify(user=user.data)
11
+
12
+
13
+ @users_bp.route("/me", methods=["PUT"])
14
+ @login_required
15
+ def update_profile():
16
+ display_name = request.json.get("display_name")
17
+ bio = request.json.get("bio")
18
+ avatar_url = request.json.get("avatar_url")
19
+ user = db.table("users").update({
20
+ "display_name": display_name,
21
+ "bio": bio,
22
+ "avatar_url": avatar_url,
23
+ }).eq("id", request.user_id).execute()
24
+ return jsonify(user=user.data[0])
25
+
26
+
27
+ @users_bp.route("/me/avatar", methods=["POST"])
28
+ @login_required
29
+ def upload_avatar():
30
+ file = request.files["avatar"]
31
+ url = storage.upload(f"avatars/{request.user_id}", file)
32
+ db.table("users").update({"avatar_url": url}).eq("id", request.user_id).execute()
33
+ return jsonify(avatar_url=url)
34
+
35
+
36
+ @users_bp.route("/<user_id>", methods=["GET"])
37
+ @login_required
38
+ def get_user(user_id):
39
+ user = db.table("users").select("id, display_name, bio, avatar_url").eq("id", user_id).single().execute()
40
+ return jsonify(user=user.data)
41
+
42
+
43
+ @users_bp.route("/search", methods=["GET"])
44
+ @login_required
45
+ def search_users():
46
+ query = request.args.get("q", "")
47
+ users = db.table("users").select("id, display_name, avatar_url").ilike("display_name", f"%{query}%").execute()
48
+ return jsonify(users=users.data)
@@ -0,0 +1,11 @@
1
+ """Email notification service — sends via Celery background tasks."""
2
+
3
+
4
+ def send_invite_email(email, workspace_name, invited_by_name):
5
+ """Queue an invite email for async delivery."""
6
+ pass
7
+
8
+
9
+ def send_task_notification(user_email, task_title, action):
10
+ """Queue a task notification email."""
11
+ pass
@@ -0,0 +1,17 @@
1
+ from flask import Flask, jsonify
2
+
3
+ app = Flask(__name__)
4
+
5
+
6
+ @app.route("/api/reports", methods=["GET"])
7
+ def list_reports():
8
+ reports = db.table("reports").select("*").execute()
9
+ return jsonify(data=reports.data)
10
+
11
+
12
+ @app.route("/api/user-stats/<user_id>", methods=["GET"])
13
+ def user_stats(user_id):
14
+ # This service also reads from the 'users' table — shared with flask_app
15
+ user = db.table("users").select("name, email").eq("id", user_id).execute()
16
+ stats = db.table("reports").select("*").eq("user_id", user_id).execute()
17
+ return jsonify(user=user.data[0], report_count=len(stats.data))
@@ -0,0 +1,48 @@
1
+ from fastapi import FastAPI, Depends
2
+ from pydantic import BaseModel
3
+ from typing import Optional
4
+
5
+ app = FastAPI()
6
+
7
+
8
+ class ItemCreate(BaseModel):
9
+ name: str
10
+ price: float
11
+ description: Optional[str] = None
12
+ category: str
13
+
14
+
15
+ class ItemResponse(BaseModel):
16
+ id: int
17
+ name: str
18
+ price: float
19
+
20
+
21
+ class UserCreate(BaseModel):
22
+ email: str
23
+ password: str
24
+ display_name: str
25
+
26
+
27
+ def get_current_user():
28
+ pass
29
+
30
+
31
+ @app.post("/api/items")
32
+ def create_item(item: ItemCreate):
33
+ return {"id": 1, "name": item.name, "price": item.price}
34
+
35
+
36
+ @app.get("/api/items/{item_id}")
37
+ def get_item(item_id: int):
38
+ return {"id": item_id, "name": "Test", "price": 9.99}
39
+
40
+
41
+ @app.post("/api/users", response_model=UserCreate)
42
+ def create_user(user: UserCreate, current_user=Depends(get_current_user)):
43
+ return user
44
+
45
+
46
+ @app.delete("/api/items/{item_id}")
47
+ def delete_item(item_id: int, current_user=Depends(get_current_user)):
48
+ return {"status": "deleted"}
@@ -0,0 +1,18 @@
1
+ from fastapi import FastAPI, Depends
2
+ from .schemas import UserCreate, UserResponse, ItemCreate
3
+
4
+ app = FastAPI()
5
+
6
+
7
+ def get_current_user():
8
+ pass
9
+
10
+
11
+ @app.post("/api/users")
12
+ def create_user(user: UserCreate):
13
+ return {"id": 1, "email": user.email}
14
+
15
+
16
+ @app.post("/api/items")
17
+ def create_item(item: ItemCreate, current_user=Depends(get_current_user)):
18
+ return {"id": 1, "name": item.name}
@@ -0,0 +1,21 @@
1
+ from pydantic import BaseModel
2
+ from typing import Optional
3
+
4
+
5
+ class UserCreate(BaseModel):
6
+ email: str
7
+ password: str
8
+ display_name: str
9
+
10
+
11
+ class UserResponse(BaseModel):
12
+ id: int
13
+ email: str
14
+ display_name: str
15
+ is_active: bool = True
16
+
17
+
18
+ class ItemCreate(BaseModel):
19
+ name: str
20
+ price: float
21
+ description: Optional[str] = None
@@ -0,0 +1,33 @@
1
+ from flask import Flask, request, jsonify
2
+
3
+ app = Flask(__name__)
4
+
5
+
6
+ @app.route("/api/users", methods=["GET"])
7
+ @login_required
8
+ def list_users():
9
+ users = db.table("users").select("*").execute()
10
+ return jsonify(data=users.data, count=len(users.data))
11
+
12
+
13
+ @app.route("/api/users/<user_id>", methods=["GET"])
14
+ @login_required
15
+ def get_user(user_id):
16
+ user = db.table("users").select("*").eq("id", user_id).execute()
17
+ return jsonify(data=user.data[0])
18
+
19
+
20
+ @app.route("/api/users", methods=["POST"])
21
+ def create_user():
22
+ name = request.json["name"]
23
+ email = request.json["email"]
24
+ password = request.json["password"]
25
+ user = db.table("users").insert({"name": name, "email": email}).execute()
26
+ return jsonify(id=user.data[0]["id"], status="created")
27
+
28
+
29
+ @app.route("/api/items/<item_id>", methods=["DELETE"])
30
+ @login_required
31
+ def delete_item(item_id):
32
+ db.table("items").delete().eq("id", item_id).execute()
33
+ return jsonify(status="deleted")
@@ -0,0 +1,7 @@
1
+ from flask import Flask
2
+ from .routes.users import users_bp
3
+ from .routes.items import items_bp
4
+
5
+ app = Flask(__name__)
6
+ app.register_blueprint(users_bp, url_prefix="/api/v1/users")
7
+ app.register_blueprint(items_bp, url_prefix="/api/v1/items")
@@ -0,0 +1,13 @@
1
+ from flask import Blueprint, request, jsonify
2
+
3
+ items_bp = Blueprint("items", __name__)
4
+
5
+
6
+ @items_bp.get("/")
7
+ def list_items():
8
+ return jsonify(items=[])
9
+
10
+
11
+ @items_bp.delete("/<item_id>")
12
+ def delete_item(item_id):
13
+ return jsonify(status="deleted")
@@ -0,0 +1,20 @@
1
+ from flask import Blueprint, request, jsonify
2
+
3
+ users_bp = Blueprint("users", __name__)
4
+
5
+
6
+ @users_bp.route("/", methods=["GET"])
7
+ def list_users():
8
+ return jsonify(users=[])
9
+
10
+
11
+ @users_bp.route("/<user_id>", methods=["GET"])
12
+ def get_user(user_id):
13
+ return jsonify(id=user_id, name="John")
14
+
15
+
16
+ @users_bp.route("/", methods=["POST"])
17
+ def create_user():
18
+ name = request.json["name"]
19
+ email = request.json["email"]
20
+ return jsonify(id=1, name=name)
@@ -0,0 +1,8 @@
1
+ from flask import Blueprint, jsonify
2
+
3
+ bp = Blueprint("public", __name__)
4
+
5
+
6
+ @bp.route("/health", methods=["GET"])
7
+ def health():
8
+ return jsonify(status="ok")
@@ -0,0 +1,26 @@
1
+ from flask import Blueprint, request, jsonify
2
+
3
+ bp = Blueprint("users", __name__)
4
+
5
+
6
+ @bp.before_request
7
+ def require_auth():
8
+ """All routes on this blueprint require authentication."""
9
+ if not request.headers.get("Authorization"):
10
+ return jsonify(error="Unauthorized"), 401
11
+
12
+
13
+ @bp.route("/", methods=["GET"])
14
+ def list_users():
15
+ return jsonify(users=[])
16
+
17
+
18
+ @bp.route("/<user_id>", methods=["GET"])
19
+ def get_user(user_id):
20
+ return jsonify(id=user_id, name="John")
21
+
22
+
23
+ @bp.route("/", methods=["POST"])
24
+ def create_user():
25
+ name = request.json["name"]
26
+ return jsonify(id=1, name=name)
File without changes
File without changes
@@ -0,0 +1,11 @@
1
+ from fastapi import APIRouter
2
+
3
+ # One level up: from app/api/ -> app/shared/models.py (../shared.models)
4
+ from ..shared.models import PaginationParams
5
+
6
+ router = APIRouter()
7
+
8
+
9
+ @router.get("/health")
10
+ def health_check():
11
+ return {"status": "ok"}
@@ -0,0 +1,18 @@
1
+ from fastapi import APIRouter
2
+
3
+ # Two levels up to schemas
4
+ from ...schemas.item import ItemCreate, ItemUpdate
5
+
6
+ router = APIRouter()
7
+
8
+
9
+ @router.post("/items")
10
+ def create_item(item: ItemCreate):
11
+ result = db.table("items").insert({"name": item.name, "price": item.price}).execute()
12
+ return {"id": 1, "name": item.name}
13
+
14
+
15
+ @router.put("/items/{item_id}")
16
+ def update_item(item_id: int, item: ItemUpdate):
17
+ db.table("items").update({"name": item.name}).eq("id", item_id).execute()
18
+ return {"status": "updated"}
@@ -0,0 +1,27 @@
1
+ from fastapi import APIRouter, Depends
2
+
3
+ # Two levels up: from app/api/v1/ -> app/schemas/user.py (../../schemas.user)
4
+ from ...schemas.user import UserCreate, UserResponse
5
+
6
+ # Three levels relative: from app/api/v1/ -> app/shared/models.py (../../shared.models)
7
+ from ...shared.models import PaginationParams
8
+
9
+ router = APIRouter()
10
+
11
+
12
+ @router.get("/users")
13
+ def list_users(pagination: PaginationParams):
14
+ users = db.table("users").select("*").execute()
15
+ return {"users": users.data, "page": pagination.page}
16
+
17
+
18
+ @router.post("/users")
19
+ def create_user(user: UserCreate):
20
+ result = db.table("users").insert({"email": user.email}).execute()
21
+ return {"id": result.data[0]["id"], "email": user.email}
22
+
23
+
24
+ @router.get("/users/{user_id}")
25
+ def get_user(user_id: int):
26
+ user = db.table("users").select("*").eq("id", user_id).execute()
27
+ return {"user": user.data[0]}