Skip to content

Commit a95ffca

Browse files
committed
feat(database): add migration to refactor adConfig to role-based structure
- Implement RefactorAdConfigToRoleBased migration - Transform adConfig structure in RemoteConfig documents - Replace old ad frequency fields with new visibleTo maps - Update FeedAdConfiguration, ArticleAdConfiguration, and InterstitialAdConfiguration - Ensure backward compatibility in down migration
1 parent 3dcf1d0 commit a95ffca

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import 'package:core/core.dart';
2+
import 'package:flutter_news_app_api_server_full_source_code/src/database/migration.dart';
3+
import 'package:logging/logging.dart';
4+
import 'package:mongo_dart/mongo_dart.dart';
5+
6+
/// {@template refactor_ad_config_to_role_based}
7+
/// A comprehensive migration to refactor the `adConfig` structure within
8+
/// `RemoteConfig` documents to a new role-based `visibleTo` map approach.
9+
///
10+
/// This migration addresses significant changes introduced by a PR (see
11+
/// [gitHubPullRequest]) that aimed to enhance flexibility and maintainability
12+
/// of ad configurations. It transforms old, role-specific ad frequency and
13+
/// placement fields into new `visibleTo` maps for `FeedAdConfiguration`,
14+
/// `ArticleAdConfiguration`, and `InterstitialAdConfiguration`.
15+
///
16+
/// The migration ensures that existing `RemoteConfig` documents are updated
17+
/// to conform to the latest model structure, preventing deserialization errors
18+
/// and enabling granular control over ad display for different user roles.
19+
/// {@endtemplate}
20+
class RefactorAdConfigToRoleBased extends Migration {
21+
/// {@macro refactor_ad_config_to_role_based}
22+
RefactorAdConfigToRoleBased()
23+
: super(
24+
version: '20250924084800',
25+
description: 'Refactor adConfig to use role-based visibleTo maps',
26+
gitHubPullRequest:
27+
'https://github.com/flutter-news-app-full-source-code/core/pull/50',
28+
);
29+
30+
@override
31+
Future<void> up(Db db, Logger log) async {
32+
log.info('Applying migration: Refactor adConfig to role-based visibleTo maps.');
33+
34+
final remoteConfigCollection = db.collection('remote_configs');
35+
36+
// Define default FeedAdFrequencyConfig for roles
37+
const defaultGuestFeedAdFrequency =
38+
FeedAdFrequencyConfig(adFrequency: 5, adPlacementInterval: 3);
39+
const defaultStandardUserFeedAdFrequency =
40+
FeedAdFrequencyConfig(adFrequency: 10, adPlacementInterval: 5);
41+
const defaultPremiumUserFeedAdFrequency =
42+
FeedAdFrequencyConfig(adFrequency: 0, adPlacementInterval: 0);
43+
44+
// Define default InterstitialAdFrequencyConfig for roles
45+
const defaultGuestInterstitialAdFrequency =
46+
InterstitialAdFrequencyConfig(transitionsBeforeShowingInterstitialAds: 5);
47+
const defaultStandardUserInterstitialAdFrequency =
48+
InterstitialAdFrequencyConfig(transitionsBeforeShowingInterstitialAds: 10);
49+
const defaultPremiumUserInterstitialAdFrequency =
50+
InterstitialAdFrequencyConfig(transitionsBeforeShowingInterstitialAds: 50000);
51+
52+
// Define default ArticleAdSlot visibility for roles
53+
final defaultArticleAdSlots = {
54+
InArticleAdSlotType.aboveArticleContinueReadingButton.name: true,
55+
InArticleAdSlotType.belowArticleContinueReadingButton.name: true,
56+
};
57+
58+
final result = await remoteConfigCollection.updateMany(
59+
// Find documents that still have the old structure (e.g., old frequency fields)
60+
where.exists('adConfig.feedAdConfiguration.frequencyConfig.guestAdFrequency'),
61+
ModifierBuilder()
62+
// --- FeedAdConfiguration Transformation ---
63+
// Remove old frequencyConfig fields
64+
..unset('adConfig.feedAdConfiguration.frequencyConfig.guestAdFrequency')
65+
..unset('adConfig.feedAdConfiguration.frequencyConfig.guestAdPlacementInterval')
66+
..unset('adConfig.feedAdConfiguration.frequencyConfig.authenticatedAdFrequency')
67+
..unset('adConfig.feedAdConfiguration.frequencyConfig.authenticatedAdPlacementInterval')
68+
..unset('adConfig.feedAdConfiguration.frequencyConfig.premiumAdFrequency')
69+
..unset('adConfig.feedAdConfiguration.frequencyConfig.premiumAdPlacementInterval')
70+
// Set the new visibleTo map for FeedAdConfiguration
71+
..set(
72+
'adConfig.feedAdConfiguration.visibleTo',
73+
{
74+
AppUserRole.guestUser.name: defaultGuestFeedAdFrequency.toJson(),
75+
AppUserRole.standardUser.name:
76+
defaultStandardUserFeedAdFrequency.toJson(),
77+
AppUserRole.premiumUser.name:
78+
defaultPremiumUserFeedAdFrequency.toJson(),
79+
},
80+
)
81+
// --- ArticleAdConfiguration Transformation ---
82+
// Remove old inArticleAdSlotConfigurations list
83+
..unset('adConfig.articleAdConfiguration.inArticleAdSlotConfigurations')
84+
// Set the new visibleTo map for ArticleAdConfiguration
85+
..set(
86+
'adConfig.articleAdConfiguration.visibleTo',
87+
{
88+
AppUserRole.guestUser.name: defaultArticleAdSlots,
89+
AppUserRole.standardUser.name: defaultArticleAdSlots,
90+
AppUserRole.premiumUser.name: defaultArticleAdSlots,
91+
},
92+
)
93+
// --- InterstitialAdConfiguration Transformation ---
94+
// Remove old feedInterstitialAdFrequencyConfig fields
95+
..unset('adConfig.interstitialAdConfiguration.feedInterstitialAdFrequencyConfig.guestTransitionsBeforeShowingInterstitialAds')
96+
..unset('adConfig.interstitialAdConfiguration.feedInterstitialAdFrequencyConfig.standardUserTransitionsBeforeShowingInterstitialAds')
97+
..unset('adConfig.interstitialAdConfiguration.feedInterstitialAdFrequencyConfig.premiumUserTransitionsBeforeShowingInterstitialAds')
98+
// Set the new visibleTo map for InterstitialAdConfiguration
99+
..set(
100+
'adConfig.interstitialAdConfiguration.visibleTo',
101+
{
102+
AppUserRole.guestUser.name:
103+
defaultGuestInterstitialAdFrequency.toJson(),
104+
AppUserRole.standardUser.name:
105+
defaultStandardUserInterstitialAdFrequency.toJson(),
106+
AppUserRole.premiumUser.name:
107+
defaultPremiumUserInterstitialAdFrequency.toJson(),
108+
},
109+
),
110+
);
111+
112+
log.info(
113+
'Updated ${result.nModified} remote_config documents '
114+
'to new role-based adConfig structure.',
115+
);
116+
}
117+
118+
@override
119+
Future<void> down(Db db, Logger log) async {
120+
log.warning(
121+
'Reverting migration: Revert adConfig to old structure '
122+
'(not recommended for production).',
123+
);
124+
// This down migration is complex and primarily for development/testing rollback.
125+
// Reverting to the old structure would require re-introducing the old fields
126+
// and potentially losing data if the new structure was used.
127+
// For simplicity in this example, we'll just unset the new fields.
128+
final result = await db.collection('remote_configs').updateMany(
129+
where.exists('adConfig.feedAdConfiguration.visibleTo'),
130+
ModifierBuilder()
131+
..unset('adConfig.feedAdConfiguration.visibleTo')
132+
..unset('adConfig.articleAdConfiguration.visibleTo')
133+
..unset('adConfig.interstitialAdConfiguration.visibleTo'),
134+
);
135+
log.warning(
136+
'Reverted ${result.nModified} remote_config documents '
137+
'by unsetting new adConfig fields.',
138+
);
139+
}
140+
}

0 commit comments

Comments
 (0)