From e2fd0ffe65a571001c21589527d8f5c9c0ca2d4d Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 3 Nov 2025 08:52:48 +0100 Subject: [PATCH 1/9] build(deps): update core and dart_frog dependencies - Update core from v1.3.1 to e7c808c - Update dart_frog from 1.2.4 to 1.2.5 - Update analyzer from 8.4.0 to 8.4.1 - Override core dependency to use specific git ref --- pubspec.lock | 12 ++++++------ pubspec.yaml | 5 +++++ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/pubspec.lock b/pubspec.lock index bb32996..41169ec 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: transitive description: name: analyzer - sha256: a40a0cee526a7e1f387c6847bd8a5ccbf510a75952ef8a28338e989558072cb0 + sha256: f51c8499b35f9b26820cfe914828a6a98a94efd5cc78b37bb7d03debae3a1d08 url: "https://pub.dev" source: hosted - version: "8.4.0" + version: "8.4.1" archive: dependency: transitive description: @@ -117,8 +117,8 @@ packages: dependency: "direct main" description: path: "." - ref: "v1.3.1" - resolved-ref: a03efff11b7577974cb444a1a6a46fee8b05ea42 + ref: e7c808c9d459233196e2eac3137a9c87d3976af3 + resolved-ref: e7c808c9d459233196e2eac3137a9c87d3976af3 url: "https://github.com/flutter-news-app-full-source-code/core.git" source: git version: "1.3.1" @@ -142,10 +142,10 @@ packages: dependency: "direct main" description: name: dart_frog - sha256: "0fc909d10ae79dd069e6a3a4769aeaa9839c93b7f6f1e71cfd37b276410875e7" + sha256: "87b5280b029aa2f80f5e9954db4b6c7cbbc2ae6adf5579150c11c40829f95a0a" url: "https://pub.dev" source: hosted - version: "1.2.4" + version: "1.2.5" dart_jsonwebtoken: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 28a449c..c0305ad 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,3 +55,8 @@ dev_dependencies: test: ^1.25.5 very_good_analysis: ^9.0.0 +dependency_overrides: + core: + git: + url: https://github.com/flutter-news-app-full-source-code/core.git + ref: e7c808c9d459233196e2eac3137a9c87d3976af3 \ No newline at end of file From d6d676716a1fabf8a542297eedb7c86f162306c1 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 3 Nov 2025 08:53:01 +0100 Subject: [PATCH 2/9] refactor(dependencies): remove local ad repository - Remove unused localAdRepository from AppDependencies - Delete DataMongodb initialization for LocalAd --- lib/src/config/app_dependencies.dart | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/lib/src/config/app_dependencies.dart b/lib/src/config/app_dependencies.dart index 773dd63..20ac872 100644 --- a/lib/src/config/app_dependencies.dart +++ b/lib/src/config/app_dependencies.dart @@ -63,7 +63,6 @@ class AppDependencies { late final DataRepository userContentPreferencesRepository; late final DataRepository remoteConfigRepository; - late final DataRepository localAdRepository; late final EmailRepository emailRepository; // Services @@ -232,16 +231,6 @@ class AppDependencies { emailRepository = EmailRepository(emailClient: emailClient); - final localAdClient = DataMongodb( - connectionManager: _mongoDbConnectionManager, - modelName: 'local_ads', - fromJson: LocalAd.fromJson, - toJson: LocalAd.toJson, - searchableFields: ['title'], - logger: Logger('DataMongodb'), - ); - localAdRepository = DataRepository(dataClient: localAdClient); - // 5. Initialize Services tokenBlacklistService = MongoDbTokenBlacklistService( connectionManager: _mongoDbConnectionManager, From 604f425af1ca891cd442b8633d2e356b799cfeaa Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 3 Nov 2025 08:54:37 +0100 Subject: [PATCH 3/9] refactor(routes): remove local ad repository provider - Remove the provider for DataRepository in the middleware - This change simplifies the middleware setup by removing an unused repository --- routes/_middleware.dart | 5 ----- 1 file changed, 5 deletions(-) diff --git a/routes/_middleware.dart b/routes/_middleware.dart index e21a84c..51acc21 100644 --- a/routes/_middleware.dart +++ b/routes/_middleware.dart @@ -127,11 +127,6 @@ Handler middleware(Handler handler) { (_) => deps.remoteConfigRepository, ), ) - .use( - provider>( - (_) => deps.localAdRepository, - ), - ) .use(provider((_) => deps.emailRepository)) .use( provider( From 61e9fa6aa18bf99a65d05afa1e65c91f582db7db Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 3 Nov 2025 08:55:47 +0100 Subject: [PATCH 4/9] refactor(data): remove local_ad references and add registry initialization log - Remove all references to 'local_ad' in DataOperationRegistry - Add initialization log message for DataOperationRegistry - Reorganize log and registry initialization order --- lib/src/registry/data_operation_registry.dart | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/lib/src/registry/data_operation_registry.dart b/lib/src/registry/data_operation_registry.dart index 89f7a83..cb11c62 100644 --- a/lib/src/registry/data_operation_registry.dart +++ b/lib/src/registry/data_operation_registry.dart @@ -44,6 +44,9 @@ typedef ItemUpdater = typedef ItemDeleter = Future Function(RequestContext context, String id, String? userId); + +final _log = Logger('DataOperationRegistry'); + /// {@template data_operation_registry} /// A centralized registry for all data handling functions (CRUD operations). /// @@ -56,12 +59,12 @@ typedef ItemDeleter = /// data operations are performed for each model, improving consistency across /// the API. /// {@endtemplate} - -final _log = Logger('DataOperationRegistry'); - class DataOperationRegistry { /// {@macro data_operation_registry} DataOperationRegistry() { + _log.info( + 'Initializing DataOperationRegistry.', + ); _registerOperations(); } @@ -113,8 +116,6 @@ class DataOperationRegistry { .read(id: id, userId: null), 'remote_config': (c, id) => c.read>().read(id: id, userId: null), - 'local_ad': (c, id) => - c.read>().read(id: id, userId: null), 'dashboard_summary': (c, id) => c.read().getSummary(), }); @@ -166,9 +167,6 @@ class DataOperationRegistry { sort: s, pagination: p, ), - 'local_ad': (c, uid, f, s, p) => c - .read>() - .readAll(userId: uid, filter: f, sort: s, pagination: p), }); // --- Register Item Creators --- @@ -196,10 +194,6 @@ class DataOperationRegistry { 'remote_config': (c, item, uid) => c .read>() .create(item: item as RemoteConfig, userId: uid), - 'local_ad': (c, item, uid) => c.read>().create( - item: item as LocalAd, - userId: uid, - ), }); // --- Register Item Updaters --- @@ -319,9 +313,6 @@ class DataOperationRegistry { 'remote_config': (c, id, item, uid) => c .read>() .update(id: id, item: item as RemoteConfig, userId: uid), - 'local_ad': (c, id, item, uid) => c - .read>() - .update(id: id, item: item as LocalAd, userId: uid), }); // --- Register Item Deleters --- @@ -343,8 +334,6 @@ class DataOperationRegistry { .delete(id: id, userId: uid), 'remote_config': (c, id, uid) => c.read>().delete(id: id, userId: uid), - 'local_ad': (c, id, uid) => - c.read>().delete(id: id, userId: uid), }); } } From 194513bba250eb8e8d2359e957041a34d9406e58 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 3 Nov 2025 08:56:11 +0100 Subject: [PATCH 5/9] refactor(registry): remove local_ad model configuration - Removed the 'local_ad' model configuration from the model registry - This change affects the ModelConfig map in model_registry.dart --- lib/src/registry/model_registry.dart | 30 ---------------------------- 1 file changed, 30 deletions(-) diff --git a/lib/src/registry/model_registry.dart b/lib/src/registry/model_registry.dart index b32e96c..fa28e4f 100644 --- a/lib/src/registry/model_registry.dart +++ b/lib/src/registry/model_registry.dart @@ -425,36 +425,6 @@ final modelRegistry = >{ requiresAuthentication: true, ), ), - 'local_ad': ModelConfig( - fromJson: LocalAd.fromJson, - getId: (ad) => ad.id, - getOwnerId: null, // LocalAd is a global resource, not user-owned - getCollectionPermission: const ModelActionPermission( - type: RequiredPermissionType.specificPermission, - permission: Permissions.localAdRead, - requiresAuthentication: true, - ), - getItemPermission: const ModelActionPermission( - type: RequiredPermissionType.specificPermission, - permission: Permissions.localAdRead, - requiresAuthentication: true, - ), - postPermission: const ModelActionPermission( - type: RequiredPermissionType.adminOnly, - permission: Permissions.localAdCreate, - requiresAuthentication: true, - ), - putPermission: const ModelActionPermission( - type: RequiredPermissionType.adminOnly, - permission: Permissions.localAdUpdate, - requiresAuthentication: true, - ), - deletePermission: const ModelActionPermission( - type: RequiredPermissionType.adminOnly, - permission: Permissions.localAdDelete, - requiresAuthentication: true, - ), - ), }; /// Type alias for the ModelRegistry map for easier provider usage. From 814d81ec499c85b1be2eec61f6ec8e0a4f23c29e Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 3 Nov 2025 08:57:26 +0100 Subject: [PATCH 6/9] refactor(seeding): remove local ads seeding and indexing - Remove local ads seeding process from database initialization - Delete local_ads collection indexing by adType - Set AdPlatformType.admob as the default primaryAdPlatform --- lib/src/services/database_seeding_service.dart | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/lib/src/services/database_seeding_service.dart b/lib/src/services/database_seeding_service.dart index fc89de8..44d101b 100644 --- a/lib/src/services/database_seeding_service.dart +++ b/lib/src/services/database_seeding_service.dart @@ -40,13 +40,6 @@ class DatabaseSeedingService { getId: (item) => item.id, toJson: (item) => item.toJson(), ); - await _seedCollection( - collectionName: 'local_ads', - fixtureData: localAdsFixturesData, - getId: (ad) => ad.id, - // ignore: unnecessary_lambdas - toJson: (item) => LocalAd.toJson(item), - ); _log.info('Database seeding process completed.'); } @@ -130,7 +123,7 @@ class DatabaseSeedingService { // Ensure primaryAdPlatform is not 'demo' for initial setup // since its not intended for any use outside the mobile client. final productionReadyAdConfig = initialConfig.adConfig.copyWith( - primaryAdPlatform: AdPlatformType.local, + primaryAdPlatform: AdPlatformType.admob, ); final productionReadyConfig = initialConfig.copyWith( @@ -191,13 +184,6 @@ class DatabaseSeedingService { .collection('countries') .createIndex(keys: {'name': 1}, name: 'countries_name_index'); - /// Index for searching local ads by adType. - /// This index supports efficient queries and filtering on the 'adType' field - /// of local ad documents. - await _db - .collection('local_ads') - .createIndex(keys: {'adType': 1}, name: 'local_ads_adType_index'); - // --- TTL and Unique Indexes via runCommand --- // The following indexes are created using the generic `runCommand` because // they require specific options not exposed by the simpler `createIndex` From 3a5c15deaa6e1f59441cf58d09e7f53a9ecb5575 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 3 Nov 2025 08:58:26 +0100 Subject: [PATCH 7/9] refactor(database): remove legacy local ad platform from remote configs - Add migration to delete 'local' ad platform from remote configs collection - Remove 'local' key from adConfig.platformAdIdentifiers map - Update primaryAdPlatform from 'local' to 'admob' where applicable - Prevent deserialization errors after removing AdPlatformType.local --- ...251103073226_remove_local_ad_platform.dart | 65 +++++++++++++++++++ .../database/migrations/all_migrations.dart | 2 + 2 files changed, 67 insertions(+) create mode 100644 lib/src/database/migrations/20251103073226_remove_local_ad_platform.dart diff --git a/lib/src/database/migrations/20251103073226_remove_local_ad_platform.dart b/lib/src/database/migrations/20251103073226_remove_local_ad_platform.dart new file mode 100644 index 0000000..8311871 --- /dev/null +++ b/lib/src/database/migrations/20251103073226_remove_local_ad_platform.dart @@ -0,0 +1,65 @@ +import 'package:flutter_news_app_api_server_full_source_code/src/database/migration.dart'; +import 'package:logging/logging.dart'; +import 'package:mongo_dart/mongo_dart.dart'; + +/// Migration to remove the legacy `local` ad platform from the `remote_configs` +/// collection. +/// +/// This migration performs two critical cleanup tasks: +/// 1. It removes the `local` key from the `adConfig.platformAdIdentifiers` map +/// in all `remote_configs` documents. +/// 2. It updates any `remote_configs` document where the `primaryAdPlatform` +/// is set to `local`, changing it to `admob`. +/// +/// This ensures data consistency after the removal of the `AdPlatformType.local` +/// enum value and prevents deserialization errors in the application. +class RemoveLocalAdPlatform extends Migration { + /// {@macro remove_local_ad_platform} + RemoveLocalAdPlatform() + : super( + prDate: '20251103073226', + prId: '57', + prSummary: + 'Removes the legacy local ad platform from the remote config, migrating existing data to use admob as the default.', + ); + + @override + Future up(Db db, Logger log) async { + final collection = db.collection('remote_configs'); + + // Step 1: Unset the 'local' key from the platformAdIdentifiers map. + // This removes the field entirely from any document where it exists. + log.info( + 'Attempting to remove "adConfig.platformAdIdentifiers.local" field...', + ); + final unsetResult = await collection.updateMany( + where.exists('adConfig.platformAdIdentifiers.local'), + modify.unset('adConfig.platformAdIdentifiers.local'), + ); + log.info( + 'Removed "adConfig.platformAdIdentifiers.local" from ${unsetResult.nModified} documents.', + ); + + // Step 2: Update the primaryAdPlatform from 'local' to 'admob'. + // This ensures that no document is left with an invalid primary platform. + log.info( + 'Attempting to migrate primaryAdPlatform from "local" to "admob"...', + ); + final updateResult = await collection.updateMany( + where.eq('adConfig.primaryAdPlatform', 'local'), + modify.set('adConfig.primaryAdPlatform', 'admob'), + ); + log.info( + 'Migrated primaryAdPlatform to "admob" for ${updateResult.nModified} documents.', + ); + } + + @override + Future down(Db db, Logger log) async { + // Reverting this change is not safe as it would require re-introducing + // an enum value that no longer exists in the code. + log.warning( + 'Reverting "RemoveLocalAdPlatform" is not supported. The "local" ad platform configuration would need to be manually restored if required.', + ); + } +} diff --git a/lib/src/database/migrations/all_migrations.dart b/lib/src/database/migrations/all_migrations.dart index 2ab7f5c..9f5095d 100644 --- a/lib/src/database/migrations/all_migrations.dart +++ b/lib/src/database/migrations/all_migrations.dart @@ -3,6 +3,7 @@ import 'package:flutter_news_app_api_server_full_source_code/src/database/migrat import 'package:flutter_news_app_api_server_full_source_code/src/database/migrations/20251013000056_add_saved_filters_to_user_preferences.dart'; import 'package:flutter_news_app_api_server_full_source_code/src/database/migrations/20251013000057_add_saved_filters_to_remote_config.dart'; import 'package:flutter_news_app_api_server_full_source_code/src/database/migrations/20251024000000_add_logo_url_to_sources.dart'; +import 'package:flutter_news_app_api_server_full_source_code/src/database/migrations/20251103073226_remove_local_ad_platform.dart'; import 'package:flutter_news_app_api_server_full_source_code/src/services/database_migration_service.dart' show DatabaseMigrationService; @@ -16,4 +17,5 @@ final List allMigrations = [ AddSavedFiltersToUserPreferences(), AddSavedFiltersToRemoteConfig(), AddLogoUrlToSources(), + RemoveLocalAdPlatform(), ]; From b9d844f952ffc38e3ed10bd88348185ac7735a2a Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 3 Nov 2025 09:10:56 +0100 Subject: [PATCH 8/9] chore: remove unnecessary comments from PR template - Removed the initial comment section in the PR template - This change simplifies the template and focuses on the actual content --- .github/PULL_REQUEST_TEMPLATE.md | 8 -------- lib/src/registry/data_operation_registry.dart | 1 - 2 files changed, 9 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 1169936..69a3a60 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,11 +1,3 @@ - - ## Status **READY/IN DEVELOPMENT/HOLD** diff --git a/lib/src/registry/data_operation_registry.dart b/lib/src/registry/data_operation_registry.dart index cb11c62..c15c686 100644 --- a/lib/src/registry/data_operation_registry.dart +++ b/lib/src/registry/data_operation_registry.dart @@ -44,7 +44,6 @@ typedef ItemUpdater = typedef ItemDeleter = Future Function(RequestContext context, String id, String? userId); - final _log = Logger('DataOperationRegistry'); /// {@template data_operation_registry} From 998a374bbb55fcd5e2e2da1558568b347ee97e75 Mon Sep 17 00:00:00 2001 From: fulleni Date: Mon, 3 Nov 2025 09:17:07 +0100 Subject: [PATCH 9/9] docs(api): update comments for route handler middleware - Remove unnecessary comments about ownership checks - Clarify that User may be null if not authenticated - Simplify description of the final data operation - Fix typo: "inner-most" should be "innermost" --- routes/api/v1/data/_middleware.dart | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/routes/api/v1/data/_middleware.dart b/routes/api/v1/data/_middleware.dart index 3dc6723..7c8084c 100644 --- a/routes/api/v1/data/_middleware.dart +++ b/routes/api/v1/data/_middleware.dart @@ -185,14 +185,12 @@ Handler middleware(Handler handler) { // route handler). // // 5. Actual Route Handler (from `index.dart` or `[id].dart`): - // - This runs last, only if all preceding middlewares pass. It will have - // access to a non-null `User` (if authenticated), `ModelConfig`, and - // `modelName` from the context. - // - It performs the data operation and any necessary handler-level - // ownership checks (if flagged by `ModelActionPermission.requiresOwnershipCheck`). + // - This runs last, only if all preceding middlewares pass. It has + // access to a `User?`, `ModelConfig`, and `modelName` from the context. + // - It performs the final data operation. // return handler - .use(authorizationMiddleware()) // Applied fourth (inner-most) + .use(authorizationMiddleware()) // Applied fourth (innermost) .use(_dataRateLimiterMiddleware()) // Applied third .use(_conditionalAuthenticationMiddleware()) // Applied second .use(