diff --git a/.pubnub.yml b/.pubnub.yml index f53adc416..7a7980b88 100644 --- a/.pubnub.yml +++ b/.pubnub.yml @@ -1,8 +1,15 @@ name: c-sharp -version: "7.4.1" +version: "7.5.0" schema: 1 scm: github.com/pubnub/c-sharp changelog: + - date: 2025-09-01 + version: v7.5.0 + changes: + - type: bug + text: "Fixed types of Meta and Actions in PNHistoryItemResult to not be objects but properly formatted Dictionaries." + - type: bug + text: "Fixed issue of getting Forbidden error while using publish with POST type along with PAM enabled keyset." - date: 2025-07-30 version: v7.4.1 changes: @@ -936,7 +943,7 @@ features: - QUERY-PARAM supported-platforms: - - version: Pubnub 'C#' 7.4.1 + version: Pubnub 'C#' 7.5.0 platforms: - Windows 10 and up - Windows Server 2008 and up @@ -947,7 +954,7 @@ supported-platforms: - .Net Framework 4.6.1+ - .Net Framework 6.0 - - version: PubnubPCL 'C#' 7.4.1 + version: PubnubPCL 'C#' 7.5.0 platforms: - Xamarin.Android - Xamarin.iOS @@ -967,7 +974,7 @@ supported-platforms: - .Net Core - .Net 6.0 - - version: PubnubUWP 'C#' 7.4.1 + version: PubnubUWP 'C#' 7.5.0 platforms: - Windows Phone 10 - Universal Windows Apps @@ -991,7 +998,7 @@ sdks: distribution-type: source distribution-repository: GitHub package-name: Pubnub - location: https://github.com/pubnub/c-sharp/releases/tag/v7.4.1.0 + location: https://github.com/pubnub/c-sharp/releases/tag/v7.5.0.0 requires: - name: ".Net" @@ -1274,7 +1281,7 @@ sdks: distribution-type: source distribution-repository: GitHub package-name: PubNubPCL - location: https://github.com/pubnub/c-sharp/releases/tag/v7.4.1.0 + location: https://github.com/pubnub/c-sharp/releases/tag/v7.5.0.0 requires: - name: ".Net Core" @@ -1633,7 +1640,7 @@ sdks: distribution-type: source distribution-repository: GitHub package-name: PubnubUWP - location: https://github.com/pubnub/c-sharp/releases/tag/v7.4.1.0 + location: https://github.com/pubnub/c-sharp/releases/tag/v7.5.0.0 requires: - name: "Universal Windows Platform Development" diff --git a/CHANGELOG b/CHANGELOG index 0f206463e..8fc84d140 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,8 @@ +v7.5.0 - September 01 2025 +----------------------------- +- Fixed: fixed types of Meta and Actions in PNHistoryItemResult to not be objects but properly formatted Dictionaries. +- Fixed: fixed issue of getting Forbidden error while using publish with POST type along with PAM enabled keyset. + v7.4.1 - July 30 2025 ----------------------------- - Fixed: added MembershipMetadata container inside PNObjectEventResult to correctly parse and forward data when object event type is "membership". diff --git a/src/Api/PubnubApi/JsonDataParse/PNFetchHistoryJsonDataParse.cs b/src/Api/PubnubApi/JsonDataParse/PNFetchHistoryJsonDataParse.cs index 018a09b38..0832151a7 100644 --- a/src/Api/PubnubApi/JsonDataParse/PNFetchHistoryJsonDataParse.cs +++ b/src/Api/PubnubApi/JsonDataParse/PNFetchHistoryJsonDataParse.cs @@ -46,12 +46,42 @@ internal static PNFetchHistoryResult GetObject(IJsonPluggableLibrary jsonPlug, L if (messagesContainer.ContainsKey("meta")) { - result.Meta = messagesContainer["meta"]; + result.Meta = jsonPlug.ConvertToDictionaryObject(messagesContainer["meta"]); } if (messagesContainer.ContainsKey("actions")) { result.Actions = messagesContainer["actions"]; + var formattedActions = new Dictionary>(); + var actionsRaw = messagesContainer["actions"] as Dictionary; + foreach (var rawAction in actionsRaw) + { + var actionItems = new List(); + var actionType = rawAction.Key; + var rawActionValues = rawAction.Value as Dictionary; + foreach (var rawActionValue in rawActionValues) + { + var actionValue = rawActionValue.Key; + var entries = rawActionValue.Value as List; + foreach (var entry in entries) + { + var entryDict = entry as Dictionary; + actionItems.Add(new PNMessageActionItem() + { + ActionTimetoken = long.Parse(entryDict["actionTimetoken"].ToString()), + Uuid = entryDict["uuid"].ToString(), + Action = new PNMessageAction() + { + Type = actionType, + Value = actionValue + }, + MessageTimetoken = result.Timetoken + }); + } + } + formattedActions[actionType] = actionItems; + } + result.ActionItems = formattedActions; } if (messagesContainer.ContainsKey("uuid") && messagesContainer["uuid"] != null) { diff --git a/src/Api/PubnubApi/JsonDataParse/PNHistoryJsonDataParse.cs b/src/Api/PubnubApi/JsonDataParse/PNHistoryJsonDataParse.cs index 4ef70c315..ef51305f4 100644 --- a/src/Api/PubnubApi/JsonDataParse/PNHistoryJsonDataParse.cs +++ b/src/Api/PubnubApi/JsonDataParse/PNHistoryJsonDataParse.cs @@ -55,7 +55,7 @@ internal static PNHistoryResult GetObject(IJsonPluggableLibrary jsonPlug, List Meta { get; internal set; } + [Obsolete("Uses old data format, please use ActionItems instead")] public object Actions { get; internal set; } + public Dictionary> ActionItems { get; internal set; } public string Uuid { get; internal set; } public int MessageType { get; internal set; } diff --git a/src/Api/PubnubApi/Properties/AssemblyInfo.cs b/src/Api/PubnubApi/Properties/AssemblyInfo.cs index 1fc496b44..8be638a23 100644 --- a/src/Api/PubnubApi/Properties/AssemblyInfo.cs +++ b/src/Api/PubnubApi/Properties/AssemblyInfo.cs @@ -11,8 +11,8 @@ [assembly: AssemblyProduct("Pubnub C# SDK")] [assembly: AssemblyCopyright("Copyright © 2021")] [assembly: AssemblyTrademark("")] -[assembly: AssemblyVersion("7.4.1.0")] -[assembly: AssemblyFileVersion("7.4.1.0")] +[assembly: AssemblyVersion("7.5.0.0")] +[assembly: AssemblyFileVersion("7.5.0.0")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. diff --git a/src/Api/PubnubApi/PubnubApi.csproj b/src/Api/PubnubApi/PubnubApi.csproj index 8e6a2fccc..3a87896ec 100644 --- a/src/Api/PubnubApi/PubnubApi.csproj +++ b/src/Api/PubnubApi/PubnubApi.csproj @@ -14,7 +14,7 @@ Pubnub - 7.4.1.0 + 7.5.0.0 PubNub C# .NET - Web Data Push API Pandu Masabathula PubNub @@ -22,8 +22,8 @@ http://pubnub.s3.amazonaws.com/2011/powered-by-pubnub/pubnub-icon-600x600.png true https://github.com/pubnub/c-sharp/ - Added MembershipMetadata container inside PNObjectEventResult to correctly parse and forward data when object event type is `membership`. -Fixed issue where some result objects like PNMessageResult had UserMetadata declared as an object instead of the standard Dictionary format for metadata. + Fixed types of Meta and Actions in PNHistoryItemResult to not be objects but properly formatted Dictionaries. +Fixed issue of getting Forbidden error while using publish with POST type along with PAM enabled keyset. Web Data Push Real-time Notifications ESB Message Broadcasting Distributed Computing PubNub is a Massively Scalable Web Push Service for Web and Mobile Games. This is a cloud-based service for broadcasting messages to thousands of web and mobile clients simultaneously diff --git a/src/Api/PubnubApi/Transport/Middleware.cs b/src/Api/PubnubApi/Transport/Middleware.cs index 48792c73d..79a297941 100644 --- a/src/Api/PubnubApi/Transport/Middleware.cs +++ b/src/Api/PubnubApi/Transport/Middleware.cs @@ -80,11 +80,11 @@ public TransportRequest PreapareTransportRequest(RequestParameter requestParamet { string signature = string.Empty; StringBuilder stringToSign = new StringBuilder(); - stringToSign.AppendFormat(CultureInfo.InvariantCulture, "{0}\n", requestParameter.RequestType); + stringToSign.AppendFormat(CultureInfo.InvariantCulture, "{0}\n", operationType == PNOperationType.PNPublishOperation ? "GET" : requestParameter.RequestType); stringToSign.AppendFormat(CultureInfo.InvariantCulture, "{0}\n", configuration.PublishKey); stringToSign.AppendFormat(CultureInfo.InvariantCulture, "{0}\n", pathString); stringToSign.AppendFormat(CultureInfo.InvariantCulture, "{0}\n", queryString); - if (!string.IsNullOrEmpty(requestParameter.BodyContentString)) stringToSign.Append(requestParameter.BodyContentString); + if (!string.IsNullOrEmpty(requestParameter.BodyContentString) && operationType != PNOperationType.PNPublishOperation) stringToSign.Append(requestParameter.BodyContentString); signature = Util.PubnubAccessManagerSign(configuration.SecretKey, stringToSign.ToString()); signature = string.Format(CultureInfo.InvariantCulture, "v2.{0}", signature.TrimEnd(new[] { '=' })); requestParameter.Query.Add("signature", signature); diff --git a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj index 49fe66601..6d708a67f 100644 --- a/src/Api/PubnubApiPCL/PubnubApiPCL.csproj +++ b/src/Api/PubnubApiPCL/PubnubApiPCL.csproj @@ -14,7 +14,7 @@ PubnubPCL - 7.4.1.0 + 7.5.0.0 PubNub C# .NET - Web Data Push API Pandu Masabathula PubNub @@ -22,8 +22,8 @@ http://pubnub.s3.amazonaws.com/2011/powered-by-pubnub/pubnub-icon-600x600.png true https://github.com/pubnub/c-sharp/ - Added MembershipMetadata container inside PNObjectEventResult to correctly parse and forward data when object event type is `membership`. -Fixed issue where some result objects like PNMessageResult had UserMetadata declared as an object instead of the standard Dictionary format for metadata. + Fixed types of Meta and Actions in PNHistoryItemResult to not be objects but properly formatted Dictionaries. +Fixed issue of getting Forbidden error while using publish with POST type along with PAM enabled keyset. Web Data Push Real-time Notifications ESB Message Broadcasting Distributed Computing PubNub is a Massively Scalable Web Push Service for Web and Mobile Games. This is a cloud-based service for broadcasting messages to thousands of web and mobile clients simultaneously diff --git a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj index 314f01e47..54b86ed77 100644 --- a/src/Api/PubnubApiUWP/PubnubApiUWP.csproj +++ b/src/Api/PubnubApiUWP/PubnubApiUWP.csproj @@ -16,7 +16,7 @@ PubnubUWP - 7.4.1.0 + 7.5.0.0 PubNub C# .NET - Web Data Push API Pandu Masabathula PubNub @@ -24,8 +24,8 @@ http://pubnub.s3.amazonaws.com/2011/powered-by-pubnub/pubnub-icon-600x600.png true https://github.com/pubnub/c-sharp/ - Added MembershipMetadata container inside PNObjectEventResult to correctly parse and forward data when object event type is `membership`. -Fixed issue where some result objects like PNMessageResult had UserMetadata declared as an object instead of the standard Dictionary format for metadata. + Fixed types of Meta and Actions in PNHistoryItemResult to not be objects but properly formatted Dictionaries. +Fixed issue of getting Forbidden error while using publish with POST type along with PAM enabled keyset. Web Data Push Real-time Notifications ESB Message Broadcasting Distributed Computing PubNub is a Massively Scalable Web Push Service for Web and Mobile Games. This is a cloud-based service for broadcasting messages to thousands of web and mobile clients simultaneously diff --git a/src/Api/PubnubApiUnity/PubnubApiUnity.csproj b/src/Api/PubnubApiUnity/PubnubApiUnity.csproj index 59c5e00a1..b55843e39 100644 --- a/src/Api/PubnubApiUnity/PubnubApiUnity.csproj +++ b/src/Api/PubnubApiUnity/PubnubApiUnity.csproj @@ -15,7 +15,7 @@ PubnubApiUnity - 7.4.1.0 + 7.5.0.0 PubNub C# .NET - Web Data Push API Pandu Masabathula PubNub diff --git a/src/UnitTests/PubnubApi.Tests/WhenAMessageIsPublished.cs b/src/UnitTests/PubnubApi.Tests/WhenAMessageIsPublished.cs index 317919e31..f39dac6e1 100644 --- a/src/UnitTests/PubnubApi.Tests/WhenAMessageIsPublished.cs +++ b/src/UnitTests/PubnubApi.Tests/WhenAMessageIsPublished.cs @@ -1680,15 +1680,10 @@ public static void IfSecretKeyCipherKeyWithoutAuthThenGetMessageWithSpecialChars [Test] public static void IfSecretKeyWithoutAuthThenPostMessageWithSpecialCharsReturnSuccess() { - server.ClearRequests(); - if (!PubnubCommon.PAMServerSideRun) - { - Assert.Ignore("Ignored due to no secret key at client side"); - } bool receivedPublishMessage = false; long publishTimetoken = 0; - string channel = "hello_my_channel ~!@#$%^&()+=[]{}|;\"<>?-_.aA1©®€™₹😜🎉"; + string channel = "hello_my_channel!@#$%^&()+=[]{}|;\"<>?-_.aA1©®€™₹😜🎉"; //string channel = "hello_my_channel"; string message = " !~`@#$%^&*()+=[]\\{}|;':\",/<>?-_.aA1©®€™₹😜🎉"; //string message = " !~"; @@ -1702,32 +1697,10 @@ public static void IfSecretKeyWithoutAuthThenPostMessageWithSpecialCharsReturnSu { PublishKey = PubnubCommon.PublishKey, SubscribeKey = PubnubCommon.SubscribeKey, - Secure = false, + SecretKey = PubnubCommon.SecretKey, IncludeRequestIdentifier = false, }; - if (PubnubCommon.PAMServerSideRun) - { - config.SecretKey = PubnubCommon.SecretKey; - } - else if (!string.IsNullOrEmpty(authKey) && !PubnubCommon.SuppressAuthKey) - { - config.AuthKey = authKey; - } - - server.RunOnHttps(false); - - pubnub = createPubNubInstance(config, authToken); - - string expected = "[1,\"Sent\",\"14722484585147754\"]"; - - server.AddRequest(new Request() - .WithMethod("GET") - .WithPath(String.Format("/publish/{0}/{1}/0/{2}/0/%22%21%22", PubnubCommon.PublishKey, PubnubCommon.SubscribeKey, channel)) - .WithParameter("pnsdk", PubnubCommon.EncodedSDK) - .WithParameter("requestid", "myRequestId") - .WithParameter("uuid", config.UserId) - .WithResponse(expected) - .WithStatusCode(System.Net.HttpStatusCode.OK)); + pubnub = createPubNubInstance(config); manualResetEventWaitTimeout = 310 * 1000; @@ -1749,7 +1722,6 @@ public static void IfSecretKeyWithoutAuthThenPostMessageWithSpecialCharsReturnSu publishManualEvent.Set(); })); publishManualEvent.WaitOne(manualResetEventWaitTimeout); - pubnub.Destroy(); pubnub.PubnubUnitTest = null; pubnub = null; @@ -1759,15 +1731,10 @@ public static void IfSecretKeyWithoutAuthThenPostMessageWithSpecialCharsReturnSu [Test] public static void IfSecretKeyCipherKeyWithoutAuthThenPostMessageWithSpecialCharsReturnSuccess() { - server.ClearRequests(); - if (!PubnubCommon.PAMServerSideRun) - { - Assert.Ignore("Ignored due to no secret key at client side"); - } bool receivedPublishMessage = false; long publishTimetoken = 0; - string channel = "hello_my_channel ~!@#$%^&()+=[]{}|;\"<>?-_.aA1©®€™₹😜🎉"; + string channel = "hello_my_channel!@#$%^&()+=[]{}|;\"<>?-_.aA1©®€™₹😜🎉"; string message = " ~`!@#$%^&*()+=[]\\{}|;':\",/<>?-_.aA1©®€™₹😜🎉"; PNConfiguration config = new PNConfiguration(new UserId("my ~`!@#$%^&*()+=[]\\{}|;':\",/<>?-_.aA1©®€™₹😜🎉uuid")) @@ -1775,33 +1742,10 @@ public static void IfSecretKeyCipherKeyWithoutAuthThenPostMessageWithSpecialChar PublishKey = PubnubCommon.PublishKey, SubscribeKey = PubnubCommon.SubscribeKey, CryptoModule = new CryptoModule(new LegacyCryptor("enigma"), null), - Secure = false, + SecretKey = PubnubCommon.SecretKey, IncludeRequestIdentifier = false, }; - if (PubnubCommon.PAMServerSideRun) - { - config.SecretKey = PubnubCommon.SecretKey; - } - else if (!string.IsNullOrEmpty(authKey) && !PubnubCommon.SuppressAuthKey) - { - config.AuthKey = authKey; - } - - server.RunOnHttps(false); - - pubnub = createPubNubInstance(config, authToken); - - string expected = "[1,\"Sent\",\"14722484585147754\"]"; - - server.AddRequest(new Request() - .WithMethod("GET") - .WithPath(String.Format("/publish/{0}/{1}/0/{2}/0/%22%21%22", PubnubCommon.PublishKey, PubnubCommon.SubscribeKey, channel)) - .WithParameter("pnsdk", PubnubCommon.EncodedSDK) - .WithParameter("requestid", "myRequestId") - .WithParameter("uuid", config.UserId) - .WithResponse(expected) - .WithStatusCode(System.Net.HttpStatusCode.OK)); - + pubnub = createPubNubInstance(config); manualResetEventWaitTimeout = 310 * 1000; ManualResetEvent publishManualEvent = new ManualResetEvent(false); @@ -2577,5 +2521,127 @@ public static void ThenPublishWithCustomMessageTypeAndSubscribeShouldReceiveCorr pubnub.PubnubUnitTest = null; pubnub = null; } + + [Test] + public static async Task ThenAwaitedPublishPostWithSecretKeySuccess() + { + var id = $"test_{new Random().Next(1000, 10000)}"; + string channel = $"channel_{id}"; + string message =$"message_{id}"; + string customType = $"customtype_{id}"; + + PNConfiguration config = new PNConfiguration(new UserId($"user_{id}")) + { + PublishKey = PubnubCommon.PublishKey, + SubscribeKey = PubnubCommon.SubscribeKey, + SecretKey = PubnubCommon.SecretKey, + }; + pubnub = createPubNubInstance(config, authToken); + manualResetEventWaitTimeout = 310 * 1000; + ManualResetEvent subscribeManualEvent = new ManualResetEvent(false); + pubnub.Subscribe() + .Channels(new string[] { channel }) + .WithPresence() + .Execute(); + string receivedMessage = null; + pubnub.AddListener(new SubscribeCallbackExt( + (p, m) => { + if (m.Channel.Equals(channel) ) + { + receivedMessage = $"{m.Message}"; + subscribeManualEvent.Set(); + } + }, + (p, pnPresenceEventResult) => { }, + (p, pnSignalResult) => { }, + (p, pnObjectEventResult) => { }, + (p, pnMessageActionEventResult) => { }, + (p, pnFileEventResult) => { }, + (p, pnStatus) => { } + )); + + // Publish the message + ManualResetEvent publishManualEvent = new ManualResetEvent(false); + await pubnub.Publish().Channel(channel).Message(message).UsePOST(true).CustomMessageType(customType) + .ExecuteAsync(); + var receivedSubscribeMessage = subscribeManualEvent.WaitOne(manualResetEventWaitTimeout); + Assert.IsTrue(receivedSubscribeMessage, "Subscribe message not received"); + pubnub.Destroy(); + pubnub.PubnubUnitTest = null; + pubnub = null; + } + + [Test] + public static async Task ThenAwaitedPublishPostWithTokenSuccess() + { + var id = $"test_{new Random().Next(1000, 10000)}"; + string channel = $"channel_{id}"; + string message =$"message_{id}"; + string customType = $"customtype_{id}"; + + PNConfiguration config = new PNConfiguration(new UserId($"user_{id}")) + { + PublishKey = PubnubCommon.PublishKey, + SubscribeKey = PubnubCommon.SubscribeKey, + SecretKey = PubnubCommon.SecretKey, + }; + pubnub = createPubNubInstance(config, authToken); + PNResult grantTokenResponse = await pubnub.GrantToken() + .TTL(15) + .AuthorizedUuid($"user_{id}") + .Resources(new PNTokenResources + { + Channels = new Dictionary + { + { channel, new PNTokenAuthValues { Read = true, Write = true, Join = true, Manage = true} } + } + }) + .ExecuteAsync(); + var token = grantTokenResponse.Result.Token; + Assert.NotNull(token, "token should not be null for publisher"); + // wait after grant token to be effective + await Task.Delay(1000); + PNConfiguration publsherConfiguration = new PNConfiguration(new UserId($"user_{id}")) + { + PublishKey = PubnubCommon.PublishKey, + SubscribeKey = PubnubCommon.SubscribeKey, + }; + var publisher = createPubNubInstance(publsherConfiguration, token); + manualResetEventWaitTimeout = 310 * 1000; + ManualResetEvent subscribeManualEvent = new ManualResetEvent(false); + pubnub.Subscribe() + .Channels(new string[] { channel }) + .WithPresence() + .Execute(); + string receivedMessage = null; + pubnub.AddListener(new SubscribeCallbackExt( + (p, m) => { + if (m.Channel.Equals(channel) ) + { + receivedMessage = $"{m.Message}"; + subscribeManualEvent.Set(); + } + }, + (p, pnPresenceEventResult) => { }, + (p, pnSignalResult) => { }, + (p, pnObjectEventResult) => { }, + (p, pnMessageActionEventResult) => { }, + (p, pnFileEventResult) => { }, + (p, pnStatus) => { } + )); + + // Publish the message + ManualResetEvent publishManualEvent = new ManualResetEvent(false); + await publisher.Publish().Channel(channel).Message(message).UsePOST(true).CustomMessageType(customType) + .ExecuteAsync(); + var receivedSubscribeMessage = subscribeManualEvent.WaitOne(manualResetEventWaitTimeout); + Assert.IsTrue(receivedSubscribeMessage, "Subscribe message not received"); + pubnub.Destroy(); + pubnub.PubnubUnitTest = null; + pubnub = null; + publisher.Destroy(); + publisher.PubnubUnitTest = null; + publisher = null; + } } } diff --git a/src/UnitTests/PubnubApi.Tests/WhenAMessageIsSignaled.cs b/src/UnitTests/PubnubApi.Tests/WhenAMessageIsSignaled.cs index eee68ad53..f5e33580b 100644 --- a/src/UnitTests/PubnubApi.Tests/WhenAMessageIsSignaled.cs +++ b/src/UnitTests/PubnubApi.Tests/WhenAMessageIsSignaled.cs @@ -701,15 +701,68 @@ public static void ThenSignalWithCustomMessageTypeShouldReturnSuccessCodeAndInfo var signalCompleted = signalManualEvent.WaitOne(manualResetEventWaitTimeout); Assert.True(signalCompleted, "Singal execution callback was not called"); - Assert.IsNotNull(result, "Result should not be null"); Assert.IsNotNull(status, "Status should not be null"); + Assert.IsFalse(status.Error, $"Error should be false, error was: {status.ErrorData?.Information}"); Assert.AreEqual(200, status.StatusCode, "StatusCode should be 200"); - Assert.IsFalse(status.Error, "Error should be false"); + Assert.IsNotNull(result, "Result should not be null"); Assert.IsTrue(result.Timetoken > 0, "Timetoken should be greater than 0"); pubnub.Destroy(); pubnub.PubnubUnitTest = null; pubnub = null; } + + [Test] + public static async Task ThenAwaitedSiganlWithSecretKeySuccess() + { + var id = $"test_{new Random().Next(1000, 10000)}"; + string channel = $"channel_{id}"; + string message =$"message_{id}"; + string customType = $"customtype_{id}"; + + PNConfiguration config = new PNConfiguration(new UserId($"user_{id}")) + { + PublishKey = PubnubCommon.PublishKey, + SubscribeKey = PubnubCommon.SubscribeKey, + SecretKey = PubnubCommon.SecretKey, + }; + pubnub = createPubNubInstance(config, authToken); + manualResetEventWaitTimeout = 310 * 1000; + ManualResetEvent subscribeManualEvent = new ManualResetEvent(false); + pubnub.Subscribe() + .Channels(new string[] { channel }) + .WithPresence() + .Execute(); + string receivedSignalEvent = null; + pubnub.AddListener(new SubscribeCallbackExt( + (p, m) => { + + }, + (p, pnPresenceEventResult) => { }, + (p, s) => + { + if (s.Channel.Equals(channel) ) + { + receivedSignalEvent = $"{s.Message}"; + subscribeManualEvent.Set(); + } + }, + (p, pnObjectEventResult) => { }, + (p, pnMessageActionEventResult) => { }, + (p, pnFileEventResult) => { }, + (p, pnStatus) => { } + )); + + // Publish the message + ManualResetEvent publishManualEvent = new ManualResetEvent(false); + await pubnub.Signal().Channel(channel).Message(message).CustomMessageType(customType) + .ExecuteAsync(); + var receivedSubscribeMessage = subscribeManualEvent.WaitOne(manualResetEventWaitTimeout); + Assert.IsTrue(receivedSubscribeMessage, "Subscribe message not received"); + Assert.AreEqual(receivedSignalEvent, message, "signal event message not matched"); + pubnub.Destroy(); + pubnub.PubnubUnitTest = null; + pubnub = null; + } } } diff --git a/src/UnitTests/PubnubApi.Tests/WhenFetchHistoryIsRequested.cs b/src/UnitTests/PubnubApi.Tests/WhenFetchHistoryIsRequested.cs index 37d99508d..03b5922a5 100644 --- a/src/UnitTests/PubnubApi.Tests/WhenFetchHistoryIsRequested.cs +++ b/src/UnitTests/PubnubApi.Tests/WhenFetchHistoryIsRequested.cs @@ -2927,5 +2927,59 @@ public static async Task FetchHistoryWithAsyncWithMessageActionsDefaultMax25() pubnub = null; Assert.IsTrue(receivedMessage, "Fetch History with Message Actions Async Failed"); } + + [Test] + public static async Task FetchHistoryShouldContainFormattedActionsAndMeta() + { + PNConfiguration config = new PNConfiguration(new UserId("mytestuuid")) + { + PublishKey = PubnubCommon.PublishKey, + SubscribeKey = PubnubCommon.SubscribeKey, + Secure = false + }; + if (PubnubCommon.PAMServerSideRun) { + config.SecretKey = PubnubCommon.SecretKey; + } else if (!string.IsNullOrEmpty(authKey) && !PubnubCommon.SuppressAuthKey) { + config.AuthKey = authKey; + } + + pubnub = createPubNubInstance(config, authToken); + var testChannel = $"foo.{Guid.NewGuid()}"; + + var listener = new SubscribeCallbackExt( + async (_, messageEvent) => + { + await pubnub.AddMessageAction() + .Action(new PNMessageAction() { Type = "edited", Value = "some_value" }) + .Channel(messageEvent.Channel).MessageTimetoken(messageEvent.Timetoken).ExecuteAsync(); + } + , (_, _) => {} + , (_, _) => {} + ); + pubnub.AddListener(listener); + pubnub.Subscribe().Channels(new []{testChannel}).Execute(); + + await Task.Delay(3500); + + await pubnub.Publish().Channel(testChannel).Message("some_message").Meta(new Dictionary() + { + {"some_meta_key", "some_meta_value"} + }).ExecuteAsync(); + + await Task.Delay(10000); + + var history = await pubnub.FetchHistory().Channels(new[] { testChannel }).IncludeMessageActions(true).IncludeMeta(true) + .Start(99999999999999999).End(00000000000000000).ExecuteAsync(); + + Assert.False(history.Status.Error, $"FetchHistory() resulted in an error: {history.Status.ErrorData?.Information}"); + Assert.True(history.Result != null, "FetchHistory() Result was null"); + Assert.True(history.Result.Messages.Count == 1, "Wrong history channels count"); + Assert.True(history.Result.Messages[testChannel].Count == 1, "Wrong history messages on channel count"); + var historyItem = history.Result.Messages[testChannel][0]; + Assert.True(historyItem.Meta.TryGetValue("some_meta_key", out var metaValue) && metaValue.ToString() == "some_meta_value", "History Item Meta didn't contain expected value."); + Assert.True(historyItem.ActionItems.TryGetValue("edited", out var action), "History Item Actions didn't contain expected key."); + Assert.True(action.Count == 1, "History Item Actions contained wrong amount of action entries"); + Assert.True(action[0].Action.Type == "edited" && action[0].Action.Value == "some_value", "History Item Action value was wrong."); + } } } diff --git a/src/UnitTests/PubnubApi.Tests/WhenMessageDeletedFromChannel.cs b/src/UnitTests/PubnubApi.Tests/WhenMessageDeletedFromChannel.cs index 79766039c..1c9951057 100644 --- a/src/UnitTests/PubnubApi.Tests/WhenMessageDeletedFromChannel.cs +++ b/src/UnitTests/PubnubApi.Tests/WhenMessageDeletedFromChannel.cs @@ -227,6 +227,30 @@ public static async Task ThenWithAsyncDeleteMessageShouldReturnSuccessMessage() pubnub.PubnubUnitTest = null; pubnub = null; + Assert.IsTrue(receivedMessage, "ThenWithAsyncDeleteMessageShouldReturnSuccessMessage - DeleteMessages Result not expected"); + } + [Test] + public static async Task ThenWithAsyncDeleteMessageShouldReturnSuccessWithSecretKey() + { + currentTestCase = "ThenWithAsyncDeleteMessageShouldReturnSuccessMessage"; + var id = $"test_{new Random().Next(1000, 10000)}"; + string channel = $"channel_{id}"; + PNConfiguration config = new PNConfiguration(new UserId($"user_{id}")) + { + PublishKey = PubnubCommon.PublishKey, + SubscribeKey = PubnubCommon.SubscribeKey, + SecretKey = PubnubCommon.SecretKey, + }; + pubnub = createPubNubInstance(config); + PNResult deleteMessagesResponse = await pubnub.DeleteMessages().Channel(channel).ExecuteAsync(); + if (deleteMessagesResponse.Result != null && deleteMessagesResponse.Status.StatusCode == 200 && !deleteMessagesResponse.Status.Error) + { + receivedMessage = true; + } + pubnub.Destroy(); + pubnub.PubnubUnitTest = null; + pubnub = null; + Assert.IsTrue(receivedMessage, "ThenWithAsyncDeleteMessageShouldReturnSuccessMessage - DeleteMessages Result not expected"); } } diff --git a/src/UnitTests/PubnubApi.Tests/WhenObjectChannelMember.cs b/src/UnitTests/PubnubApi.Tests/WhenObjectChannelMember.cs index 4d5df2c68..f5ef811cf 100644 --- a/src/UnitTests/PubnubApi.Tests/WhenObjectChannelMember.cs +++ b/src/UnitTests/PubnubApi.Tests/WhenObjectChannelMember.cs @@ -2056,8 +2056,9 @@ public static async Task ThenGetChannelMembersShouldReturnMembershipWithStatusTy PNConfiguration configuration = new PNConfiguration(new UserId($"user{r.Next(100,1000)}")) { - PublishKey = PubnubCommon.NonPAMPublishKey, - SubscribeKey = PubnubCommon.NONPAMSubscribeKey + PublishKey = PubnubCommon.PublishKey, + SubscribeKey = PubnubCommon.SubscribeKey, + SecretKey = PubnubCommon.SecretKey, }; pubnub = createPubNubInstance(configuration); diff --git a/src/UnitTests/PubnubApi.Tests/WhenObjectChannelMetadata.cs b/src/UnitTests/PubnubApi.Tests/WhenObjectChannelMetadata.cs index 4cdc7d8d7..88b9ae088 100644 --- a/src/UnitTests/PubnubApi.Tests/WhenObjectChannelMetadata.cs +++ b/src/UnitTests/PubnubApi.Tests/WhenObjectChannelMetadata.cs @@ -848,6 +848,8 @@ public static async Task ThenSetAndGetChannelMembersShouldReturnSuccess() Assert.IsNotNull(setResult, "Set result should not be null"); Assert.IsFalse(setResult.Status.Error, "Set operation should not have errors"); + await Task.Delay(4000); + // Get channel members PNResult getResult = await pubnub.GetChannelMembers() .Channel(testChannel) @@ -1137,5 +1139,82 @@ public static async Task ThenChannelMetadataShouldSupportStatusAndTypeFields() pubnub.PubnubUnitTest = null; pubnub = null; } + [Test] + public static async Task ThenChannelMetadataSetShouldWorkWithSecretKey() + { + var r = new Random(); + string channelMetadataId = $"channel{r.Next(100, 1000)}"; + string status = $"status{r.Next(100, 1000)}"; + string type = $"type{r.Next(100, 1000)}"; + string name = $"name{r.Next(100, 1000)}"; + PNConfiguration configuration = new PNConfiguration(new UserId($"user{r.Next(100,1000)}")) + { + PublishKey = PubnubCommon.PublishKey, + SubscribeKey = PubnubCommon.SubscribeKey, + SecretKey = PubnubCommon.SecretKey, + }; + pubnub = createPubNubInstance(configuration); + var channelMetadata = await pubnub.SetChannelMetadata(). + Channel(channelMetadataId). + Status(status). + Name(name). + Type(type). + Custom(new Dictionary{ {"key", "value"}}). + ExecuteAsync(); + Assert.That(channelMetadata.Result, Is.Not.Null); + Assert.AreEqual(channelMetadata.Status.StatusCode, 200); + pubnub.Destroy(); + pubnub = null; + } + + [Test] + public static async Task ThenChannelMetadataSetShouldWorkWithToken() + { + var r = new Random(); + string channelMetadataId = $"channel{r.Next(100, 1000)}"; + string status = $"status{r.Next(100, 1000)}"; + string type = $"type{r.Next(100, 1000)}"; + string name = $"name{r.Next(100, 1000)}"; + var id = $"test_{new Random().Next(1000, 10000)}"; + PNConfiguration configuration = new PNConfiguration(new UserId($"user_{id}")) + { + PublishKey = PubnubCommon.PublishKey, + SubscribeKey = PubnubCommon.SubscribeKey, + SecretKey = PubnubCommon.SecretKey, + }; + pubnub = createPubNubInstance(configuration); + PNResult grantTokenResponse = await pubnub.GrantToken() + .TTL(15) + .AuthorizedUuid($"user_{id}") + .Resources(new PNTokenResources + { + Uuids = new Dictionary + { + { channelMetadataId, new PNTokenAuthValues { Read = true, Write = true, Join = true, Manage = true} } + } + }) + .ExecuteAsync(); + var token = grantTokenResponse.Result.Token; + Assert.NotNull(token, "token should not be null for uuid setter"); + // wait after grant token to be effective + await Task.Delay(1000); + PNConfiguration channelMetadataSetterConfiguration = new PNConfiguration(new UserId($"user_{id}")) + { + PublishKey = PubnubCommon.PublishKey, + SubscribeKey = PubnubCommon.SubscribeKey, + }; + var channelMetadataSetter = createPubNubInstance(channelMetadataSetterConfiguration, token); + var setChannelMetadata = await pubnub.SetChannelMetadata(). + Channel(channelMetadataId). + Status(status). + Name(name). + Type(type). + Custom(new Dictionary{ {"key", "value"}}). + ExecuteAsync(); + Assert.That(setChannelMetadata.Result, Is.Not.Null); + Assert.AreEqual(setChannelMetadata.Status.StatusCode, 200); + pubnub.Destroy(); + channelMetadataSetter.Destroy(); + } } } \ No newline at end of file diff --git a/src/UnitTests/PubnubApi.Tests/WhenObjectMembership.cs b/src/UnitTests/PubnubApi.Tests/WhenObjectMembership.cs index 8af687888..a31a9bf37 100644 --- a/src/UnitTests/PubnubApi.Tests/WhenObjectMembership.cs +++ b/src/UnitTests/PubnubApi.Tests/WhenObjectMembership.cs @@ -2779,8 +2779,6 @@ public static async Task TestSetMembershipsObjectCallback() PublishKey = PubnubCommon.PublishKey, SubscribeKey = PubnubCommon.SubscribeKey, SecretKey = PubnubCommon.SecretKey, - Secure = false, - LogLevel = PubnubLogLevel.All }; pubnub = createPubNubInstance(config); @@ -2794,18 +2792,14 @@ await pubnub.RemoveMemberships().Channels(new List() { channelMetadataId .ExecuteAsync(); await Task.Delay(4000); + PNObjectEventResult eventResult = null; var setReset = new ManualResetEvent(false); SubscribeCallbackExt eventListener = new SubscribeCallbackExt( - delegate(Pubnub pnObj, PNObjectEventResult eventResult) + delegate(Pubnub pnObj, PNObjectEventResult ojectEventResult) { Debug.WriteLine("PNObjectEventResult JSON:" + pubnub.JsonPluggableLibrary.SerializeToJsonString(eventResult)); - Assert.IsNotNull(eventResult.MembershipMetadata, "eventResult.MembershipMetadata should not be null"); - Assert.AreEqual(eventResult.MembershipMetadata.Channel, channelMetadataId, "Wrong Channel ID in object event result"); - Assert.AreEqual(eventResult.MembershipMetadata.Uuid, uuidMetadataId, "Wrong User ID in object event result"); - Assert.AreEqual(eventResult.MembershipMetadata.Status, "active", "Wrong Status in object event result"); - Assert.AreEqual(eventResult.MembershipMetadata.Type, "membership", "Wrong Type in object event result"); - + eventResult = ojectEventResult; setReset.Set(); }, delegate(Pubnub pnObj, PNStatus status) @@ -2853,6 +2847,11 @@ await pubnub.RemoveMemberships().Channels(new List() { channelMetadataId var set = setReset.WaitOne(20000); Assert.True(set, "Didn't receive objects callback after setting memberships."); + Assert.IsNotNull(eventResult.MembershipMetadata, "eventResult.MembershipMetadata should not be null"); + Assert.AreEqual(eventResult.MembershipMetadata.Channel, channelMetadataId, "Wrong Channel ID in object event result"); + Assert.AreEqual(eventResult.MembershipMetadata.Uuid, uuidMetadataId, "Wrong User ID in object event result"); + Assert.AreEqual(eventResult.MembershipMetadata.Status, "active", "Wrong Status in object event result"); + Assert.AreEqual(eventResult.MembershipMetadata.Type, "membership", "Wrong Type in object event result"); //Cleanup await pubnub.RemoveMemberships().Channels(new List() { channelMetadataId }).Uuid(uuidMetadataId) diff --git a/src/UnitTests/PubnubApi.Tests/WhenObjectUuidMetadata.cs b/src/UnitTests/PubnubApi.Tests/WhenObjectUuidMetadata.cs index 2d37dfe72..8e4e4991a 100644 --- a/src/UnitTests/PubnubApi.Tests/WhenObjectUuidMetadata.cs +++ b/src/UnitTests/PubnubApi.Tests/WhenObjectUuidMetadata.cs @@ -1013,7 +1013,7 @@ public static async Task ThenUuidMetadataShouldSupportStatusAndTypeFields() }; channelSubscription.Subscribe(); - await Task.Delay(2000); + await Task.Delay(4000); // now set the UUID metadata. var setUuidMetadata = await pubnub.SetUuidMetadata(). @@ -1036,7 +1036,7 @@ public static async Task ThenUuidMetadataShouldSupportStatusAndTypeFields() Assert.That(setUuidMetadata.Result.Custom.ContainsKey("key"), Is.True, "Custom should contain 'key'"); Assert.That(setUuidMetadata.Result.Custom["key"].ToString(), Is.EqualTo("value"), "Custom key value should match"); - await Task.Delay(2000); + await Task.Delay(4000); var getUuidMetadata = await pubnub.GetUuidMetadata().Uuid(uuidMetadataId).IncludeStatus(true).IncludeType(true).IncludeCustom(true).ExecuteAsync(); @@ -1061,7 +1061,7 @@ public static async Task ThenUuidMetadataShouldSupportStatusAndTypeFields() var removeUuidMetadata = await pubnub.RemoveUuidMetadata().Uuid(uuidMetadataId).ExecuteAsync(); // Wait for delete event - await Task.Delay(1000); + await Task.Delay(4000); // Cleanup channelSubscription.Unsubscribe(); @@ -1069,5 +1069,83 @@ public static async Task ThenUuidMetadataShouldSupportStatusAndTypeFields() pubnub.PubnubUnitTest = null; pubnub = null; } + + [Test] + public static async Task ThenUuidMetadataSetShouldWorkWithSecretKey() + { + var r = new Random(); + string uuidMetadataId = $"uuid{r.Next(100, 1000)}"; + string status = $"status{r.Next(100, 1000)}"; + string type = $"type{r.Next(100, 1000)}"; + string name = $"name{r.Next(100, 1000)}"; + PNConfiguration configuration = new PNConfiguration(new UserId($"user{r.Next(100,1000)}")) + { + PublishKey = PubnubCommon.PublishKey, + SubscribeKey = PubnubCommon.SubscribeKey, + SecretKey = PubnubCommon.SecretKey, + }; + pubnub = createPubNubInstance(configuration); + var setUuidMetadata = await pubnub.SetUuidMetadata(). + Uuid(uuidMetadataId). + Status(status). + Name(name). + Type(type). + Custom(new Dictionary{ {"key", "value"}}). + ExecuteAsync(); + Assert.That(setUuidMetadata.Result, Is.Not.Null); + Assert.AreEqual(setUuidMetadata.Status.StatusCode, 200); + pubnub.Destroy(); + pubnub = null; + } + + [Test] + public static async Task ThenUuideMetadataSetShouldWorkWithToken() + { + var r = new Random(); + string uuidMetadataId = $"uuid{r.Next(100, 1000)}"; + string status = $"status{r.Next(100, 1000)}"; + string type = $"type{r.Next(100, 1000)}"; + string name = $"name{r.Next(100, 1000)}"; + var id = $"test_{new Random().Next(1000, 10000)}"; + PNConfiguration configuration = new PNConfiguration(new UserId($"user_{id}")) + { + PublishKey = PubnubCommon.PublishKey, + SubscribeKey = PubnubCommon.SubscribeKey, + SecretKey = PubnubCommon.SecretKey, + }; + pubnub = createPubNubInstance(configuration); + PNResult grantTokenResponse = await pubnub.GrantToken() + .TTL(15) + .AuthorizedUuid($"user_{id}") + .Resources(new PNTokenResources + { + Uuids = new Dictionary + { + { uuidMetadataId, new PNTokenAuthValues { Read = true, Write = true, Join = true, Manage = true} } + } + }) + .ExecuteAsync(); + var token = grantTokenResponse.Result.Token; + Assert.NotNull(token, "token should not be null for uuid setter"); + // wait after grant token to be effective + await Task.Delay(1000); + PNConfiguration uuidSetterConfiguration = new PNConfiguration(new UserId($"user_{id}")) + { + PublishKey = PubnubCommon.PublishKey, + SubscribeKey = PubnubCommon.SubscribeKey, + }; + var uuidSetter = createPubNubInstance(uuidSetterConfiguration, token); + var setUuidMetadata = await pubnub.SetUuidMetadata(). + Uuid(uuidMetadataId). + Status(status). + Name(name). + Type(type). + Custom(new Dictionary{ {"key", "value"}}). + ExecuteAsync(); + Assert.That(setUuidMetadata.Result, Is.Not.Null); + Assert.AreEqual(setUuidMetadata.Status.StatusCode, 200); + pubnub.Destroy(); + uuidSetter.Destroy(); + } } }