Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6f7f276
test(audience-sdk): pin Constants.LibraryName against package.json name
ImmutableJeffrey May 2, 2026
710a756
test(audience-sdk): backoff assertions derive from Constants.HttpBack…
ImmutableJeffrey May 2, 2026
ba8c062
test(audience-sdk): cover IdentityTypeExtensions.ParseLowercaseString
ImmutableJeffrey May 2, 2026
f4a0520
test(audience-sdk): parametrise DistributionPlatform casing across al…
ImmutableJeffrey May 2, 2026
482e9e6
test(audience-sdk): pin AudienceErrorMessages and AudienceArgumentMes…
ImmutableJeffrey May 2, 2026
d12464f
test(audience-sdk,sample): unity-only DeviceCollector and UXML alignm…
ImmutableJeffrey May 2, 2026
0533538
test(audience-sdk): centralise per-test event names in TestEventNames
ImmutableJeffrey May 2, 2026
6a1b88e
refactor(audience-sample): centralise custom demo event names in Samp…
ImmutableJeffrey May 2, 2026
014fb25
refactor(audience-sample): centralise custom demo property keys in Sa…
ImmutableJeffrey May 2, 2026
d0be68f
test(audience-sdk): centralise per-test fixture data values in TestFi…
ImmutableJeffrey May 2, 2026
5654104
test(audience-sdk): add missing meta file for TestEventNames
ImmutableJeffrey May 2, 2026
d27fc2e
test(audience-sdk): extend TestEventNames with transport and queue sc…
ImmutableJeffrey May 2, 2026
4bacd74
refactor(audience-sample): use EventPropertyKeys for typed-event fiel…
ImmutableJeffrey May 2, 2026
5edc012
test(audience-sdk): extend TestFixtures with more scenario values
ImmutableJeffrey May 2, 2026
febd837
test(audience-sample): centralise live-fire test fixture values
ImmutableJeffrey May 2, 2026
a825ab3
refactor(audience-sample): centralise log payload JSON keys in Sample…
ImmutableJeffrey May 2, 2026
3326732
refactor(audience-sample): centralise status messages in SampleAppUi.…
ImmutableJeffrey May 2, 2026
abca7ca
test(audience-sdk): centralise HTTP test response body and exception …
ImmutableJeffrey May 2, 2026
d1385bf
refactor(audience-sample): use SampleAppUi.Css references for inline …
ImmutableJeffrey May 2, 2026
289619e
refactor(audience-sample): centralise "Copied!" flash label in Sample…
ImmutableJeffrey May 2, 2026
f3be00a
test(audience-sdk): centralise paired JSON round-trip fixtures
ImmutableJeffrey May 2, 2026
78870d8
test(audience-sdk): extend TestFixtures with currency, casing, and sc…
ImmutableJeffrey May 2, 2026
e2d1ce2
test(audience-sdk): centralise ConstantsTests path and key fixtures a…
ImmutableJeffrey May 2, 2026
27b6039
Merge pull request #728 from immutable/chore/sdk-272-pr-4-fixtures-an…
nattb8 Aug 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,15 @@ internal readonly struct EventSpec

internal static readonly EventSpec[] Catalogue =
{
new EventSpec("sign_up", new[] { EventField.Text("method", optional: true) }),
new EventSpec("sign_in", new[] { EventField.Text("method", optional: true) }),
new EventSpec("email_acquired", new[] { EventField.Text("source", optional: true) }),
new EventSpec("wishlist_add", new[] {
EventField.Text("gameId"),
EventField.Text("source", optional: true),
EventField.Text("platform", optional: true),
new EventSpec(SampleAppCustomEvents.SignUp, new[] { EventField.Text(SampleAppCustomEventPropertyKeys.Method, optional: true) }),
new EventSpec(SampleAppCustomEvents.SignIn, new[] { EventField.Text(SampleAppCustomEventPropertyKeys.Method, optional: true) }),
new EventSpec(SampleAppCustomEvents.EmailAcquired, new[] { EventField.Text(SampleAppCustomEventPropertyKeys.Source, optional: true) }),
new EventSpec(SampleAppCustomEvents.WishlistAdd, new[] {
EventField.Text(SampleAppCustomEventPropertyKeys.GameId),
EventField.Text(SampleAppCustomEventPropertyKeys.Source, optional: true),
EventField.Text(SampleAppCustomEventPropertyKeys.Platform, optional: true),
}),
new EventSpec("wishlist_remove", new[] { EventField.Text("gameId") }),
new EventSpec(SampleAppCustomEvents.WishlistRemove, new[] { EventField.Text(SampleAppCustomEventPropertyKeys.GameId) }),
new EventSpec(EventNames.Purchase, new[] {
EventField.Text(EventPropertyKeys.Currency),
EventField.Number(EventPropertyKeys.Value),
Expand Down Expand Up @@ -91,16 +91,16 @@ internal readonly struct EventSpec
EventField.Text(EventPropertyKeys.ItemId, optional: true),
}),
new EventSpec(EventNames.MilestoneReached, new[] { EventField.Text(EventPropertyKeys.Name) }),
new EventSpec("game_page_viewed", new[] {
EventField.Text("gameId"),
EventField.Text("gameName", optional: true),
EventField.Text("slug", optional: true),
new EventSpec(SampleAppCustomEvents.GamePageViewed, new[] {
EventField.Text(SampleAppCustomEventPropertyKeys.GameId),
EventField.Text(SampleAppCustomEventPropertyKeys.GameName, optional: true),
EventField.Text(SampleAppCustomEventPropertyKeys.Slug, optional: true),
}),
new EventSpec("link_clicked", new[] {
EventField.Text("url"),
EventField.Text("label", optional: true),
EventField.Text("source", optional: true),
EventField.Text("gameId", optional: true),
new EventSpec(SampleAppCustomEvents.LinkClicked, new[] {
EventField.Text(SampleAppCustomEventPropertyKeys.Url),
EventField.Text(SampleAppCustomEventPropertyKeys.Label, optional: true),
EventField.Text(SampleAppCustomEventPropertyKeys.Source, optional: true),
EventField.Text(SampleAppCustomEventPropertyKeys.GameId, optional: true),
}),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ private void RegisterHandlers()
if (label.ClassListContains(SampleAppUi.Css.Copied)) return;
if (string.IsNullOrEmpty(label.text) || label.text == SampleAppUi.StatusBar.EmptyText) return;
GUIUtility.systemCopyBuffer = label.text;
label.text = "Copied!";
label.text = SampleAppUi.ButtonText.CopiedFlash;
FlashCopied(label);
});
}
Expand Down Expand Up @@ -533,17 +533,17 @@ private void RefreshStatusBar()
string? derivedFromKey = keyEmpty ? null : (isTest ? Constants.SandboxBaseUrl : Constants.ProductionBaseUrl);
string? endpoint = hasOverride ? overrideUrl : derivedFromKey;
bool warnState = hasOverride || (!keyEmpty && !isTest);
SetStatusCell(_statusEndpoint, endpoint, warnState ? "state-warn" : "state-ok");
SetStatusCell(_statusEndpoint, endpoint, warnState ? SampleAppUi.Css.StateWarn : SampleAppUi.Css.StateOk);
_prodWarning.EnableInClassList(SampleAppUi.HiddenClass, hasOverride || keyEmpty || isTest);

var consent = _initialised ? ImmutableAudience.CurrentConsent : ConsentOrder[Mathf.Clamp(_initialConsent?.index ?? 0, 0, ConsentOrder.Length - 1)];
int cIdx = Array.IndexOf(ConsentOrder, consent);
SetStatusCell(_statusConsent, consent.ToLowercaseString(), cIdx >= 0 ? ConsentStateClass[cIdx] : "dim");
SetStatusCell(_statusConsent, consent.ToLowercaseString(), cIdx >= 0 ? ConsentStateClass[cIdx] : SampleAppUi.Css.Dim);

SetStatusCell(_statusAnon, _initialised ? ImmutableAudience.AnonymousId : null, "dim");
SetStatusCell(_statusUser, _initialised ? ImmutableAudience.UserId : null, "dim");
SetStatusCell(_statusSession, _initialised ? ImmutableAudience.SessionId : null, "dim");
SetStatusCell(_statusQueue, _initialised ? ImmutableAudience.QueueSize.ToString(CultureInfo.InvariantCulture) : null, "dim");
SetStatusCell(_statusAnon, _initialised ? ImmutableAudience.AnonymousId : null, SampleAppUi.Css.Dim);
SetStatusCell(_statusUser, _initialised ? ImmutableAudience.UserId : null, SampleAppUi.Css.Dim);
SetStatusCell(_statusSession, _initialised ? ImmutableAudience.SessionId : null, SampleAppUi.Css.Dim);
SetStatusCell(_statusQueue, _initialised ? ImmutableAudience.QueueSize.ToString(CultureInfo.InvariantCulture) : null, SampleAppUi.Css.Dim);
}

private void RefreshConsentPills()
Expand Down
66 changes: 33 additions & 33 deletions examples/audience/Assets/SampleApp/Scripts/AudienceSample.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,30 +71,30 @@ private void OnShutdown() => RunAndLog(SampleAppUi.LogLabels.Shutdown, () =>
_initialised = false;
ResetIdentityMirror();
OnSdkStateChanged();
return "SDK stopped";
return SampleAppUi.Messages.SdkStopped;
});

private void OnReset() => RunAndLog(SampleAppUi.LogLabels.Reset, () =>
{
ImmutableAudience.Reset();
ResetIdentityMirror();
OnSdkStateChanged();
return "anonymous ID regenerated, queue cleared";
return SampleAppUi.Messages.AnonymousIdRegeneratedQueueCleared;
});

private async Task OnFlushAsync()
{
try { await ImmutableAudience.FlushAsync(); AppendLog(SampleAppUi.LogLabels.Flush, "queue flushed", LogLevel.Ok, LogSource.App); OnSdkStateChanged(); }
try { await ImmutableAudience.FlushAsync(); AppendLog(SampleAppUi.LogLabels.Flush, SampleAppUi.Messages.QueueFlushed, LogLevel.Ok, LogSource.App); OnSdkStateChanged(); }
catch (Exception ex) { AppendLog(SampleAppUi.LogLabels.Flush, ex.Message, LogLevel.Err, LogSource.App); }
}

private async Task OnDeleteDataAsync()
{
AppendLog(SampleAppUi.LogLabels.DeleteData, "erasure request dispatched", LogLevel.Info, LogSource.App);
AppendLog(SampleAppUi.LogLabels.DeleteData, SampleAppUi.Messages.ErasureRequestDispatched, LogLevel.Info, LogSource.App);
try
{
await ImmutableAudience.DeleteData();
AppendLog(SampleAppUi.LogLabels.DeleteData, "backend acknowledged", LogLevel.Ok, LogSource.App);
AppendLog(SampleAppUi.LogLabels.DeleteData, SampleAppUi.Messages.BackendAcknowledged, LogLevel.Ok, LogSource.App);
}
catch (Exception ex)
{
Expand All @@ -110,8 +110,8 @@ private void OnPage() => RunAndLog(SampleAppUi.LogLabels.Page, () =>
{
GuardConsentForTrack();
var screen = SceneManager.GetActiveScene().name;
var props = new Dictionary<string, object> { ["path"] = screen };
ImmutableAudience.Track("screen_viewed", props);
var props = new Dictionary<string, object> { [SampleAppCustomEventPropertyKeys.Path] = screen };
ImmutableAudience.Track(SampleAppCustomEvents.ScreenViewed, props);
return Json.Serialize(props, 2);
});

Expand All @@ -130,17 +130,17 @@ private void OnSendCatalogueEvent(EventSpec spec, Dictionary<string, VisualEleme
ImmutableAudience.Track(typed);
return Json.Serialize(new Dictionary<string, object>
{
["event"] = spec.Name,
["overload"] = "typed",
[SampleAppUi.LogPayloadKeys.Event] = spec.Name,
[SampleAppUi.LogPayloadKeys.Overload] = SampleAppUi.LogPayloadKeys.OverloadValues.Typed,
[MessageFields.Properties] = typed.ToProperties(),
}, 2);
}

ImmutableAudience.Track(spec.Name, props.Count > 0 ? props : null);
return Json.Serialize(new Dictionary<string, object>
{
["event"] = spec.Name,
["overload"] = "string",
[SampleAppUi.LogPayloadKeys.Event] = spec.Name,
[SampleAppUi.LogPayloadKeys.Overload] = SampleAppUi.LogPayloadKeys.OverloadValues.String,
[MessageFields.Properties] = props,
}, 2);
});
Expand All @@ -154,7 +154,7 @@ private void OnSendCustomEvent() => RunAndLog(SampleAppUi.LogLabels.Track, () =>
var f = CaptureCustomEventForm();
var props = string.IsNullOrEmpty(f.RawProps) ? null : JsonReader.DeserializeObject(f.RawProps);
ImmutableAudience.Track(f.Name, props);
var echo = new Dictionary<string, object> { ["event"] = f.Name };
var echo = new Dictionary<string, object> { [SampleAppUi.LogPayloadKeys.Event] = f.Name };
if (props != null) echo[MessageFields.Properties] = props;
return Json.Serialize(echo, 2);
});
Expand All @@ -171,14 +171,14 @@ private void OnSetConsent(ConsentLevel level) => RunAndLog(SampleAppUi.LogLabels
if (!level.CanIdentify()) ResetIdentityMirror();
var payload = new Dictionary<string, object>
{
["from"] = previous.ToLowercaseString(),
["to"] = level.ToLowercaseString(),
[SampleAppUi.LogPayloadKeys.From] = previous.ToLowercaseString(),
[SampleAppUi.LogPayloadKeys.To] = level.ToLowercaseString(),
};
var effects = new List<string>();
if (previous == ConsentLevel.None && level != ConsentLevel.None) effects.Add(SampleAppUi.Messages.QueueStartedSessionCreated);
if (level == ConsentLevel.None) effects.Add(SampleAppUi.Messages.QueuePurgedAnonymousIdCleared);
if (!level.CanIdentify() && previous.CanIdentify()) effects.Add(SampleAppUi.Messages.UserIdCleared);
if (effects.Count > 0) payload["effects"] = effects;
if (effects.Count > 0) payload[SampleAppUi.LogPayloadKeys.Effects] = effects;
OnSdkStateChanged();
return Json.Serialize(payload, 2);
});
Expand All @@ -198,9 +198,9 @@ private void OnIdentify() => RunAndLog(SampleAppUi.LogLabels.Identify, () =>
OnSdkStateChanged();
var payload = new Dictionary<string, object>
{
["id"] = f.Id,
[MessageFields.IdentityType] = f.Type,
["accepted"] = accepted,
[SampleAppUi.LogPayloadKeys.Id] = f.Id,
[MessageFields.IdentityType] = f.Type,
[SampleAppUi.LogPayloadKeys.Accepted] = accepted,
};
if (traits != null) payload[MessageFields.Traits] = traits;
return Json.Serialize(payload, 2);
Expand Down Expand Up @@ -233,9 +233,9 @@ private void OnAlias() => RunAndLog(SampleAppUi.LogLabels.Alias, () =>
}
return Json.Serialize(new Dictionary<string, object>
{
["from"] = new Dictionary<string, object> { ["id"] = f.FromId, [MessageFields.IdentityType] = f.FromType },
["to"] = new Dictionary<string, object> { ["id"] = f.ToId, [MessageFields.IdentityType] = f.ToType },
["accepted"] = accepted,
[SampleAppUi.LogPayloadKeys.From] = new Dictionary<string, object> { [SampleAppUi.LogPayloadKeys.Id] = f.FromId, [MessageFields.IdentityType] = f.FromType },
[SampleAppUi.LogPayloadKeys.To] = new Dictionary<string, object> { [SampleAppUi.LogPayloadKeys.Id] = f.ToId, [MessageFields.IdentityType] = f.ToType },
[SampleAppUi.LogPayloadKeys.Accepted] = accepted,
}, 2);
});

Expand All @@ -246,8 +246,8 @@ private void OnAlias() => RunAndLog(SampleAppUi.LogLabels.Alias, () =>
private void OnSdkError(AudienceError err) =>
AppendLog(SampleAppUi.LogLabels.OnError, Json.Serialize(new Dictionary<string, object>
{
["code"] = err.Code.ToString(),
["message"] = err.Message,
[SampleAppUi.LogPayloadKeys.Code] = err.Code.ToString(),
[SampleAppUi.LogPayloadKeys.Message] = err.Message,
}, 2), LogLevel.Err, LogSource.Sdk);

// SDK Log.Writer adapter. May fire from any thread; AppendLog handles
Expand Down Expand Up @@ -288,7 +288,7 @@ private static void GuardConsentForTrack()
var consent = ImmutableAudience.CurrentConsent;
if (!consent.CanTrack())
throw new InvalidOperationException(
$"track dropped — consent is {consent.ToLowercaseString()}; raise to anonymous or full to queue events");
string.Format(SampleAppUi.Messages.TrackDroppedConsentFmt, consent.ToLowercaseString()));
}

// Refresh* are idempotent reads, so calling all four every time is
Expand Down Expand Up @@ -320,7 +320,7 @@ private AudienceConfig BuildAudienceConfig(InitForm form, Action<AudienceError>
if (form.FlushIntervalMs is int flushMs && flushMs > 0)
{
if (flushMs < 1000)
AppendLog(SampleAppUi.LogLabels.Init, $"flushInterval {flushMs}ms below 1s — clamped", LogLevel.Warn, LogSource.App);
AppendLog(SampleAppUi.LogLabels.Init, string.Format(SampleAppUi.Messages.FlushIntervalBelowOneSecondClampedFmt, flushMs), LogLevel.Warn, LogSource.App);
config.FlushIntervalSeconds = Math.Max(1, flushMs / 1000);
}
if (form.FlushSize is int flushSize && flushSize > 0)
Expand All @@ -334,17 +334,17 @@ private static Dictionary<string, object> BuildConfigEcho(AudienceConfig config)
{
var echo = new Dictionary<string, object>
{
["consent"] = config.Consent.ToString(),
["debug"] = config.Debug,
["flushIntervalSeconds"] = config.FlushIntervalSeconds,
["flushSize"] = config.FlushSize,
["packageVersion"] = config.PackageVersion,
["shutdownFlushTimeoutMs"] = config.ShutdownFlushTimeoutMs,
[SampleAppUi.LogPayloadKeys.Consent] = config.Consent.ToString(),
[SampleAppUi.LogPayloadKeys.Debug] = config.Debug,
[SampleAppUi.LogPayloadKeys.FlushIntervalSeconds] = config.FlushIntervalSeconds,
[SampleAppUi.LogPayloadKeys.FlushSize] = config.FlushSize,
[SampleAppUi.LogPayloadKeys.PackageVersion] = config.PackageVersion,
[SampleAppUi.LogPayloadKeys.ShutdownFlushTimeoutMs] = config.ShutdownFlushTimeoutMs,
};
if (!string.IsNullOrEmpty(config.PublishableKey))
echo["publishableKey"] = RedactPublishableKey(config.PublishableKey);
echo[SampleAppUi.LogPayloadKeys.PublishableKey] = RedactPublishableKey(config.PublishableKey);
if (!string.IsNullOrEmpty(config.PersistentDataPath))
echo["persistentDataPath"] = config.PersistentDataPath;
echo[SampleAppUi.LogPayloadKeys.PersistentDataPath] = config.PersistentDataPath;
return echo;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
namespace Immutable.Audience.Samples.SampleApp
{
// Sample-only event names that demonstrate the custom Track pattern.
internal static class SampleAppCustomEvents
{
internal const string SignUp = "sign_up";
internal const string SignIn = "sign_in";
internal const string EmailAcquired = "email_acquired";
internal const string WishlistAdd = "wishlist_add";
internal const string WishlistRemove = "wishlist_remove";
internal const string GamePageViewed = "game_page_viewed";
internal const string LinkClicked = "link_clicked";
internal const string ScreenViewed = "screen_viewed";
}

// Property keys for the sample-app demo events.
internal static class SampleAppCustomEventPropertyKeys
{
internal const string Method = "method";
internal const string Source = "source";
internal const string GameId = "gameId";
internal const string Platform = "platform";
internal const string GameName = "gameName";
internal const string Slug = "slug";
internal const string Url = "url";
internal const string Label = "label";
internal const string Path = "path";
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading