Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change WPS ConnectionContext.States to a Dict<string, BinaryData> #25504

Merged
26 commits merged into from
Dec 6, 2021
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion eng/Packages.Data.props
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
<PackageReference Update="Azure.Messaging.EventHubs" Version="5.6.2" />
<PackageReference Update="Azure.Messaging.EventGrid" Version="4.7.0" />
<PackageReference Update="Azure.Messaging.ServiceBus" Version="7.5.0" />
<PackageReference Update="Azure.Messaging.WebPubSub" Version="1.0.0-beta.2" />
<PackageReference Update="Azure.Messaging.WebPubSub" Version="1.0.0" />
<PackageReference Update="Azure.Identity" Version="1.5.0" />
<PackageReference Update="Azure.Security.KeyVault.Secrets" Version="4.2.0" />
<PackageReference Update="Azure.Security.KeyVault.Keys" Version="4.2.0" />
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
# Release History

## 1.1.0-beta.1 (Unreleased)

### Features Added

### Breaking Changes
## 1.1.0 (2021-11-24)

### Bugs Fixed
- Changed the `ConnectionContext`'s `ConnectionStates` to correctly serialize as proper JSON when used with JavaScript.

### Other Changes
### Breaking Changes
- JavaScript developers using `request.connectionContext.states` no longer need to `JSON.parse(...)` its values. The values are already valid JSON.

## 1.0.0 (2021-11-09)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using Newtonsoft.Json;

namespace Microsoft.Azure.WebJobs.Extensions.WebPubSub
{
internal class ConnectionStatesNewtonsoftConverter : JsonConverter<IReadOnlyDictionary<string, BinaryData>>
{
JialinXin marked this conversation as resolved.
Show resolved Hide resolved
public override bool CanRead => false;
public override bool CanWrite => true;

public override IReadOnlyDictionary<string, BinaryData> ReadJson(JsonReader reader, Type objectType, IReadOnlyDictionary<string, BinaryData> existingValue, bool hasExistingValue, JsonSerializer serializer) =>
throw new NotImplementedException();

public override void WriteJson(JsonWriter writer, IReadOnlyDictionary<string, BinaryData> value, JsonSerializer serializer)
{
writer.WriteStartObject();
if (value != null)
{
foreach (KeyValuePair<string, BinaryData> pair in value)
{
writer.WritePropertyName(pair.Key);
writer.WriteRawValue(pair.Value.ToString());
JialinXin marked this conversation as resolved.
Show resolved Hide resolved
}
}
writer.WriteEndObject();
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ internal static void RegisterJsonConverter()
{
new StringEnumConverter(),
new BinaryDataJsonConverter(),
new JsonElementJsonConverter(),
new ConnectionStatesNewtonsoftConverter(),
},
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>$(RequiredTargetFrameworks)</TargetFrameworks>
<PackageId>Microsoft.Azure.WebJobs.Extensions.WebPubSub</PackageId>
<PackageTags>Azure, WebPubSub</PackageTags>
<Description>Azure Functions extension for the WebPubSub service</Description>
<Version>1.1.0-beta.1</Version>
<Version>1.1.0</Version>
<!--The ApiCompatVersion is managed automatically and should not generally be modified manually.-->
<ApiCompatVersion>1.0.0</ApiCompatVersion>
<NoWarn>$(NoWarn);AZC0001;CS8632;CA1056;CA2227</NoWarn>
Expand All @@ -20,6 +20,11 @@

<ItemGroup>
<ProjectReference Include="..\..\Microsoft.Azure.WebPubSub.Common\src\Microsoft.Azure.WebPubSub.Common.csproj" />
<ProjectReference Include="..\..\Azure.Messaging.WebPubSub\src\Azure.Messaging.WebPubSub.csproj" />

<!--
TODO: Changing to a PackageReference since we only want to depend on the already GA'ed version of WebPubSub. Change back after release.
<ProjectReference Include="..\..\Azure.Messaging.WebPubSub\src\Azure.Messaging.WebPubSub.csproj" />
-->
<PackageReference Include="Azure.Messaging.WebPubSub" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,10 @@ internal static Dictionary<string, object> DecodeConnectionStates(this string co
if (!string.IsNullOrEmpty(connectionStates))
{
var states = new Dictionary<string, object>();
var rawData = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(Convert.FromBase64String(connectionStates));
foreach (var state in rawData)
var statesObj = JsonDocument.Parse(Convert.FromBase64String(connectionStates));
foreach (var item in statesObj.RootElement.EnumerateObject())
{
states.Add(state.Key, state.Value);
states.Add(item.Name, BinaryData.FromString(item.Value.GetRawText()));
}
return states;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.Azure.WebPubSub.Common;
using Newtonsoft.Json.Linq;
Expand Down Expand Up @@ -347,17 +348,30 @@ public void TestWebPubSubConnectionJsonSerialize()
}

[TestCase]
public void TestWebPubSubContext_UserEventStates()
public void TestWebPubSubContext_UserEventStates_Legacy()
{
WebPubSubConfigProvider.RegisterJsonConverter();
var states = new Dictionary<string, object>
{
{ "aKey", "aValue"},
{ "bKey", 123 },
{ "cKey", new StateTestClass() }
};
var decodedStates = states.EncodeConnectionStates().DecodeConnectionStates();
var context = new WebPubSubConnectionContext(connectionId: "connectionId", userId: "userA", eventName: "connected", eventType: WebPubSubEventType.System, hub: null, states: decodedStates);
var states =
new Dictionary<string, object>
{
{ "aKey", "aValue"},
{ "bKey", 123 },
{ "cKey", new StateTestClass() }
}
.EncodeConnectionStates()
.DecodeConnectionStates();
JialinXin marked this conversation as resolved.
Show resolved Hide resolved

// Use the Dictionary<string, object> states .ctor overload
var context = new WebPubSubConnectionContext(
eventType: WebPubSubEventType.System,
eventName: "connected",
hub: null,
connectionId: "connectionId",
userId: "userA",
signature: null,
origin: null,
states: states,
headers: null);
var test = new WebPubSubContext(new UserEventRequest(context, BinaryData.FromString("test"), WebPubSubDataType.Text));

var jObj = JObject.FromObject(test);
Expand All @@ -379,17 +393,140 @@ public void TestWebPubSubContext_UserEventStates()
Assert.AreEqual(123, states1["bKey"]);
}

[TestCase]
public void TestWebPubSubContext_UserEventStates()
{
WebPubSubConfigProvider.RegisterJsonConverter();
var states =
new Dictionary<string, object>
{
{ "aKey", "aValue"},
{ "bKey", 123 },
{ "cKey", new StateTestClass() }
}
.EncodeConnectionStates()
.DecodeConnectionStates()
.ToDictionary(p => p.Key, p => (BinaryData)p.Value);

// Use the Dictionary<string, BinaryData> connectionStates .ctor overload
var context = new WebPubSubConnectionContext(
eventType: WebPubSubEventType.System,
eventName: "connected",
hub: null,
connectionId: "connectionId",
userId: "userA",
connectionStates: states);
var test = new WebPubSubContext(new UserEventRequest(context, BinaryData.FromString("test"), WebPubSubDataType.Text));

var jObj = JObject.FromObject(test);
var request = jObj["request"];
Assert.NotNull(request);
Assert.AreEqual("test", request["data"].ToString());
Assert.NotNull(jObj["response"]);
Assert.AreEqual("", jObj["errorMessage"].ToString());
Assert.AreEqual("False", jObj["hasError"].ToString());
Assert.AreEqual("False", jObj["isPreflight"].ToString());
var context1 = request["connectionContext"];
Assert.NotNull(context1);
var states1 = context1["states"].ToObject<IReadOnlyDictionary<string, object>>();
Assert.NotNull(states1);
Assert.AreEqual("aValue", states1["aKey"]);
Assert.NotNull(states1);
Assert.AreEqual(123, states1["bKey"]);
}

[TestCase]
public void TestWebPubSubContext_UserEventStates_AllBinaryData()
{
var ex = Assert.Throws<ArgumentException>(() =>
new WebPubSubConnectionContext(
eventType: WebPubSubEventType.System,
eventName: "connected",
hub: null,
connectionId: "connectionId",
userId: "userA",
signature: null,
origin: null,
states: new Dictionary<string, object>
{
{ "aKey", "aValue"},
{ "bKey", 123 },
{ "cKey", new StateTestClass() }
},
headers: null));
Assert.AreEqual("states", ex.ParamName);
StringAssert.Contains("BinaryData", ex.Message);
}

[TestCase]
public void TestWebPubSubContext_UserEventStates_NotDoubleSerialized()
{
WebPubSubConfigProvider.RegisterJsonConverter();
JialinXin marked this conversation as resolved.
Show resolved Hide resolved
var states =
new Dictionary<string, object>
{
{ "aKey", "aValue"},
{ "bKey", 123 },
{ "cKey", new StateTestClass() }
}
.EncodeConnectionStates()
.DecodeConnectionStates()
.ToDictionary(p => p.Key, p => (BinaryData)p.Value);
string json = JsonSerializer.Serialize(
new WebPubSubConnectionContext(
eventType: WebPubSubEventType.System,
eventName: "connected",
hub: null,
connectionId: "connectionId",
userId: "userA",
connectionStates: states));
var jCtx = JObject.Parse(json);
Assert.AreEqual("aValue", jCtx["states"]["aKey"].Value<string>());
Assert.AreEqual(123, jCtx["states"]["bKey"].Value<int>());
Assert.AreEqual("GA", jCtx["states"]["cKey"]["Title"].Value<string>());
Assert.AreEqual(1, jCtx["states"]["cKey"]["Version"].Value<int>());
}

[TestCase]
public void TestWebPubSubContext_UserEventStates_CollectionsInSync()
{
WebPubSubConfigProvider.RegisterJsonConverter();
var states =
new Dictionary<string, object>
{
{ "aKey", "aValue"},
{ "bKey", 123 },
{ "cKey", new StateTestClass() }
}
.EncodeConnectionStates()
.DecodeConnectionStates()
.ToDictionary(p => p.Key, p => (BinaryData)p.Value);
var ctx = new WebPubSubConnectionContext(
eventType: WebPubSubEventType.System,
eventName: "connected",
hub: null,
connectionId: "connectionId",
userId: "userA",
connectionStates: states);
CollectionAssert.AreEquivalent(ctx.ConnectionStates.Keys, ctx.States.Keys);
Assert.AreEqual(ctx.ConnectionStates["aKey"].ToString(), ctx.States["aKey"]);
Assert.AreEqual(ctx.ConnectionStates["bKey"].ToString(), ctx.States["bKey"]);
Assert.AreEqual(ctx.ConnectionStates["cKey"].ToString(), ctx.States["cKey"]);
}

private static HttpResponseMessage BuildResponse(string input, RequestType requestType, bool hasTestStates = false)
{
Dictionary<string, object> states = null;
if (hasTestStates)
{
states = new Dictionary<string, object>
{
{ "testKey", "value1" },
};
states =
new Dictionary<string, object>
{
{ "testKey", "value" }
}
.EncodeConnectionStates().DecodeConnectionStates(); // Required now that we enforce BinaryData
}
var context = new WebPubSubConnectionContext(WebPubSubEventType.System, "connect", "testhub", "Connection-Id1", states: states);
var context = new WebPubSubConnectionContext(WebPubSubEventType.System, "connect", "testhub", "Connection-Id1", states: states, userId: null, signature: null, origin: null, headers: null);
return Utilities.BuildValidResponse(input, requestType, context);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,9 @@
# Release History

## 1.0.0-beta.2 (Unreleased)
## 1.0.0-beta.2 (2021-11-24)
JialinXin marked this conversation as resolved.
Show resolved Hide resolved

### Features Added

### Breaking Changes

### Bugs Fixed

### Other Changes
- Add a `ConnectionStates` dictionary to `UserEventResponse` and `ConnectEventResponse` to manage connection state.

## 1.0.0-beta.1 (2021-11-09)

Expand Down
Loading