1- //
2- // ignore_for_file: lines_longer_than_80_chars, avoid_catches_without_on_clauses, avoid_catching_errors
3-
41import 'dart:io' ;
52
63import 'package:dart_frog/dart_frog.dart' ;
7- import 'package:ht_api/src/registry/model_registry.dart' ;
8- import 'package:dart_frog/dart_frog.dart' ;
4+ // Import RequestId from the middleware file where it's defined
95import 'package:ht_api/src/registry/model_registry.dart' ;
106import 'package:ht_data_repository/ht_data_repository.dart' ;
117import 'package:ht_http_client/ht_http_client.dart' ; // Import exceptions
12- import 'package:ht_shared/ht_shared.dart' ; // Import models, SuccessApiResponse, PaginatedResponse
8+ // Import models, SuccessApiResponse, PaginatedResponse, ResponseMetadata
9+ import 'package:ht_shared/ht_shared.dart' ;
10+
11+ import '../../../_middleware.dart' ;
1312
1413/// Handles requests for the /api/v1/data collection endpoint.
1514/// Dispatches requests to specific handlers based on the HTTP method.
@@ -18,13 +17,18 @@ Future<Response> onRequest(RequestContext context) async {
1817 final modelName = context.read <String >();
1918 // Read ModelConfig for fromJson (needed for POST)
2019 final modelConfig = context.read <ModelConfig <dynamic >>();
20+ // Read the unique RequestId provided by the root middleware
21+ // Note: This assumes RequestId is always provided by `routes/_middleware.dart`
22+ final requestId = context.read <RequestId >().id;
2123
2224 try {
2325 switch (context.request.method) {
2426 case HttpMethod .get :
25- return await _handleGet (context, modelName);
27+ // Pass requestId down to the handler
28+ return await _handleGet (context, modelName, requestId);
2629 case HttpMethod .post:
27- return await _handlePost (context, modelName, modelConfig);
30+ // Pass requestId down to the handler
31+ return await _handlePost (context, modelName, modelConfig, requestId);
2832 // Add cases for other methods if needed in the future
2933 default :
3034 // Methods not allowed on the collection endpoint
@@ -38,8 +42,9 @@ Future<Response> onRequest(RequestContext context) async {
3842 rethrow ;
3943 } catch (e, stackTrace) {
4044 // Handle any other unexpected errors locally (e.g., provider resolution)
45+ // Include requestId in the server log for easier debugging
4146 print (
42- 'Unexpected error in /data/index.dart handler: $e \n $stackTrace ' ,
47+ '[ReqID: $ requestId ] Unexpected error in /data/index.dart handler: $e \n $stackTrace ' ,
4348 );
4449 return Response (
4550 statusCode: HttpStatus .internalServerError,
@@ -50,8 +55,12 @@ Future<Response> onRequest(RequestContext context) async {
5055
5156// --- GET Handler ---
5257/// Handles GET requests: Retrieves all items for the specified model
53- /// (with optional query/pagination).
54- Future <Response > _handleGet (RequestContext context, String modelName) async {
58+ /// (with optional query/pagination). Includes request metadata in response.
59+ Future <Response > _handleGet (
60+ RequestContext context,
61+ String modelName,
62+ String requestId, // Receive requestId
63+ ) async {
5564 // Read query parameters
5665 final queryParams = context.request.uri.queryParameters;
5766 final startAfterId = queryParams['startAfterId' ];
@@ -112,8 +121,9 @@ Future<Response> _handleGet(RequestContext context, String modelName) async {
112121 }
113122 } catch (e) {
114123 // Catch potential provider errors during context.read within this handler
124+ // Include requestId in the server log
115125 print (
116- 'Error reading repository provider for model "$modelName " in _handleGet: $e ' ,
126+ '[ReqID: $ requestId ] Error reading repository provider for model "$modelName " in _handleGet: $e ' ,
117127 );
118128 return Response (
119129 statusCode: HttpStatus .internalServerError,
@@ -122,10 +132,16 @@ Future<Response> _handleGet(RequestContext context, String modelName) async {
122132 );
123133 }
124134
125- // Wrap the PaginatedResponse in SuccessApiResponse and serialize
135+ // Create metadata including the request ID and current timestamp
136+ final metadata = ResponseMetadata (
137+ requestId: requestId,
138+ timestamp: DateTime .now ().toUtc (), // Use UTC for consistency
139+ );
140+
141+ // Wrap the PaginatedResponse in SuccessApiResponse with metadata
126142 final successResponse = SuccessApiResponse <PaginatedResponse <dynamic >>(
127143 data: paginatedResponse,
128- // metadata: ResponseMetadata(timestamp: DateTime.now()) , // Optional
144+ metadata: metadata , // Include the created metadata
129145 );
130146
131147 // Need to provide the correct toJsonT for PaginatedResponse
@@ -135,15 +151,18 @@ Future<Response> _handleGet(RequestContext context, String modelName) async {
135151 ),
136152 );
137153
154+ // Return 200 OK with the wrapped and serialized response
138155 return Response .json (body: responseJson);
139156}
140157
141158// --- POST Handler ---
142159/// Handles POST requests: Creates a new item for the specified model.
160+ /// Includes request metadata in response.
143161Future <Response > _handlePost (
144162 RequestContext context,
145163 String modelName,
146164 ModelConfig modelConfig,
165+ String requestId, // Receive requestId
147166) async {
148167 final requestBody = await context.request.json () as Map <String , dynamic >? ;
149168 if (requestBody == null ) {
@@ -159,13 +178,15 @@ Future<Response> _handlePost(
159178 newItem = modelConfig.fromJson (requestBody);
160179 } on TypeError catch (e) {
161180 // Catch errors during deserialization (e.g., missing required fields)
162- print ('Deserialization TypeError in POST /data: $e ' );
181+ // Include requestId in the server log
182+ print ('[ReqID: $requestId ] Deserialization TypeError in POST /data: $e ' );
163183 return Response .json (
164184 statusCode: HttpStatus .badRequest, // 400
165185 body: {
166186 'error' : {
167187 'code' : 'INVALID_REQUEST_BODY' ,
168- 'message' : 'Invalid request body: Missing or invalid required field(s).' ,
188+ 'message' :
189+ 'Invalid request body: Missing or invalid required field(s).' ,
169190 // 'details': e.toString(), // Optional: Include details in dev
170191 },
171192 },
@@ -176,39 +197,58 @@ Future<Response> _handlePost(
176197 dynamic createdItem; // Use dynamic
177198 // Repository exceptions (like BadRequestException from create) will propagate
178199 // up to the main onRequest try/catch and be re-thrown to the middleware.
179- switch (modelName) {
180- case 'headline' :
181- final repo = context.read <HtDataRepository <Headline >>();
182- createdItem = await repo.create (newItem as Headline );
183- case 'category' :
184- final repo = context.read <HtDataRepository <Category >>();
185- createdItem = await repo.create (newItem as Category );
186- case 'source' :
187- final repo = context.read <HtDataRepository <Source >>();
188- createdItem = await repo.create (newItem as Source );
189- case 'country' :
190- final repo = context.read <HtDataRepository <Country >>();
191- createdItem = await repo.create (newItem as Country );
192- default :
193- // This case should ideally be caught by middleware, but added for safety
194- return Response (
195- statusCode: HttpStatus .internalServerError,
196- body:
197- 'Internal Server Error: Unsupported model type "$modelName " reached handler.' ,
198- );
200+ try {
201+ switch (modelName) {
202+ case 'headline' :
203+ final repo = context.read <HtDataRepository <Headline >>();
204+ createdItem = await repo.create (newItem as Headline );
205+ case 'category' :
206+ final repo = context.read <HtDataRepository <Category >>();
207+ createdItem = await repo.create (newItem as Category );
208+ case 'source' :
209+ final repo = context.read <HtDataRepository <Source >>();
210+ createdItem = await repo.create (newItem as Source );
211+ case 'country' :
212+ final repo = context.read <HtDataRepository <Country >>();
213+ createdItem = await repo.create (newItem as Country );
214+ default :
215+ // This case should ideally be caught by middleware, but added for safety
216+ return Response (
217+ statusCode: HttpStatus .internalServerError,
218+ body:
219+ 'Internal Server Error: Unsupported model type "$modelName " reached handler.' ,
220+ );
221+ }
222+ } catch (e) {
223+ // Catch potential provider errors during context.read within this handler
224+ // Include requestId in the server log
225+ print (
226+ '[ReqID: $requestId ] Error reading repository provider for model "$modelName " in _handlePost: $e ' ,
227+ );
228+ return Response (
229+ statusCode: HttpStatus .internalServerError,
230+ body:
231+ 'Internal Server Error: Could not resolve repository for model "$modelName ".' ,
232+ );
199233 }
200234
201- // Wrap the created item in SuccessApiResponse and serialize
235+ // Create metadata including the request ID and current timestamp
236+ final metadata = ResponseMetadata (
237+ requestId: requestId,
238+ timestamp: DateTime .now ().toUtc (), // Use UTC for consistency
239+ );
240+
241+ // Wrap the created item in SuccessApiResponse with metadata
202242 final successResponse = SuccessApiResponse <dynamic >(
203243 data: createdItem,
204- // metadata: ResponseMetadata(timestamp: DateTime.now()) , // Optional
244+ metadata: metadata , // Include the created metadata
205245 );
206246
207247 // Provide the correct toJsonT for the specific model type
208248 final responseJson = successResponse.toJson (
209249 (item) => (item as dynamic ).toJson (), // Assuming all models have toJson
210250 );
211251
212- // Return the serialized response
252+ // Return 201 Created with the wrapped and serialized response
213253 return Response .json (statusCode: HttpStatus .created, body: responseJson);
214254}
0 commit comments