Skip to content

Commit 315de82

Browse files
committed
refactor(routes): split data endpoint handlers into separate functions
- Separated GET, PUT, and DELETE handlers for individual item endpoint - Separated GET and POST handlers for collection endpoint - This refactoring improves code readability and maintainability by breaking down the large onRequest functions into smaller, more focused functions.
1 parent 3d881ce commit 315de82

File tree

2 files changed

+346
-331
lines changed

2 files changed

+346
-331
lines changed

routes/api/v1/data/[id].dart

Lines changed: 188 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// ignore_for_file: lines_longer_than_80_chars, avoid_catches_without_on_clauses, avoid_catching_errors
2+
// ignore_for_file: lines_longer_than_80_chars, avoid_catches_without_on_clauses, avoid_catching_errors, no_default_cases
33

44
import 'dart:io';
55

@@ -10,190 +10,26 @@ import 'package:ht_http_client/ht_http_client.dart'; // Import exceptions
1010
import 'package:ht_shared/ht_shared.dart'; // Import models
1111

1212
/// Handles requests for the /api/v1/data/[id] endpoint.
13-
/// Supports:
14-
/// - GET: Retrieves a single item by its ID for the specified model.
15-
/// - PUT: Updates an existing item by its ID for the specified model.
16-
/// - DELETE: Deletes an item by its ID for the specified model.
13+
/// Dispatches requests to specific handlers based on the HTTP method.
1714
Future<Response> onRequest(RequestContext context, String id) async {
1815
// Read dependencies provided by middleware
1916
final modelName = context.read<String>();
20-
// Read ModelConfig for fromJson/getId (needed for PUT)
17+
// Read ModelConfig for fromJson (needed for PUT)
2118
final modelConfig = context.read<ModelConfig<dynamic>>();
2219

2320
try {
24-
// --- GET Request ---
25-
if (context.request.method == HttpMethod.get) {
26-
Map<String, dynamic> itemJson;
27-
// Removed inner try-catch block to allow exceptions to propagate
28-
switch (modelName) {
29-
case 'headline':
30-
final repo = context.read<HtDataRepository<Headline>>();
31-
final item = await repo.read(id);
32-
// Serialize using the specific model's toJson method
33-
itemJson = item.toJson();
34-
case 'category':
35-
final repo = context.read<HtDataRepository<Category>>();
36-
final item = await repo.read(id);
37-
itemJson = item.toJson();
38-
case 'source':
39-
final repo = context.read<HtDataRepository<Source>>();
40-
final item = await repo.read(id);
41-
itemJson = item.toJson();
42-
case 'country':
43-
final repo = context.read<HtDataRepository<Country>>();
44-
final item = await repo.read(id);
45-
itemJson = item.toJson();
46-
default:
47-
// This case should ideally be caught by middleware, but added for safety
48-
return Response(
49-
statusCode: HttpStatus.internalServerError,
50-
body:
51-
'Internal Server Error: Unsupported model type "$modelName" reached handler.',
52-
);
53-
}
54-
// Return the serialized item
55-
return Response.json(body: itemJson);
56-
}
57-
58-
// --- PUT Request ---
59-
if (context.request.method == HttpMethod.put) {
60-
final requestBody = await context.request.json() as Map<String, dynamic>?;
61-
if (requestBody == null) {
62-
return Response(
63-
statusCode: HttpStatus.badRequest,
64-
body: 'Missing or invalid request body.',
65-
);
66-
}
67-
68-
// Deserialize using ModelConfig's fromJson, catching TypeErrors
69-
dynamic itemToUpdate; // Use dynamic initially
70-
try {
71-
itemToUpdate = modelConfig.fromJson(requestBody);
72-
} on TypeError catch (e) {
73-
// Catch errors during deserialization (e.g., missing required fields)
74-
print('Deserialization TypeError in PUT /data/[id]: $e');
75-
return Response.json(
76-
statusCode: HttpStatus.badRequest, // 400
77-
body: {
78-
'error': {
79-
'code': 'INVALID_REQUEST_BODY',
80-
'message':
81-
'Invalid request body: Missing or invalid required field(s).',
82-
// 'details': e.toString(), // Optional: Include details in dev
83-
},
84-
},
85-
);
86-
}
87-
88-
// ID validation moved inside the switch block after type casting
89-
90-
Map<String, dynamic> updatedJson;
91-
// Removed inner try-catch block to allow exceptions to propagate
92-
switch (modelName) {
93-
case 'headline':
94-
{
95-
// Added block scope
96-
final repo = context.read<HtDataRepository<Headline>>();
97-
final typedItem = itemToUpdate as Headline; // Cast to specific type
98-
// Validate ID consistency
99-
if (typedItem.id != id) {
100-
return Response(
101-
statusCode: HttpStatus.badRequest,
102-
body:
103-
'Bad Request: ID in request body ("${typedItem.id}") does not match ID in path ("$id").',
104-
);
105-
}
106-
final updatedItem = await repo.update(id, typedItem);
107-
updatedJson = updatedItem.toJson();
108-
} // End block scope
109-
case 'category':
110-
{
111-
// Added block scope
112-
final repo = context.read<HtDataRepository<Category>>();
113-
final typedItem = itemToUpdate as Category; // Cast to specific type
114-
// Validate ID consistency
115-
if (typedItem.id != id) {
116-
return Response(
117-
statusCode: HttpStatus.badRequest,
118-
body:
119-
'Bad Request: ID in request body ("${typedItem.id}") does not match ID in path ("$id").',
120-
);
121-
}
122-
final updatedItem = await repo.update(id, typedItem);
123-
updatedJson = updatedItem.toJson();
124-
} // End block scope
125-
case 'source':
126-
{
127-
// Added block scope
128-
final repo = context.read<HtDataRepository<Source>>();
129-
final typedItem = itemToUpdate as Source; // Cast to specific type
130-
// Validate ID consistency
131-
if (typedItem.id != id) {
132-
return Response(
133-
statusCode: HttpStatus.badRequest,
134-
body:
135-
'Bad Request: ID in request body ("${typedItem.id}") does not match ID in path ("$id").',
136-
);
137-
}
138-
final updatedItem = await repo.update(id, typedItem);
139-
updatedJson = updatedItem.toJson();
140-
} // End block scope
141-
case 'country':
142-
{
143-
// Added block scope
144-
final repo = context.read<HtDataRepository<Country>>();
145-
final typedItem = itemToUpdate as Country; // Cast to specific type
146-
// Validate ID consistency
147-
if (typedItem.id != id) {
148-
return Response(
149-
statusCode: HttpStatus.badRequest,
150-
body:
151-
'Bad Request: ID in request body ("${typedItem.id}") does not match ID in path ("$id").',
152-
);
153-
}
154-
final updatedItem = await repo.update(id, typedItem);
155-
updatedJson = updatedItem.toJson();
156-
} // End block scope
157-
default:
158-
// This case should ideally be caught by middleware, but added for safety
159-
return Response(
160-
statusCode: HttpStatus.internalServerError,
161-
body:
162-
'Internal Server Error: Unsupported model type "$modelName" reached handler.',
163-
);
164-
}
165-
// Return the serialized updated item
166-
return Response.json(body: updatedJson);
167-
}
168-
169-
// --- DELETE Request ---
170-
if (context.request.method == HttpMethod.delete) {
171-
// Removed inner try-catch block to allow exceptions to propagate
172-
// No serialization needed, just call delete based on type
173-
switch (modelName) {
174-
case 'headline':
175-
await context.read<HtDataRepository<Headline>>().delete(id);
176-
case 'category':
177-
await context.read<HtDataRepository<Category>>().delete(id);
178-
case 'source':
179-
await context.read<HtDataRepository<Source>>().delete(id);
180-
case 'country':
181-
await context.read<HtDataRepository<Country>>().delete(id);
182-
default:
183-
// This case should ideally be caught by middleware, but added for safety
184-
return Response(
185-
statusCode: HttpStatus.internalServerError,
186-
body:
187-
'Internal Server Error: Unsupported model type "$modelName" reached handler.',
188-
);
189-
}
190-
// Return 204 No Content for successful deletion
191-
return Response(statusCode: HttpStatus.noContent);
21+
switch (context.request.method) {
22+
case HttpMethod.get:
23+
return await _handleGet(context, id, modelName);
24+
case HttpMethod.put:
25+
return await _handlePut(context, id, modelName, modelConfig);
26+
case HttpMethod.delete:
27+
return await _handleDelete(context, id, modelName);
28+
// Add cases for other methods if needed in the future
29+
default:
30+
// Methods not allowed on the item endpoint
31+
return Response(statusCode: HttpStatus.methodNotAllowed);
19232
}
193-
194-
// --- Other Methods ---
195-
// Methods not allowed on the item endpoint
196-
return Response(statusCode: HttpStatus.methodNotAllowed);
19733
} on HtHttpException catch (_) {
19834
// Let the errorHandler middleware handle HtHttpExceptions (incl. NotFound)
19935
rethrow;
@@ -211,3 +47,177 @@ Future<Response> onRequest(RequestContext context, String id) async {
21147
);
21248
}
21349
}
50+
51+
// --- GET Handler ---
52+
/// Handles GET requests: Retrieves a single item by its ID.
53+
Future<Response> _handleGet(
54+
RequestContext context,
55+
String id,
56+
String modelName,
57+
) async {
58+
Map<String, dynamic> itemJson;
59+
// Repository exceptions (like NotFoundException) will propagate up.
60+
switch (modelName) {
61+
case 'headline':
62+
final repo = context.read<HtDataRepository<Headline>>();
63+
final item = await repo.read(id);
64+
itemJson = item.toJson();
65+
case 'category':
66+
final repo = context.read<HtDataRepository<Category>>();
67+
final item = await repo.read(id);
68+
itemJson = item.toJson();
69+
case 'source':
70+
final repo = context.read<HtDataRepository<Source>>();
71+
final item = await repo.read(id);
72+
itemJson = item.toJson();
73+
case 'country':
74+
final repo = context.read<HtDataRepository<Country>>();
75+
final item = await repo.read(id);
76+
itemJson = item.toJson();
77+
default:
78+
// This case should ideally be caught by middleware, but added for safety
79+
return Response(
80+
statusCode: HttpStatus.internalServerError,
81+
body:
82+
'Internal Server Error: Unsupported model type "$modelName" reached handler.',
83+
);
84+
}
85+
// Return the serialized item
86+
return Response.json(body: itemJson);
87+
}
88+
89+
// --- PUT Handler ---
90+
/// Handles PUT requests: Updates an existing item by its ID.
91+
Future<Response> _handlePut(
92+
RequestContext context,
93+
String id,
94+
String modelName,
95+
ModelConfig modelConfig,
96+
) async {
97+
final requestBody = await context.request.json() as Map<String, dynamic>?;
98+
if (requestBody == null) {
99+
return Response(
100+
statusCode: HttpStatus.badRequest,
101+
body: 'Missing or invalid request body.',
102+
);
103+
}
104+
105+
// Deserialize using ModelConfig's fromJson, catching TypeErrors locally
106+
dynamic itemToUpdate; // Use dynamic initially
107+
try {
108+
itemToUpdate = modelConfig.fromJson(requestBody);
109+
} on TypeError catch (e) {
110+
// Catch errors during deserialization (e.g., missing required fields)
111+
print('Deserialization TypeError in PUT /data/[id]: $e');
112+
return Response.json(
113+
statusCode: HttpStatus.badRequest, // 400
114+
body: {
115+
'error': {
116+
'code': 'INVALID_REQUEST_BODY',
117+
'message': 'Invalid request body: Missing or invalid required field(s).',
118+
// 'details': e.toString(), // Optional: Include details in dev
119+
},
120+
},
121+
);
122+
}
123+
124+
Map<String, dynamic> updatedJson;
125+
// Repository exceptions (like NotFoundException, BadRequestException)
126+
// will propagate up.
127+
switch (modelName) {
128+
case 'headline':
129+
{
130+
final repo = context.read<HtDataRepository<Headline>>();
131+
final typedItem = itemToUpdate as Headline;
132+
if (typedItem.id != id) {
133+
return Response(
134+
statusCode: HttpStatus.badRequest,
135+
body:
136+
'Bad Request: ID in request body ("${typedItem.id}") does not match ID in path ("$id").',
137+
);
138+
}
139+
final updatedItem = await repo.update(id, typedItem);
140+
updatedJson = updatedItem.toJson();
141+
}
142+
case 'category':
143+
{
144+
final repo = context.read<HtDataRepository<Category>>();
145+
final typedItem = itemToUpdate as Category;
146+
if (typedItem.id != id) {
147+
return Response(
148+
statusCode: HttpStatus.badRequest,
149+
body:
150+
'Bad Request: ID in request body ("${typedItem.id}") does not match ID in path ("$id").',
151+
);
152+
}
153+
final updatedItem = await repo.update(id, typedItem);
154+
updatedJson = updatedItem.toJson();
155+
}
156+
case 'source':
157+
{
158+
final repo = context.read<HtDataRepository<Source>>();
159+
final typedItem = itemToUpdate as Source;
160+
if (typedItem.id != id) {
161+
return Response(
162+
statusCode: HttpStatus.badRequest,
163+
body:
164+
'Bad Request: ID in request body ("${typedItem.id}") does not match ID in path ("$id").',
165+
);
166+
}
167+
final updatedItem = await repo.update(id, typedItem);
168+
updatedJson = updatedItem.toJson();
169+
}
170+
case 'country':
171+
{
172+
final repo = context.read<HtDataRepository<Country>>();
173+
final typedItem = itemToUpdate as Country;
174+
if (typedItem.id != id) {
175+
return Response(
176+
statusCode: HttpStatus.badRequest,
177+
body:
178+
'Bad Request: ID in request body ("${typedItem.id}") does not match ID in path ("$id").',
179+
);
180+
}
181+
final updatedItem = await repo.update(id, typedItem);
182+
updatedJson = updatedItem.toJson();
183+
}
184+
default:
185+
// This case should ideally be caught by middleware, but added for safety
186+
return Response(
187+
statusCode: HttpStatus.internalServerError,
188+
body:
189+
'Internal Server Error: Unsupported model type "$modelName" reached handler.',
190+
);
191+
}
192+
// Return the serialized updated item
193+
return Response.json(body: updatedJson);
194+
}
195+
196+
// --- DELETE Handler ---
197+
/// Handles DELETE requests: Deletes an item by its ID.
198+
Future<Response> _handleDelete(
199+
RequestContext context,
200+
String id,
201+
String modelName,
202+
) async {
203+
// Repository exceptions (like NotFoundException) will propagate up.
204+
switch (modelName) {
205+
case 'headline':
206+
await context.read<HtDataRepository<Headline>>().delete(id);
207+
case 'category':
208+
await context.read<HtDataRepository<Category>>().delete(id);
209+
case 'source':
210+
await context.read<HtDataRepository<Source>>().delete(id);
211+
case 'country':
212+
await context.read<HtDataRepository<Country>>().delete(id);
213+
default:
214+
// This case should ideally be caught by middleware, but added for safety
215+
return Response(
216+
statusCode: HttpStatus.internalServerError,
217+
body:
218+
'Internal Server Error: Unsupported model type "$modelName" reached handler.',
219+
);
220+
}
221+
// Return 204 No Content for successful deletion
222+
return Response(statusCode: HttpStatus.noContent);
223+
}

0 commit comments

Comments
 (0)