Skip to content

Commit dc94809

Browse files
authored
Change WPS ConnectionContext.States to a Dict<string, BinaryData> (Azure#25504)
This looks more like the rest of Track 2 and also removes the `TryGetState<T>` method because BinaryData already has https://docs.microsoft.com/en-us/dotnet/api/system.binarydata.toobjectfromjson?view=dotnet-plat-ext-6.0#System_BinaryData_ToObjectFromJson__1_System_Text_Json_JsonSerializerOptions_.
1 parent e662d76 commit dc94809

34 files changed

+888
-305
lines changed

eng/Packages.Data.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
<PackageReference Update="Azure.Messaging.EventHubs" Version="5.6.2" />
8787
<PackageReference Update="Azure.Messaging.EventGrid" Version="4.7.0" />
8888
<PackageReference Update="Azure.Messaging.ServiceBus" Version="7.5.0" />
89-
<PackageReference Update="Azure.Messaging.WebPubSub" Version="1.0.0-beta.2" />
89+
<PackageReference Update="Azure.Messaging.WebPubSub" Version="1.0.0" />
9090
<PackageReference Update="Azure.Identity" Version="1.5.0" />
9191
<PackageReference Update="Azure.Security.KeyVault.Secrets" Version="4.2.0" />
9292
<PackageReference Update="Azure.Security.KeyVault.Keys" Version="4.2.0" />

sdk/webpubsub/Microsoft.Azure.WebJobs.Extensions.WebPubSub/CHANGELOG.md

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
# Release History
22

3-
## 1.1.0-beta.1 (Unreleased)
4-
5-
### Features Added
6-
7-
### Breaking Changes
3+
## 1.1.0 (2021-11-24)
84

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

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

1311
## 1.0.0 (2021-11-09)
1412

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Collections.Generic;
6+
using Newtonsoft.Json;
7+
using Newtonsoft.Json.Linq;
8+
9+
namespace Microsoft.Azure.WebJobs.Extensions.WebPubSub
10+
{
11+
/// <summary>
12+
/// JsonConverter works for Functions JavaScript language object converters.
13+
/// </summary>
14+
internal class ConnectionStatesNewtonsoftConverter : JsonConverter<IReadOnlyDictionary<string, BinaryData>>
15+
{
16+
public override IReadOnlyDictionary<string, BinaryData> ReadJson(JsonReader reader, Type objectType, IReadOnlyDictionary<string, BinaryData> existingValue, bool hasExistingValue, JsonSerializer serializer)
17+
{
18+
var dict = new Dictionary<string, BinaryData>();
19+
var jDict = JToken.Load(reader).ToObject<Dictionary<string, JToken>>();
20+
foreach (var item in jDict)
21+
{
22+
dict.Add(item.Key, BinaryData.FromString(JsonConvert.SerializeObject(item.Value)));
23+
}
24+
25+
return dict;
26+
}
27+
28+
public override void WriteJson(JsonWriter writer, IReadOnlyDictionary<string, BinaryData> value, JsonSerializer serializer)
29+
{
30+
writer.WriteStartObject();
31+
if (value != null)
32+
{
33+
foreach (KeyValuePair<string, BinaryData> pair in value)
34+
{
35+
writer.WritePropertyName(pair.Key);
36+
writer.WriteRawValue(pair.Value.ToString());
37+
}
38+
}
39+
writer.WriteEndObject();
40+
}
41+
}
42+
}

sdk/webpubsub/Microsoft.Azure.WebJobs.Extensions.WebPubSub/src/Bindings/JsonElementJsonConverter.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.

sdk/webpubsub/Microsoft.Azure.WebJobs.Extensions.WebPubSub/src/Bindings/Output/WebPubSubAction.cs

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,6 @@ internal string ActionName
2323
{
2424
return GetType().Name;
2525
}
26-
set
27-
{
28-
// used in type-less for deserialize.
29-
_ = value;
30-
}
3126
}
3227

3328
/// <summary>

sdk/webpubsub/Microsoft.Azure.WebJobs.Extensions.WebPubSub/src/Bindings/WebPubSubContextJsonConverter.cs

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,10 @@ public override WebPubSubContext ReadJson(JsonReader reader, Type objectType, We
2121
public override void WriteJson(JsonWriter writer, WebPubSubContext value, JsonSerializer serializer)
2222
{
2323
serializer.Converters.Add(new HttpResponseMessageJsonConverter());
24-
// Request is using System.Json, use string as bridge to convert.
25-
var request = ConvertString(value.Request);
2624
var jobj = new JObject();
2725
if (value.Request != null)
2826
{
29-
jobj.Add(new JProperty(WebPubSubContext.RequestPropertyName, JObject.Parse(request)));
27+
jobj.Add(new JProperty(WebPubSubContext.RequestPropertyName, JObject.FromObject(value.Request, serializer)));
3028
}
3129
if (value.Response != null)
3230
{
@@ -37,16 +35,5 @@ public override void WriteJson(JsonWriter writer, WebPubSubContext value, JsonSe
3735
jobj.Add(WebPubSubContext.IsPreflightPropertyName, value.IsPreflight);
3836
jobj.WriteTo(writer);
3937
}
40-
41-
private static string ConvertString(WebPubSubEventRequest request) =>
42-
request switch
43-
{
44-
ConnectedEventRequest connected => SystemJson.JsonSerializer.Serialize<ConnectedEventRequest>(connected),
45-
ConnectEventRequest connect => SystemJson.JsonSerializer.Serialize<ConnectEventRequest>(connect),
46-
UserEventRequest userEvent => SystemJson.JsonSerializer.Serialize<UserEventRequest>(userEvent),
47-
DisconnectedEventRequest disconnected => SystemJson.JsonSerializer.Serialize<DisconnectedEventRequest>(disconnected),
48-
PreflightRequest validation => SystemJson.JsonSerializer.Serialize<PreflightRequest>(validation),
49-
_ => null
50-
};
5138
}
5239
}

sdk/webpubsub/Microsoft.Azure.WebJobs.Extensions.WebPubSub/src/Config/WebPubSubConfigProvider.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ internal static void RegisterJsonConverter()
160160
{
161161
new StringEnumConverter(),
162162
new BinaryDataJsonConverter(),
163-
new JsonElementJsonConverter(),
163+
new ConnectionStatesNewtonsoftConverter(),
164164
},
165165
ContractResolver = new CamelCasePropertyNamesContractResolver()
166166
};
Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<TargetFrameworks>$(RequiredTargetFrameworks)</TargetFrameworks>
55
<PackageId>Microsoft.Azure.WebJobs.Extensions.WebPubSub</PackageId>
66
<PackageTags>Azure, WebPubSub</PackageTags>
77
<Description>Azure Functions extension for the WebPubSub service</Description>
8-
<Version>1.1.0-beta.1</Version>
8+
<Version>1.1.0</Version>
99
<!--The ApiCompatVersion is managed automatically and should not generally be modified manually.-->
1010
<ApiCompatVersion>1.0.0</ApiCompatVersion>
1111
<NoWarn>$(NoWarn);AZC0001;CS8632;CA1056;CA2227</NoWarn>
1212
<IsExtensionClientLibrary>true</IsExtensionClientLibrary>
1313
</PropertyGroup>
1414

15+
<ItemGroup>
16+
<Compile Include="..\..\Microsoft.Azure.WebPubSub.Common\src\Shared\ConnectionStatesConverter.cs" LinkBase="Bindings\Shared" />
17+
<Compile Include="..\..\Microsoft.Azure.WebPubSub.Common\src\Shared\WebPubSubStatusCode.cs" LinkBase="Bindings\Shared" />
18+
</ItemGroup>
19+
1520
<ItemGroup>
1621
<PackageReference Include="Microsoft.AspNetCore.Http" />
1722
<PackageReference Include="Microsoft.Azure.WebJobs" />
@@ -20,6 +25,11 @@
2025

2126
<ItemGroup>
2227
<ProjectReference Include="..\..\Microsoft.Azure.WebPubSub.Common\src\Microsoft.Azure.WebPubSub.Common.csproj" />
23-
<ProjectReference Include="..\..\Azure.Messaging.WebPubSub\src\Azure.Messaging.WebPubSub.csproj" />
28+
29+
<!--
30+
TODO: Changing to a PackageReference since we only want to depend on the already GA'ed version of WebPubSub. Change back after release.
31+
<ProjectReference Include="..\..\Azure.Messaging.WebPubSub\src\Azure.Messaging.WebPubSub.csproj" />
32+
-->
33+
<PackageReference Include="Azure.Messaging.WebPubSub" />
2434
</ItemGroup>
2535
</Project>

sdk/webpubsub/Microsoft.Azure.WebJobs.Extensions.WebPubSub/src/Services/WebPubSubRequestExtensions.cs

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
using Microsoft.AspNetCore.Http;
1414
using Microsoft.Azure.WebPubSub.Common;
1515
using Microsoft.Extensions.Primitives;
16+
using Newtonsoft.Json.Linq;
1617

1718
namespace Microsoft.Azure.WebJobs.Extensions.WebPubSub
1819
{
@@ -21,12 +22,6 @@ namespace Microsoft.Azure.WebJobs.Extensions.WebPubSub
2122
/// </summary>
2223
internal static class WebPubSubRequestExtensions
2324
{
24-
/// <summary>
25-
/// Parse request to system/user type ServiceRequest.
26-
/// </summary>
27-
/// <param name="request">Upstream HttpRequest.</param>
28-
/// <param name="options"></param>
29-
/// <returns>Deserialize <see cref="WebPubSubEventRequest"/></returns>
3025
public static async Task<WebPubSubEventRequest> ReadWebPubSubRequestAsync(this HttpRequest request, WebPubSubValidationOptions options)
3126
{
3227
if (request == null)
@@ -146,35 +141,34 @@ internal static bool IsValidSignature(this WebPubSubConnectionContext connection
146141
return false;
147142
}
148143

149-
internal static Dictionary<string, object> DecodeConnectionStates(this string connectionStates)
144+
internal static Dictionary<string, BinaryData> DecodeConnectionStates(this string connectionStates)
150145
{
151146
if (!string.IsNullOrEmpty(connectionStates))
152147
{
153148
var states = new Dictionary<string, object>();
154-
var rawData = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(Convert.FromBase64String(connectionStates));
155-
foreach (var state in rawData)
156-
{
157-
states.Add(state.Key, state.Value);
158-
}
159-
return states;
149+
return JsonSerializer.Deserialize<IReadOnlyDictionary<string, BinaryData>>(Convert.FromBase64String(connectionStates), ConnectionStatesConverter.Options)
150+
.ToDictionary(k => k.Key, v => v.Value);
160151
}
161152
return null;
162153
}
163154

164-
internal static Dictionary<string,object> UpdateStates(this WebPubSubConnectionContext connectionContext, IReadOnlyDictionary<string, object> newStates)
155+
internal static Dictionary<string, object> UpdateStates(this WebPubSubConnectionContext connectionContext, IReadOnlyDictionary<string, BinaryData> newStates)
165156
{
166157
// states cleared.
167158
if (newStates == null)
168159
{
169160
return null;
170161
}
171162

172-
if (connectionContext.States?.Count > 0 || newStates.Count > 0)
163+
if (connectionContext.ConnectionStates?.Count > 0 || newStates.Count > 0)
173164
{
174165
var states = new Dictionary<string, object>();
175-
if (connectionContext.States?.Count > 0)
166+
if (connectionContext.ConnectionStates?.Count > 0)
176167
{
177-
states = connectionContext.States.ToDictionary(x => x.Key, y => y.Value);
168+
foreach (var state in connectionContext.ConnectionStates)
169+
{
170+
states.Add(state.Key, state.Value);
171+
}
178172
}
179173

180174
// response states keep empty is no change.
@@ -194,7 +188,8 @@ internal static Dictionary<string,object> UpdateStates(this WebPubSubConnectionC
194188

195189
internal static string EncodeConnectionStates(this Dictionary<string, object> value)
196190
{
197-
return Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(value)));
191+
IReadOnlyDictionary<string, BinaryData> readOnlyDict = value.ToDictionary(x => x.Key, y => y.Value is BinaryData data ? data : FromObjectAsJsonExtended(y.Value));
192+
return Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonSerializer.Serialize(readOnlyDict, ConnectionStatesConverter.Options)));
198193
}
199194

200195
private static bool TryParseCloudEvents(this HttpRequest request, out WebPubSubConnectionContext connectionContext)
@@ -216,7 +211,7 @@ private static bool TryParseCloudEvents(this HttpRequest request, out WebPubSubC
216211
userId = request.Headers.GetFirstHeaderValueOrDefault(Constants.Headers.CloudEvents.UserId);
217212
}
218213

219-
Dictionary<string, object> states = null;
214+
Dictionary<string, BinaryData> states = null;
220215
// connection states.
221216
if (request.Headers.ContainsKey(Constants.Headers.CloudEvents.State))
222217
{
@@ -298,5 +293,18 @@ private static WebPubSubDataType GetDataType(this string mediaType) =>
298293
Constants.ContentTypes.JsonContentType => WebPubSubDataType.Json,
299294
_ => throw new ArgumentException($"Invalid content type: {mediaType}")
300295
};
296+
297+
// support JToken for backward compatiblity.
298+
private static BinaryData FromObjectAsJsonExtended<T>(T item, JsonSerializerOptions? options = null)
299+
{
300+
if (item is JToken)
301+
{
302+
return BinaryData.FromString(Newtonsoft.Json.JsonConvert.SerializeObject(item));
303+
}
304+
else
305+
{
306+
return BinaryData.FromObjectAsJson(item, options);
307+
}
308+
}
301309
}
302310
}

sdk/webpubsub/Microsoft.Azure.WebJobs.Extensions.WebPubSub/src/Trigger/WebPubSubTriggerDispatcher.cs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313
using Microsoft.Azure.WebPubSub.Common;
1414
using Microsoft.Extensions.Logging;
1515

16+
using NewtonsoftJsonLinq = Newtonsoft.Json.Linq;
17+
1618
namespace Microsoft.Azure.WebJobs.Extensions.WebPubSub
1719
{
1820
internal class WebPubSubTriggerDispatcher : IWebPubSubTriggerDispatcher
@@ -146,12 +148,15 @@ await executor.Executor.TryExecuteAsync(new TriggeredFunctionData
146148
// Skip no returns
147149
if (response != null)
148150
{
149-
var validResponse = Utilities.BuildValidResponse(response, requestType, context);
150-
151-
if (validResponse != null)
151+
if (response is WebPubSubEventResponse wpsResponse)
152152
{
153-
return validResponse;
153+
return Utilities.BuildValidResponse(wpsResponse, requestType, context);
154154
}
155+
if (response is NewtonsoftJsonLinq.JToken jResponse)
156+
{
157+
return Utilities.BuildValidResponse(jResponse, requestType, context);
158+
}
159+
155160
_logger.LogWarning($"Invalid response type {response.GetType()} regarding current request: {requestType}");
156161
}
157162
}
@@ -191,7 +196,7 @@ private static bool TryParseCloudEvents(HttpRequestMessage request, out WebPubSu
191196
{
192197
userId = values.SingleOrDefault();
193198
}
194-
Dictionary<string, object> states = null;
199+
Dictionary<string, BinaryData> states = null;
195200
if (request.Headers.TryGetValues(Constants.Headers.CloudEvents.State, out var connectionStates))
196201
{
197202
states = connectionStates.SingleOrDefault().DecodeConnectionStates();

0 commit comments

Comments
 (0)