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