@@ -10,7 +10,6 @@ import 'package:flutter_news_app_api_server_full_source_code/src/registry/model_
1010import 'package:mongo_dart/mongo_dart.dart' ;
1111
1212/// Handles requests for the /api/v1/data collection endpoint.
13- /// Dispatches requests to specific handlers based on the HTTP method.
1413Future <Response > onRequest (RequestContext context) async {
1514 switch (context.request.method) {
1615 case HttpMethod .get :
@@ -23,214 +22,171 @@ Future<Response> onRequest(RequestContext context) async {
2322}
2423
2524/// Handles GET requests: Retrieves a collection of items.
26- ///
27- /// This handler now accepts a single, JSON-encoded `filter` parameter for
28- /// MongoDB-style queries, along with `sort` and pagination parameters.
2925Future <Response > _handleGet (RequestContext context) async {
30- // Read dependencies provided by middleware
3126 final modelName = context.read <String >();
3227 final modelConfig = context.read <ModelConfig <dynamic >>();
3328 final authenticatedUser = context.read <User >();
34-
35- // --- Parse Query Parameters ---
3629 final params = context.request.uri.queryParameters;
3730
38- // 1. Parse Filter (MongoDB-style)
39- Map <String , dynamic >? filter;
40- if (params.containsKey ('filter' )) {
41- try {
42- filter = jsonDecode (params['filter' ]! ) as Map <String , dynamic >;
43- } on FormatException catch (e) {
44- throw BadRequestException (
45- 'Invalid "filter" parameter: Not valid JSON. $e ' ,
46- );
47- }
48- }
31+ final filter = params.containsKey ('filter' )
32+ ? jsonDecode (params['filter' ]! ) as Map <String , dynamic >
33+ : null ;
4934
50- // 2. Parse Sort
51- List <SortOption >? sort;
52- if (params.containsKey ('sort' )) {
53- try {
54- sort = params['sort' ]! .split (',' ).map ((s) {
55- final parts = s.split (':' );
56- final field = parts[0 ];
57- final order = (parts.length > 1 && parts[1 ] == 'desc' )
58- ? SortOrder .desc
59- : SortOrder .asc;
60- return SortOption (field, order);
61- }).toList ();
62- } catch (e) {
63- throw const BadRequestException (
64- 'Invalid "sort" parameter format. Use "field:order,field2:order".' ,
65- );
66- }
67- }
35+ final sort = params.containsKey ('sort' )
36+ ? (params['sort' ]! .split (',' ).map ((s) {
37+ final parts = s.split (':' );
38+ final field = parts[0 ];
39+ final order = (parts.length > 1 && parts[1 ] == 'desc' )
40+ ? SortOrder .desc
41+ : SortOrder .asc;
42+ return SortOption (field, order);
43+ }).toList ())
44+ : null ;
6845
69- // 3. Parse Pagination
70- PaginationOptions ? pagination;
71- if (params.containsKey ('limit' ) || params.containsKey ('cursor' )) {
72- final limit = int .tryParse (params['limit' ] ?? '' );
73- pagination = PaginationOptions (cursor: params['cursor' ], limit: limit);
74- }
46+ final pagination =
47+ (params.containsKey ('limit' ) || params.containsKey ('cursor' ))
48+ ? PaginationOptions (
49+ cursor: params['cursor' ],
50+ limit: int .tryParse (params['limit' ] ?? '' ),
51+ )
52+ : null ;
7553
76- // --- Repository Call ---
7754 final userIdForRepoCall =
7855 (modelConfig.getOwnerId != null &&
79- ! context.read <PermissionService >().isAdmin (authenticatedUser))
80- ? authenticatedUser.id
81- : null ;
82-
83- dynamic responseData;
84-
85- // The switch statement now only dispatches to the correct repository type.
86- // The query logic is handled by the repository/client.
87- switch (modelName) {
88- case 'headline' :
89- final repo = context.read <DataRepository <Headline >>();
90- responseData = await repo.readAll (
91- userId: userIdForRepoCall,
92- filter: filter,
93- sort: sort,
94- pagination: pagination,
95- );
96- case 'topic' :
97- final repo = context.read <DataRepository <Topic >>();
98- responseData = await repo.readAll (
99- userId: userIdForRepoCall,
100- filter: filter,
101- sort: sort,
102- pagination: pagination,
103- );
104- case 'source' :
105- final repo = context.read <DataRepository <Source >>();
106- responseData = await repo.readAll (
107- userId: userIdForRepoCall,
108- filter: filter,
109- sort: sort,
110- pagination: pagination,
111- );
112- case 'country' :
113- final repo = context.read <DataRepository <Country >>();
114- responseData = await repo.readAll (
115- userId: userIdForRepoCall,
116- filter: filter,
117- sort: sort,
118- pagination: pagination,
119- );
120- case 'language' :
121- final repo = context.read <DataRepository <Language >>();
122- responseData = await repo.readAll (
123- userId: userIdForRepoCall,
124- filter: filter,
125- sort: sort,
126- pagination: pagination,
127- );
128- case 'user' :
129- final repo = context.read <DataRepository <User >>();
130- responseData = await repo.readAll (
131- userId: userIdForRepoCall,
132- filter: filter,
133- sort: sort,
134- pagination: pagination,
135- );
136- default :
137- throw OperationFailedException (
138- 'Unsupported model type "$modelName " for GET all.' ,
139- );
140- }
56+ ! context.read <PermissionService >().isAdmin (authenticatedUser))
57+ ? authenticatedUser.id
58+ : null ;
59+
60+ final responseData = await _readAllItems (
61+ context,
62+ modelName,
63+ userIdForRepoCall,
64+ filter,
65+ sort,
66+ pagination,
67+ );
14168
14269 return ResponseHelper .success (
14370 context: context,
14471 data: responseData,
145- toJsonT: (paginated) => (paginated as PaginatedResponse <dynamic >).toJson (
146- (item) => (item as dynamic ).toJson () as Map <String , dynamic >,
147- ),
72+ toJsonT: (paginated) => (paginated as PaginatedResponse <dynamic >)
73+ .toJson ((item) => (item as dynamic ).toJson () as Map <String , dynamic >),
14874 );
14975}
15076
15177/// Handles POST requests: Creates a new item in a collection.
15278Future <Response > _handlePost (RequestContext context) async {
153- // Read dependencies from middleware
15479 final modelName = context.read <String >();
15580 final modelConfig = context.read <ModelConfig <dynamic >>();
15681 final authenticatedUser = context.read <User >();
15782
158- // --- Parse Body ---
15983 final requestBody = await context.request.json () as Map <String , dynamic >? ;
16084 if (requestBody == null ) {
16185 throw const BadRequestException ('Missing or invalid request body.' );
16286 }
16387
164- // Standardize ID and timestamps before model creation
16588 final now = DateTime .now ().toUtc ().toIso8601String ();
16689 requestBody['id' ] = ObjectId ().oid;
16790 requestBody['createdAt' ] = now;
16891 requestBody['updatedAt' ] = now;
16992
170- dynamic itemToCreate;
171- try {
172- itemToCreate = modelConfig.fromJson (requestBody);
173- } on TypeError catch (e) {
174- throw BadRequestException (
175- 'Invalid request body: Missing or invalid required field(s). $e ' ,
176- );
177- }
93+ final itemToCreate = modelConfig.fromJson (requestBody);
17894
179- // --- Repository Call ---
18095 final userIdForRepoCall =
18196 (modelConfig.getOwnerId != null &&
182- ! context.read <PermissionService >().isAdmin (authenticatedUser))
183- ? authenticatedUser.id
184- : null ;
97+ ! context.read <PermissionService >().isAdmin (authenticatedUser))
98+ ? authenticatedUser.id
99+ : null ;
100+
101+ final createdItem = await _createItem (
102+ context,
103+ modelName,
104+ itemToCreate,
105+ userIdForRepoCall,
106+ );
185107
186- dynamic createdItem;
108+ return ResponseHelper .success (
109+ context: context,
110+ data: createdItem,
111+ toJsonT: (item) => (item as dynamic ).toJson () as Map <String , dynamic >,
112+ statusCode: HttpStatus .created,
113+ );
114+ }
115+
116+ // =============================================================================
117+ // --- Helper Functions ---
118+ // =============================================================================
119+
120+ /// Encapsulates the logic for reading a collection of items by type.
121+ Future <PaginatedResponse <dynamic >> _readAllItems (
122+ RequestContext context,
123+ String modelName,
124+ String ? userId,
125+ Map <String , dynamic >? filter,
126+ List <SortOption >? sort,
127+ PaginationOptions ? pagination,
128+ ) {
187129 switch (modelName) {
188130 case 'headline' :
189- final repo = context.read <DataRepository <Headline >>();
190- createdItem = await repo.create (
191- item: itemToCreate as Headline ,
192- userId: userIdForRepoCall,
193- );
131+ return context.read <DataRepository <Headline >>().readAll (
132+ userId: userId, filter: filter, sort: sort, pagination: pagination);
194133 case 'topic' :
195- final repo = context.read <DataRepository <Topic >>();
196- createdItem = await repo.create (
197- item: itemToCreate as Topic ,
198- userId: userIdForRepoCall,
199- );
134+ return context.read <DataRepository <Topic >>().readAll (
135+ userId: userId, filter: filter, sort: sort, pagination: pagination);
200136 case 'source' :
201- final repo = context.read <DataRepository <Source >>();
202- createdItem = await repo.create (
203- item: itemToCreate as Source ,
204- userId: userIdForRepoCall,
205- );
137+ return context.read <DataRepository <Source >>().readAll (
138+ userId: userId, filter: filter, sort: sort, pagination: pagination);
206139 case 'country' :
207- final repo = context.read <DataRepository <Country >>();
208- createdItem = await repo.create (
209- item: itemToCreate as Country ,
210- userId: userIdForRepoCall,
211- );
140+ return context.read <DataRepository <Country >>().readAll (
141+ userId: userId, filter: filter, sort: sort, pagination: pagination);
212142 case 'language' :
213- final repo = context.read <DataRepository <Language >>();
214- createdItem = await repo.create (
215- item: itemToCreate as Language ,
216- userId: userIdForRepoCall,
143+ return context.read <DataRepository <Language >>().readAll (
144+ userId: userId, filter: filter, sort: sort, pagination: pagination);
145+ case 'user' :
146+ return context.read <DataRepository <User >>().readAll (
147+ userId: userId, filter: filter, sort: sort, pagination: pagination);
148+ default :
149+ throw OperationFailedException (
150+ 'Unsupported model type "$modelName " for GET all.' ,
217151 );
152+ }
153+ }
154+
155+ /// Encapsulates the logic for creating an item by its type.
156+ Future <dynamic > _createItem (
157+ RequestContext context,
158+ String modelName,
159+ dynamic itemToCreate,
160+ String ? userId,
161+ ) {
162+ switch (modelName) {
163+ case 'headline' :
164+ return context
165+ .read <DataRepository <Headline >>()
166+ .create (item: itemToCreate as Headline , userId: userId);
167+ case 'topic' :
168+ return context
169+ .read <DataRepository <Topic >>()
170+ .create (item: itemToCreate as Topic , userId: userId);
171+ case 'source' :
172+ return context
173+ .read <DataRepository <Source >>()
174+ .create (item: itemToCreate as Source , userId: userId);
175+ case 'country' :
176+ return context
177+ .read <DataRepository <Country >>()
178+ .create (item: itemToCreate as Country , userId: userId);
179+ case 'language' :
180+ return context
181+ .read <DataRepository <Language >>()
182+ .create (item: itemToCreate as Language , userId: userId);
218183 case 'remote_config' :
219- final repo = context.read <DataRepository <RemoteConfig >>();
220- createdItem = await repo.create (
221- item: itemToCreate as RemoteConfig ,
222- userId: userIdForRepoCall,
223- );
184+ return context
185+ .read <DataRepository <RemoteConfig >>()
186+ .create (item: itemToCreate as RemoteConfig , userId: userId);
224187 default :
225188 throw OperationFailedException (
226189 'Unsupported model type "$modelName " for POST.' ,
227190 );
228191 }
229-
230- return ResponseHelper .success (
231- context: context,
232- data: createdItem,
233- toJsonT: (item) => (item as dynamic ).toJson () as Map <String , dynamic >,
234- statusCode: HttpStatus .created,
235- );
236192}
0 commit comments