diff --git a/.gitignore b/.gitignore index c4bc275..210803d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ obj/ .vs/ .idea/ TestResults/ +.DS_Store diff --git a/Auth/LoginViaDeviceRequest.cs b/Auth/LoginViaDeviceRequest.cs index e58e8b3..82b0896 100644 --- a/Auth/LoginViaDeviceRequest.cs +++ b/Auth/LoginViaDeviceRequest.cs @@ -1,25 +1,23 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Auth; public class LoginViaDeviceRequest { [JsonProperty("deviceId")] - public string DeviceId { get; set; } + public string? DeviceId { get; set; } [JsonProperty("key")] - public string Key { get; set; } + public string? Key { get; set; } [JsonProperty("language")] - public string Language { get; set; } + public string? Language { get; set; } [JsonProperty("nickname")] - public string Nickname { get; set; } + public string? Nickname { get; set; } [JsonProperty("region")] - public string Region { get; set; } + public string? Region { get; set; } } diff --git a/Auth/LoginViaDeviceResponse.cs b/Auth/LoginViaDeviceResponse.cs index 15df7b7..af50ca7 100644 --- a/Auth/LoginViaDeviceResponse.cs +++ b/Auth/LoginViaDeviceResponse.cs @@ -1,16 +1,14 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Auth; public class LoginViaDeviceResponse { [JsonProperty("accessToken")] - public string AccessToken { get; set; } + public string? AccessToken { get; set; } [JsonProperty("refreshToken")] - public string RefreshToken { get; set; } + public string? RefreshToken { get; set; } } diff --git a/Auth/RefreshAccessTokenRequest.cs b/Auth/RefreshAccessTokenRequest.cs index 53193d0..eba67c0 100644 --- a/Auth/RefreshAccessTokenRequest.cs +++ b/Auth/RefreshAccessTokenRequest.cs @@ -1,13 +1,11 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Auth; public class RefreshAccessTokenRequest { [JsonProperty("refreshToken")] - public string RefreshToken { get; set; } + public string? RefreshToken { get; set; } } diff --git a/Auth/RefreshAccessTokenResponse.cs b/Auth/RefreshAccessTokenResponse.cs index 8d0d4e4..60b4caa 100644 --- a/Auth/RefreshAccessTokenResponse.cs +++ b/Auth/RefreshAccessTokenResponse.cs @@ -1,16 +1,14 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Auth; public class RefreshAccessTokenResponse { [JsonProperty("accessToken")] - public string AccessToken { get; set; } + public string? AccessToken { get; set; } [JsonProperty("refreshToken")] - public string RefreshToken { get; set; } + public string? RefreshToken { get; set; } } diff --git a/BattlePass/AddBattlePassXpRequest.cs b/BattlePass/AddBattlePassXpRequest.cs index b62701b..42d193c 100644 --- a/BattlePass/AddBattlePassXpRequest.cs +++ b/BattlePass/AddBattlePassXpRequest.cs @@ -1,25 +1,23 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.BattlePass; public class AddBattlePassXpRequest { [JsonProperty("amount")] - public long Amount { get; set; } + public long? Amount { get; set; } [JsonProperty("nodeId")] - public string NodeId { get; set; } + public string? NodeId { get; set; } [JsonProperty("runId")] - public string RunId { get; set; } + public string? RunId { get; set; } [JsonProperty("scenarioId")] - public string ScenarioId { get; set; } + public string? ScenarioId { get; set; } [JsonProperty("source")] - public string Source { get; set; } + public string? Source { get; set; } } diff --git a/BattlePass/AddBattlePassXpResponse.cs b/BattlePass/AddBattlePassXpResponse.cs index 561e286..8607406 100644 --- a/BattlePass/AddBattlePassXpResponse.cs +++ b/BattlePass/AddBattlePassXpResponse.cs @@ -1,7 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; using RudderSdk.Core.Models; namespace RudderSdk.Core.Models.BattlePass; @@ -9,18 +7,18 @@ namespace RudderSdk.Core.Models.BattlePass; public class AddBattlePassXpResponse { [JsonProperty("level")] - public int Level { get; set; } + public int? Level { get; set; } [JsonProperty("leveledUp")] - public bool LeveledUp { get; set; } + public bool? LeveledUp { get; set; } [JsonProperty("maxLevel")] - public bool MaxLevel { get; set; } + public bool? MaxLevel { get; set; } [JsonProperty("plan")] - public ExecutionPlan Plan { get; set; } + public ExecutionPlan? Plan { get; set; } [JsonProperty("xp")] - public long Xp { get; set; } + public long? Xp { get; set; } } diff --git a/BattlePass/BattlePassReward.cs b/BattlePass/BattlePassReward.cs deleted file mode 100644 index 9ef1aa8..0000000 --- a/BattlePass/BattlePassReward.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Code generated by apigen. DO NOT EDIT. -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; - -namespace RudderSdk.Core.Models.BattlePass; - -public class BattlePassReward -{ - [JsonProperty("amount")] - public long Amount { get; set; } - - [JsonProperty("currency")] - public string Currency { get; set; } - - [JsonProperty("itemId")] - public string ItemId { get; set; } - -} diff --git a/BattlePass/ClaimBattlePassRewardRequest.cs b/BattlePass/ClaimBattlePassRewardRequest.cs index ebd6752..51dd9fd 100644 --- a/BattlePass/ClaimBattlePassRewardRequest.cs +++ b/BattlePass/ClaimBattlePassRewardRequest.cs @@ -1,25 +1,23 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.BattlePass; public class ClaimBattlePassRewardRequest { [JsonProperty("level")] - public int Level { get; set; } + public int? Level { get; set; } [JsonProperty("nodeId")] - public string NodeId { get; set; } + public string? NodeId { get; set; } [JsonProperty("runId")] - public string RunId { get; set; } + public string? RunId { get; set; } [JsonProperty("scenarioId")] - public string ScenarioId { get; set; } + public string? ScenarioId { get; set; } [JsonProperty("track")] - public string Track { get; set; } + public string? Track { get; set; } } diff --git a/BattlePass/ClaimBattlePassRewardResponse.cs b/BattlePass/ClaimBattlePassRewardResponse.cs index 0f45aa4..e6eb3eb 100644 --- a/BattlePass/ClaimBattlePassRewardResponse.cs +++ b/BattlePass/ClaimBattlePassRewardResponse.cs @@ -1,22 +1,22 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; +using RudderSdk.Core.Models; namespace RudderSdk.Core.Models.BattlePass; public class ClaimBattlePassRewardResponse { [JsonProperty("alreadyClaimed")] - public bool AlreadyClaimed { get; set; } + public bool? AlreadyClaimed { get; set; } [JsonProperty("error")] - public string Error { get; set; } + public string? Error { get; set; } [JsonProperty("granted")] - public List Granted { get; set; } + public List? Granted { get; set; } [JsonProperty("success")] - public bool Success { get; set; } + public bool? Success { get; set; } } diff --git a/BattlePass/ClaimedTier.cs b/BattlePass/ClaimedTier.cs index 2e39486..f3a7718 100644 --- a/BattlePass/ClaimedTier.cs +++ b/BattlePass/ClaimedTier.cs @@ -1,16 +1,14 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.BattlePass; public class ClaimedTier { [JsonProperty("level")] - public int Level { get; set; } + public int? Level { get; set; } [JsonProperty("track")] - public string Track { get; set; } + public string? Track { get; set; } } diff --git a/BattlePass/GetBattlePassProgressRequest.cs b/BattlePass/GetBattlePassProgressRequest.cs index 34dff9d..6f633ea 100644 --- a/BattlePass/GetBattlePassProgressRequest.cs +++ b/BattlePass/GetBattlePassProgressRequest.cs @@ -1,16 +1,14 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.BattlePass; public class GetBattlePassProgressRequest { [JsonProperty("nodeId")] - public string NodeId { get; set; } + public string? NodeId { get; set; } [JsonProperty("scenarioId")] - public string ScenarioId { get; set; } + public string? ScenarioId { get; set; } } diff --git a/BattlePass/GetBattlePassProgressResponse.cs b/BattlePass/GetBattlePassProgressResponse.cs index 14b77f8..522e908 100644 --- a/BattlePass/GetBattlePassProgressResponse.cs +++ b/BattlePass/GetBattlePassProgressResponse.cs @@ -1,6 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; namespace RudderSdk.Core.Models.BattlePass; @@ -8,15 +7,15 @@ namespace RudderSdk.Core.Models.BattlePass; public class GetBattlePassProgressResponse { [JsonProperty("claimedTiers")] - public List ClaimedTiers { get; set; } + public List? ClaimedTiers { get; set; } [JsonProperty("level")] - public int Level { get; set; } + public int? Level { get; set; } [JsonProperty("premiumOwned")] - public bool PremiumOwned { get; set; } + public bool? PremiumOwned { get; set; } [JsonProperty("xp")] - public long Xp { get; set; } + public long? Xp { get; set; } } diff --git a/BattlePass/PurchaseBattlePassPremiumRequest.cs b/BattlePass/PurchaseBattlePassPremiumRequest.cs index f6f6393..6208bf0 100644 --- a/BattlePass/PurchaseBattlePassPremiumRequest.cs +++ b/BattlePass/PurchaseBattlePassPremiumRequest.cs @@ -1,22 +1,20 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.BattlePass; public class PurchaseBattlePassPremiumRequest { [JsonProperty("idempotencyKey")] - public string IdempotencyKey { get; set; } + public string? IdempotencyKey { get; set; } [JsonProperty("nodeId")] - public string NodeId { get; set; } + public string? NodeId { get; set; } [JsonProperty("runId")] - public string RunId { get; set; } + public string? RunId { get; set; } [JsonProperty("scenarioId")] - public string ScenarioId { get; set; } + public string? ScenarioId { get; set; } } diff --git a/BattlePass/PurchaseBattlePassPremiumResponse.cs b/BattlePass/PurchaseBattlePassPremiumResponse.cs index 79190fb..7ffbdf6 100644 --- a/BattlePass/PurchaseBattlePassPremiumResponse.cs +++ b/BattlePass/PurchaseBattlePassPremiumResponse.cs @@ -1,7 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; using RudderSdk.Core.Models; namespace RudderSdk.Core.Models.BattlePass; @@ -9,12 +7,12 @@ namespace RudderSdk.Core.Models.BattlePass; public class PurchaseBattlePassPremiumResponse { [JsonProperty("error")] - public string Error { get; set; } + public string? Error { get; set; } [JsonProperty("plan")] - public ExecutionPlan Plan { get; set; } + public ExecutionPlan? Plan { get; set; } [JsonProperty("success")] - public bool Success { get; set; } + public bool? Success { get; set; } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 348a382..af55d56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## 0.4.0 + +- Generated DTOs live under `Models/` (`RudderSdk.Core.Models`). Wire fields + are nullable. `ListQuestsRequest` is gone; `Quests.ListAsync` POSTs with + no body. `BoundaryNode.WaitDeadline` is `DateTimeOffset?`. +- `LeaderboardSession.ClaimAsync` / `Claim` complete the leaderboard node + with handle `onClaim`. The server matches live rank to an authored place. +- `RewardClaimedAsync` / `RewardClaimed` are obsolete aliases and will be + removed in the next SDK version. +- `rank_not_eligible` on Claim leaves the session open (retry Claim or End); + it no longer fails the run. + ## 0.3.0 ### Removed diff --git a/Catalog/CatalogItem.cs b/Catalog/CatalogItem.cs deleted file mode 100644 index a7ee673..0000000 --- a/Catalog/CatalogItem.cs +++ /dev/null @@ -1,22 +0,0 @@ -// Code generated by apigen. DO NOT EDIT. -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; - -namespace RudderSdk.Core.Models.Catalog; - -public class CatalogItem -{ - [JsonProperty("name")] - public string Name { get; set; } - - [JsonProperty("properties")] - public JToken Properties { get; set; } - - [JsonProperty("slug")] - public string Slug { get; set; } - - [JsonProperty("tags")] - public List Tags { get; set; } - -} diff --git a/Catalog/ListCatalogItemsResponse.cs b/Catalog/ListCatalogItemsResponse.cs deleted file mode 100644 index cd69c0a..0000000 --- a/Catalog/ListCatalogItemsResponse.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Code generated by apigen. DO NOT EDIT. -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; - -namespace RudderSdk.Core.Models.Catalog; - -public class ListCatalogItemsResponse -{ - [JsonProperty("items")] - public List Items { get; set; } - -} diff --git a/ErrorResponse.cs b/ErrorResponse.cs deleted file mode 100644 index 15c1cc6..0000000 --- a/ErrorResponse.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Code generated by apigen. DO NOT EDIT. -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; - -namespace RudderSdk.Core.Models; - -public class ErrorResponse -{ - [JsonProperty("code")] - public string Code { get; set; } - - [JsonProperty("error")] - public string Error { get; set; } - - [JsonProperty("requestId")] - public string RequestId { get; set; } - -} diff --git a/Inventory/GetInventoryResponse.cs b/Inventory/GetInventoryResponse.cs index 0d26b3a..7e1d11d 100644 --- a/Inventory/GetInventoryResponse.cs +++ b/Inventory/GetInventoryResponse.cs @@ -1,6 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; namespace RudderSdk.Core.Models.Inventory; @@ -8,6 +7,6 @@ namespace RudderSdk.Core.Models.Inventory; public class GetInventoryResponse { [JsonProperty("items")] - public List Items { get; set; } + public List? Items { get; set; } } diff --git a/Inventory/PlayerInventoryItem.cs b/Inventory/PlayerInventoryItem.cs index 3f3d375..6a43be1 100644 --- a/Inventory/PlayerInventoryItem.cs +++ b/Inventory/PlayerInventoryItem.cs @@ -1,25 +1,25 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using System.Collections.Generic; +using System; namespace RudderSdk.Core.Models.Inventory; public class PlayerInventoryItem { [JsonProperty("amount")] - public long Amount { get; set; } + public long? Amount { get; set; } [JsonProperty("nameOverride")] - public string NameOverride { get; set; } + public string? NameOverride { get; set; } [JsonProperty("propertiesOverride")] - public JToken PropertiesOverride { get; set; } + public JToken? PropertiesOverride { get; set; } [JsonProperty("slug")] - public string Slug { get; set; } + public string? Slug { get; set; } [JsonProperty("updatedAt")] - public string UpdatedAt { get; set; } + public DateTimeOffset? UpdatedAt { get; set; } } diff --git a/Leaderboards/GetRankingResponse.cs b/Leaderboards/GetRankingResponse.cs index 8dfbbba..10c96a6 100644 --- a/Leaderboards/GetRankingResponse.cs +++ b/Leaderboards/GetRankingResponse.cs @@ -1,6 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; namespace RudderSdk.Core.Models.Leaderboards; @@ -8,9 +7,9 @@ namespace RudderSdk.Core.Models.Leaderboards; public class GetRankingResponse { [JsonProperty("entries")] - public List Entries { get; set; } + public List? Entries { get; set; } [JsonProperty("total")] - public long Total { get; set; } + public long? Total { get; set; } } diff --git a/Leaderboards/RankEntry.cs b/Leaderboards/RankEntry.cs index 8f64890..da8f8e5 100644 --- a/Leaderboards/RankEntry.cs +++ b/Leaderboards/RankEntry.cs @@ -1,22 +1,20 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Leaderboards; public class RankEntry { [JsonProperty("playerId")] - public string PlayerId { get; set; } + public string? PlayerId { get; set; } [JsonProperty("playerName")] - public string PlayerName { get; set; } + public string? PlayerName { get; set; } [JsonProperty("rank")] - public long Rank { get; set; } + public long? Rank { get; set; } [JsonProperty("score")] - public double Score { get; set; } + public double? Score { get; set; } } diff --git a/Leaderboards/SubmitScoreRequest.cs b/Leaderboards/SubmitScoreRequest.cs index 69436f5..5e05de6 100644 --- a/Leaderboards/SubmitScoreRequest.cs +++ b/Leaderboards/SubmitScoreRequest.cs @@ -1,16 +1,14 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Leaderboards; public class SubmitScoreRequest { [JsonProperty("score")] - public double Score { get; set; } + public double? Score { get; set; } [JsonProperty("slug")] - public string Slug { get; set; } + public string? Slug { get; set; } } diff --git a/BoundaryNode.cs b/Models/BoundaryNode.cs similarity index 50% rename from BoundaryNode.cs rename to Models/BoundaryNode.cs index 1adfa56..9b5b268 100644 --- a/BoundaryNode.cs +++ b/Models/BoundaryNode.cs @@ -1,31 +1,30 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; +using System; namespace RudderSdk.Core.Models; public class BoundaryNode { [JsonProperty("callbackUrl")] - public string CallbackUrl { get; set; } + public string? CallbackUrl { get; set; } [JsonProperty("enforcement")] - public string Enforcement { get; set; } + public string? Enforcement { get; set; } [JsonProperty("enteredAt")] - public string EnteredAt { get; set; } + public DateTimeOffset? EnteredAt { get; set; } [JsonProperty("nodeId")] - public string NodeId { get; set; } + public string? NodeId { get; set; } [JsonProperty("sourceHandle")] - public string SourceHandle { get; set; } + public string? SourceHandle { get; set; } [JsonProperty("sourceNodeId")] - public string SourceNodeId { get; set; } + public string? SourceNodeId { get; set; } [JsonProperty("waitDeadline")] - public string WaitDeadline { get; set; } + public DateTimeOffset? WaitDeadline { get; set; } } diff --git a/Models/ErrorResponse.cs b/Models/ErrorResponse.cs new file mode 100644 index 0000000..3a83b32 --- /dev/null +++ b/Models/ErrorResponse.cs @@ -0,0 +1,23 @@ +// Code generated by apigen. DO NOT EDIT. +using Newtonsoft.Json; + +namespace RudderSdk.Core.Models; + +public class ErrorResponse +{ + [JsonProperty("code")] + public string? Code { get; set; } + + [JsonProperty("error")] + public string? Error { get; set; } + + [JsonProperty("index")] + public int? Index { get; set; } + + [JsonProperty("playerId")] + public string? PlayerId { get; set; } + + [JsonProperty("requestId")] + public string? RequestId { get; set; } + +} diff --git a/ExecutionPlan.cs b/Models/ExecutionPlan.cs similarity index 53% rename from ExecutionPlan.cs rename to Models/ExecutionPlan.cs index 0230c58..5311ab4 100644 --- a/ExecutionPlan.cs +++ b/Models/ExecutionPlan.cs @@ -8,30 +8,30 @@ namespace RudderSdk.Core.Models; public class ExecutionPlan { [JsonProperty("boundaryNodes")] - public List BoundaryNodes { get; set; } + public List? BoundaryNodes { get; set; } [JsonProperty("context")] - public JToken Context { get; set; } + public JToken? Context { get; set; } [JsonProperty("edges")] - public List Edges { get; set; } + public List? Edges { get; set; } [JsonProperty("nodes")] - public List Nodes { get; set; } + public List? Nodes { get; set; } [JsonProperty("planId")] - public string PlanId { get; set; } + public string? PlanId { get; set; } [JsonProperty("runId")] - public string RunId { get; set; } + public string? RunId { get; set; } [JsonProperty("scenarioId")] - public string ScenarioId { get; set; } + public string? ScenarioId { get; set; } [JsonProperty("startNodeId")] - public string StartNodeId { get; set; } + public string? StartNodeId { get; set; } [JsonProperty("userId")] - public string UserId { get; set; } + public string? UserId { get; set; } } diff --git a/ExecutionPlanNode.cs b/Models/ExecutionPlanNode.cs similarity index 63% rename from ExecutionPlanNode.cs rename to Models/ExecutionPlanNode.cs index 7a0dba9..cf40b6b 100644 --- a/ExecutionPlanNode.cs +++ b/Models/ExecutionPlanNode.cs @@ -1,19 +1,18 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models; public class ExecutionPlanNode { [JsonProperty("data")] - public JToken Data { get; set; } + public JToken? Data { get; set; } [JsonProperty("id")] - public string Id { get; set; } + public string? Id { get; set; } [JsonProperty("type")] - public string Type { get; set; } + public string? Type { get; set; } } diff --git a/PlanEdge.cs b/Models/PlanEdge.cs similarity index 51% rename from PlanEdge.cs rename to Models/PlanEdge.cs index 2f2969f..9e897c6 100644 --- a/PlanEdge.cs +++ b/Models/PlanEdge.cs @@ -1,25 +1,23 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models; public class PlanEdge { [JsonProperty("id")] - public string Id { get; set; } + public string? Id { get; set; } [JsonProperty("source")] - public string Source { get; set; } + public string? Source { get; set; } [JsonProperty("sourceHandle")] - public string SourceHandle { get; set; } + public string? SourceHandle { get; set; } [JsonProperty("target")] - public string Target { get; set; } + public string? Target { get; set; } [JsonProperty("targetHandle")] - public string TargetHandle { get; set; } + public string? TargetHandle { get; set; } } diff --git a/Models/Reward.cs b/Models/Reward.cs new file mode 100644 index 0000000..2641fb3 --- /dev/null +++ b/Models/Reward.cs @@ -0,0 +1,17 @@ +// Code generated by apigen. DO NOT EDIT. +using Newtonsoft.Json; + +namespace RudderSdk.Core.Models; + +public class Reward +{ + [JsonProperty("amount")] + public long? Amount { get; set; } + + [JsonProperty("currency")] + public string? Currency { get; set; } + + [JsonProperty("itemId")] + public string? ItemId { get; set; } + +} diff --git a/RudderErrorCodes.cs b/Models/RudderErrorCodes.cs similarity index 94% rename from RudderErrorCodes.cs rename to Models/RudderErrorCodes.cs index 777e8c3..e3ddfa2 100644 --- a/RudderErrorCodes.cs +++ b/Models/RudderErrorCodes.cs @@ -1,6 +1,6 @@ // Code generated by apigen. DO NOT EDIT. -namespace RudderSdk.Core; +namespace RudderSdk.Core.Models; public static class RudderErrorCodes { diff --git a/Player/Player.cs b/Player/Player.cs index 7f6b751..70dcb49 100644 --- a/Player/Player.cs +++ b/Player/Player.cs @@ -1,28 +1,27 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; +using System; namespace RudderSdk.Core.Models.Player; public class Player { [JsonProperty("createdAt")] - public string CreatedAt { get; set; } + public DateTimeOffset? CreatedAt { get; set; } [JsonProperty("id")] - public string Id { get; set; } + public string? Id { get; set; } [JsonProperty("language")] - public string Language { get; set; } + public string? Language { get; set; } [JsonProperty("nickname")] - public string Nickname { get; set; } + public string? Nickname { get; set; } [JsonProperty("projectId")] - public string ProjectId { get; set; } + public string? ProjectId { get; set; } [JsonProperty("region")] - public string Region { get; set; } + public string? Region { get; set; } } diff --git a/Player/PlayerProfile.cs b/Player/PlayerProfile.cs index 4f61035..701cb9a 100644 --- a/Player/PlayerProfile.cs +++ b/Player/PlayerProfile.cs @@ -1,6 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; namespace RudderSdk.Core.Models.Player; @@ -8,9 +7,9 @@ namespace RudderSdk.Core.Models.Player; public class PlayerProfile { [JsonProperty("player")] - public Player Player { get; set; } + public Player? Player { get; set; } [JsonProperty("wallets")] - public List Wallets { get; set; } + public List? Wallets { get; set; } } diff --git a/Player/Wallet.cs b/Player/Wallet.cs index 363cd65..b92bf84 100644 --- a/Player/Wallet.cs +++ b/Player/Wallet.cs @@ -1,16 +1,14 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Player; public class Wallet { [JsonProperty("balance")] - public long Balance { get; set; } + public long? Balance { get; set; } [JsonProperty("currency")] - public string Currency { get; set; } + public string? Currency { get; set; } } diff --git a/ProjectStorage/GetProjectStorageResponse.cs b/ProjectStorage/GetProjectStorageResponse.cs index 33a2b5a..db49126 100644 --- a/ProjectStorage/GetProjectStorageResponse.cs +++ b/ProjectStorage/GetProjectStorageResponse.cs @@ -1,6 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; namespace RudderSdk.Core.Models.ProjectStorage; @@ -8,9 +7,9 @@ namespace RudderSdk.Core.Models.ProjectStorage; public class GetProjectStorageResponse { [JsonProperty("items")] - public List Items { get; set; } + public List? Items { get; set; } [JsonProperty("nextCursor")] - public string NextCursor { get; set; } + public string? NextCursor { get; set; } } diff --git a/ProjectStorage/ProjectStorageItem.cs b/ProjectStorage/ProjectStorageItem.cs index 3eaea64..f78ec8f 100644 --- a/ProjectStorage/ProjectStorageItem.cs +++ b/ProjectStorage/ProjectStorageItem.cs @@ -1,37 +1,36 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; +using System; namespace RudderSdk.Core.Models.ProjectStorage; public class ProjectStorageItem { [JsonProperty("data")] - public string Data { get; set; } + public string? Data { get; set; } [JsonProperty("expiresAt")] - public string ExpiresAt { get; set; } + public DateTimeOffset? ExpiresAt { get; set; } [JsonProperty("id")] - public string Id { get; set; } + public string? Id { get; set; } [JsonProperty("readPermission")] - public string ReadPermission { get; set; } + public string? ReadPermission { get; set; } [JsonProperty("size")] - public long Size { get; set; } + public long? Size { get; set; } [JsonProperty("type")] - public string Type { get; set; } + public string? Type { get; set; } [JsonProperty("updatedAt")] - public string UpdatedAt { get; set; } + public DateTimeOffset? UpdatedAt { get; set; } [JsonProperty("version")] - public long Version { get; set; } + public long? Version { get; set; } [JsonProperty("writePermission")] - public string WritePermission { get; set; } + public string? WritePermission { get; set; } } diff --git a/ProjectStorage/ProjectStorageUpdateItem.cs b/ProjectStorage/ProjectStorageUpdateItem.cs index 038201a..36ff008 100644 --- a/ProjectStorage/ProjectStorageUpdateItem.cs +++ b/ProjectStorage/ProjectStorageUpdateItem.cs @@ -1,16 +1,14 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.ProjectStorage; public class ProjectStorageUpdateItem { [JsonProperty("data")] - public string Data { get; set; } + public string? Data { get; set; } [JsonProperty("type")] - public string Type { get; set; } + public string? Type { get; set; } } diff --git a/ProjectStorage/UpdateProjectStorageRequest.cs b/ProjectStorage/UpdateProjectStorageRequest.cs index 7899851..3c6e2a0 100644 --- a/ProjectStorage/UpdateProjectStorageRequest.cs +++ b/ProjectStorage/UpdateProjectStorageRequest.cs @@ -1,6 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; namespace RudderSdk.Core.Models.ProjectStorage; @@ -8,9 +7,9 @@ namespace RudderSdk.Core.Models.ProjectStorage; public class UpdateProjectStorageRequest { [JsonProperty("idempotencyKey")] - public string IdempotencyKey { get; set; } + public string? IdempotencyKey { get; set; } [JsonProperty("items")] - public List Items { get; set; } + public List? Items { get; set; } } diff --git a/Quests/ClaimQuestRequest.cs b/Quests/ClaimQuestRequest.cs index 373e834..8e11b6f 100644 --- a/Quests/ClaimQuestRequest.cs +++ b/Quests/ClaimQuestRequest.cs @@ -1,13 +1,11 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Quests; public class ClaimQuestRequest { [JsonProperty("questId")] - public string QuestId { get; set; } + public string? QuestId { get; set; } } diff --git a/Quests/ClaimQuestResponse.cs b/Quests/ClaimQuestResponse.cs index f1701f2..361f89f 100644 --- a/Quests/ClaimQuestResponse.cs +++ b/Quests/ClaimQuestResponse.cs @@ -1,22 +1,22 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; +using RudderSdk.Core.Models; namespace RudderSdk.Core.Models.Quests; public class ClaimQuestResponse { [JsonProperty("alreadyClaimed")] - public bool AlreadyClaimed { get; set; } + public bool? AlreadyClaimed { get; set; } [JsonProperty("error")] - public string Error { get; set; } + public string? Error { get; set; } [JsonProperty("granted")] - public List Granted { get; set; } + public List? Granted { get; set; } [JsonProperty("success")] - public bool Success { get; set; } + public bool? Success { get; set; } } diff --git a/Quests/ListQuestsRequest.cs b/Quests/ListQuestsRequest.cs deleted file mode 100644 index 08207e9..0000000 --- a/Quests/ListQuestsRequest.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Code generated by apigen. DO NOT EDIT. -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; - -namespace RudderSdk.Core.Models.Quests; - -public class ListQuestsRequest -{ -} diff --git a/Quests/ListQuestsResponse.cs b/Quests/ListQuestsResponse.cs index b5eb3c0..2431889 100644 --- a/Quests/ListQuestsResponse.cs +++ b/Quests/ListQuestsResponse.cs @@ -1,6 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; namespace RudderSdk.Core.Models.Quests; @@ -8,6 +7,6 @@ namespace RudderSdk.Core.Models.Quests; public class ListQuestsResponse { [JsonProperty("quests")] - public List Quests { get; set; } + public List? Quests { get; set; } } diff --git a/Quests/Quest.cs b/Quests/Quest.cs index fb9911f..b87ce9c 100644 --- a/Quests/Quest.cs +++ b/Quests/Quest.cs @@ -1,25 +1,25 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; +using RudderSdk.Core.Models; namespace RudderSdk.Core.Models.Quests; public class Quest { [JsonProperty("id")] - public string Id { get; set; } + public string? Id { get; set; } [JsonProperty("name")] - public string Name { get; set; } + public string? Name { get; set; } [JsonProperty("objectives")] - public List Objectives { get; set; } + public List? Objectives { get; set; } [JsonProperty("rewards")] - public List Rewards { get; set; } + public List? Rewards { get; set; } [JsonProperty("status")] - public string Status { get; set; } + public string? Status { get; set; } } diff --git a/Quests/QuestObjectiveProgress.cs b/Quests/QuestObjectiveProgress.cs index 7750834..8cbbb8d 100644 --- a/Quests/QuestObjectiveProgress.cs +++ b/Quests/QuestObjectiveProgress.cs @@ -1,25 +1,23 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Quests; public class QuestObjectiveProgress { [JsonProperty("completed")] - public bool Completed { get; set; } + public bool? Completed { get; set; } [JsonProperty("current")] - public long Current { get; set; } + public long? Current { get; set; } [JsonProperty("metric")] - public string Metric { get; set; } + public string? Metric { get; set; } [JsonProperty("objectiveId")] - public string ObjectiveId { get; set; } + public string? ObjectiveId { get; set; } [JsonProperty("target")] - public long Target { get; set; } + public long? Target { get; set; } } diff --git a/Quests/QuestReward.cs b/Quests/QuestReward.cs deleted file mode 100644 index 2535a56..0000000 --- a/Quests/QuestReward.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Code generated by apigen. DO NOT EDIT. -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; - -namespace RudderSdk.Core.Models.Quests; - -public class QuestReward -{ - [JsonProperty("amount")] - public long Amount { get; set; } - - [JsonProperty("currency")] - public string Currency { get; set; } - - [JsonProperty("itemId")] - public string ItemId { get; set; } - -} diff --git a/Quests/ReportQuestProgressRequest.cs b/Quests/ReportQuestProgressRequest.cs index 93c6d96..4b52718 100644 --- a/Quests/ReportQuestProgressRequest.cs +++ b/Quests/ReportQuestProgressRequest.cs @@ -1,16 +1,14 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Quests; public class ReportQuestProgressRequest { [JsonProperty("amount")] - public long Amount { get; set; } + public long? Amount { get; set; } [JsonProperty("metric")] - public string Metric { get; set; } + public string? Metric { get; set; } } diff --git a/Quests/ReportQuestProgressResponse.cs b/Quests/ReportQuestProgressResponse.cs index ed76646..ef90c2e 100644 --- a/Quests/ReportQuestProgressResponse.cs +++ b/Quests/ReportQuestProgressResponse.cs @@ -1,6 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; namespace RudderSdk.Core.Models.Quests; @@ -8,6 +7,6 @@ namespace RudderSdk.Core.Models.Quests; public class ReportQuestProgressResponse { [JsonProperty("completedQuestIds")] - public List CompletedQuestIds { get; set; } + public List? CompletedQuestIds { get; set; } } diff --git a/RemoteConfig/ListRemoteConfigsResponse.cs b/RemoteConfig/ListRemoteConfigsResponse.cs index 999d261..90b437f 100644 --- a/RemoteConfig/ListRemoteConfigsResponse.cs +++ b/RemoteConfig/ListRemoteConfigsResponse.cs @@ -1,6 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; namespace RudderSdk.Core.Models.RemoteConfig; @@ -8,6 +7,6 @@ namespace RudderSdk.Core.Models.RemoteConfig; public class ListRemoteConfigsResponse { [JsonProperty("configs")] - public Dictionary Configs { get; set; } + public Dictionary? Configs { get; set; } } diff --git a/RemoteConfig/RemoteConfig.cs b/RemoteConfig/RemoteConfig.cs index 019c1d1..64fee57 100644 --- a/RemoteConfig/RemoteConfig.cs +++ b/RemoteConfig/RemoteConfig.cs @@ -1,40 +1,39 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; +using System; namespace RudderSdk.Core.Models.RemoteConfig; public class RemoteConfig { [JsonProperty("active")] - public bool Active { get; set; } + public bool? Active { get; set; } [JsonProperty("createdAt")] - public string CreatedAt { get; set; } + public DateTimeOffset? CreatedAt { get; set; } [JsonProperty("description")] - public string Description { get; set; } + public string? Description { get; set; } [JsonProperty("environment")] - public string Environment { get; set; } + public string? Environment { get; set; } [JsonProperty("id")] - public string Id { get; set; } + public string? Id { get; set; } [JsonProperty("key")] - public string Key { get; set; } + public string? Key { get; set; } [JsonProperty("projectId")] - public string ProjectId { get; set; } + public string? ProjectId { get; set; } [JsonProperty("updatedAt")] - public string UpdatedAt { get; set; } + public DateTimeOffset? UpdatedAt { get; set; } [JsonProperty("value")] - public string Value { get; set; } + public string? Value { get; set; } [JsonProperty("valueType")] - public string ValueType { get; set; } + public string? ValueType { get; set; } } diff --git a/Rudder.Core.csproj b/Rudder.Core.csproj index f2e74e3..102ac73 100644 --- a/Rudder.Core.csproj +++ b/Rudder.Core.csproj @@ -6,7 +6,7 @@ Rudder.Core RudderSdk.Core Rudder.Core - 0.3.0 + 0.4.0 Rudder Rudder LiveOps client SDK for .NET: auth, player, stores, battle pass, quests, leaderboards, inventory, remote config, scenarios, storage and realtime. MIT diff --git a/Scenarios/GetScenarioRunRequest.cs b/Scenarios/GetScenarioRunRequest.cs index f0088d9..db3c0d2 100644 --- a/Scenarios/GetScenarioRunRequest.cs +++ b/Scenarios/GetScenarioRunRequest.cs @@ -1,13 +1,11 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Scenarios; public class GetScenarioRunRequest { [JsonProperty("runId")] - public string RunId { get; set; } + public string? RunId { get; set; } } diff --git a/Scenarios/GetScenarioRunResponse.cs b/Scenarios/GetScenarioRunResponse.cs index 47d6c59..4643115 100644 --- a/Scenarios/GetScenarioRunResponse.cs +++ b/Scenarios/GetScenarioRunResponse.cs @@ -1,7 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; using RudderSdk.Core.Models; namespace RudderSdk.Core.Models.Scenarios; @@ -9,12 +7,12 @@ namespace RudderSdk.Core.Models.Scenarios; public class GetScenarioRunResponse { [JsonProperty("plan")] - public ExecutionPlan Plan { get; set; } + public ExecutionPlan? Plan { get; set; } [JsonProperty("runId")] - public string RunId { get; set; } + public string? RunId { get; set; } [JsonProperty("status")] - public string Status { get; set; } + public string? Status { get; set; } } diff --git a/Scenarios/HandleScenarioCallbackRequest.cs b/Scenarios/HandleScenarioCallbackRequest.cs index cbeb678..922875b 100644 --- a/Scenarios/HandleScenarioCallbackRequest.cs +++ b/Scenarios/HandleScenarioCallbackRequest.cs @@ -1,22 +1,20 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Scenarios; public class HandleScenarioCallbackRequest { [JsonProperty("handle")] - public string Handle { get; set; } + public string? Handle { get; set; } [JsonProperty("nodeId")] - public string NodeId { get; set; } + public string? NodeId { get; set; } [JsonProperty("runId")] - public string RunId { get; set; } + public string? RunId { get; set; } [JsonProperty("scenarioId")] - public string ScenarioId { get; set; } + public string? ScenarioId { get; set; } } diff --git a/Scenarios/HandleScenarioCallbackResponse.cs b/Scenarios/HandleScenarioCallbackResponse.cs index d46975c..2b9c47c 100644 --- a/Scenarios/HandleScenarioCallbackResponse.cs +++ b/Scenarios/HandleScenarioCallbackResponse.cs @@ -1,7 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; using RudderSdk.Core.Models; namespace RudderSdk.Core.Models.Scenarios; @@ -9,6 +7,6 @@ namespace RudderSdk.Core.Models.Scenarios; public class HandleScenarioCallbackResponse { [JsonProperty("plan")] - public ExecutionPlan Plan { get; set; } + public ExecutionPlan? Plan { get; set; } } diff --git a/Scenarios/TriggerScenarioRequest.cs b/Scenarios/TriggerScenarioRequest.cs index cd9974d..7c56def 100644 --- a/Scenarios/TriggerScenarioRequest.cs +++ b/Scenarios/TriggerScenarioRequest.cs @@ -1,13 +1,11 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Scenarios; public class TriggerScenarioRequest { [JsonProperty("event")] - public string Event { get; set; } + public string? Event { get; set; } } diff --git a/Scenarios/TriggerScenarioResponse.cs b/Scenarios/TriggerScenarioResponse.cs index f182f88..e36946a 100644 --- a/Scenarios/TriggerScenarioResponse.cs +++ b/Scenarios/TriggerScenarioResponse.cs @@ -1,6 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; using RudderSdk.Core.Models; @@ -9,6 +8,6 @@ namespace RudderSdk.Core.Models.Scenarios; public class TriggerScenarioResponse { [JsonProperty("plans")] - public List Plans { get; set; } + public List? Plans { get; set; } } diff --git a/Scenarios/UpdateScenarioCounterRequest.cs b/Scenarios/UpdateScenarioCounterRequest.cs index 756f222..1457c0d 100644 --- a/Scenarios/UpdateScenarioCounterRequest.cs +++ b/Scenarios/UpdateScenarioCounterRequest.cs @@ -1,25 +1,23 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Scenarios; public class UpdateScenarioCounterRequest { [JsonProperty("amount")] - public long Amount { get; set; } + public long? Amount { get; set; } [JsonProperty("counterKey")] - public string CounterKey { get; set; } + public string? CounterKey { get; set; } [JsonProperty("nodeId")] - public string NodeId { get; set; } + public string? NodeId { get; set; } [JsonProperty("runId")] - public string RunId { get; set; } + public string? RunId { get; set; } [JsonProperty("scenarioId")] - public string ScenarioId { get; set; } + public string? ScenarioId { get; set; } } diff --git a/Scenarios/UpdateScenarioCounterResponse.cs b/Scenarios/UpdateScenarioCounterResponse.cs index 285effd..892f9ea 100644 --- a/Scenarios/UpdateScenarioCounterResponse.cs +++ b/Scenarios/UpdateScenarioCounterResponse.cs @@ -1,7 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; using RudderSdk.Core.Models; namespace RudderSdk.Core.Models.Scenarios; @@ -9,9 +7,9 @@ namespace RudderSdk.Core.Models.Scenarios; public class UpdateScenarioCounterResponse { [JsonProperty("completed")] - public bool Completed { get; set; } + public bool? Completed { get; set; } [JsonProperty("plan")] - public ExecutionPlan Plan { get; set; } + public ExecutionPlan? Plan { get; set; } } diff --git a/Services/QuestsService.cs b/Services/QuestsService.cs index ec98fb8..a2c124f 100644 --- a/Services/QuestsService.cs +++ b/Services/QuestsService.cs @@ -18,10 +18,9 @@ public sealed class QuestsService /// Lists the player's quests with per-objective progress and rewards. public async Task> ListAsync(CancellationToken cancellationToken = default) { - var response = await _client.SendAsync( + var response = await _client.SendAsync( "POST", "/sdk/v1/quests/list", - new ListQuestsRequest(), cancellationToken).ConfigureAwait(false); return response?.Quests ?? new List(); diff --git a/Services/ScenarioService.cs b/Services/ScenarioService.cs index c979df6..4ef5e15 100644 --- a/Services/ScenarioService.cs +++ b/Services/ScenarioService.cs @@ -230,7 +230,7 @@ public sealed partial class ScenarioService // The server reports objective completion; it no longer returns a plan from the // counter endpoint. On completion, cross the node's onComplete handle (which // advances the run) — idempotent if the consumer also completes the session. - if (response != null && response.Completed) + if (response != null && response.Completed == true) await CompleteNodeAsync(runId, nodeId, "onComplete", cancellationToken).ConfigureAwait(false); } catch (Exception ex) diff --git a/Services/Scenarios/ScenarioService.Dispatch.cs b/Services/Scenarios/ScenarioService.Dispatch.cs index ecd7a83..42179b9 100644 --- a/Services/Scenarios/ScenarioService.Dispatch.cs +++ b/Services/Scenarios/ScenarioService.Dispatch.cs @@ -62,9 +62,9 @@ public sealed partial class ScenarioService // Prefer server-provided WaitDeadline from the plan boundary over local calculation. // The server stamps WaitDeadline on server-enforced wait boundaries (see StampBoundaries). var boundary = run.Plan.BoundaryNodes?.FirstOrDefault( - b => b.SourceNodeId == state.NodeId && !string.IsNullOrEmpty(b.WaitDeadline) + b => b.SourceNodeId == state.NodeId && b.WaitDeadline.HasValue ); - if (boundary != null && DateTimeOffset.TryParse(boundary.WaitDeadline, null, DateTimeStyles.RoundtripKind, out var parsed)) + if (boundary?.WaitDeadline is { } parsed) { state.WaitDeadlineUtc = parsed; } diff --git a/Services/Scenarios/ScenarioService.Runtime.cs b/Services/Scenarios/ScenarioService.Runtime.cs index 830e95b..3847b81 100644 --- a/Services/Scenarios/ScenarioService.Runtime.cs +++ b/Services/Scenarios/ScenarioService.Runtime.cs @@ -40,6 +40,11 @@ public sealed partial class ScenarioService return ex is OperationCanceledException || ex is RudderNetworkException; } + private static bool IsRankNotEligible(Exception ex) + { + return ex is RudderApiException api && api.Code == "rank_not_eligible"; + } + private IReadOnlyList StartPlans(IEnumerable? plans) { var started = new List(); @@ -181,6 +186,8 @@ public sealed partial class ScenarioService } catch (Exception ex) { + if (IsRankNotEligible(ex)) + throw; FailRun(run, nodeId, ex); } } diff --git a/Services/Scenarios/Sessions/BattlePassSession.cs b/Services/Scenarios/Sessions/BattlePassSession.cs index 9203b0f..658ceb7 100644 --- a/Services/Scenarios/Sessions/BattlePassSession.cs +++ b/Services/Scenarios/Sessions/BattlePassSession.cs @@ -69,7 +69,7 @@ public sealed class BattlePassSession IdempotencyKey = Guid.NewGuid().ToString() }, cancellationToken).ConfigureAwait(false); - if (response != null && response.Success) + if (response != null && response.Success == true) await Context.CompleteAsync("onPremiumPurchase", cancellationToken).ConfigureAwait(false); return response!; diff --git a/Services/Scenarios/Sessions/LeaderboardSession.cs b/Services/Scenarios/Sessions/LeaderboardSession.cs index 70eb9ee..0752d39 100644 --- a/Services/Scenarios/Sessions/LeaderboardSession.cs +++ b/Services/Scenarios/Sessions/LeaderboardSession.cs @@ -1,3 +1,4 @@ +using System; using System.Threading; using System.Threading.Tasks; @@ -23,9 +24,17 @@ public sealed class LeaderboardSession /// Advances the run through the onEnd handle (fire-and-forget). public void End() => Context.Complete("onEnd"); - /// Advances the run through the onRewardClaimed handle. - public Task RewardClaimedAsync(CancellationToken cancellationToken = default) => Context.CompleteAsync("onRewardClaimed", cancellationToken); + /// Advances the run through the onClaim handle. The server matches live rank to a place. + public Task ClaimAsync(CancellationToken cancellationToken = default) => Context.CompleteAsync("onClaim", cancellationToken); - /// Advances the run through the onRewardClaimed handle (fire-and-forget). - public void RewardClaimed() => Context.Complete("onRewardClaimed"); + /// Advances the run through the onClaim handle (fire-and-forget). + public void Claim() => Context.Complete("onClaim"); + + /// Advances the run through the onClaim handle. + [Obsolete("Use ClaimAsync. Removed in the next SDK version.")] + public Task RewardClaimedAsync(CancellationToken cancellationToken = default) => ClaimAsync(cancellationToken); + + /// Advances the run through the onClaim handle (fire-and-forget). + [Obsolete("Use Claim. Removed in the next SDK version.")] + public void RewardClaimed() => Claim(); } diff --git a/Storage/GetStorageResponse.cs b/Storage/GetStorageResponse.cs index 4088bcc..44afcd1 100644 --- a/Storage/GetStorageResponse.cs +++ b/Storage/GetStorageResponse.cs @@ -1,6 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; namespace RudderSdk.Core.Models.Storage; @@ -8,9 +7,9 @@ namespace RudderSdk.Core.Models.Storage; public class GetStorageResponse { [JsonProperty("items")] - public List Items { get; set; } + public List? Items { get; set; } [JsonProperty("nextCursor")] - public string NextCursor { get; set; } + public string? NextCursor { get; set; } } diff --git a/Storage/StorageItem.cs b/Storage/StorageItem.cs index 8c6f894..c8caa01 100644 --- a/Storage/StorageItem.cs +++ b/Storage/StorageItem.cs @@ -1,19 +1,17 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Storage; public class StorageItem { [JsonProperty("data")] - public string Data { get; set; } + public string? Data { get; set; } [JsonProperty("id")] - public string Id { get; set; } + public string? Id { get; set; } [JsonProperty("type")] - public string Type { get; set; } + public string? Type { get; set; } } diff --git a/Storage/UpdateStorageRequest.cs b/Storage/UpdateStorageRequest.cs index 107e29a..5822160 100644 --- a/Storage/UpdateStorageRequest.cs +++ b/Storage/UpdateStorageRequest.cs @@ -1,6 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; namespace RudderSdk.Core.Models.Storage; @@ -8,9 +7,9 @@ namespace RudderSdk.Core.Models.Storage; public class UpdateStorageRequest { [JsonProperty("idempotencyKey")] - public string IdempotencyKey { get; set; } + public string? IdempotencyKey { get; set; } [JsonProperty("items")] - public List Items { get; set; } + public List? Items { get; set; } } diff --git a/Stores/ListStoresResponse.cs b/Stores/ListStoresResponse.cs index 2eac0df..52f33fb 100644 --- a/Stores/ListStoresResponse.cs +++ b/Stores/ListStoresResponse.cs @@ -1,6 +1,5 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using System.Collections.Generic; namespace RudderSdk.Core.Models.Stores; @@ -8,9 +7,9 @@ namespace RudderSdk.Core.Models.Stores; public class ListStoresResponse { [JsonProperty("stores")] - public List Stores { get; set; } + public List? Stores { get; set; } [JsonProperty("total")] - public int Total { get; set; } + public int? Total { get; set; } } diff --git a/Stores/Offer.cs b/Stores/Offer.cs index 52f1373..5f9d9e3 100644 --- a/Stores/Offer.cs +++ b/Stores/Offer.cs @@ -1,6 +1,6 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using System; using System.Collections.Generic; namespace RudderSdk.Core.Models.Stores; @@ -8,24 +8,24 @@ namespace RudderSdk.Core.Models.Stores; public class Offer { [JsonProperty("contents")] - public List Contents { get; set; } + public List? Contents { get; set; } [JsonProperty("createdAt")] - public string CreatedAt { get; set; } + public DateTimeOffset? CreatedAt { get; set; } [JsonProperty("id")] - public string Id { get; set; } + public string? Id { get; set; } [JsonProperty("maxPurchases")] - public int MaxPurchases { get; set; } + public int? MaxPurchases { get; set; } [JsonProperty("name")] - public string Name { get; set; } + public string? Name { get; set; } [JsonProperty("price")] - public OfferPrice Price { get; set; } + public OfferPrice? Price { get; set; } [JsonProperty("updatedAt")] - public string UpdatedAt { get; set; } + public DateTimeOffset? UpdatedAt { get; set; } } diff --git a/Stores/OfferContent.cs b/Stores/OfferContent.cs index 4e097b1..5a79607 100644 --- a/Stores/OfferContent.cs +++ b/Stores/OfferContent.cs @@ -1,16 +1,14 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Stores; public class OfferContent { [JsonProperty("amount")] - public long Amount { get; set; } + public long? Amount { get; set; } [JsonProperty("itemId")] - public string ItemId { get; set; } + public string? ItemId { get; set; } } diff --git a/Stores/OfferPrice.cs b/Stores/OfferPrice.cs index e24a0db..51edeeb 100644 --- a/Stores/OfferPrice.cs +++ b/Stores/OfferPrice.cs @@ -1,16 +1,14 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Stores; public class OfferPrice { [JsonProperty("amount")] - public long Amount { get; set; } + public long? Amount { get; set; } [JsonProperty("currency")] - public string Currency { get; set; } + public string? Currency { get; set; } } diff --git a/Stores/PurchaseOfferRequest.cs b/Stores/PurchaseOfferRequest.cs index 96830b2..c72a45b 100644 --- a/Stores/PurchaseOfferRequest.cs +++ b/Stores/PurchaseOfferRequest.cs @@ -1,19 +1,17 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Stores; public class PurchaseOfferRequest { [JsonProperty("idempotencyKey")] - public string IdempotencyKey { get; set; } + public string? IdempotencyKey { get; set; } [JsonProperty("offerId")] - public string OfferId { get; set; } + public string? OfferId { get; set; } [JsonProperty("storeSlug")] - public string StoreSlug { get; set; } + public string? StoreSlug { get; set; } } diff --git a/Stores/PurchaseOfferResponse.cs b/Stores/PurchaseOfferResponse.cs index df7a603..6749f1c 100644 --- a/Stores/PurchaseOfferResponse.cs +++ b/Stores/PurchaseOfferResponse.cs @@ -1,19 +1,17 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System.Collections.Generic; namespace RudderSdk.Core.Models.Stores; public class PurchaseOfferResponse { [JsonProperty("error")] - public string Error { get; set; } + public string? Error { get; set; } [JsonProperty("purchaseId")] - public string PurchaseId { get; set; } + public string? PurchaseId { get; set; } [JsonProperty("success")] - public bool Success { get; set; } + public bool? Success { get; set; } } diff --git a/Stores/Store.cs b/Stores/Store.cs index 81583ab..149781e 100644 --- a/Stores/Store.cs +++ b/Stores/Store.cs @@ -1,6 +1,7 @@ // Code generated by apigen. DO NOT EDIT. using Newtonsoft.Json; using Newtonsoft.Json.Linq; +using System; using System.Collections.Generic; namespace RudderSdk.Core.Models.Stores; @@ -8,39 +9,39 @@ namespace RudderSdk.Core.Models.Stores; public class Store { [JsonProperty("createdAt")] - public string CreatedAt { get; set; } + public DateTimeOffset? CreatedAt { get; set; } [JsonProperty("data")] - public JToken Data { get; set; } + public JToken? Data { get; set; } [JsonProperty("description")] - public string Description { get; set; } + public string? Description { get; set; } [JsonProperty("environment")] - public string Environment { get; set; } + public string? Environment { get; set; } [JsonProperty("id")] - public string Id { get; set; } + public string? Id { get; set; } [JsonProperty("name")] - public string Name { get; set; } + public string? Name { get; set; } [JsonProperty("offers")] - public List Offers { get; set; } + public List? Offers { get; set; } [JsonProperty("projectId")] - public string ProjectId { get; set; } + public string? ProjectId { get; set; } [JsonProperty("scenarioId")] - public string ScenarioId { get; set; } + public string? ScenarioId { get; set; } [JsonProperty("slug")] - public string Slug { get; set; } + public string? Slug { get; set; } [JsonProperty("status")] - public string Status { get; set; } + public string? Status { get; set; } [JsonProperty("updatedAt")] - public string UpdatedAt { get; set; } + public DateTimeOffset? UpdatedAt { get; set; } } diff --git a/skills/rudder-csharp-sdk/SKILL.md b/skills/rudder-csharp-sdk/SKILL.md new file mode 100644 index 0000000..f690a76 --- /dev/null +++ b/skills/rudder-csharp-sdk/SKILL.md @@ -0,0 +1,105 @@ +--- +name: rudder-csharp-sdk +description: Use when working with the Rudder C# SDK Rudder.Core (namespace RudderSdk.Core) — client init, transports, auth/session lifecycle, and the per-domain services (player, stores, inventory, battle pass, quests, leaderboards, remote config, scenarios, storage, realtime). +--- + +# Rudder C# SDK (Rudder.Core) + +.NET client SDK for the Rudder LiveOps platform. NuGet package `Rudder.Core`, +namespace `RudderSdk.Core` (DTOs under `RudderSdk.Core.Models.*`). Targets +`netstandard2.1` (works in Unity, .NET, Xamarin). JSON: Newtonsoft.Json +(`JToken`/`JObject` appear in public models). Everything is `async` with an +optional `CancellationToken` as the last parameter. + +## Install + +``` +dotnet add package Rudder.Core +``` + +## Client init + +Only `BaseUrl` and `ProjectKey` are required; the constructor throws +`ArgumentException` when either is missing. Production API: +`https://api.rudder.build`. The project key is issued in the dashboard +(https://app.rudder.build/). + +```csharp +using RudderSdk.Core; + +var client = new RudderClient(new RudderClientOptions +{ + BaseUrl = "https://api.rudder.build", + ProjectKey = "your-project-key" +}); +``` + +Defaults injected when the option is null (see reference/client.md for the +full options surface and the abstraction interfaces): + +- `Transport` → `HttpClientTransport` (10 s request timeout) +- `TokenStore` → `InMemoryTokenStore` (session lost on restart — plug a + durable `ITokenStore` for production) +- `DeviceIdProvider` → `GuidDeviceIdProvider` (new GUID per run — plug a + persistent `IDeviceIdProvider` for production) + +`RealtimeUrl` + `RealtimeTransportFactory` are required only for +`client.Realtime`; there is no default websocket transport in Rudder.Core. + +## Request pipeline (applies to every service) + +- All calls go to `{BaseUrl}/sdk/v1/...` with the stored access token as a + `Bearer` header. +- A 401 triggers one single-flight token refresh + (`POST /sdk/v1/authorization/refresh`) and one transparent retry. If the + refresh fails, tokens are cleared and `Auth.AuthStateChanged` fires + `RudderAuthState.SignedOut` — the game must sign in again. +- Non-success statuses throw `RudderApiException` subclasses; network + failures throw `RudderNetworkException` (`StatusCode == 0`). See + reference/errors.md. +- `client.Update(deltaTime)` must be called every frame — it pumps the + scenario runtime (wait-node deadlines) and the realtime transport. + +## Capability map + +| Domain | Entry point | Reference | +|---|---|---| +| Client, options, transports, abstractions | `RudderClient`, `RudderClientOptions` | reference/client.md | +| Errors, exceptions, error codes | `RudderApiException`, `RudderErrorCodes` | reference/errors.md | +| Auth (device/custom login, refresh, logout) | `client.Auth` | reference/auth.md | +| Player profile + wallets | `client.Player` | reference/player.md | +| Remote config (cached typed reads) | `client.RemoteConfig` | reference/remote-config.md | +| Player key-value storage | `client.Storage` | reference/storage.md | +| Project (global) storage | `client.ProjectStorage` | reference/project-storage.md | +| Stores and offer purchases | `client.Stores` | reference/stores.md | +| Leaderboards | `client.Leaderboards.FindBySlug(slug)` | reference/leaderboards.md | +| Inventory | `client.Inventory` | reference/inventory.md | +| Battle pass | `client.BattlePass` (+ scenario session) | reference/battlepass.md | +| Quests (global) | `client.Quests` | reference/quests.md | +| Scenarios (event-driven plans, node sessions) | `client.Scenario` | reference/scenarios.md | +| Realtime websocket | `client.Realtime` | reference/realtime.md | + +Important cross-cutting facts: + +- Battle pass state is tied to a scenario battle-pass node: every + `BattlePassService` call carries `ScenarioId`/`NodeId` (mutations also + `RunId`). During a scenario run, `BattlePassSession` supplies them — prefer + the session API inside a run. +- Global quests (`client.Quests`) are distinct from scenario quest nodes + (`Scenario.OnQuest` → `QuestSession`). +- Mutations that take an idempotency key auto-generate a random GUID when the + key is omitted (`Stores.PurchaseAsync`, `ProjectStorage.SaveAsync`, + `BattlePass.PurchasePremiumAsync`). Pass a stable key to make retries safe. +- Generated DTOs (`// Code generated by apigen`) use nullable properties and + `JsonProperty` snake_case names; never edit them by hand. + +## Relationship to the Unity package (rudder.sdk) + +`rudder.sdk` (UPM, liveops-unity-sdk) wraps `Rudder.Core.dll` with Unity +adapters: `UnityWebRequest` transport, `PlayerPrefs` token store, a websocket +realtime transport, and a `Rudder` bootstrap component +(`RudderSdk.Unity` namespace). The domain services and models are the same +ones documented here — for Unity games, consume them through the package's +adapters instead of wiring `RudderClientOptions` by hand. Unity requires +`com.unity.nuget.newtonsoft-json` since Rudder.Core serializes with +Newtonsoft.Json. diff --git a/skills/rudder-csharp-sdk/reference/auth.md b/skills/rudder-csharp-sdk/reference/auth.md new file mode 100644 index 0000000..de064f1 --- /dev/null +++ b/skills/rudder-csharp-sdk/reference/auth.md @@ -0,0 +1,58 @@ +# Auth — `client.Auth` (`AuthService`) + +Source: `Services/AuthService.cs`, `RudderClient.cs` (refresh pipeline), +`Auth/*.cs` (generated DTOs), `RudderAuthState.cs`. + +## Methods + +```csharp +public event Action? AuthStateChanged; + +public Task LoginWithDeviceAsync( + string region, string language, string? nickname = null, + CancellationToken cancellationToken = default); + +public Task LoginWithCustomAsync( + JToken customData, string region, string language, string? nickname = null, + CancellationToken cancellationToken = default); + +public Task RefreshAsync(); // false = session could not be renewed (tokens cleared) + +public void Logout(); // drops the stored session, fires SignedOut +``` + +`RudderAuthState`: `SignedIn`, `SignedOut`. The event fires after a +successful login and after logout or a failed token refresh. + +## Endpoints + +- `POST /sdk/v1/authorization/device` — body `{key, deviceId, region, + language, nickname?}`; `key` is the project key, `deviceId` comes from + `IDeviceIdProvider`. +- `POST /sdk/v1/authorization/custom` — body `{key, customData, region, + language, nickname?}`; `customData` is an arbitrary `JToken` forwarded to + the project's custom authorization webhook. +- `POST /sdk/v1/authorization/refresh` — body `{refreshToken}`; used by the + automatic pipeline and `RefreshAsync`. + +## Response models (both logins) + +```csharp +public class LoginViaDeviceResponse / LoginViaCustomResponse +{ + public string? AccessToken { get; set; } // "accessToken" + public string? RefreshToken { get; set; } // "refreshToken" +} +``` + +## Behavior notes + +- A successful login stores the token pair in the configured `ITokenStore` + and fires `SignedIn`. +- If the server returns an incomplete session (missing tokens), login throws + `InvalidOperationException`. +- Token refresh is automatic: any API call that gets a 401 triggers one + single-flight refresh (concurrent 401s share one request) and one + transparent retry. A failed refresh clears the tokens and fires + `SignedOut`; the original call then throws `RudderAuthException`. +- `RefreshAsync()` shares the same single-flight path. diff --git a/skills/rudder-csharp-sdk/reference/battlepass.md b/skills/rudder-csharp-sdk/reference/battlepass.md new file mode 100644 index 0000000..0f48579 --- /dev/null +++ b/skills/rudder-csharp-sdk/reference/battlepass.md @@ -0,0 +1,147 @@ +# Battle pass — `client.BattlePass` (`BattlePassService`) + +Source: `Services/BattlePassService.cs`, +`Services/Scenarios/Sessions/BattlePassSession.cs`, +`Services/Scenarios/Sessions/BattlePassLevelSession.cs`, `BattlePass/*.cs` +(generated DTOs). + +Battle pass state is tied to a scenario battle-pass node, so every call +carries `ScenarioId`/`NodeId` (mutations also `RunId`). Inside a scenario run, +`Scenario.OnBattlePass` hands you a `BattlePassSession` that supplies these +ids — prefer it over calling the service directly. + +## BattlePassService + +```csharp +public const string TrackFree = "free"; +public const string TrackPremium = "premium"; + +// POST /sdk/v1/battlepass/progress +public Task GetProgressAsync( + string scenarioId, string nodeId, CancellationToken cancellationToken = default); + +// POST /sdk/v1/battlepass/xp +public Task AddXpAsync( + AddBattlePassXpRequest request, CancellationToken cancellationToken = default); + +// POST /sdk/v1/battlepass/claim — idempotent server-side +public Task ClaimRewardAsync( + ClaimBattlePassRewardRequest request, CancellationToken cancellationToken = default); + +// POST /sdk/v1/battlepass/premium — charges the wallet; a random +// IdempotencyKey is generated when request.IdempotencyKey is null +public Task PurchasePremiumAsync( + PurchaseBattlePassPremiumRequest request, CancellationToken cancellationToken = default); +``` + +## Request DTOs (`RudderSdk.Core.Models.BattlePass`) + +```csharp +public class GetBattlePassProgressRequest +{ + public string? NodeId { get; set; } // "nodeId" + public string? ScenarioId { get; set; } // "scenarioId" +} + +public class AddBattlePassXpRequest +{ + public long? Amount { get; set; } // "amount" + public string? NodeId { get; set; } // "nodeId" + public string? RunId { get; set; } // "runId" + public string? ScenarioId { get; set; } // "scenarioId" + public string? Source { get; set; } // "source" +} + +public class ClaimBattlePassRewardRequest +{ + public int? Level { get; set; } // "level" + public string? NodeId { get; set; } // "nodeId" + public string? RunId { get; set; } // "runId" + public string? ScenarioId { get; set; } // "scenarioId" + public string? Track { get; set; } // "track" — TrackFree / TrackPremium +} + +public class PurchaseBattlePassPremiumRequest +{ + public string? IdempotencyKey { get; set; } // "idempotencyKey" + public string? NodeId { get; set; } // "nodeId" + public string? RunId { get; set; } // "runId" + public string? ScenarioId { get; set; } // "scenarioId" +} +``` + +## Response DTOs + +```csharp +public class GetBattlePassProgressResponse +{ + public List? ClaimedTiers { get; set; } // "claimedTiers" + public int? Level { get; set; } // "level" + public bool? PremiumOwned { get; set; } // "premiumOwned" + public long? Xp { get; set; } // "xp" +} + +public class ClaimedTier { public int? Level { get; set; } public string? Track { get; set; } } + +public class AddBattlePassXpResponse +{ + public int? Level { get; set; } // "level" + public bool? LeveledUp { get; set; } // "leveledUp" + public bool? MaxLevel { get; set; } // "maxLevel" + public ExecutionPlan? Plan { get; set; } // "plan" — scenario continuation + public long? Xp { get; set; } // "xp" +} + +public class ClaimBattlePassRewardResponse +{ + public bool? AlreadyClaimed { get; set; } // "alreadyClaimed" + public string? Error { get; set; } // "error" + public List? Granted { get; set; } // "granted" + public bool? Success { get; set; } // "success" +} + +public class PurchaseBattlePassPremiumResponse +{ + public string? Error { get; set; } // "error" + public ExecutionPlan? Plan { get; set; } // "plan" + public bool? Success { get; set; } // "success" +} +``` + +`Reward` (`RudderSdk.Core.Models`): `Amount` (long?), `Currency` (string?), +`ItemId` (string?). Responses carry business errors in `Error` — check +`Success`. Claim failures surface codes such as `RudderErrorCodes.LevelNotReached`. + +## BattlePassSession (scenario node session) + +Raised via `Scenario.OnBattlePass`. Bound to the node's scenario/node/run ids: + +```csharp +public ScenarioNodeContext Context { get; } +public string Id { get; } // node id +public T Get(string key, T defaultValue = default!); + +public Task GetProgressAsync(CancellationToken ct = default); +public Task AddXpAsync(string source, long amount, CancellationToken ct = default); +public Task ClaimRewardAsync(int level, string track, CancellationToken ct = default); + +// Purchases premium, then crosses the onPremiumPurchase handle on success. +public Task PurchasePremiumAsync(CancellationToken ct = default); + +// Boundary crossings (async + fire-and-forget variants): +public Task LevelUpAsync(CancellationToken ct = default); public void LevelUp(); // onLevelUp +public Task MaxLevelAsync(CancellationToken ct = default); public void MaxLevel(); // onMaxLevel +public Task CompleteAsync(CancellationToken ct = default); public void Complete(); // onComplete +``` + +## BattlePassLevelSession (scenario battlepass_level node) + +Raised via `Scenario.OnBattlePassLevel`; a single claimable tier: + +```csharp +public int Level { get; } // reads the "levelNumber" node data key +public Task ClaimAsync(CancellationToken ct = default); public void Claim(); +``` + +`ClaimAsync` crosses `onComplete`; the server accepts it only once the player +has reached the node's configured level. diff --git a/skills/rudder-csharp-sdk/reference/client.md b/skills/rudder-csharp-sdk/reference/client.md new file mode 100644 index 0000000..9b75da5 --- /dev/null +++ b/skills/rudder-csharp-sdk/reference/client.md @@ -0,0 +1,117 @@ +# Client, options, transports, abstractions + +Source: `RudderClient.cs`, `RudderClientOptions.cs`, `HttpClientTransport.cs`, +`InMemoryTokenStore.cs`, `GuidDeviceIdProvider.cs`, `Abstractions/*.cs`. + +## RudderClient + +```csharp +public sealed class RudderClient +{ + public AuthService Auth { get; } + public PlayerService Player { get; } + public RemoteConfigService RemoteConfig { get; } + public StorageService Storage { get; } + public ProjectStorageService ProjectStorage { get; } + public StoresService Stores { get; } + public LeaderboardsService Leaderboards { get; } + public InventoryService Inventory { get; } + public BattlePassService BattlePass { get; } + public QuestsService Quests { get; } + public ScenarioService Scenario { get; } + public RealtimeService Realtime { get; } + + public string ProjectKey { get; } + public string? RealtimeUrl { get; } + public IClock Clock { get; } // defaults to DateTimeOffset.UtcNow + + public RudderClient(RudderClientOptions options); + public void Update(float deltaTime); // pumps Scenario + Realtime; call every frame +} +``` + +Constructor throws `ArgumentException` when `BaseUrl` or `ProjectKey` is +missing. Every API call goes through the client: a 401 triggers one +single-flight refresh and one transparent retry (see errors.md). + +## RudderClientOptions + +```csharp +public sealed class RudderClientOptions +{ + public string? BaseUrl { get; set; } // required + public string? RealtimeUrl { get; set; } // required only for Realtime + public string? ProjectKey { get; set; } // required + public IRudderTransport? Transport { get; set; } // default HttpClientTransport + public ITokenStore? TokenStore { get; set; } // default InMemoryTokenStore + public IDeviceIdProvider? DeviceIdProvider { get; set; }// default GuidDeviceIdProvider + public IRudderLogger? Logger { get; set; } // null = silent + public IClock? Clock { get; set; } // override in tests + public IPlanStateStore? PlanStateStore { get; set; } // scenario persistence between launches + public IPlanScheduler? Scheduler { get; set; } // optional delayed plan work + public IRealtimeTransportFactory? RealtimeTransportFactory { get; set; } // required only for Realtime +} +``` + +## Default implementations + +- `HttpClientTransport(string baseUrl, HttpClient? httpClient = null)` — + `IRudderTransport` over `HttpClient`, Newtonsoft.Json bodies, Bearer header, + 10 s default timeout. Maps 401/404/429 to dedicated exception subclasses, + other non-success to `RudderApiException`, connectivity/timeout to + `RudderNetworkException`. +- `InMemoryTokenStore` — tokens in memory only; sessions do not survive an + app restart. +- `GuidDeviceIdProvider` — `Guid.NewGuid().ToString("N")` per instance; every + run looks like a fresh device to the backend. + +## Abstractions (namespace `RudderSdk.Core.Abstractions`) + +```csharp +public interface IRudderTransport +{ + Task SendAsync( + string method, string path, TRequest? request, + string? accessToken, CancellationToken cancellationToken = default); +} + +public interface ITokenStore +{ + string? GetAccessToken(); + string? GetRefreshToken(); + void SaveTokens(string accessToken, string refreshToken); + void Clear(); +} + +public interface IDeviceIdProvider { string DeviceId { get; } } + +public interface IRudderLogger { void Log(RudderLogLevel level, string message); } +public enum RudderLogLevel { Debug, Info, Warning, Error } + +public interface IClock { DateTimeOffset UtcNow { get; } } + +public interface IPlanStateStore { string? State { get; set; } } // serialized scenario-run blob + +public interface IPlanScheduler +{ + Task ScheduleAsync(TimeSpan delay, CancellationToken cancellationToken = default); +} + +public interface IRealtimeTransport +{ + event Action Closed; + event Action> Received; + event Action Error; + bool IsConnected { get; } + Task ConnectAsync(Uri uri, TimeSpan timeout, CancellationToken cancellationToken = default); + Task SendAsync(ArraySegment data, CancellationToken cancellationToken = default); + Task CloseAsync(CancellationToken cancellationToken = default); + void Update(float deltaTime); +} + +public interface IRealtimeTransportFactory { IRealtimeTransport Create(); } +``` + +Production guidance from the sources: provide a durable `ITokenStore` +(player prefs, keychain) and a persistent `IDeviceIdProvider` (Unity uses +`SystemInfo.deviceUniqueIdentifier`). diff --git a/skills/rudder-csharp-sdk/reference/errors.md b/skills/rudder-csharp-sdk/reference/errors.md new file mode 100644 index 0000000..0368dd3 --- /dev/null +++ b/skills/rudder-csharp-sdk/reference/errors.md @@ -0,0 +1,69 @@ +# Errors, exceptions, error codes + +Source: `Exceptions/*.cs`, `Models/RudderErrorCodes.cs`, +`Models/ErrorResponse.cs`, `HttpClientTransport.cs` (status mapping), +`Services/Scenarios/ScenarioService.Runtime.cs` (`TransientBoundaryException`). + +## Exception hierarchy + +All API failures derive from `RudderApiException`: + +```csharp +public class RudderApiException : Exception +{ + public int StatusCode { get; } // 0 when the request never reached the server + public string Code { get; } // machine-readable API error code, "" when absent + public string? RequestId { get; } // server-issued request id for support tickets +} +``` + +| Exception | When | +|---|---| +| `RudderAuthException` | HTTP 401 after the automatic refresh+retry also failed — session is over, sign in again | +| `RudderNotFoundException` | HTTP 404 | +| `RudderRateLimitException` | HTTP 429 — back off and retry later | +| `RudderApiException` (base) | any other non-success status | +| `RudderNetworkException` | DNS/connectivity failure or client-side timeout; `StatusCode == 0`; retrying is safe for idempotent operations | + +The transport parses the error body as `ErrorResponse`: + +```csharp +public class ErrorResponse // RudderSdk.Core.Models +{ + public string? Code { get; set; } // "code" + public string? Error { get; set; } // "error" — human-readable message + public int? Index { get; set; } // "index" + public string? PlayerId { get; set; } // "playerId" + public string? RequestId { get; set; } // "requestId" +} +``` + +Non-JSON error bodies fall back to the HTTP reason phrase. + +## Error code constants + +`RudderSdk.Core.Models.RudderErrorCodes` (generated; scenario/battle-pass/quest +domain codes): + +```csharp +EarlyCompletion = "early_completion" +Forbidden = "forbidden" +LevelNotReached = "level_not_reached" +NodeNotActive = "node_not_active" +ObjectivesIncomplete = "objectives_incomplete" +RunExpired = "run_expired" +RunNotActive = "run_not_active" +ScenarioNotActive = "scenario_not_active" +UnknownRun = "unknown_run" +``` + +Match against `RudderApiException.Code`. + +## Scenario boundary errors + +`TransientBoundaryException` (public sealed, wraps the transport error) is +used inside the scenario runtime for transient boundary-callback failures +(timeout / `RudderNetworkException`): the run is NOT advanced, the node stays +active and the handle stays pending for retry. A failed scenario counter +update never fails the run — it is logged via `IRudderLogger` and the node +stays active. diff --git a/skills/rudder-csharp-sdk/reference/inventory.md b/skills/rudder-csharp-sdk/reference/inventory.md new file mode 100644 index 0000000..83ab95e --- /dev/null +++ b/skills/rudder-csharp-sdk/reference/inventory.md @@ -0,0 +1,31 @@ +# Inventory — `client.Inventory` (`InventoryService`) + +Source: `Services/InventoryService.cs`, `Inventory/*.cs` (generated DTOs). + +## Methods + +```csharp +// GET /sdk/v1/inventory — lists the items the player owns +public Task> GetAsync(CancellationToken cancellationToken = default); +``` + +## Models (`RudderSdk.Core.Models.Inventory`) + +```csharp +public class PlayerInventoryItem +{ + public long? Amount { get; set; } // "amount" + public string? NameOverride { get; set; } // "nameOverride" + public JToken? PropertiesOverride { get; set; } // "propertiesOverride" + public string? Slug { get; set; } // "slug" + public DateTimeOffset? UpdatedAt { get; set; } // "updatedAt" +} + +public class GetInventoryResponse +{ + public List? Items { get; set; } // "items" +} +``` + +The inventory is read-only from the client; amounts change through store +purchases and scenario/quest/battle-pass rewards. diff --git a/skills/rudder-csharp-sdk/reference/leaderboards.md b/skills/rudder-csharp-sdk/reference/leaderboards.md new file mode 100644 index 0000000..c08fd5f --- /dev/null +++ b/skills/rudder-csharp-sdk/reference/leaderboards.md @@ -0,0 +1,60 @@ +# Leaderboards — `client.Leaderboards` (`LeaderboardsService` + `LeaderboardHandle`) + +Source: `Services/LeaderboardsService.cs`, `Leaderboards/*.cs` (generated +DTOs). + +## Usage pattern + +Resolve a (cached) handle by slug, then work through the handle: + +```csharp +var board = client.Leaderboards.FindBySlug("weekly-race"); +await board.SubmitAsync(1250.5); +var entries = await board.ListAsync(limit: 50); +``` + +## Signatures + +```csharp +public sealed class LeaderboardsService +{ + public LeaderboardHandle FindBySlug(string slug); // cached per slug +} + +public sealed class LeaderboardHandle +{ + public string Slug { get; } + public IReadOnlyList Entries { get; } // entries from the last ListAsync call + + // POST /sdk/v1/leaderboards/{slug}/submit-score — submits for the current player + public Task SubmitAsync(double score, CancellationToken cancellationToken = default); + + // GET /sdk/v1/leaderboards/{slug}/ranking?limit={limit} — also caches into Entries + public Task> ListAsync(int limit = 100, CancellationToken cancellationToken = default); +} +``` + +`limit <= 0` is omitted from the query (server default applies). + +## Models (`RudderSdk.Core.Models.Leaderboards`) + +```csharp +public class RankEntry +{ + public string? PlayerId { get; set; } // "playerId" + public string? PlayerName { get; set; } // "playerName" + public long? Rank { get; set; } // "rank" + public double? Score { get; set; } // "score" +} + +public class GetRankingResponse +{ + public List? Entries { get; set; } // "entries" + public long? Total { get; set; } // "total" +} +``` + +Note: the working tree contains an untracked `Services/LeaderboardHandle.cs` +with a stale, slightly different `LeaderboardHandle` (no `Entries` cache). The +committed definition in `Services/LeaderboardsService.cs` documented above is +authoritative. diff --git a/skills/rudder-csharp-sdk/reference/player.md b/skills/rudder-csharp-sdk/reference/player.md new file mode 100644 index 0000000..a91f164 --- /dev/null +++ b/skills/rudder-csharp-sdk/reference/player.md @@ -0,0 +1,40 @@ +# Player — `client.Player` (`PlayerService`) + +Source: `Services/PlayerService.cs`, `Player/*.cs` (generated DTOs). + +## Methods + +```csharp +public Task GetProfileAsync(CancellationToken cancellationToken = default); +``` + +`GET /sdk/v1/player/information` — returns the player profile with wallets. + +## Models (`RudderSdk.Core.Models.Player`) + +```csharp +public class PlayerProfile +{ + public Player? Player { get; set; } // "player" + public List? Wallets { get; set; } // "wallets" +} + +public class Player +{ + public DateTimeOffset? CreatedAt { get; set; } // "createdAt" + public string? Id { get; set; } // "id" + public string? Language { get; set; } // "language" + public string? Nickname { get; set; } // "nickname" + public string? ProjectId { get; set; } // "projectId" + public string? Region { get; set; } // "region" +} + +public class Wallet +{ + public long? Balance { get; set; } // "balance" + public string? Currency { get; set; } // "currency" +} +``` + +Wallet balances change through store purchases and scenario/quest/battle-pass +rewards; there is no client-side wallet mutation API. diff --git a/skills/rudder-csharp-sdk/reference/project-storage.md b/skills/rudder-csharp-sdk/reference/project-storage.md new file mode 100644 index 0000000..ceb4258 --- /dev/null +++ b/skills/rudder-csharp-sdk/reference/project-storage.md @@ -0,0 +1,59 @@ +# Project storage — `client.ProjectStorage` (`ProjectStorageService`) + +Source: `Services/ProjectStorageService.cs`, `ProjectStorage/*.cs` (generated +DTOs). + +Global key-value storage shared across all players of the project. Read is +open to every signed-in player; server-side permissions +(`readPermission`/`writePermission`) govern access. + +## Methods + +```csharp +// GET /sdk/v1/project-storage?types={type}&limit={limit}&cursor={cursor} +public Task GetAsync( + string? type = null, int limit = 100, string? cursor = null, + CancellationToken cancellationToken = default); + +// Follows cursor pagination over all items. +public IAsyncEnumerable ListAllAsync( + string? type = null, int limit = 100, + CancellationToken cancellationToken = default); + +// PUT /sdk/v1/project-storage — upserts items. When idempotencyKey is null a +// random GUID is generated; pass a stable key to make retries safe. +public Task SaveAsync( + IEnumerable items, string? idempotencyKey = null, + CancellationToken cancellationToken = default); +``` + +There is no client-side delete for project storage. + +## Models (`RudderSdk.Core.Models.ProjectStorage`) + +```csharp +public class ProjectStorageItem +{ + public string? Data { get; set; } // "data" + public DateTimeOffset? ExpiresAt { get; set; } // "expiresAt" + public string? Id { get; set; } // "id" + public string? ReadPermission { get; set; } // "readPermission" + public long? Size { get; set; } // "size" + public string? Type { get; set; } // "type" + public DateTimeOffset? UpdatedAt { get; set; } // "updatedAt" + public long? Version { get; set; } // "version" + public string? WritePermission { get; set; } // "writePermission" +} + +public class ProjectStorageUpdateItem // write shape: only type + data +{ + public string? Data { get; set; } // "data" + public string? Type { get; set; } // "type" +} + +public class GetProjectStorageResponse +{ + public List? Items { get; set; } // "items" + public string? NextCursor { get; set; } // "nextCursor" +} +``` diff --git a/skills/rudder-csharp-sdk/reference/quests.md b/skills/rudder-csharp-sdk/reference/quests.md new file mode 100644 index 0000000..9e5d49b --- /dev/null +++ b/skills/rudder-csharp-sdk/reference/quests.md @@ -0,0 +1,71 @@ +# Quests — `client.Quests` (`QuestsService`) + +Source: `Services/QuestsService.cs`, `Services/QuestMetrics.cs`, +`Quests/*.cs` (generated DTOs). + +Global quests (list + claim + metric reports). Distinct from scenario quest +nodes, which advance through `QuestSession` (`Scenario.OnQuest`) — see +scenarios.md. + +## Methods + +```csharp +// POST /sdk/v1/quests/list +public Task> ListAsync(CancellationToken cancellationToken = default); + +// POST /sdk/v1/quests/claim — claims a completed quest's rewards (idempotent server-side) +public Task ClaimAsync(string questId, CancellationToken cancellationToken = default); + +// POST /sdk/v1/quests/progress — reports progress for a metric; returns the +// ids of quests completed by THIS report +public Task> ReportProgressAsync( + string metric, long amount, CancellationToken cancellationToken = default); +``` + +## Metric strings — `QuestMetrics` + +```csharp +public static class QuestMetrics +{ + public static string PurchaseOffer(string offerId); // "purchase.offer:{offerId}" + public static string PurchaseItem(string itemId); // "purchase.item:{itemId}" +} +``` + +Purchase metrics are auto-reported by the shop purchase fan-out; any other +metric is a custom string passed to `ReportProgressAsync`. + +## Models (`RudderSdk.Core.Models.Quests`) + +```csharp +public class Quest +{ + public string? Id { get; set; } // "id" + public string? Name { get; set; } // "name" + public List? Objectives { get; set; }// "objectives" + public List? Rewards { get; set; } // "rewards" + public string? Status { get; set; } // "status" +} + +public class QuestObjectiveProgress +{ + public bool? Completed { get; set; } // "completed" + public long? Current { get; set; } // "current" + public string? Metric { get; set; } // "metric" + public string? ObjectiveId { get; set; } // "objectiveId" + public long? Target { get; set; } // "target" +} + +public class ClaimQuestResponse +{ + public bool? AlreadyClaimed { get; set; } // "alreadyClaimed" + public string? Error { get; set; } // "error" + public List? Granted { get; set; } // "granted" + public bool? Success { get; set; } // "success" +} +``` + +`Reward` (`RudderSdk.Core.Models`): `Amount` (long?), `Currency` (string?), +`ItemId` (string?). Claim a quest when its status reads `"completed"`; failed +claims surface `Error` and codes such as +`RudderErrorCodes.ObjectivesIncomplete` / `EarlyCompletion`. diff --git a/skills/rudder-csharp-sdk/reference/realtime.md b/skills/rudder-csharp-sdk/reference/realtime.md new file mode 100644 index 0000000..f8e5521 --- /dev/null +++ b/skills/rudder-csharp-sdk/reference/realtime.md @@ -0,0 +1,51 @@ +# Realtime — `client.Realtime` (`RealtimeService` + `RealtimeSession`) + +Source: `Services/RealtimeService.cs`, `Abstractions/IRealtimeTransport.cs`, +`Abstractions/IRealtimeTransportFactory.cs`. + +Websocket channel. Requires `RudderClientOptions.RealtimeUrl` and +`RudderClientOptions.RealtimeTransportFactory` — Rudder.Core ships no default +websocket transport (the Unity package `rudder.sdk` provides one). The current +access token authorizes the connection, so sign in first. + +## RealtimeService + +```csharp +public RealtimeSession? Session { get; } // null when not connected +public bool IsConnected { get; } + +// Connects to the configured RealtimeUrl. Throws InvalidOperationException +// when RealtimeUrl is not configured. +public Task ConnectAsync(TimeSpan? timeout = null, CancellationToken cancellationToken = default); + +// Connects to an explicit URL. Throws InvalidOperationException when +// RealtimeTransportFactory is missing or no access token is stored. +public Task ConnectAsync(Uri uri, TimeSpan? timeout = null, CancellationToken cancellationToken = default); + +public Task DisconnectAsync(CancellationToken cancellationToken = default); + +public void Update(float deltaTime); // pumps the transport; call every frame (client.Update does this) +``` + +Default connect timeout: 10 seconds. + +## RealtimeSession + +```csharp +public string AccessToken { get; } // token the connection was authorized with +public bool IsConnected { get; } + +public event Action? Closed; +public event Action? Error; +public event Action>? MessageReceived; + +public Task SendAsync(byte[] payload, CancellationToken cancellationToken = default); +public Task DisconnectAsync(CancellationToken cancellationToken = default); +``` + +There is no message envelope or channel abstraction — payloads are raw bytes; +define the message shape with the server side of your project. + +Note: the working tree contains an untracked `Services/RealtimeSession.cs` +with a stale `RealtimeSession` (no `AccessToken`). The committed definition in +`Services/RealtimeService.cs` documented above is authoritative. diff --git a/skills/rudder-csharp-sdk/reference/remote-config.md b/skills/rudder-csharp-sdk/reference/remote-config.md new file mode 100644 index 0000000..255a900 --- /dev/null +++ b/skills/rudder-csharp-sdk/reference/remote-config.md @@ -0,0 +1,52 @@ +# Remote config — `client.RemoteConfig` (`RemoteConfigService`) + +Source: `Services/RemoteConfigService.cs`, `RemoteConfig/*.cs` (generated DTOs). + +## Methods and properties + +```csharp +public bool IsLoaded { get; } // true after first successful LoadAsync +public IReadOnlyDictionary Configs { get; } // cache, keyed by config key + +public Task> LoadAsync(CancellationToken cancellationToken = default); +public Task GetConfigAsync(string key, CancellationToken cancellationToken = default); +public T Get(string key, T defaultValue = default!); +public Task GetAsync(string key, T defaultValue = default!, CancellationToken cancellationToken = default); +``` + +Endpoints: `GET /sdk/v1/remote-configs` (list), `GET /sdk/v1/remote-configs/{key}`. + +## Behavior notes + +- `LoadAsync` fetches all configs and rebuilds the cache. A config whose raw + JSON has `"active": false` is excluded; a missing `active` flag means active. +- `Get` reads from the cache only and returns `defaultValue` when the key + is missing or the value cannot be parsed (it never throws on parse errors). +- `GetAsync` loads the cache on first use, then reads from it. +- `GetConfigAsync(key)` fetches one config straight from the server, + bypassing the cache. +- Typed parsing honors the config's `valueType`: `number`/`int`/`integer`/ + `float`/`double` (invariant culture), `bool`/`boolean`, `json`/`object` + (deserialized via Newtonsoft.Json); anything else goes through + `Convert.ChangeType`. `string` values are returned as-is. + +## Model (`RudderSdk.Core.Models.RemoteConfig`) + +```csharp +public class RemoteConfig +{ + public bool? Active { get; set; } // "active" + public DateTimeOffset? CreatedAt { get; set; } // "createdAt" + public string? Description { get; set; } // "description" + public string? Environment { get; set; } // "environment" + public string? Id { get; set; } // "id" + public string? Key { get; set; } // "key" + public string? ProjectId { get; set; } // "projectId" + public DateTimeOffset? UpdatedAt { get; set; } // "updatedAt" + public string? Value { get; set; } // "value" + public string? ValueType { get; set; } // "valueType" +} +``` + +Scenario `remote_config_override` nodes apply patches directly into this +cache before `Scenario.OnConfigChanged` fires (see scenarios.md). diff --git a/skills/rudder-csharp-sdk/reference/scenarios.md b/skills/rudder-csharp-sdk/reference/scenarios.md new file mode 100644 index 0000000..0046cd0 --- /dev/null +++ b/skills/rudder-csharp-sdk/reference/scenarios.md @@ -0,0 +1,149 @@ +# Scenarios — `client.Scenario` (`ScenarioService`) + +Source: `Services/ScenarioService.cs`, `Services/Scenarios/ScenarioService.{Dispatch,NodeTypes,Persistence,Runtime}.cs`, +`Services/Scenarios/Models/*.cs`, `Services/Scenarios/Sessions/*.cs`, +`Scenarios/*.cs` (generated DTOs), `Models/*.cs`. + +Scenario runtime for server-issued execution plans. `TriggerAsync(eventName)` +starts plans; active nodes surface as typed sessions through the `On*` events +and are advanced by completing those sessions (which crosses server-validated +boundaries). Requires `Update(deltaTime)` every frame for wait-node deadlines. + +## ScenarioService surface + +```csharp +public sealed partial class ScenarioService +{ + // Node events + public event Action? OnNotification; + public event Action? OnStoreOffer; + public event Action? OnLeaderboard; + public event Action? OnConfigChanged; // patches already applied + public event Action? OnWait; + public event Action? OnQuest; + public event Action? OnBattlePass; + public event Action? OnBattlePassLevel; + + // Run lifecycle events + public event Action? OnScenarioCompleted; // run finished all nodes + public event Action? OnScenarioFailed; // run died on unrecoverable error + + public bool IsRunning { get; } // at least one run active + public string? CurrentNodeId { get; } // first active node id across runs + public IReadOnlyList ActiveRuns { get; } // snapshots + + // POST /sdk/v1/scenarios/trigger — body {event}; returns the runs this call started + public Task> TriggerAsync(string eventName, CancellationToken ct = default); + + // Restores persisted runs (needs RudderClientOptions.PlanStateStore), + // reconciles with the server, re-dispatches active nodes. Call once after login. + public Task RestoreAsync(CancellationToken ct = default); + + public void Clear(); // drops all runs and the persisted state + + public void Update(float deltaTime); // completes wait nodes past their deadline + + // Completes the FIRST active node of the FIRST run with the given handle. + public Task RespondAsync(string handle, CancellationToken ct = default); + public void Respond(string handle); // fire-and-forget + + // Adds progress to a counter of the FIRST active node of the FIRST run. + public Task UpdateProgressAsync(string counterKey, long amount, CancellationToken ct = default); +} +``` + +Prefer session methods over `RespondAsync`/`UpdateProgressAsync` — the +parameterless variants only address the first active node of the first run. + +## Node types and session handles + +| Node `type` | Event | Session completion handles | +|---|---|---| +| `notification` | `OnNotification` | `output` (`CompleteAsync`/`Complete`) | +| `store` | `OnStoreOffer` | `onPurchase`, `onDecline` (`Purchase*`/`Decline*`, one-shot via `IsResolved`) | +| `wait` | `OnWait` | auto-completes `onComplete` at `WaitSession.DeadlineUtc` | +| `remote_config_override` | `OnConfigChanged` | auto-completes `output` after applying patches to `RemoteConfig` | +| `quest` | `OnQuest` | `onComplete`, `onFail`; `AddProgressAsync(counterKey, amount)` feeds counters | +| `leaderboard` | `OnLeaderboard` | `onEnd` (`End*`), `onClaim` (`Claim*` / obsolete `RewardClaimed*`) | +| `battlepass` | `OnBattlePass` | `onLevelUp`, `onMaxLevel`, `onComplete`, `onPremiumPurchase` (see battlepass.md) | +| `battlepass_level` | `OnBattlePassLevel` | `onComplete` via `ClaimAsync` — server checks the configured level | + +An unsupported node type fails the run (`OnScenarioFailed`) instead of +stalling. All sessions expose `Context` (`ScenarioNodeContext`), `Id` (node +id) and `Get(key, defaultValue)` for reading node data. + +## ScenarioNodeContext + +```csharp +public PlanRun Run { get; } +public ExecutionPlanNode Node { get; } +public string RunId / PlanId / ScenarioId / NodeId / Type { get; } +public JObject Data { get; } +public T Get(string key, T defaultValue = default!); +public T Get(); // whole payload +public Dictionary AsObjectDictionary(); +``` + +## PlanRun and failure event + +```csharp +public sealed class PlanRun +{ + public string RunId { get; } + public string PlanId { get; } + public string ScenarioId { get; } + public string UserId { get; } + public IReadOnlyList ActiveNodeIds { get; } + public ExecutionPlan Plan { get; } +} + +public sealed class ScenarioFailedEvent +{ + public PlanRun Run { get; } + public string NodeId { get; } + public Exception Exception { get; } +} +``` + +## ExecutionPlan (generated, `RudderSdk.Core.Models`) + +```csharp +public class ExecutionPlan +{ + public List? BoundaryNodes { get; set; } + public JToken? Context { get; set; } + public List? Edges { get; set; } + public List? Nodes { get; set; } + public string? PlanId { get; set; } + public string? RunId { get; set; } + public string? ScenarioId { get; set; } + public string? StartNodeId { get; set; } + public string? UserId { get; set; } +} +``` + +`ExecutionPlanNode`: `Id`, `Type`, `Data` (`JToken`). `PlanEdge`: `Id`, +`Source`, `SourceHandle`, `Target`, `TargetHandle`. `BoundaryNode`: +`CallbackUrl`, `Enforcement`, `EnteredAt`, `NodeId`, `SourceHandle`, +`SourceNodeId`, `WaitDeadline`. + +## Runtime semantics + +- Boundaries: crossing a handle that has a server boundary calls + `POST /sdk/v1/scenarios/callback` first; the returned continuation plan + replaces/starts the run before the handle is marked complete. On failure the + runtime reconciles via `POST /sdk/v1/scenarios/run`; `unknown_run` / + `expired` statuses drop the run. Transient failures + (`TransientBoundaryException`: timeouts, `RudderNetworkException`) leave the + node active for retry; terminal errors fail the run. `rank_not_eligible` on + leaderboard Claim leaves the node active (retry Claim or End). +- Counter updates (`POST /sdk/v1/scenarios/counter`) never fail the run; when + the server reports the objective completed, the node crosses `onComplete` + automatically. +- Dedup/idempotency: a plan with an already-running `RunId` is not restarted; + completed (node, handle) pairs are not re-crossed. +- Wait deadlines prefer the server-stamped `BoundaryNode.WaitDeadline` over + locally computed `duration`+`unit` node data (`days`/`hours`/`minutes`/ + `seconds` and abbreviations). +- Persistence: with `IPlanStateStore` configured, every state change is + serialized into `State`; call `RestoreAsync` once after startup/login. diff --git a/skills/rudder-csharp-sdk/reference/storage.md b/skills/rudder-csharp-sdk/reference/storage.md new file mode 100644 index 0000000..3b8b260 --- /dev/null +++ b/skills/rudder-csharp-sdk/reference/storage.md @@ -0,0 +1,53 @@ +# Player storage — `client.Storage` (`StorageService`) + +Source: `Services/StorageService.cs`, `Storage/*.cs` (generated DTOs). + +Player-scoped key-value storage. Items carry a `type` (the "key" dimension) +and a string `data` payload. + +## Methods + +```csharp +// GET /sdk/v1/storage?types={type}&limit={limit}&cursor={cursor} +public Task GetAsync( + string? type = null, int limit = 100, string? cursor = null, + CancellationToken cancellationToken = default); + +// Follows cursor pagination over all items. +public IAsyncEnumerable ListAllAsync( + string? type = null, int limit = 100, + CancellationToken cancellationToken = default); + +// PUT /sdk/v1/storage — upserts items. NOTE: the generated request DTO has an +// IdempotencyKey field, but SaveAsync does not set it (sent as null). +public Task SaveAsync(IEnumerable items, CancellationToken cancellationToken = default); + +// DELETE /sdk/v1/storage?type={type} — deletes ALL items of the given type. +public Task DeleteAsync(string type, CancellationToken cancellationToken = default); +``` + +## Models (`RudderSdk.Core.Models.Storage`) + +```csharp +public class StorageItem +{ + public string? Data { get; set; } // "data" + public string? Id { get; set; } // "id" + public string? Type { get; set; } // "type" +} + +public class GetStorageResponse +{ + public List? Items { get; set; } // "items" + public string? NextCursor { get; set; } // "nextCursor" +} + +public class UpdateStorageRequest +{ + public string? IdempotencyKey { get; set; } // "idempotencyKey" + public List? Items { get; set; } // "items" +} +``` + +`limit <= 0` is omitted from the query (server default applies). `data` is an +opaque string — serialize JSON into it yourself. diff --git a/skills/rudder-csharp-sdk/reference/stores.md b/skills/rudder-csharp-sdk/reference/stores.md new file mode 100644 index 0000000..7f175b1 --- /dev/null +++ b/skills/rudder-csharp-sdk/reference/stores.md @@ -0,0 +1,69 @@ +# Stores — `client.Stores` (`StoresService`) + +Source: `Services/StoresService.cs`, `Stores/*.cs` (generated DTOs). + +In-game stores with offers priced in wallet currencies. Purchasing charges the +player's wallet and grants the offer contents. + +## Methods + +```csharp +// GET /sdk/v1/stores +public Task> ListAsync(CancellationToken cancellationToken = default); + +// GET /sdk/v1/stores/{slug} +public Task GetAsync(string slug, CancellationToken cancellationToken = default); + +// POST /sdk/v1/stores/{storeSlug}/offers/{offerId}/purchase +// When idempotencyKey is null a random GUID is generated; pass a stable key to +// make retries safe. +public Task PurchaseAsync( + string storeSlug, string offerId, string? idempotencyKey = null, + CancellationToken cancellationToken = default); +``` + +## Models (`RudderSdk.Core.Models.Stores`) + +```csharp +public class Store +{ + public string? Id { get; set; } // "id" + public string? Slug { get; set; } // "slug" + public string? Name { get; set; } // "name" + public string? Description { get; set; } // "description" + public string? Status { get; set; } // "status" + public string? Environment { get; set; } // "environment" + public string? ProjectId { get; set; } // "projectId" + public string? ScenarioId { get; set; } // "scenarioId" + public JToken? Data { get; set; } // "data" — free-form + public List? Offers { get; set; } // "offers" + public DateTimeOffset? CreatedAt { get; set; } // "createdAt" + public DateTimeOffset? UpdatedAt { get; set; } // "updatedAt" +} + +public class Offer +{ + public string? Id { get; set; } // "id" + public string? Name { get; set; } // "name" + public OfferPrice? Price { get; set; } // "price" + public List? Contents { get; set; }// "contents" + public int? MaxPurchases { get; set; } // "maxPurchases" + public DateTimeOffset? CreatedAt { get; set; } // "createdAt" + public DateTimeOffset? UpdatedAt { get; set; } // "updatedAt" +} + +public class OfferPrice { public long? Amount { get; set; } public string? Currency { get; set; } } +public class OfferContent { public long? Amount { get; set; } public string? ItemId { get; set; } } + +public class PurchaseOfferResponse +{ + public string? Error { get; set; } // "error" + public string? PurchaseId { get; set; } // "purchaseId" + public bool? Success { get; set; } // "success" +} +``` + +`PurchaseOfferResponse.Error` carries a business error string when +`Success` is false — check the flag instead of relying on exceptions alone. +Purchases also auto-report quest metrics (`purchase.offer:{offerId}`) — see +quests.md.