Skip to content

Commit 86ae6ef

Browse files
Merge pull request #2 from CodeSignal/dev
Updated documentation and storing docs in json files
2 parents 24bbfd0 + b673366 commit 86ae6ef

File tree

6 files changed

+210
-155
lines changed

6 files changed

+210
-155
lines changed

app/routes/docs.py

Lines changed: 35 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,116 +1,35 @@
1+
import os
12
from flask import Blueprint, current_app, json, Response
23
from config.auth_config import AuthMethod
34

45
docs_bp = Blueprint("docs", __name__)
56

7+
def load_json_file(filename):
8+
"""Load and parse a JSON file."""
9+
try:
10+
with open(filename, 'r') as f:
11+
return json.load(f)
12+
except (FileNotFoundError, json.JSONDecodeError):
13+
return {}
14+
615
@docs_bp.route("", methods=["GET"])
716
def api_docs():
817
"""Provide comprehensive API documentation."""
918
docs = {}
1019

20+
# Load route documentation in specific order
21+
route_files = ['todos.json', 'notes.json'] # Define order explicitly
22+
docs_dir = os.path.join(current_app.root_path, '..', 'docs', 'routes')
23+
24+
for filename in route_files:
25+
route_docs = load_json_file(os.path.join(docs_dir, filename))
26+
docs.update(route_docs)
27+
28+
# Load authentication documentation
1129
auth_docs = _get_auth_docs()
1230
if auth_docs:
1331
docs["authentication"] = auth_docs
14-
15-
docs.update({
16-
"/todos": {
17-
"GET": {
18-
"description": "Fetch all TODO items with optional filtering and pagination.",
19-
"query_params": {
20-
"done": "Filter by completion status (true/false).",
21-
"title": "Filter by TODO item title prefix.",
22-
"page": "Page number for pagination (optional, starts at 1).",
23-
"limit": "Number of items per page (optional)."
24-
},
25-
"responses": {
26-
"200": "List of todo items"
27-
}
28-
},
29-
"POST": {
30-
"description": "Add a new TODO item.",
31-
"body_params": {
32-
"title": "The TODO item title (required).",
33-
"done": "Completion status (optional, default: false).",
34-
"description": "Detailed TODO item description (optional)."
35-
},
36-
"responses": {
37-
"201": "Created todo item",
38-
"400": "Invalid request (missing title)"
39-
}
40-
}
41-
},
42-
"/todos/<int:todo_id>": {
43-
"GET": {
44-
"description": "Fetch a single TODO item by its ID.",
45-
"responses": {
46-
"200": "Todo item",
47-
"404": "Todo not found"
48-
}
49-
},
50-
"PUT": {
51-
"description": "Replace an existing TODO item by its ID (all fields required).",
52-
"body_params": {
53-
"title": "The TODO item title (required).",
54-
"done": "Completion status (required).",
55-
"description": "Detailed TODO item description (required)."
56-
},
57-
"responses": {
58-
"200": "Updated todo item",
59-
"400": "Invalid request (missing required fields)",
60-
"404": "Todo not found"
61-
}
62-
},
63-
"PATCH": {
64-
"description": "Update part of a TODO item by its ID (any field can be provided).",
65-
"body_params": {
66-
"title": "The TODO item title (optional).",
67-
"done": "Completion status (optional).",
68-
"description": "Detailed TODO item description (optional)."
69-
},
70-
"responses": {
71-
"200": "Updated todo item",
72-
"400": "Invalid request (empty body)",
73-
"404": "Todo not found"
74-
}
75-
},
76-
"DELETE": {
77-
"description": "Delete a TODO item by its ID.",
78-
"responses": {
79-
"204": "Todo deleted successfully",
80-
"404": "Todo not found"
81-
}
82-
}
83-
},
84-
"/notes": {
85-
"POST": {
86-
"description": "Upload a new note file.",
87-
"content_type": "multipart/form-data",
88-
"form_params": {
89-
"file": "The .txt file to upload (required, max size: 1MB)"
90-
},
91-
"responses": {
92-
"201": "Note created successfully",
93-
"400": "Invalid request (empty file, wrong format, etc.)"
94-
}
95-
}
96-
},
97-
"/notes/<note_name>": {
98-
"GET": {
99-
"description": "Download a note by its name.",
100-
"responses": {
101-
"200": "Note file content",
102-
"404": "Note not found"
103-
}
104-
},
105-
"DELETE": {
106-
"description": "Delete a note by its name.",
107-
"responses": {
108-
"204": "Note deleted successfully",
109-
"404": "Note not found"
110-
}
111-
}
112-
}
113-
})
32+
11433
return Response(
11534
json.dumps(docs, sort_keys=False, indent=2) + "\n",
11635
mimetype='application/json'
@@ -122,60 +41,21 @@ def _get_auth_docs():
12241
if not auth_config or auth_config.auth_method == AuthMethod.NONE:
12342
return None
12443

125-
auth_docs = {
126-
AuthMethod.API_KEY: {
127-
"method": "api_key",
128-
"description": "API Key authentication required for protected endpoints.",
129-
"how_to_authenticate": "Include your API key in the X-API-Key header for all requests.",
130-
"example": {
131-
"headers": {
132-
"X-API-Key": "your-api-key-here"
133-
}
134-
},
135-
"protected_endpoints": ["/todos/*", "/notes/*"]
136-
},
137-
AuthMethod.JWT: {
138-
"method": "jwt",
139-
"description": "JWT (JSON Web Token) authentication required for protected endpoints.",
140-
"how_to_authenticate": "1. Get a token via /auth/login or /auth/signup\n2. Include the token in the Authorization header.",
141-
"endpoints": {
142-
"/auth/signup": {
143-
"method": "POST",
144-
"body": {"username": "string", "password": "string"},
145-
"response": {"token": "string"}
146-
},
147-
"/auth/login": {
148-
"method": "POST",
149-
"body": {"username": "string", "password": "string"},
150-
"response": {"token": "string"}
151-
}
152-
},
153-
"example": {
154-
"headers": {
155-
"Authorization": "Bearer your-jwt-token-here"
156-
}
157-
},
158-
"protected_endpoints": ["/todos/*", "/notes/*"]
159-
},
160-
AuthMethod.SESSION: {
161-
"method": "session",
162-
"description": "Session-based authentication required for protected endpoints.",
163-
"how_to_authenticate": "1. Login via /auth/login or signup via /auth/signup\n2. Session cookie will be automatically managed by your browser.",
164-
"endpoints": {
165-
"/auth/signup": {
166-
"method": "POST",
167-
"body": {"username": "string", "password": "string"}
168-
},
169-
"/auth/login": {
170-
"method": "POST",
171-
"body": {"username": "string", "password": "string"}
172-
},
173-
"/auth/logout": {
174-
"method": "POST"
175-
}
176-
},
177-
"protected_endpoints": ["/todos/*", "/notes/*"]
178-
}
44+
auth_method_map = {
45+
AuthMethod.API_KEY: 'api_key',
46+
AuthMethod.JWT: 'jwt',
47+
AuthMethod.SESSION: 'session'
17948
}
18049

181-
return auth_docs.get(auth_config.auth_method, {"error": "Unknown authentication method"})
50+
method_name = auth_method_map.get(auth_config.auth_method)
51+
if not method_name:
52+
return {"error": "Unknown authentication method"}
53+
54+
auth_file = os.path.join(
55+
current_app.root_path,
56+
'..',
57+
'docs',
58+
'auth',
59+
f'{method_name}.json'
60+
)
61+
return load_json_file(auth_file)

docs/auth/api_key.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"method": "api_key",
3+
"description": "API Key authentication required for protected endpoints.",
4+
"how_to_authenticate": "Include your API key in the X-API-Key header for all requests.",
5+
"example": {
6+
"headers": {
7+
"X-API-Key": "your-api-key-here"
8+
}
9+
},
10+
"protected_endpoints": ["/todos/*", "/notes/*"]
11+
}

docs/auth/jwt.json

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
{
2+
"method": "jwt",
3+
"description": "JWT (JSON Web Token) authentication required for protected endpoints.",
4+
"how_to_authenticate": "Create account via /auth/signup, get tokens via /auth/login, include the access token in the Authorization header, use /auth/refresh with refresh token to get new tokens, use /auth/logout with both tokens to end session",
5+
"endpoints": {
6+
"/auth/signup": {
7+
"method": "POST",
8+
"body": {"username": "string", "password": "string"},
9+
"response": {"message": "Signup successful. Please log in to continue."}
10+
},
11+
"/auth/login": {
12+
"method": "POST",
13+
"body": {"username": "string", "password": "string"},
14+
"response": {
15+
"message": "Login successful",
16+
"access_token": "string",
17+
"refresh_token": "string"
18+
}
19+
},
20+
"/auth/refresh": {
21+
"method": "POST",
22+
"body": {"refresh_token": "string"},
23+
"response": {
24+
"access_token": "string",
25+
"refresh_token": "string"
26+
}
27+
},
28+
"/auth/logout": {
29+
"method": "POST",
30+
"headers": {"Authorization": "Bearer <access_token>"},
31+
"body": {"refresh_token": "string"},
32+
"response": {"message": "string"}
33+
}
34+
},
35+
"example": {
36+
"headers": {
37+
"Authorization": "Bearer your-jwt-access-token-here"
38+
}
39+
},
40+
"protected_endpoints": ["/todos/*", "/notes/*"]
41+
}

docs/auth/session.json

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
{
2+
"method": "session",
3+
"description": "Session-based authentication required for protected endpoints.",
4+
"how_to_authenticate": "Create account via /auth/signup, login via /auth/login to create a session, session cookie will be automatically managed by your client, use /auth/logout to end your session",
5+
"endpoints": {
6+
"/auth/signup": {
7+
"method": "POST",
8+
"body": {"username": "string", "password": "string"},
9+
"response": {"message": "Signup successful. Please log in to continue."}
10+
},
11+
"/auth/login": {
12+
"method": "POST",
13+
"body": {"username": "string", "password": "string"},
14+
"response": {"message": "Login successful"}
15+
},
16+
"/auth/logout": {
17+
"method": "POST",
18+
"response": {"message": "Logout successful"}
19+
}
20+
},
21+
"protected_endpoints": ["/todos/*", "/notes/*"]
22+
}

docs/routes/notes.json

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"/notes": {
3+
"POST": {
4+
"description": "Upload a new note file.",
5+
"content_type": "multipart/form-data",
6+
"form_params": {
7+
"file": "The .txt file to upload (required, max size: 1MB)"
8+
},
9+
"responses": {
10+
"201": "Note created successfully",
11+
"400": "Invalid request (empty file, wrong format, etc.)"
12+
}
13+
}
14+
},
15+
"/notes/<note_name>": {
16+
"GET": {
17+
"description": "Download a note by its name.",
18+
"responses": {
19+
"200": "Note file content",
20+
"404": "Note not found"
21+
}
22+
},
23+
"DELETE": {
24+
"description": "Delete a note by its name.",
25+
"responses": {
26+
"204": "Note deleted successfully",
27+
"404": "Note not found"
28+
}
29+
}
30+
}
31+
}

docs/routes/todos.json

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
{
2+
"/todos": {
3+
"GET": {
4+
"description": "Fetch all TODO items with optional filtering and pagination.",
5+
"query_params": {
6+
"done": "Filter by completion status (true/false).",
7+
"title": "Filter by TODO item title prefix.",
8+
"page": "Page number for pagination (optional, starts at 1).",
9+
"limit": "Number of items per page (optional)."
10+
},
11+
"responses": {
12+
"200": "List of todo items"
13+
}
14+
},
15+
"POST": {
16+
"description": "Add a new TODO item.",
17+
"body_params": {
18+
"title": "The TODO item title (required).",
19+
"done": "Completion status (optional, default: false).",
20+
"description": "Detailed TODO item description (optional)."
21+
},
22+
"responses": {
23+
"201": "Created todo item",
24+
"400": "Invalid request (missing title)"
25+
}
26+
}
27+
},
28+
"/todos/<int:todo_id>": {
29+
"GET": {
30+
"description": "Fetch a single TODO item by its ID.",
31+
"responses": {
32+
"200": "Todo item",
33+
"404": "Todo not found"
34+
}
35+
},
36+
"PUT": {
37+
"description": "Replace an existing TODO item by its ID (all fields required).",
38+
"body_params": {
39+
"title": "The TODO item title (required).",
40+
"done": "Completion status (required).",
41+
"description": "Detailed TODO item description (required)."
42+
},
43+
"responses": {
44+
"200": "Updated todo item",
45+
"400": "Invalid request (missing required fields)",
46+
"404": "Todo not found"
47+
}
48+
},
49+
"PATCH": {
50+
"description": "Update part of a TODO item by its ID (any field can be provided).",
51+
"body_params": {
52+
"title": "The TODO item title (optional).",
53+
"done": "Completion status (optional).",
54+
"description": "Detailed TODO item description (optional)."
55+
},
56+
"responses": {
57+
"200": "Updated todo item",
58+
"400": "Invalid request (empty body)",
59+
"404": "Todo not found"
60+
}
61+
},
62+
"DELETE": {
63+
"description": "Delete a TODO item by its ID.",
64+
"responses": {
65+
"204": "Todo deleted successfully",
66+
"404": "Todo not found"
67+
}
68+
}
69+
}
70+
}

0 commit comments

Comments
 (0)