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

Add Newtonsoft CamelCase config support #248

Merged
merged 4 commits into from
Jun 30, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 18 additions & 14 deletions src/SignalRServiceExtension/Config/DependencyInjectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using Microsoft.AspNetCore.SignalR.Protocol;
using Microsoft.Azure.SignalR.Management;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Newtonsoft.Json;

namespace Microsoft.Azure.WebJobs.Extensions.SignalRService
{
Expand All @@ -15,18 +15,27 @@ internal static class DependencyInjectionExtensions

public static IServiceCollection SetHubProtocol(this IServiceCollection services, IConfiguration configuration)
{
if (Environment.Version.Major == 4 && configuration[Constants.AzureSignalRHubProtocol] != null)
var hubProtocolConfig = configuration[Constants.AzureSignalRHubProtocol];
if (hubProtocolConfig is not null && Environment.Version.Major == 4)
{
// Actually is .Net Core 2.x
// .Net Core 2.x is always Newtonsoft.Json.
throw new InvalidOperationException(HubProtocolError);
}
#if NETCOREAPP3_1 || NETCOREAPP3_0 || NETSTANDARD2_0
else if (!DotnetRuntime(configuration) || UserSpecifyNewtonsoft(configuration))

//indicates Newtonsoft, camcelCase
if (configuration.GetValue(Constants.AzureSignalRNewtonsoftCamelCase, false))
{
// .Net Core 3.1, overwrite the System.Text.Json Protocol.
services.TryAddEnumerable(ServiceDescriptor.Singleton<IHubProtocol, NewtonsoftJsonHubProtocol>());
// The default options is camelCase.
return services.AddNewtonsoftHubProtocol(o => { });
}
#endif

if (!DotnetRuntime(configuration) || Enum.TryParse<HubProtocol>(hubProtocolConfig, out var result) && result == HubProtocol.NewtonsoftJson)
{
// Reset the options to keep backward compatibility.
return services.AddNewtonsoftHubProtocol(o => o.PayloadSerializerSettings = new JsonSerializerSettings());
}

//If hubProtocolConfig is SystemTextJson for .Net Core 3.1, do nothing, as transient mode doesn't accept it and persisent mode is already System.Text.Json by default.
return services;
}

Expand All @@ -36,10 +45,5 @@ private static bool DotnetRuntime(IConfiguration configuration)
//unit test environment
return workerRuntime == null || workerRuntime == Constants.DotnetWorker;
}

private static bool UserSpecifyNewtonsoft(IConfiguration configuration)
{
return configuration.GetValue(Constants.AzureSignalRHubProtocol, HubProtocol.SystemTextJson) == HubProtocol.NewtonsoftJson;
}
}
}
1 change: 1 addition & 0 deletions src/SignalRServiceExtension/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ internal static class Constants
public const string AzureSignalRConnectionStringName = "AzureSignalRConnectionString";
public const string AzureSignalREndpoints = "Azure:SignalR:Endpoints";
public const string AzureSignalRHubProtocol = "Azure:SignalR:HubProtocol";
public static string AzureSignalRNewtonsoftCamelCase = $"{AzureSignalRHubProtocol}:{HubProtocol.NewtonsoftJson}:CamelCase";
public const string ServiceTransportTypeName = "AzureSignalRServiceTransportType";
public const string AsrsHeaderPrefix = "X-ASRS-";
public const string AsrsConnectionIdHeader = AsrsHeaderPrefix + "Connection-Id";
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using Microsoft.AspNetCore.SignalR.Protocol;
using Microsoft.Azure.SignalR.Management;
using Microsoft.Azure.SignalR.Tests.Common;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Serialization;
using Xunit;

namespace SignalRServiceExtension.Tests
{
public class SetNewtonsoftHubProtocolFacts
{
[Fact]
public void EmptyHubProtocolSetting_DoNothing()
{
var configuration = new ConfigurationBuilder().AddInMemoryCollection().Build();
var services = new ServiceCollection()
.SetHubProtocol(configuration);
Assert.Empty(services);
}

#if NETCOREAPP2_1

[Fact]
public void SetHubProtocol_Throw()
{
var configuration = new ConfigurationBuilder().AddInMemoryCollection().Build();
configuration[Constants.AzureSignalRHubProtocol] = HubProtocol.SystemTextJson.ToString();
var services = new ServiceCollection();
Assert.Throws<InvalidOperationException>(() => services.SetHubProtocol(configuration));
}

#endif

#if NETCOREAPP3_1

[Fact]
public void SetSystemTextJson_DoNothing()
{
var configuration = new ConfigurationBuilder().AddInMemoryCollection().Build();
configuration[Constants.AzureSignalRHubProtocol] = HubProtocol.SystemTextJson.ToString();
var services = new ServiceCollection()
.SetHubProtocol(configuration);
Assert.Empty(services);
}

[Fact]
public async Task SetNewtonsoftPersistent()
{
var serviceManagerStore = CreateServiceManagerStore(out var configuration);
configuration[Constants.ServiceTransportTypeName] = "Persistent";
var hubContext = await serviceManagerStore.GetOrAddByConnectionStringKey(Constants.AzureSignalRConnectionStringName).GetAsync("hub") as ServiceHubContextImpl;
var hubProtocol = hubContext.ServiceProvider.GetRequiredService<IHubProtocol>();
Assert.IsType<NewtonsoftJsonHubProtocol>(hubProtocol);
var newtonsoftOptions = hubContext.ServiceProvider.GetRequiredService<IOptions<NewtonsoftJsonHubProtocolOptions>>();
Assert.Null(newtonsoftOptions.Value.PayloadSerializerSettings.ContractResolver);
}

[Fact]
public async Task SetNewtonsoftInTransientModeAsync()
{
var serviceManagerStore = CreateServiceManagerStore(out var configuration);
var hubContext = await serviceManagerStore.GetOrAddByConnectionStringKey(Constants.AzureSignalRConnectionStringName).GetAsync("hub") as ServiceHubContextImpl;

var restHubProtocol = hubContext.ServiceProvider.GetRequiredService<IRestHubProtocol>();
Assert.IsType<NewtonsoftRestHubProtocol>(restHubProtocol);
var newtonsoftOptions = hubContext.ServiceProvider.GetRequiredService<IOptions<NewtonsoftServiceHubProtocolOptions>>();
Assert.Null(newtonsoftOptions.Value.PayloadSerializerSettings.ContractResolver);
}

[Fact]
public async Task SetNewtonsoftCamelCaseInTransientModeAsync()
{
var serviceManagerStore = CreateServiceManagerStore(out var configuration);
configuration["Azure:SignalR:HubProtocol:NewtonsoftJson:CamelCase"] = "true";

var hubContext = await serviceManagerStore.GetOrAddByConnectionStringKey(Constants.AzureSignalRConnectionStringName).GetAsync("hub") as ServiceHubContextImpl;
var restHubProtocol = hubContext.ServiceProvider.GetRequiredService<IRestHubProtocol>();
Assert.IsType<NewtonsoftRestHubProtocol>(restHubProtocol);
var newtonsoftOptions = hubContext.ServiceProvider.GetRequiredService<IOptions<NewtonsoftServiceHubProtocolOptions>>();
Assert.IsType<CamelCasePropertyNamesContractResolver>(newtonsoftOptions.Value.PayloadSerializerSettings.ContractResolver);
}

[Fact]
public async Task SetNewtonsoftCamelCaseInPersistentModeAsync()
{
var serviceManagerStore = CreateServiceManagerStore(out var configuration);
configuration["Azure:SignalR:HubProtocol:NewtonsoftJson:CamelCase"] = "true";
configuration[Constants.ServiceTransportTypeName] = "Persistent";

var hubContext = await serviceManagerStore.GetOrAddByConnectionStringKey(Constants.AzureSignalRConnectionStringName).GetAsync("hub") as ServiceHubContextImpl;
var hubProtocol = hubContext.ServiceProvider.GetRequiredService<IHubProtocol>();
Assert.IsType<NewtonsoftJsonHubProtocol>(hubProtocol);
var newtonsoftOptions = hubContext.ServiceProvider.GetRequiredService<IOptions<NewtonsoftJsonHubProtocolOptions>>();
Assert.IsType<CamelCasePropertyNamesContractResolver>(newtonsoftOptions.Value.PayloadSerializerSettings.ContractResolver);
}

private ServiceManagerStore CreateServiceManagerStore(out IConfiguration configuration)
{
configuration = new ConfigurationBuilder().AddInMemoryCollection().Build();
configuration[Constants.AzureSignalRHubProtocol] = HubProtocol.NewtonsoftJson.ToString();
configuration[Constants.AzureSignalRConnectionStringName] = FakeEndpointUtils.GetFakeConnectionString(1).Single();

return new ServiceManagerStore(configuration, NullLoggerFactory.Instance, null);
}
#endif
}
}