public async Task ExampleUsage2() { // Get your key from https://console.developers.google.com/apis/credentials var apiKey = "AIzaSyCtcFQMgRIUHhSuXggm4BtXT4eZvUrBWN0"; // See https://docs.google.com/spreadsheets/d/1Hwu4ZtRR0iXD65Wuj_XyJxLw4PN8SE0sRgnBKeVoq3A var sheetId = "1Hwu4ZtRR0iXD65Wuj_XyJxLw4PN8SE0sRgnBKeVoq3A"; var sheetName = "MySheet1"; // Has to match the sheet name IKeyValueStore onlineNewsStore = new GoogleSheetsKeyValueStore(new InMemoryKeyValueStore(), apiKey, sheetId, sheetName); IKeyValueStore onDeviceEventsStore = new InMemoryKeyValueStore(); IKeyValueStore newsStore = new DualStore(onlineNewsStore, onDeviceEventsStore); var newsLocalDataStore = new InMemoryKeyValueStore().GetTypeAdapter <News.LocalData>(); NewsManager manager = new NewsManager(newsLocalDataStore, newsStore.GetTypeAdapter <News>()); var title = "First App Start"; var descr = "You just started the app the first time!"; var url = "https://github.com/cs-util-com/cscore"; var urlText = "Show details.."; News n = News.NewLocalNewsEvent(title, descr, url, urlText); await onDeviceEventsStore.Set(n.key, n); IEnumerable <News> allNews = await manager.GetAllNews(); Assert.Contains(allNews, x => x.title == title); Log.d(JsonWriter.AsPrettyString(allNews)); IEnumerable <News> unreadNews = await manager.GetAllUnreadNews(); Assert.Contains(unreadNews, x => x.title == title); }
public async Task ExampleUsage1() { // Get your key from https://console.developers.google.com/apis/credentials var apiKey = "AIzaSyCtcFQMgRIUHhSuXggm4BtXT4eZvUrBWN0"; // https://docs.google.com/spreadsheets/d/1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM contains the sheetId: var sheetId = "1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM"; var sheetName = "MySheet1"; // Has to match the sheet name var googleSheetsStore = new GoogleSheetsKeyValueStore(new InMemoryKeyValueStore(), apiKey, sheetId, sheetName); var testStore = new FeatureFlagStore(new InMemoryKeyValueStore(), googleSheetsStore); IoC.inject.SetSingleton <FeatureFlagManager <FeatureFlag> >(new FeatureFlagManager <FeatureFlag>(testStore)); // Open https://docs.google.com/spreadsheets/d/1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM for these: Assert.False(await FeatureFlag.IsEnabled("MyFlag1")); Assert.True(await FeatureFlag.IsEnabled("MyFlag2")); var key3 = "MyFlag3"; IFeatureFlag flag3 = await FeatureFlagManager <FeatureFlag> .instance.GetFeatureFlag(key3); Assert.Equal(40, flag3.rolloutPercentage); // Rollout of feature 3 is at 40% // The local random value that was chosen determines if the flag is enabled or not: if (flag3.localState.randomPercentage <= flag3.rolloutPercentage) { Assert.True(await FeatureFlag.IsEnabled(key3)); } else { Assert.False(await FeatureFlag.IsEnabled(key3)); } }
public async Task ExampleUsage1() { // Get your key from https://console.developers.google.com/apis/credentials var exampleApiKey = "AIzaSyCtcFQMgRIUHhSuXggm4BtXT4eZvUrBWN0"; // See https://docs.google.com/spreadsheets/d/1qZjRA_uLsImX-VHpJ1nnrCIASmU20Tbaakf5Le5Wrs8 var spreadsheetId = "1qZjRA_uLsImX-VHpJ1nnrCIASmU20Tbaakf5Le5Wrs8"; var sheetName = "UpdateEntriesV2"; // Has to match the sheet name var cache = new InMemoryKeyValueStore(); var store = new GoogleSheetsKeyValueStore(cache, exampleApiKey, spreadsheetId, sheetName); // Use the GoogleSheets store as the source for the update information: var updateChecker = new DefaultAppUpdateChecker(store, null); var entries = await updateChecker.DownloadAllUpdateEntries(); Assert.Equal(5, entries.Count()); // Use the EnvironmentV2.instance.systemInfo values to match against all entries: var matchingEntries = await updateChecker.DownloadMatchingUpdateEntries(); if (matchingEntries.Count > 0) { Assert.Single(matchingEntries); var instructions = matchingEntries.First().GetUpdateInstructions(); Assert.Equal("https://github.com/cs-util-com/cscore", instructions.url); Log.d("instructions: " + JsonWriter.AsPrettyString(instructions)); } else { Log.e("Test cant be fully done on current system: " + JsonWriter.AsPrettyString(EnvironmentV2.instance.systemInfo)); } }
public async Task ExampleUsage1() { // Get your key from https://console.developers.google.com/apis/credentials var apiKey = "AIzaSyCtcFQMgRIUHhSuXggm4BtXT4eZvUrBWN0"; // See https://docs.google.com/spreadsheets/d/1Hwu4ZtRR0iXD65Wuj_XyJxLw4PN8SE0sRgnBKeVoq3A var sheetId = "1Hwu4ZtRR0iXD65Wuj_XyJxLw4PN8SE0sRgnBKeVoq3A"; var sheetName = "MySheet1"; // Has to match the sheet name IKeyValueStore newsStore = new GoogleSheetsKeyValueStore(new InMemoryKeyValueStore(), apiKey, sheetId, sheetName); var newsLocalDataStore = new InMemoryKeyValueStore().GetTypeAdapter <News.LocalData>(); NewsManager manager = new NewsManager(newsLocalDataStore, newsStore.GetTypeAdapter <News>()); IEnumerable <News> allNews = await manager.GetAllNews(); var news1 = allNews.First(); Assert.NotNull(news1); Assert.Equal("New", news1.type); Assert.Equal(News.NewsType.New, news1.GetNewsType()); Assert.True(news1.GetDate(true).IsUtc()); // Mark that the user has read the news: await manager.MarkNewsAsRead(news1); Assert.True(allNews.First().localData.isRead); Assert.True((await manager.GetAllNews()).First().localData.isRead); Assert.True((await newsLocalDataStore.Get(news1.key, null)).isRead); IEnumerable <News> unreadNews = await manager.GetAllUnreadNews(); Assert.Contains(news1, allNews); Assert.DoesNotContain(news1, unreadNews); }
public static async Task <ProgressionSystem <FeatureFlag> > SetupWithGSheets(string apiKey, string sheetId, string sheetName) { var cachedFlags = FileBasedKeyValueStore.New("FeatureFlags"); var cachedFlagsLocalData = FileBasedKeyValueStore.New("FeatureFlags_LocalData"); var googleSheetsStore = new GoogleSheetsKeyValueStore(cachedFlags, apiKey, sheetId, sheetName); return(await Setup(new FeatureFlagStore(cachedFlagsLocalData, googleSheetsStore))); }
public async Task ExtendedTest1() { // Get your key from https://console.developers.google.com/apis/credentials var apiKey = "AIzaSyCtcFQMgRIUHhSuXggm4BtXT4eZvUrBWN0"; // See https://docs.google.com/spreadsheets/d/1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM var sheetId = "1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM"; var sheetName = "MySheet1"; // Has to match the sheet name var googleSheetsStore = new GoogleSheetsKeyValueStore(new InMemoryKeyValueStore(), apiKey, sheetId, sheetName); var localStore = new InMemoryKeyValueStore(); var testStore = new FeatureFlagStore(localStore, googleSheetsStore); IoC.inject.SetSingleton <FeatureFlagManager <FeatureFlag> >(new FeatureFlagManager <FeatureFlag>(testStore)); var key1 = "MyFlag1"; var key2 = "MyFlag2"; var key3 = "MyFlag3"; Assert.NotNull(await googleSheetsStore.Get <FeatureFlag>(key1, null)); Assert.NotNull(await googleSheetsStore.Get <FeatureFlag>(key2, null)); Assert.NotNull(await testStore.Get(key2, null)); Assert.NotNull(await FeatureFlagManager <FeatureFlag> .instance.GetFeatureFlag(key2)); Assert.False(await FeatureFlag.IsEnabled(key1)); Assert.True(await FeatureFlag.IsEnabled(key2)); var flag3_1 = await FeatureFlagManager <FeatureFlag> .instance.GetFeatureFlag(key3); Assert.Equal(40, flag3_1.rolloutPercentage); var localState3_1 = await localStore.Get <IFeatureFlagLocalState>(key3, null); var percent3_1 = localState3_1.randomPercentage; Assert.NotEqual(0, percent3_1); if (percent3_1 < flag3_1.rolloutPercentage) { Assert.True(await FeatureFlag.IsEnabled(key3)); } else { Assert.False(await FeatureFlag.IsEnabled(key3)); } localState3_1.randomPercentage = flag3_1.rolloutPercentage - 1; await localStore.Set(key3, localState3_1); var localState3_2 = await localStore.Get <IFeatureFlagLocalState>(key3, null); Assert.NotEqual(percent3_1, localState3_2.randomPercentage); var flag3_2 = await FeatureFlagManager <FeatureFlag> .instance.GetFeatureFlag(key3); Assert.Equal(localState3_2.randomPercentage, flag3_2.localState.randomPercentage); Assert.True(await flag3_2.IsEnabled()); Assert.True(await FeatureFlag.IsEnabled(key3)); }
public static NewsManager NewManagerViaGSheets(string apiKey, string sheetId, string sheetName) { var newsDir = EnvironmentV2.instance.GetOrAddTempFolder("NewsManagerCache"); var gSheetsCache = new FileBasedKeyValueStore(newsDir.GetChildDir("GSheetsData")); var newsLocalDataCache = new FileBasedKeyValueStore(newsDir.GetChildDir("LocalData")); IKeyValueStore newsStore = new GoogleSheetsKeyValueStore(gSheetsCache, apiKey, sheetId, sheetName); return(new NewsManager(newsLocalDataCache.GetTypeAdapter <News.LocalData>(), newsStore.GetTypeAdapter <News>())); }
private static async Task <ProgressionSystem <FeatureFlag> > NewInMemoryTestXpSystem(string apiKey, string sheetId, string sheetName) { var cachedFlags = new InMemoryKeyValueStore(); var googleSheetsStore = new GoogleSheetsKeyValueStore(cachedFlags, apiKey, sheetId, sheetName); var cachedFlagsLocalData = new InMemoryKeyValueStore(); var analytics = new LocalAnalytics(new InMemoryKeyValueStore()); analytics.createStoreFor = (_ => new InMemoryKeyValueStore().GetTypeAdapter <AppFlowEvent>()); var featureFlagStore = new FeatureFlagStore(cachedFlagsLocalData, googleSheetsStore); return(await DefaultProgressionSystem.Setup(featureFlagStore, analytics)); }
public async Task TestGoogleSheetsKeyValueStore() { // Get your key from https://console.developers.google.com/apis/credentials var apiKey = "AIzaSyCtcFQMgRIUHhSuXggm4BtXT4eZvUrBWN0"; // See https://docs.google.com/spreadsheets/d/13R9y6lnUMgRPC0PinJ23tACC6Flgogxa7h7SVaaLhT0 var sheetId = "13R9y6lnUMgRPC0PinJ23tACC6Flgogxa7h7SVaaLhT0"; var sheetName = "UpdateEntriesV1"; // Has to match the sheet name var refreshDelayInMs = 1000; // The cache is where the data from the sheet will be locally stored in: var cache = new InMemoryKeyValueStore(); // Could also persist to disc here var store = new GoogleSheetsKeyValueStore(cache, apiKey, sheetId, sheetName, refreshDelayInMs); var download1 = store.dowloadOnlineDataDebounced(); var download2 = store.dowloadOnlineDataDebounced(); var download3 = store.dowloadOnlineDataDebounced(); // After the refresh delay redownload was allowed again: await TaskV2.Delay(refreshDelayInMs * 3); Assert.True(await download1); // first trigger downloaded the data Assert.NotEmpty(store.latestRawSheetData); // Triggering it instant a second time will not download the data again: Assert.False(await download2); // Second trigger was skipped Assert.True(await download3); var entry1 = await store.Get <MySheetEntry>("1", null); Assert.NotNull(entry1); Assert.Equal("a", entry1.myString1); Assert.Equal(new List <int>() { 1, 2, 3, 4 }, entry1.myArray1); Assert.Equal("b", entry1.myObj1.a); Assert.Equal(5, entry1.myInt1); Assert.Equal(1.4, entry1.myDouble1); var entry2 = await store.Get <MySheetEntry>("2", null); Assert.NotNull(entry2); Assert.Null(entry2.myString1); Assert.Empty(entry2.myArray1); Assert.Null(entry2.myObj1); Assert.Equal(0, entry2.myInt1); Assert.Equal(0, entry2.myDouble1); }
public async Task TestDefaultProgressionSystemSetup() { // Get your key from https://console.developers.google.com/apis/credentials var apiKey = "AIzaSyCtcFQMgRIUHhSuXggm4BtXT4eZvUrBWN0"; // https://docs.google.com/spreadsheets/d/1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM contains the sheetId: var sheetId = "1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM"; var sheetName = "MySheet1"; // Has to match the sheet name var cachedFlags = FileBasedKeyValueStore.New("FeatureFlags"); var cachedFlagsLocalData = FileBasedKeyValueStore.New("FeatureFlags_LocalData"); var googleSheetsStore = new GoogleSheetsKeyValueStore(cachedFlags, apiKey, sheetId, sheetName); DefaultProgressionSystem.Setup(new TestFeatureFlagStore(cachedFlagsLocalData, googleSheetsStore)); AppFlow.TrackEvent(EventConsts.catMethod, "Some event"); Assert.False(await FeatureFlag.IsEnabled("MyFlag4")); Log.d("Now take a look at the folders in " + cachedFlags.folderForAllFiles.Parent.GetFullFileSystemPath()); }
public static Func <string, Task <Dictionary <string, Translation> > > LoadLocaleFromGoogleSheets( IKeyValueStore localCache, string apiKey, string sheetId, string initialSheetName = "en-US") { var translationDatabase = new GoogleSheetsKeyValueStore(localCache, apiKey, sheetId, initialSheetName); return(async(string localeName) => { if (localeName != translationDatabase.sheetName) { translationDatabase.sheetName = localeName; } var downloadTask = translationDatabase.dowloadOnlineDataDebounced(); if (downloadTask != null && !await downloadTask) // Make sure the data is downloaded at least once { Log.w($"Could not download {localeName} translation data from sheet {sheetId}, device offline?"); } return await ConvertToDictionary(await translationDatabase.GetAll <Translation>()); }); }
public async Task TestDefaultProgressionSystem() { // Get your key from https://console.developers.google.com/apis/credentials var apiKey = "AIzaSyCtcFQMgRIUHhSuXggm4BtXT4eZvUrBWN0"; // https://docs.google.com/spreadsheets/d/1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM contains the sheetId: var sheetId = "1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM"; var sheetName = "MySheet1"; // Has to match the sheet name var googleSheetsStore = new GoogleSheetsKeyValueStore(new InMemoryKeyValueStore(), apiKey, sheetId, sheetName); var ffm = new FeatureFlagManager <FeatureFlag>(new FeatureFlagStore(new InMemoryKeyValueStore(), googleSheetsStore)); IoC.inject.SetSingleton <FeatureFlagManager <FeatureFlag> >(ffm); LocalAnalytics analytics = new LocalAnalytics(); AppFlow.AddAppFlowTracker(new AppFlowToStore(analytics)); var xpSystem = new ProgressionSystem <FeatureFlag>(analytics, ffm); IoC.inject.SetSingleton <IProgressionSystem <FeatureFlag> >(xpSystem); await CleanupFilesFromTest(analytics, xpSystem); // Simulate User progression by causing analytics events: var eventCount = 1000; for (int i = 0; i < eventCount; i++) { AppFlow.TrackEvent(EventConsts.catMutation, "User did mutation nr " + i); } var flagId4 = "MyFlag4"; var flag4 = await FeatureFlagManager <FeatureFlag> .instance.GetFeatureFlag(flagId4); Assert.Equal(1000, flag4.requiredXp); // The user needs >= 1000 XP for the feature // Now that the user has 1000 XP the condition of the TestXpSystem is met: Assert.True(await FeatureFlag.IsEnabled(flagId4)); // The number of mutation events: Assert.Equal(eventCount, xpSystem.cachedCategoryCounts[EventConsts.catMutation]); // Since there are only mutation events the XP is equal to the factor*event count: Assert.Equal(await xpSystem.GetLatestXp(), eventCount * xpSystem.xpFactors[EventConsts.catMutation]); await CleanupFilesFromTest(analytics, xpSystem); }
public async Task ExampleUsage2() { // Get your key from https://console.developers.google.com/apis/credentials var apiKey = "AIzaSyCtcFQMgRIUHhSuXggm4BtXT4eZvUrBWN0"; // https://docs.google.com/spreadsheets/d/1rl1vi-LUhOgoY_QrMJsm2UE0SdiL4EbOtLwfNPsavxQ contains the sheetId: var sheetId = "1rl1vi-LUhOgoY_QrMJsm2UE0SdiL4EbOtLwfNPsavxQ"; var sheetName = "UsageRules1"; // Has to match the sheet name var source = new GoogleSheetsKeyValueStore(new InMemoryKeyValueStore(), apiKey, sheetId, sheetName); var store = source.GetTypeAdapter <UsageRule>(); var analytics = CreateLocalAnalyticsSystem(); IEnumerable <UsageRule> rules = await store.GetRulesInitialized(analytics); foreach (var rule in rules) { if (await rule.isTrue()) { Log.d(JsonWriter.AsPrettyString(rule)); // TODO } } Assert.Single(rules.Filter(r => !r.andRules.IsNullOrEmpty())); }
public async Task TestProgressiveDisclosure() { // Get your key from https://console.developers.google.com/apis/credentials var apiKey = "AIzaSyCtcFQMgRIUHhSuXggm4BtXT4eZvUrBWN0"; // https://docs.google.com/spreadsheets/d/1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM contains the sheetId: var sheetId = "1KBamVmgEUX-fyogMJ48TT6h2kAMKyWU1uBL5skCGRBM"; var sheetName = "MySheet1"; // Has to match the sheet name var googleSheetsStore = new GoogleSheetsKeyValueStore(new InMemoryKeyValueStore(), apiKey, sheetId, sheetName); var testStore = new FeatureFlagStore(new InMemoryKeyValueStore(), googleSheetsStore); IoC.inject.SetSingleton <FeatureFlagManager <FeatureFlag> >(new FeatureFlagManager <FeatureFlag>(testStore)); // Make sure user would normally be included in the rollout: var flagId4 = "MyFlag4"; var flag4 = await FeatureFlagManager <FeatureFlag> .instance.GetFeatureFlag(flagId4); Assert.Equal(flagId4, flag4.id); Assert.Equal(100, flag4.rolloutPercentage); // There is no user progression system setup so the requiredXp value of the feature flag is ignored: Assert.True(await FeatureFlag.IsEnabled(flagId4)); Assert.True(await flag4.IsFeatureUnlocked()); // Setup progression system and check again: var xpSystem = new TestXpSystem(); IoC.inject.SetSingleton <IProgressionSystem <FeatureFlag> >(xpSystem); // Now that there is a progression system Assert.False(await flag4.IsFeatureUnlocked()); Assert.False(await FeatureFlag.IsEnabled(flagId4)); var eventCount = 1000; var store = new MutationObserverKeyValueStore().WithFallbackStore(new InMemoryKeyValueStore()); // Lets assume the users xp correlates with the number of triggered local analytics events: store.onSet = delegate { xpSystem.currentXp++; return(Task.FromResult(true)); }; // Set the store to be the target of the local analytics so that whenever any LocalAnalytics analytics = new LocalAnalytics(store); analytics.createStoreFor = (_) => new InMemoryKeyValueStore().GetTypeAdapter <AppFlowEvent>(); // Setup the AppFlow logic to use LocalAnalytics: AppFlow.AddAppFlowTracker(new AppFlowToStore(analytics)); // Simulate User progression by causing analytics events: for (int i = 0; i < eventCount; i++) { AppFlow.TrackEvent(EventConsts.catMutation, "User did mutation nr " + i); } // Get the analtics store for category "Mutations": var mutationStore = await analytics.GetStoreForCategory(EventConsts.catMutation).GetAll(); Assert.Equal(eventCount, mutationStore.Count()); // All events so far were mutations Assert.True(eventCount <= xpSystem.currentXp); // The user received xp for each mutation Assert.Equal(1000, flag4.requiredXp); // The user needs >= 1000 xp for the feature // Now that the user has more than 1000 xp the condition of the TestXpSystem is met: Assert.True(await flag4.IsFeatureUnlocked()); Assert.True(await FeatureFlag.IsEnabled(flagId4)); }