diff --git a/Dan.Common/Extensions/HostBuilderExtensions.cs b/Dan.Common/Extensions/HostBuilderExtensions.cs index f472a1d..3bac46b 100644 --- a/Dan.Common/Extensions/HostBuilderExtensions.cs +++ b/Dan.Common/Extensions/HostBuilderExtensions.cs @@ -1,6 +1,4 @@ using System.Reflection; -using System.Text.Json; -using System.Text.Json.Serialization; using Azure.Core.Serialization; using Dan.Common.Interfaces; using Microsoft.Azure.Functions.Worker; @@ -15,9 +13,12 @@ namespace Dan.Common.Extensions; public static class HostBuilderExtensions { /// - /// Sets up the isolated worker function with default configuration and wiring with ConfigureFunctionsWorkerDefaults(), handling application insights - /// logging and correct JSON serialization settings. Also adds defaults services; HttpClientFactory with a circuit-breaker enabled named client (use Constants.SafeHttpClient) - /// which should be used for outbound requests to the data source. Also expects to find a service implementing IEvidenceSourceMetadata. + /// Sets up the isolated worker function with default configuration and wiring with ConfigureFunctionsWorkerDefaults(), + /// handling application insights + /// logging and correct JSON serialization settings. Also adds defaults services; HttpClientFactory with a + /// circuit-breaker enabled named client (use Constants.SafeHttpClient) + /// which should be used for outbound requests to the data source. Also expects to find a service implementing + /// IEvidenceSourceMetadata. /// /// The host builder /// The host builder for additional chaining @@ -25,66 +26,75 @@ public static class HostBuilderExtensions public static IHostBuilder ConfigureDanPluginDefaults(this IHostBuilder builder) { builder.ConfigureFunctionsWorkerDefaults(workerBuilder => - { - workerBuilder - // Using preview package Microsoft.Azure.Functions.Worker.ApplicationInsights, see https://github.com/Azure/azure-functions-dotnet-worker/pull/944 - // Requires APPLICATIONINSIGHTS_CONNECTION_STRING being set. Note that host.json logging settings will have to be replicated to worker.json - .AddApplicationInsights() - .AddApplicationInsightsLogger(); - }, options => { options.Serializer = new NewtonsoftJsonObjectSerializer(); }) + { + workerBuilder + // Using preview package Microsoft.Azure.Functions.Worker.ApplicationInsights, see https://github.com/Azure/azure-functions-dotnet-worker/pull/944 + // Requires APPLICATIONINSIGHTS_CONNECTION_STRING being set. Note that host.json logging settings will have to be replicated to worker.json + .AddApplicationInsights() + .AddApplicationInsightsLogger(); + }, options => + { + options.Serializer = new NewtonsoftJsonObjectSerializer( + // Use Newtonsoft.Json for serializing in order to support TypeNameHandling and other annotations on. This should be ported to System.Text.Json at some point. + new JsonSerializerSettings + { + TypeNameHandling = TypeNameHandling.Auto, + NullValueHandling = NullValueHandling.Ignore + }); + }) .ConfigureServices((context, services) => { services.AddLogging(); services.AddHttpClient(); - var openCircuitTimeSeconds = int.TryParse(context.Configuration["DefaultCircuitBreakerOpenCircuitTimeSeconds"], out var result) ? result : 10; - var failuresBeforeTripping = int.TryParse(context.Configuration["DefaultCircuitBreakerFailureBeforeTripping"], out result) ? result : 4; + var openCircuitTimeSeconds = + int.TryParse(context.Configuration["DefaultCircuitBreakerOpenCircuitTimeSeconds"], out var result) + ? result + : 10; + var failuresBeforeTripping = + int.TryParse(context.Configuration["DefaultCircuitBreakerFailureBeforeTripping"], out result) + ? result + : 4; - var registry = new PolicyRegistry() + var registry = new PolicyRegistry { { Constants.SafeHttpClientPolicy, HttpPolicyExtensions.HandleTransientHttpError() .CircuitBreakerAsync( - handledEventsAllowedBeforeBreaking: failuresBeforeTripping, - durationOfBreak: TimeSpan.FromSeconds(openCircuitTimeSeconds)) + failuresBeforeTripping, + TimeSpan.FromSeconds(openCircuitTimeSeconds)) } }; services.AddPolicyRegistry(registry); - var httpClientTimeoutSeconds = int.TryParse(context.Configuration["SafeHttpClientTimeout"], out result) ? result : 30; + var httpClientTimeoutSeconds = int.TryParse(context.Configuration["SafeHttpClientTimeout"], out result) + ? result + : 30; // Client configured with circuit breaker policies services.AddHttpClient(Constants.SafeHttpClient, client => { client.Timeout = TimeSpan.FromSeconds(httpClientTimeoutSeconds); }) .AddPolicyHandlerFromRegistry(Constants.SafeHttpClientPolicy); - services.Configure(options => - { - options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; - options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull; - options.Converters.Add(new JsonStringEnumConverter()); - }); - - // Try to add the first IEvidenceSourceMetadata implementation we can find + // Try to add the first IEvidenceSourceMetadata implementation we can find in the entry assembly var evidenceSourceMetadataServiceType = typeof(IEvidenceSourceMetadata); - var assembly = Assembly.GetExecutingAssembly(); + var assembly = Assembly.GetEntryAssembly(); - var implementationType = assembly.GetTypes() - .FirstOrDefault(t => t.GetInterfaces().Any(i => i == evidenceSourceMetadataServiceType)); + var implementationType = assembly?.GetTypes() + .FirstOrDefault(t => t.GetInterfaces().Any(i => i == evidenceSourceMetadataServiceType)); if (implementationType == null) - { throw new NotImplementedException( - $"Missing implementation of {nameof(IEvidenceSourceMetadata)}"); - } + $"Missing implementation of {nameof(IEvidenceSourceMetadata)} in entry assembly {assembly?.FullName ?? "(unmanaged assembly)"}"); - if (!services.Any(s => s.ServiceType == evidenceSourceMetadataServiceType && s.ImplementationType == implementationType)) - { - services.Add(new ServiceDescriptor(evidenceSourceMetadataServiceType, implementationType, ServiceLifetime.Singleton)); - } + if (!services.Any(s => + s.ServiceType == evidenceSourceMetadataServiceType && + s.ImplementationType == implementationType)) + services.Add(new ServiceDescriptor(evidenceSourceMetadataServiceType, implementationType, + ServiceLifetime.Singleton)); }); return builder; } -} +} \ No newline at end of file