1
1
//
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
3
3
4
4
import 'dart:io' ;
5
5
@@ -10,190 +10,26 @@ import 'package:ht_http_client/ht_http_client.dart'; // Import exceptions
10
10
import 'package:ht_shared/ht_shared.dart' ; // Import models
11
11
12
12
/// 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.
17
14
Future <Response > onRequest (RequestContext context, String id) async {
18
15
// Read dependencies provided by middleware
19
16
final modelName = context.read <String >();
20
- // Read ModelConfig for fromJson/getId (needed for PUT)
17
+ // Read ModelConfig for fromJson (needed for PUT)
21
18
final modelConfig = context.read <ModelConfig <dynamic >>();
22
19
23
20
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);
192
32
}
193
-
194
- // --- Other Methods ---
195
- // Methods not allowed on the item endpoint
196
- return Response (statusCode: HttpStatus .methodNotAllowed);
197
33
} on HtHttpException catch (_) {
198
34
// Let the errorHandler middleware handle HtHttpExceptions (incl. NotFound)
199
35
rethrow ;
@@ -211,3 +47,177 @@ Future<Response> onRequest(RequestContext context, String id) async {
211
47
);
212
48
}
213
49
}
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