diff --git a/routes/api/v1/data/[id]/index.dart b/routes/api/v1/data/[id]/index.dart index 4ea8e99..5221241 100644 --- a/routes/api/v1/data/[id]/index.dart +++ b/routes/api/v1/data/[id]/index.dart @@ -84,18 +84,33 @@ Future _handlePut(RequestContext context, String id) async { ); } - final bodyItemId = modelConfig.getId(itemToUpdate); - if (bodyItemId != id) { - throw BadRequestException( - 'Bad Request: ID in request body ("$bodyItemId") does not match ID in path ("$id").', - ); + try { + final bodyItemId = modelConfig.getId(itemToUpdate); + if (bodyItemId != id) { + throw BadRequestException( + 'Bad Request: ID in request body ("$bodyItemId") does not match ID in path ("$id").', + ); + } + } catch (e) { + // Ignore if getId throws, as the ID might not be in the body, + // which can be acceptable for some models. + _logger.info('Could not get ID from PUT body: $e'); } if (modelName == 'user_content_preferences') { - await userPreferenceLimitService.checkUpdatePreferences( - authenticatedUser, - itemToUpdate as UserContentPreferences, - ); + if (itemToUpdate is UserContentPreferences) { + await userPreferenceLimitService.checkUpdatePreferences( + authenticatedUser, + itemToUpdate, + ); + } else { + _logger.severe( + 'Type Error: Expected UserContentPreferences for limit check, but got ${itemToUpdate.runtimeType}.', + ); + throw const OperationFailedException( + 'Internal Server Error: Model type mismatch for limit check.', + ); + } } final userIdForRepoCall = _getUserIdForRepoCall( diff --git a/routes/api/v1/data/index.dart b/routes/api/v1/data/index.dart index dbce3f6..379d3e0 100644 --- a/routes/api/v1/data/index.dart +++ b/routes/api/v1/data/index.dart @@ -28,20 +28,34 @@ Future _handleGet(RequestContext context) async { final authenticatedUser = context.read(); final params = context.request.uri.queryParameters; - final filter = params.containsKey('filter') - ? jsonDecode(params['filter']!) as Map - : null; + Map? filter; + if (params.containsKey('filter')) { + try { + filter = jsonDecode(params['filter']!) as Map; + } on FormatException catch (e) { + throw BadRequestException( + 'Invalid "filter" parameter: Not valid JSON. $e', + ); + } + } - final sort = params.containsKey('sort') - ? (params['sort']!.split(',').map((s) { - final parts = s.split(':'); - final field = parts[0]; - final order = (parts.length > 1 && parts[1] == 'desc') - ? SortOrder.desc - : SortOrder.asc; - return SortOption(field, order); - }).toList()) - : null; + List? sort; + if (params.containsKey('sort')) { + try { + sort = params['sort']!.split(',').map((s) { + final parts = s.split(':'); + final field = parts[0]; + final order = (parts.length > 1 && parts[1] == 'desc') + ? SortOrder.desc + : SortOrder.asc; + return SortOption(field, order); + }).toList(); + } catch (e) { + throw const BadRequestException( + 'Invalid "sort" parameter format. Use "field:order,field2:order".', + ); + } + } final pagination = (params.containsKey('limit') || params.containsKey('cursor')) @@ -91,7 +105,14 @@ Future _handlePost(RequestContext context) async { requestBody['createdAt'] = now; requestBody['updatedAt'] = now; - final itemToCreate = modelConfig.fromJson(requestBody); + dynamic itemToCreate; + try { + itemToCreate = modelConfig.fromJson(requestBody); + } on TypeError catch (e) { + throw BadRequestException( + 'Invalid request body: Missing or invalid required field(s). $e', + ); + } final userIdForRepoCall = (modelConfig.getOwnerId != null &&