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
44import 'dart:io';
55
@@ -10,190 +10,26 @@ import 'package:ht_http_client/ht_http_client.dart'; // Import exceptions
1010import '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.
1714Future<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