Skip to content

Commit 51eba76

Browse files
Erik Bylundkirre-bylund
authored andcommitted
feature: Add convenience method to get a specific notification from response
1 parent 45fcfb9 commit 51eba76

File tree

2 files changed

+289
-15
lines changed

2 files changed

+289
-15
lines changed

Runtime/Game/Requests/NotificationRequests.cs

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,59 @@ public class LootLockerListNotificationsResponse : LootLockerResponse
551551
/// Pagination data for this set of notifications
552552
/// </summary>
553553
public LootLockerExtendedPagination Pagination { get; set; }
554+
/// <summary>
555+
/// Will mark all unread notifications in this response as read in LootLocker (though remember to check the response if it succeeded)
556+
/// </summary>
557+
/// <param name="onComplete">Action for handling the server response</param>
558+
public void MarkUnreadNotificationsAsRead(Action<LootLockerReadNotificationsResponse> onComplete)
559+
{
560+
if (UnreadNotifications == null || UnreadNotifications.Count <= 0)
561+
{
562+
onComplete?.Invoke(new LootLockerReadNotificationsResponse { errorData = null, EventId = "", statusCode = 204, success = true, text = "{}" });
563+
return;
564+
}
565+
LootLockerSDKManager.MarkNotificationsAsRead(UnreadNotifications.ToArray(), onComplete);
566+
}
567+
568+
/// <summary>
569+
/// Get notifications by their identifying value. The out is an array because many notifications are not unique. For example triggers that can be triggered multiple times.
570+
/// For Triggers the identifying value is the key of the trigger
571+
/// For Google Play Store purchases it is the product id
572+
/// For Apple App Store purchases it is the transaction id
573+
/// For LootLocker virtual purchases it is the catalog item id
574+
/// </summary>
575+
/// <param name="identifyingValue">The identifying value of the notification you want to fetch.</param>
576+
/// <param name="notifications">A list of notifications that were found for the given identifying value or null if none were found.</param>
577+
/// <returns>True if notifications were found for the identifying value. False if notifications couldn't be found for this value or if the underlying lookup table is corrupt.</returns>
578+
public bool TryGetNotificationsByIdentifyingValue(string identifyingValue, out LootLockerNotification[] notifications)
579+
{
580+
notifications = null;
581+
if (!NotificationLookupTable.TryGetValue(identifyingValue, out var indexes))
582+
{
583+
return false;
584+
}
585+
586+
var foundNotifications = new List<LootLockerNotification>();
587+
foreach (var index in indexes)
588+
{
589+
if (index < 0 || index >= Notifications.Length)
590+
{
591+
// The notifications array is not the same as when the lookup table was populated
592+
return false;
593+
}
594+
var notification = Notifications[index];
595+
if (!notification.Content.ContextAsDictionary.ContainsValue(identifyingValue))
596+
{
597+
// The notifications array is not the same as when the lookup table was populated
598+
return false;
599+
}
600+
foundNotifications.Add(notification);
601+
}
602+
603+
notifications = foundNotifications.ToArray();
604+
return true;
605+
}
606+
554607
/// <summary>
555608
/// Populate convenience structures
556609
/// </summary>
@@ -560,6 +613,8 @@ public void PopulateConvenienceStructures()
560613
{
561614
return;
562615
}
616+
617+
int i = 0;
563618
foreach (var notification in Notifications)
564619
{
565620
notification.Content.ContextAsDictionary = new Dictionary<string, string>();
@@ -571,23 +626,42 @@ public void PopulateConvenienceStructures()
571626
{
572627
notification.Content.ContextAsDictionary.Add(contextEntry.Key, contextEntry.Value);
573628
}
629+
630+
string identifyingKey = null;
631+
if (notification.Source.Equals(LootLockerStaticStrings.LootLockerNotificationSources.Triggers, StringComparison.OrdinalIgnoreCase))
632+
{
633+
identifyingKey = LootLockerStaticStrings.LootLockerStandardContextKeys.Triggers.Key;
634+
}
635+
else if (notification.Source.Equals(LootLockerStaticStrings.LootLockerNotificationSources.Purchasing.LootLocker, StringComparison.OrdinalIgnoreCase))
636+
{
637+
identifyingKey = LootLockerStaticStrings.LootLockerStandardContextKeys.Purchasing.LootLocker.CatalogItemId;
638+
}
639+
else if (notification.Source.Equals(LootLockerStaticStrings.LootLockerNotificationSources.Purchasing.GooglePlayStore, StringComparison.OrdinalIgnoreCase))
640+
{
641+
identifyingKey = LootLockerStaticStrings.LootLockerStandardContextKeys.Purchasing.GooglePlayStore.ProductId;
642+
}
643+
else if (notification.Source.Equals(LootLockerStaticStrings.LootLockerNotificationSources.Purchasing.AppleAppStore, StringComparison.OrdinalIgnoreCase))
644+
{
645+
identifyingKey = LootLockerStaticStrings.LootLockerStandardContextKeys.Purchasing.AppleAppStore.TransactionId;
646+
}
647+
648+
if (identifyingKey != null && notification.Content.ContextAsDictionary.TryGetValue(identifyingKey, out var value) && value != null)
649+
{
650+
if (NotificationLookupTable.TryGetValue(value, out var indexes))
651+
{
652+
indexes.Add(i);
653+
}
654+
else
655+
{
656+
NotificationLookupTable.Add(value, new List<int> { i });
657+
}
658+
}
659+
++i;
574660
}
575661
}
576-
/// <summary>
577-
/// Will mark all unread notifications in this response as read in LootLocker (though remember to check the response if it succeeded)
578-
/// </summary>
579-
/// <param name="onComplete">Action for handling the server response</param>
580-
public void MarkUnreadNotificationsAsRead(Action<LootLockerReadNotificationsResponse> onComplete)
581-
{
582-
if (UnreadNotifications == null || UnreadNotifications.Count <= 0)
583-
{
584-
onComplete?.Invoke(new LootLockerReadNotificationsResponse { errorData = null, EventId = "", statusCode = 204, success = true, text = "{}" });
585-
return;
586-
}
587-
LootLockerSDKManager.MarkNotificationsAsRead(UnreadNotifications.ToArray(), onComplete);
588-
}
589662

590663
private readonly List<string> UnreadNotifications = new List<string>();
664+
private readonly Dictionary<string, List<int>> NotificationLookupTable = new Dictionary<string, List<int>>();
591665
};
592666

593667
/// <summary>

Tests/LootLockerTests/PlayMode/NotificationTests.cs

Lines changed: 202 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.Collections;
33
using System.Collections.Generic;
4+
using System.Globalization;
45
using System.Linq;
56
using LootLocker;
67
using LootLocker.LootLockerEnums;
@@ -36,7 +37,7 @@ public IEnumerator Setup()
3637
}
3738

3839
bool gameCreationCallCompleted = false;
39-
LootLockerTestGame.CreateGame(testName: "NotificationTest" + TestCounter + " ", onComplete: (success, errorMessage, game) =>
40+
LootLockerTestGame.CreateGame(testName: "NotificationTest" + TestCounter, onComplete: (success, errorMessage, game) =>
4041
{
4142
if (!success)
4243
{
@@ -552,5 +553,204 @@ public IEnumerator Notifications_MarkAllNotificationsAsReadUsingConvenienceMetho
552553
Assert.IsTrue(listUnreadNotificationsAfterMarkAsReadResponse.success, "Listing unread notifications after marking specific as read failed");
553554
Assert.AreEqual(CreatedTriggers.Count - notificationIdsToMarkAsRead.Length, listUnreadNotificationsAfterMarkAsReadResponse.Notifications.Length, "Not all notifications that were marked as read actually were");
554555
}
555-
}
556+
557+
[UnityTest]
558+
public IEnumerator Notifications_ConvenienceLookupTable_CanLookUpAllNotificationTypes()
559+
{
560+
Assert.IsFalse(SetupFailed, "Setup did not succeed");
561+
562+
// Given
563+
string TriggerIdentifyingValue = "trigger_key";
564+
string triggerNotification1Id = GUID.Generate().ToString();
565+
string triggerNotification2Id = GUID.Generate().ToString();
566+
string triggerNotification3Id = GUID.Generate().ToString();
567+
string LootLockerVirtualStorePurchaseIdentifyingValue = "catalog_item_id";
568+
string lootLockerVirtualStoreNotification1Id = GUID.Generate().ToString();
569+
string AppleAppStorePurchaseIdentifyingValue = "transaction_id";
570+
string appleAppStoreNotification1Id = GUID.Generate().ToString();
571+
string GooglePlayStoreStorePurchaseIdentifyingValue = "product_id";
572+
string googlePlayStoreNotification1Id = GUID.Generate().ToString();
573+
var fakeResponse = new LootLockerListNotificationsResponse()
574+
{
575+
statusCode = 200,
576+
success = true,
577+
text = "",
578+
errorData = null,
579+
EventId = "1234",
580+
Pagination = new LootLockerExtendedPagination
581+
{
582+
errors = null,
583+
current_page = 1,
584+
prev_page = null,
585+
next_page = null,
586+
last_page = 1,
587+
offset = 0,
588+
per_page = 100,
589+
total = 6
590+
},
591+
Notifications = new LootLockerNotification[]
592+
{
593+
GenerateLootLockerNotification(triggerNotification1Id, LootLocker.LootLockerStaticStrings.LootLockerNotificationSources.Triggers, TriggerIdentifyingValue),
594+
GenerateLootLockerNotification(triggerNotification2Id, LootLocker.LootLockerStaticStrings.LootLockerNotificationSources.Triggers, TriggerIdentifyingValue),
595+
GenerateLootLockerNotification(triggerNotification3Id, LootLocker.LootLockerStaticStrings.LootLockerNotificationSources.Triggers, "some_other_trigger"),
596+
GenerateLootLockerNotification(lootLockerVirtualStoreNotification1Id, LootLocker.LootLockerStaticStrings.LootLockerNotificationSources.Purchasing.LootLocker, LootLockerVirtualStorePurchaseIdentifyingValue),
597+
GenerateLootLockerNotification(appleAppStoreNotification1Id, LootLocker.LootLockerStaticStrings.LootLockerNotificationSources.Purchasing.AppleAppStore, AppleAppStorePurchaseIdentifyingValue),
598+
GenerateLootLockerNotification(googlePlayStoreNotification1Id, LootLocker.LootLockerStaticStrings.LootLockerNotificationSources.Purchasing.GooglePlayStore, GooglePlayStoreStorePurchaseIdentifyingValue),
599+
}
600+
};
601+
602+
fakeResponse.PopulateConvenienceStructures();
603+
604+
// When
605+
bool triggerLookupSucceeded = fakeResponse.TryGetNotificationsByIdentifyingValue(TriggerIdentifyingValue, out var triggerNotifications);
606+
bool lootLockerVirtualStoreLookupSucceeded = fakeResponse.TryGetNotificationsByIdentifyingValue(LootLockerVirtualStorePurchaseIdentifyingValue, out var lootLockerVirtualStoreNotifications);
607+
bool appleAppStoreLookupSucceeded = fakeResponse.TryGetNotificationsByIdentifyingValue(AppleAppStorePurchaseIdentifyingValue, out var appleAppStoreNotifications);
608+
bool googlePlayStoreLookupSucceeded = fakeResponse.TryGetNotificationsByIdentifyingValue(GooglePlayStoreStorePurchaseIdentifyingValue, out var googlePlayStoreNotifications);
609+
610+
// Then
611+
Assert.IsTrue(triggerLookupSucceeded, "Trigger notification lookup failed");
612+
Assert.IsNotEmpty(triggerNotifications, "Trigger notification lookup array was empty");
613+
Assert.AreEqual(2, triggerNotifications.Length, "The right amount of trigger notifications were not retrieved");
614+
var retrievedNotificationIds = triggerNotifications.Take(2).Select(notification => notification.Id).ToArray();
615+
Assert.Contains(triggerNotification1Id, retrievedNotificationIds, "The retrieved trigger notifications did not contain the expected id");
616+
Assert.Contains(triggerNotification2Id, retrievedNotificationIds, "The retrieved trigger notifications did not contain the expected id");
617+
618+
Assert.IsTrue(lootLockerVirtualStoreLookupSucceeded, "LootLocker Virtual Store notification lookup failed");
619+
Assert.IsNotEmpty(lootLockerVirtualStoreNotifications, "LootLocker Virtual Store notification lookup array was empty");
620+
Assert.AreEqual(lootLockerVirtualStoreNotification1Id, lootLockerVirtualStoreNotifications[0].Id, "The retrieved lootlocker virtual store notification id was not as expected");
621+
622+
Assert.IsTrue(appleAppStoreLookupSucceeded, "Apple app store notification lookup failed");
623+
Assert.IsNotEmpty(appleAppStoreNotifications, "Apple app store notification lookup array was empty");
624+
Assert.AreEqual(appleAppStoreNotification1Id, appleAppStoreNotifications[0].Id, "The retrieved Apple app store notification id was not as expected");
625+
626+
Assert.IsTrue(googlePlayStoreLookupSucceeded, "Google Play store notification lookup failed");
627+
Assert.IsNotEmpty(googlePlayStoreNotifications, "Google Play store notification lookup array was empty");
628+
Assert.AreEqual(googlePlayStoreNotification1Id, googlePlayStoreNotifications[0].Id, "The retrieved Google Play store notification id was not as expected");
629+
630+
yield break;
631+
}
632+
633+
private static LootLockerNotification GenerateLootLockerNotification(string notificationId, string source, string identifyingValue)
634+
{
635+
LootLockerNotificationContextEntry[] context = null;
636+
if (source.Equals(LootLocker.LootLockerStaticStrings.LootLockerNotificationSources.Triggers, StringComparison.OrdinalIgnoreCase))
637+
{
638+
context = new[]
639+
{
640+
new LootLockerNotificationContextEntry
641+
{
642+
Key = LootLocker.LootLockerStaticStrings.LootLockerStandardContextKeys.Triggers.Key,
643+
Value = identifyingValue
644+
},
645+
new LootLockerNotificationContextEntry
646+
{
647+
Key = LootLocker.LootLockerStaticStrings.LootLockerStandardContextKeys.Triggers.Id,
648+
Value = GUID.Generate().ToString(),
649+
},
650+
new LootLockerNotificationContextEntry
651+
{
652+
Key = LootLocker.LootLockerStaticStrings.LootLockerStandardContextKeys.Triggers.Limit,
653+
Value = "10"
654+
}
655+
};
656+
}
657+
else if (source.Equals(LootLocker.LootLockerStaticStrings.LootLockerNotificationSources.Purchasing.LootLocker, StringComparison.OrdinalIgnoreCase))
658+
{
659+
context = new[]
660+
{
661+
new LootLockerNotificationContextEntry
662+
{
663+
Key = LootLocker.LootLockerStaticStrings.LootLockerStandardContextKeys.Purchasing.LootLocker.CatalogItemId,
664+
Value = identifyingValue
665+
},
666+
new LootLockerNotificationContextEntry
667+
{
668+
Key = LootLocker.LootLockerStaticStrings.LootLockerStandardContextKeys.Purchasing.LootLocker.CatalogId,
669+
Value = GUID.Generate().ToString(),
670+
}
671+
};
672+
}
673+
else if (source.Equals(LootLocker.LootLockerStaticStrings.LootLockerNotificationSources.Purchasing.GooglePlayStore, StringComparison.OrdinalIgnoreCase))
674+
{
675+
context = new[]
676+
{
677+
new LootLockerNotificationContextEntry
678+
{
679+
Key = LootLocker.LootLockerStaticStrings.LootLockerStandardContextKeys.Purchasing.GooglePlayStore.ProductId,
680+
Value = identifyingValue
681+
},
682+
new LootLockerNotificationContextEntry
683+
{
684+
Key = LootLocker.LootLockerStaticStrings.LootLockerStandardContextKeys.Purchasing.GooglePlayStore.CatalogItemId,
685+
Value = GUID.Generate().ToString(),
686+
},
687+
new LootLockerNotificationContextEntry
688+
{
689+
Key = LootLocker.LootLockerStaticStrings.LootLockerStandardContextKeys.Purchasing.GooglePlayStore.CatalogId,
690+
Value = GUID.Generate().ToString()
691+
}
692+
};
693+
}
694+
else if (source.Equals(LootLocker.LootLockerStaticStrings.LootLockerNotificationSources.Purchasing.AppleAppStore, StringComparison.OrdinalIgnoreCase))
695+
{
696+
context = new[]
697+
{
698+
new LootLockerNotificationContextEntry
699+
{
700+
Key = LootLocker.LootLockerStaticStrings.LootLockerStandardContextKeys.Purchasing.AppleAppStore.TransactionId,
701+
Value = identifyingValue
702+
},
703+
new LootLockerNotificationContextEntry
704+
{
705+
Key = LootLocker.LootLockerStaticStrings.LootLockerStandardContextKeys.Purchasing.AppleAppStore.CatalogItemId,
706+
Value = GUID.Generate().ToString(),
707+
},
708+
new LootLockerNotificationContextEntry
709+
{
710+
Key = LootLocker.LootLockerStaticStrings.LootLockerStandardContextKeys.Purchasing.AppleAppStore.CatalogId,
711+
Value = GUID.Generate().ToString()
712+
}
713+
};
714+
}
715+
return new LootLockerNotification
716+
{
717+
Id = notificationId,
718+
Created_at = DateTime.Now,
719+
Expiration_date = DateTime.Now.AddDays(30).ToString(CultureInfo.InvariantCulture),
720+
Player_id = GUID.Generate().ToString(),
721+
Priority = LootLockerNotificationPriority.medium,
722+
Read = false,
723+
Read_at = null,
724+
Notification_type = LootLocker.LootLockerStaticStrings.LootLockerNotificationTypes.PullRewardAcquired,
725+
Source = source,
726+
Content = new LootLockerNotificationContent
727+
{
728+
Context = context,
729+
Body = new LootLockerNotificationContentBody
730+
{
731+
Kind = LootLockerNotificationContentKind.currency,
732+
Asset = null,
733+
Group = null,
734+
Progression_reset = null,
735+
Progression_points = null,
736+
Currency = new LootLockerNotificationRewardCurrency
737+
{
738+
Amount = "100",
739+
Created_at = DateTime.Now,
740+
Currency_id = GUID.Generate().ToString(),
741+
Reward_id = GUID.Generate().ToString(),
742+
Updated_at = DateTime.Now,
743+
Details = new LootLockerNotificationRewardCurrencyDetails
744+
{
745+
Amount = "100",
746+
Code = "GLD",
747+
Id = GUID.Generate().ToString(),
748+
Name = "Gold"
749+
}
750+
}
751+
}
752+
}
753+
};
754+
}
755+
}
556756
}

0 commit comments

Comments
 (0)