From 673cb37edf2aad6d8b18fb092168db2e5e923471 Mon Sep 17 00:00:00 2001 From: luczhan Date: Wed, 13 Jul 2022 15:00:01 -0700 Subject: [PATCH 01/17] add benchmark project --- Directory.Packages.props | 1 + ...re.WebJobs.Extensions.Sql.Benchmark.csproj | 19 + benchmark/Program.cs | 19 + benchmark/SqlInputBindingBenchmarks.cs | 83 + benchmark/packages.lock.json | 2282 +++++++++++++++++ samples/samples-csharp/local.settings.json | 5 +- test/Common/TestUtils.cs | 1 + test/Integration/IntegrationTestBase.cs | 62 +- 8 files changed, 2442 insertions(+), 30 deletions(-) create mode 100644 benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj create mode 100644 benchmark/Program.cs create mode 100644 benchmark/SqlInputBindingBenchmarks.cs create mode 100644 benchmark/packages.lock.json diff --git a/Directory.Packages.props b/Directory.Packages.props index 469da8513..b0f8aefed 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -15,5 +15,6 @@ + \ No newline at end of file diff --git a/benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj b/benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj new file mode 100644 index 000000000..f143eff35 --- /dev/null +++ b/benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj @@ -0,0 +1,19 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/benchmark/Program.cs b/benchmark/Program.cs new file mode 100644 index 000000000..33681e54c --- /dev/null +++ b/benchmark/Program.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using BenchmarkDotNet.Running; +using BenchmarkDotNet.Configs; + +namespace Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark +{ + public class Program + { + public static void Main() + { + BenchmarkRunner.Run( + ManualConfig + .Create(DefaultConfig.Instance) + .WithOptions(ConfigOptions.DisableOptimizationsValidator)); + } + } +} \ No newline at end of file diff --git a/benchmark/SqlInputBindingBenchmarks.cs b/benchmark/SqlInputBindingBenchmarks.cs new file mode 100644 index 000000000..b73c1fa5f --- /dev/null +++ b/benchmark/SqlInputBindingBenchmarks.cs @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; +using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common; +using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples; +using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Integration; +using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common; +using BenchmarkDotNet.Attributes; + + +namespace Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark +{ + public class InputBindingBenchmarks : IntegrationTestBase + { + [GlobalSetup] + public void GlobalSetup() + { + this.SetupDatabase(); + this.StartFunctionHost(nameof(GetProducts), SupportedLanguages.CSharp); + + // Generate T-SQL to insert n rows of data with cost + Product[] products = GetProductsWithSameCost(10, 100); + this.InsertProducts(products); + } + + [Benchmark] + [Arguments("getproducts", "100")] + public async Task GetProductsTest(string function, string args) + { + // Run the function + return await this.SendInputRequest(function, args); + } + + [GlobalCleanup] + public void GlobalCleanup() + { + Console.WriteLine("-----GLOBAL CLEANUP CALLED-----"); + this.Dispose(); + } + + private async Task SendInputRequest(string functionName, string query = "") + { + string requestUri = $"http://localhost:{this.Port}/api/{functionName}/{query}"; + + return await this.SendGetRequest(requestUri); + } + + private static Product[] GetProductsWithSameCost(int n, int cost) + { + var result = new Product[n]; + for (int i = 0; i < n; i++) + { + result[i] = new Product + { + ProductID = i, + Name = "test", + Cost = cost + }; + } + return result; + } + + private void InsertProducts(Product[] products) + { + if (products.Length == 0) + { + return; + } + + var queryBuilder = new StringBuilder(); + foreach (Product p in products) + { + queryBuilder.AppendLine($"INSERT INTO dbo.Products VALUES({p.ProductID}, '{p.Name}', {p.Cost});"); + } + + this.ExecuteNonQuery(queryBuilder.ToString()); + } + } +} \ No newline at end of file diff --git a/benchmark/packages.lock.json b/benchmark/packages.lock.json new file mode 100644 index 000000000..343cf446a --- /dev/null +++ b/benchmark/packages.lock.json @@ -0,0 +1,2282 @@ +{ + "version": 2, + "dependencies": { + ".NETCoreApp,Version=v3.1": { + "BenchmarkDotNet": { + "type": "Direct", + "requested": "[0.13.1, )", + "resolved": "0.13.1", + "contentHash": "LWR6kL3MWc4ByzSrqi6nccbO4UT5pySiB5h9L2LSHoqVdHySTbtLYYulz3atWhPyhtIQIMz6kQjvuBjFM03zkA==", + "dependencies": { + "BenchmarkDotNet.Annotations": "0.13.1", + "CommandLineParser": "2.4.3", + "Iced": "1.8.0", + "Microsoft.CodeAnalysis.CSharp": "2.10.0", + "Microsoft.Diagnostics.NETCore.Client": "0.2.61701", + "Microsoft.Diagnostics.Runtime": "1.1.126102", + "Microsoft.Diagnostics.Tracing.TraceEvent": "2.0.61", + "Microsoft.DotNet.PlatformAbstractions": "2.1.0", + "Microsoft.Win32.Registry": "4.5.0", + "Perfolizer": "0.2.1", + "System.Management": "4.5.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Emit.Lightweight": "4.3.0", + "System.Threading.Tasks.Extensions": "4.5.2", + "System.ValueTuple": "4.5.0" + } + }, + "Azure.Core": { + "type": "Transitive", + "resolved": "1.19.0", + "contentHash": "lcDjG635DPE4fU5tqSueVMmzrx0QrIfPuY0+y6evHN5GanQ0GB+/4nuMHMmoNPwEow6OUPkJu4cZQxfHJQXPdA==", + "dependencies": { + "Microsoft.Bcl.AsyncInterfaces": "1.0.0", + "System.Buffers": "4.5.1", + "System.Diagnostics.DiagnosticSource": "4.6.0", + "System.Memory": "4.5.4", + "System.Memory.Data": "1.0.2", + "System.Numerics.Vectors": "4.5.0", + "System.Text.Encodings.Web": "4.7.2", + "System.Text.Json": "4.6.0", + "System.Threading.Tasks.Extensions": "4.5.2" + } + }, + "Azure.Identity": { + "type": "Transitive", + "resolved": "1.4.0", + "contentHash": "vvjdoDQb9WQyLkD1Uo5KFbwlW7xIsDMihz3yofskym2SimXswbSXuK7QSR1oHnBLBRMdamnVHLpSKQZhJUDejg==", + "dependencies": { + "Azure.Core": "1.14.0", + "Microsoft.Identity.Client": "4.30.1", + "Microsoft.Identity.Client.Extensions.Msal": "2.18.4", + "System.Memory": "4.5.4", + "System.Security.Cryptography.ProtectedData": "4.5.0", + "System.Text.Json": "4.6.0", + "System.Threading.Tasks.Extensions": "4.5.2" + } + }, + "Azure.Storage.Blobs": { + "type": "Transitive", + "resolved": "12.10.0", + "contentHash": "yaijs9DPfn34C/X4TX+0TAxANEhuKSrFE650gkF9g1pz/nQljv86zOOtDwNwD5UsAY5LyrOiCASGo2dhuIxvdg==", + "dependencies": { + "Azure.Storage.Common": "12.9.0", + "System.Text.Json": "4.6.0" + } + }, + "Azure.Storage.Common": { + "type": "Transitive", + "resolved": "12.9.0", + "contentHash": "GuoigTmzz9HrCGdcdu7LyjD4pDr2XPt72LlWWTDyno+nYrjyuNwpwRFBvK/brxJvQFRHofQcBskf8vOxVxnI8g==", + "dependencies": { + "Azure.Core": "1.19.0" + } + }, + "Azure.Storage.Queues": { + "type": "Transitive", + "resolved": "12.8.0", + "contentHash": "YR60fGZHXfUDffSq0zG7RIk9M0LQsS0FNDyslmuP0YS+sOl93TKQXV1wYSROg64VrJoXfLAqP1jRIaeLEhQ/hw==", + "dependencies": { + "Azure.Storage.Common": "12.9.0", + "System.Memory.Data": "1.0.2", + "System.Text.Json": "4.6.0" + } + }, + "BenchmarkDotNet.Annotations": { + "type": "Transitive", + "resolved": "0.13.1", + "contentHash": "OvHMw/GYfdrrJAM28zOXQ94kdv1s0s92ZrbkH+/79I557ONPEH/urMF8iNKuYYgLsziC4isw233L3GKq6Twe/A==" + }, + "Castle.Core": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "b5rRL5zeaau1y/5hIbI+6mGw3cwun16YjkHZnV9RRT5UyUIFsgLmNXJ0YnIN9p8Hw7K7AbG1q1UclQVU3DinAQ==", + "dependencies": { + "NETStandard.Library": "1.6.1", + "System.Collections.Specialized": "4.3.0", + "System.ComponentModel": "4.3.0", + "System.ComponentModel.TypeConverter": "4.3.0", + "System.Diagnostics.TraceSource": "4.3.0", + "System.Dynamic.Runtime": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Xml.XmlDocument": "4.3.0" + } + }, + "CommandLineParser": { + "type": "Transitive", + "resolved": "2.4.3", + "contentHash": "U2FC9Y8NyIxxU6MpFFdWWu1xwiqz/61v/Doou7kmVjpeIEMLWyiNNkzNlSE84kyJ0O1LKApuEj5z48Ow0Hi4OQ==" + }, + "Iced": { + "type": "Transitive", + "resolved": "1.8.0", + "contentHash": "KQqoTZg3wf+eqG8ztGqlz9TozC/Dw/jnN82JkIRGZXTg/by0aPiQIMGb+b7hvvkOLnmCuWr3Ghr0mA2I+ASX1A==" + }, + "Microsoft.AspNet.WebApi.Client": { + "type": "Transitive", + "resolved": "5.2.4", + "contentHash": "OdBVC2bQWkf9qDd7Mt07ev4SwIdu6VmLBMTWC0D5cOP/HWSXyv/77otwtXVrAo42duNjvXOjzjP5oOI9m1+DTQ==", + "dependencies": { + "Newtonsoft.Json": "10.0.1", + "Newtonsoft.Json.Bson": "1.0.1" + } + }, + "Microsoft.AspNetCore.Authentication.Abstractions": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "7hfl2DQoATexr0OVw8PwJSNqnu9gsbSkuHkwmHdss5xXCuY2nIfsTjj2NoKeGtp6N94ECioAP78FUfFOMj+TTg==", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "2.1.0", + "Microsoft.Extensions.Logging.Abstractions": "2.1.0", + "Microsoft.Extensions.Options": "2.1.0" + } + }, + "Microsoft.AspNetCore.Authentication.Core": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "NKbmBzPW2zTaZLNKkCIL7LMpr4XfXVOPJ5SNzikTe2PX3juLkupb/5oTF45wiw5srUbU6QD0cY9u3jgYUELwnQ==", + "dependencies": { + "Microsoft.AspNetCore.Authentication.Abstractions": "2.1.0", + "Microsoft.AspNetCore.Http": "2.1.0", + "Microsoft.AspNetCore.Http.Extensions": "2.1.0" + } + }, + "Microsoft.AspNetCore.Authorization": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "QUMtMVY7mQeJWlP8wmmhZf1HEGM/V8prW/XnYeKDpEniNBCRw0a3qktRb9aBU0vR+bpJwWZ0ibcB8QOvZEmDHQ==", + "dependencies": { + "Microsoft.Extensions.Logging.Abstractions": "2.1.0", + "Microsoft.Extensions.Options": "2.1.0" + } + }, + "Microsoft.AspNetCore.Authorization.Policy": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "e/wxbmwHza+Y6hmM/xiQdsVX5Xh0cPHFbDTGR3kIK7a+jyBSc8CPAJOA5g0ziikLEp5Cm/Qux+CsWad53QoNOw==", + "dependencies": { + "Microsoft.AspNetCore.Authentication.Abstractions": "2.1.0", + "Microsoft.AspNetCore.Authorization": "2.1.0" + } + }, + "Microsoft.AspNetCore.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "1TQgBfd/NPZLR2o/h6l5Cml2ZCF5hsyV4h9WEwWwAIavrbdTnaNozGGcTOd4AOgQvogMM9UM1ajflm9Cwd0jLQ==", + "dependencies": { + "Microsoft.AspNetCore.Hosting.Server.Abstractions": "2.1.0", + "Microsoft.AspNetCore.Http.Abstractions": "2.1.0", + "Microsoft.Extensions.Hosting.Abstractions": "2.1.0" + } + }, + "Microsoft.AspNetCore.Hosting.Server.Abstractions": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "YTKMi2vHX6P+WHEVpW/DS+eFHnwivCSMklkyamcK1ETtc/4j8H3VR0kgW8XIBqukNxhD8k5wYt22P7PhrWSXjQ==", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.1.0", + "Microsoft.Extensions.Configuration.Abstractions": "2.1.0" + } + }, + "Microsoft.AspNetCore.Http.Abstractions": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "kQUEVOU4loc8CPSb2WoHFTESqwIa8Ik7ysCBfTwzHAd0moWovc9JQLmhDIHlYLjHbyexqZAlkq/FPRUZqokebw==", + "dependencies": { + "Microsoft.AspNetCore.Http.Features": "2.1.1", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.AspNetCore.Http.Extensions": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "M8Gk5qrUu5nFV7yE3SZgATt/5B1a5Qs8ZnXXeO/Pqu68CEiBHJWc10sdGdO5guc3zOFdm7H966mVnpZtEX4vSA==", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "2.1.0", + "Microsoft.Extensions.FileProviders.Abstractions": "2.1.0", + "Microsoft.Net.Http.Headers": "2.1.0", + "System.Buffers": "4.5.0" + } + }, + "Microsoft.AspNetCore.Http.Features": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "VklZ7hWgSvHBcDtwYYkdMdI/adlf7ebxTZ9kdzAhX+gUs5jSHE9mZlTamdgf9miSsxc1QjNazHXTDJdVPZKKTw==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.1.1" + } + }, + "Microsoft.AspNetCore.JsonPatch": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "JE5LRurYn0rglbY/Nj3sB1a+yGPacyYHsuLRgvZtmjLG73R0zEfSIjGmzwtIym0HDLX0RIym8q+BLH4w1nWdog==", + "dependencies": { + "Microsoft.CSharp": "4.5.0", + "Newtonsoft.Json": "11.0.2" + } + }, + "Microsoft.AspNetCore.Mvc.Abstractions": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "NhocJc6vRjxjM8opxpbjYhdN7WbsW07eT5hZOzv87bPxwEL98Hw+D+JIu9DsPm0ce7Rao1qN1BP7w8GMhRFH0Q==", + "dependencies": { + "Microsoft.AspNetCore.Routing.Abstractions": "2.1.0", + "Microsoft.Net.Http.Headers": "2.1.0" + } + }, + "Microsoft.AspNetCore.Mvc.Core": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "AtNtFLtFgZglupwiRK/9ksFg1xAXyZ1otmKtsNSFn9lIwHCQd1xZHIph7GTZiXVWn51jmauIUTUMSWdpaJ+f+A==", + "dependencies": { + "Microsoft.AspNetCore.Authentication.Core": "2.1.0", + "Microsoft.AspNetCore.Authorization.Policy": "2.1.0", + "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0", + "Microsoft.AspNetCore.Http": "2.1.0", + "Microsoft.AspNetCore.Http.Extensions": "2.1.0", + "Microsoft.AspNetCore.Mvc.Abstractions": "2.1.0", + "Microsoft.AspNetCore.ResponseCaching.Abstractions": "2.1.0", + "Microsoft.AspNetCore.Routing": "2.1.0", + "Microsoft.Extensions.DependencyInjection": "2.1.0", + "Microsoft.Extensions.DependencyModel": "2.1.0", + "Microsoft.Extensions.FileProviders.Abstractions": "2.1.0", + "Microsoft.Extensions.Logging.Abstractions": "2.1.0", + "System.Diagnostics.DiagnosticSource": "4.5.0", + "System.Threading.Tasks.Extensions": "4.5.0" + } + }, + "Microsoft.AspNetCore.Mvc.Formatters.Json": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "Xkbx6LWehUL44rx0gcry+qY013m5LbAjqWfdeisdiSPx2bU/q4EdteRY+zDmO8vT3jKbWcAuvTVUf6AcPPQpTQ==", + "dependencies": { + "Microsoft.AspNetCore.JsonPatch": "2.1.0", + "Microsoft.AspNetCore.Mvc.Core": "2.1.0" + } + }, + "Microsoft.AspNetCore.Mvc.WebApiCompatShim": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "pYsNGveHyMCHQ+xpUIsTHtFFv7Xm+q2pmL3UmL6QujO5ICu/bcnSlwu9FEQhXYQ+cDxfO2VShdM/OrkWzNFGFw==", + "dependencies": { + "Microsoft.AspNet.WebApi.Client": "5.2.4", + "Microsoft.AspNetCore.Mvc.Core": "2.1.0", + "Microsoft.AspNetCore.Mvc.Formatters.Json": "2.1.0", + "Microsoft.AspNetCore.WebUtilities": "2.1.0" + } + }, + "Microsoft.AspNetCore.ResponseCaching.Abstractions": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "Ht/KGFWYqcUDDi+VMPkQNzY7wQ0I2SdqXMEPl6AsOW8hmO3ZS4jIPck6HGxIdlk7ftL9YITJub0cxBmnuq+6zQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.1.0" + } + }, + "Microsoft.AspNetCore.Routing": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "eRdsCvtUlLsh0O2Q8JfcpTUhv0m5VCYkgjZTCdniGAq7F31B3gNrBTn9VMqz14m+ZxPUzNqudfDFVTAQlrI/5Q==", + "dependencies": { + "Microsoft.AspNetCore.Http.Extensions": "2.1.0", + "Microsoft.AspNetCore.Routing.Abstractions": "2.1.0", + "Microsoft.Extensions.Logging.Abstractions": "2.1.0", + "Microsoft.Extensions.ObjectPool": "2.1.0", + "Microsoft.Extensions.Options": "2.1.0" + } + }, + "Microsoft.AspNetCore.Routing.Abstractions": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "LXmnHeb3v+HTfn74M46s+4wLaMkplj1Yl2pRf+2mfDDsQ7PN0+h8AFtgip5jpvBvFHQ/Pei7S+cSVsSTHE67fQ==", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "2.1.0" + } + }, + "Microsoft.AspNetCore.WebUtilities": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "PGKIZt4+412Z/XPoSjvYu/QIbTxcAQuEFNoA1Pw8a9mgmO0ZhNBmfaNyhgXFf7Rq62kP0tT/2WXpxdcQhkFUPA==", + "dependencies": { + "Microsoft.Net.Http.Headers": "2.1.1", + "System.Text.Encodings.Web": "4.5.0" + } + }, + "Microsoft.Azure.Functions.Analyzers": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "8nQq/IlK9BMBchRw3lfChSKaFNjMUOxXcPcDC3rkMd5PeWRm54nz2Owr6fZjPHMYJ36XX/9PGOfjn4jyiRojjw==" + }, + "Microsoft.Azure.WebJobs.Core": { + "type": "Transitive", + "resolved": "3.0.31", + "contentHash": "iStV0MQ9env8R2F+8cbaynMK4TDkU6bpPVLdOzIs83iriHYMF+uWF6WZWI8ZJahWR37puQWc86u1YCsoIZEocA==", + "dependencies": { + "System.ComponentModel.Annotations": "4.4.0", + "System.Diagnostics.TraceSource": "4.3.0" + } + }, + "Microsoft.Azure.WebJobs.Extensions": { + "type": "Transitive", + "resolved": "3.0.6", + "contentHash": "y7RgGsJFHhlD/2SIoQpgyjM1By9sbBpY9YaQCS183z743rErqBGAO3BrJ01z52IIveP2NW/7qLD1zm/bFI2MPg==", + "dependencies": { + "Microsoft.Azure.WebJobs": "3.0.14", + "Microsoft.Azure.WebJobs.Host.Storage": "3.0.14", + "ncrontab.signed": "3.3.0" + } + }, + "Microsoft.Azure.WebJobs.Extensions.Http": { + "type": "Transitive", + "resolved": "3.0.2", + "contentHash": "JvC3fESMMbNkYbpaJ4vkK4Xaw1yZy4HSxxqwoaI3Ls2Y5/qBrHftPy0WJQgmXcGjgE/o/aAmuixdTfrj5OQDJQ==", + "dependencies": { + "Microsoft.AspNet.WebApi.Client": "5.2.4", + "Microsoft.AspNetCore.Http": "2.1.0", + "Microsoft.AspNetCore.Mvc.Formatters.Json": "2.1.0", + "Microsoft.AspNetCore.Mvc.WebApiCompatShim": "2.1.0", + "Microsoft.AspNetCore.Routing": "2.1.0", + "Microsoft.Azure.WebJobs": "3.0.2" + } + }, + "Microsoft.Azure.WebJobs.Extensions.Storage.Blobs": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "7zfLwrm2xqx2+NaYpFk3Jp4x56Rr/RHFyawaDbdlGmfK+8UDoCZMkwk65j0eg+bVtxNaorOojK5PfX39kUo9dQ==", + "dependencies": { + "Azure.Storage.Blobs": "12.10.0", + "Azure.Storage.Queues": "12.8.0", + "Microsoft.Azure.WebJobs": "3.0.30", + "Microsoft.Extensions.Azure": "1.1.1" + } + }, + "Microsoft.Azure.WebJobs.Extensions.Storage.Queues": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "TvpZFiQ13W926FxpZxhbP2v1yJyy3tO1chBcJhvJmGE45Rox3sOb9ED36SJVMGEUVS2N/CiJuibrcb2HfPsKZg==", + "dependencies": { + "Azure.Storage.Queues": "12.8.0", + "Microsoft.Azure.WebJobs": "3.0.30", + "Microsoft.Extensions.Azure": "1.1.1" + } + }, + "Microsoft.Azure.WebJobs.Host.Storage": { + "type": "Transitive", + "resolved": "3.0.14", + "contentHash": "1M9VzF4/skqaFezaNI6QUo5ftI1ifGAc6TQfbDdi94lUkUcnSnlBq/uBzx1P73SQgY9WzIE0KZn2+DOx6CL9Sg==", + "dependencies": { + "Microsoft.Azure.WebJobs": "3.0.14", + "WindowsAzure.Storage": "9.3.1" + } + }, + "Microsoft.Bcl.AsyncInterfaces": { + "type": "Transitive", + "resolved": "1.0.0", + "contentHash": "K63Y4hORbBcKLWH5wnKgzyn7TOfYzevIEwIedQHBIkmkEBA9SCqgvom+XTuE+fAFGvINGkhFItaZ2dvMGdT5iw==" + }, + "Microsoft.CodeAnalysis.Analyzers": { + "type": "Transitive", + "resolved": "2.6.1", + "contentHash": "VsT6gg2SPeToP8SK7PEcsH6Ftryb7aOqnXh9xg11zBeov05+63gP3k/TvrR+v85XIa8Nn0y3+qNl4M+qzNLBfw==" + }, + "Microsoft.CodeAnalysis.Common": { + "type": "Transitive", + "resolved": "2.10.0", + "contentHash": "w57ebW3QIRFPoFFX6GCa6eF2FmuHYaWEJ/sMMHq+PBnHB51dEzLIoAQft1Byqe5nrSo4UUV6v4tad8fkTrKl8w==", + "dependencies": { + "Microsoft.CodeAnalysis.Analyzers": "2.6.1", + "System.AppContext": "4.3.0", + "System.Collections": "4.3.0", + "System.Collections.Concurrent": "4.3.0", + "System.Collections.Immutable": "1.5.0", + "System.Console": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.FileVersionInfo": "4.3.0", + "System.Diagnostics.StackTrace": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Dynamic.Runtime": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO.Compression": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Linq": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Metadata": "1.6.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.X509Certificates": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Text.Encoding.CodePages": "4.3.0", + "System.Text.Encoding.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.Tasks.Extensions": "4.3.0", + "System.Threading.Tasks.Parallel": "4.3.0", + "System.Threading.Thread": "4.3.0", + "System.ValueTuple": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0", + "System.Xml.XDocument": "4.3.0", + "System.Xml.XPath.XDocument": "4.3.0", + "System.Xml.XmlDocument": "4.3.0" + } + }, + "Microsoft.CodeAnalysis.CSharp": { + "type": "Transitive", + "resolved": "2.10.0", + "contentHash": "bTr6j4V7G4ZPhRDUdowdtbEvXsQA4w1TYfOtXiYdv8TF7STl9ShOKtlSVzAusmeEWsZksJm9D1VSxt6XIyNB0w==", + "dependencies": { + "Microsoft.CodeAnalysis.Common": "[2.10.0]" + } + }, + "Microsoft.CodeCoverage": { + "type": "Transitive", + "resolved": "17.0.0", + "contentHash": "+B+09FPYBtf+cXfZOPIgpnP5mzLq5QdlBo+JEFy9CdqBaWHWE/YMY0Mos9uDsZhcgFegJm9GigAgMyqBZyfq+Q==" + }, + "Microsoft.CSharp": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "kaj6Wb4qoMuH3HySFJhxwQfe8R/sJsNJnANrvv8WdFPMoNbKY5htfNscv+LHCu5ipz+49m2e+WQXpLXr9XYemQ==" + }, + "Microsoft.Data.SqlClient.SNI.runtime": { + "type": "Transitive", + "resolved": "3.0.0", + "contentHash": "n1sNyjJgu2pYWKgw3ZPikw3NiRvG4kt7Ya5MK8u77Rgj/1bTFqO/eDF4k5W9H5GXplMZCpKkNbp5kNBICgSB0w==" + }, + "Microsoft.Diagnostics.NETCore.Client": { + "type": "Transitive", + "resolved": "0.2.61701", + "contentHash": "/whUqXLkTiUvG+vfSFd77DHHsLZW2HztZt+ACOpuvGyLKoGGN86M8cR1aYfRW6fxXF3SVGMKMswcL485SQEDuQ==" + }, + "Microsoft.Diagnostics.Runtime": { + "type": "Transitive", + "resolved": "1.1.126102", + "contentHash": "2lyoyld8bd/zSq5HJPkyXVtsSdfS30qr75V96S4nEJ/nUiUp0WfGjxnTcZXBLCqzwE0DLUR0lUcNpMp0gEtuzA==" + }, + "Microsoft.Diagnostics.Tracing.TraceEvent": { + "type": "Transitive", + "resolved": "2.0.61", + "contentHash": "czZJRJZEZbGyBauIXYfWIfVV6Nx88L55RARKmEb7ja+nmb1yI+LiROgnD1N0Fyh/RnzvUUD/J0YYMkAEBT1Z6w==", + "dependencies": { + "System.Runtime.CompilerServices.Unsafe": "4.5.2" + } + }, + "Microsoft.DotNet.PlatformAbstractions": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "9KPDwvb/hLEVXYruVHVZ8BkebC8j17DmPb56LnqRF74HqSPLjCkrlFUjOtFpQPA2DeADBRTI/e69aCfRBfrhxw==", + "dependencies": { + "System.AppContext": "4.1.0", + "System.Collections": "4.0.11", + "System.IO": "4.1.0", + "System.IO.FileSystem": "4.0.1", + "System.Reflection.TypeExtensions": "4.1.0", + "System.Runtime.Extensions": "4.1.0", + "System.Runtime.InteropServices": "4.1.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.0.0" + } + }, + "Microsoft.Extensions.Azure": { + "type": "Transitive", + "resolved": "1.1.1", + "contentHash": "3BruEhX5qrQ7wSX/2qw6rQJNBuXgXg3gHZyN/64eVpZRPjkgE4+OhrRIpWbNqw+XPg9pGzzjfwdri9v4eOdtsw==", + "dependencies": { + "Azure.Core": "1.19.0", + "Azure.Identity": "1.4.0", + "Microsoft.Extensions.Configuration.Abstractions": "2.1.0", + "Microsoft.Extensions.Configuration.Binder": "2.1.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0", + "Microsoft.Extensions.Logging.Abstractions": "2.1.0", + "Microsoft.Extensions.Options": "2.1.0" + } + }, + "Microsoft.Extensions.Configuration": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "LjVKO6P2y52c5ZhTLX/w8zc5H4Y3J/LJsgqTBj49TtFq/hAtVNue/WA0F6/7GMY90xhD7K0MDZ4qpOeWXbLvzg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.1.1" + } + }, + "Microsoft.Extensions.Configuration.Abstractions": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "VfuZJNa0WUshZ/+8BFZAhwFKiKuu/qOUCFntfdLpHj7vcRnsGHqd3G2Hse78DM+pgozczGM63lGPRLmy+uhUOA==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.1.1" + } + }, + "Microsoft.Extensions.Configuration.Binder": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "fcLCTS03poWE4v9tSNBr3pWn0QwGgAn1vzqHXlXgvqZeOc7LvQNzaWcKRQZTdEc3+YhQKwMsOtm3VKSA2aWQ8w==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.1.1" + } + }, + "Microsoft.Extensions.Configuration.EnvironmentVariables": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "fZIoU1kxy9zu4KjjabcA79jws6Fk1xmub/VQMrClVqRXZrWt9lYmyjJjw7x0KZtl+Y1hs8qDDaFDrpR1Mso6Wg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.1.0" + } + }, + "Microsoft.Extensions.Configuration.FileExtensions": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "xvbjRAIo2Iwxk7vsMg49RwXPOOm5rtvr0frArvlg1uviS60ouVkOLouCNvOv/eRgWYINPbHAU9p//zEjit38Og==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.1.0", + "Microsoft.Extensions.FileProviders.Physical": "2.1.0" + } + }, + "Microsoft.Extensions.Configuration.Json": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "9OCdAv7qiRtRlXQnECxW9zINUK8bYPKbNp5x8FQaLZbm/flv7mPvo1muZ1nsKGMZF4uL4Bl6nHw2v1fi3MqQ1Q==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.1.0", + "Microsoft.Extensions.Configuration.FileExtensions": "2.1.0", + "Newtonsoft.Json": "11.0.2" + } + }, + "Microsoft.Extensions.DependencyInjection": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "gqQviLfuA31PheEGi+XJoZc1bc9H9RsPa9Gq9XuDct7XGWSR9eVXjK5Sg7CSUPhTFHSuxUFY12wcTYLZ4zM1hg==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0" + } + }, + "Microsoft.Extensions.DependencyInjection.Abstractions": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "MgYpU5cwZohUMKKg3sbPhvGG+eAZ/59E9UwPwlrUkyXU+PGzqwZg9yyQNjhxuAWmoNoFReoemeCku50prYSGzA==" + }, + "Microsoft.Extensions.DependencyModel": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "nS2XKqi+1A1umnYNLX2Fbm/XnzCxs5i+zXVJ3VC6r9t2z0NZr9FLnJN4VQpKigdcWH/iFTbMuX6M6WQJcTjVIg==", + "dependencies": { + "Microsoft.DotNet.PlatformAbstractions": "2.1.0", + "Newtonsoft.Json": "9.0.1", + "System.Diagnostics.Debug": "4.0.11", + "System.Dynamic.Runtime": "4.0.11", + "System.Linq": "4.1.0" + } + }, + "Microsoft.Extensions.FileProviders.Abstractions": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "itv+7XBu58pxi8mykxx9cUO1OOVYe0jmQIZVSZVp5lOcLxB7sSV2bnHiI1RSu6Nxne/s6+oBla3ON5CCMSmwhQ==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.1.0" + } + }, + "Microsoft.Extensions.FileProviders.Physical": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "A9xLomqD4tNFqDfleapx2C14ZcSjCTzn/4Od0W/wBYdlLF2tYDJ204e75HjpWDVTkr03kgdZbM3QZ6ZeDsrBYg==", + "dependencies": { + "Microsoft.Extensions.FileProviders.Abstractions": "2.1.0", + "Microsoft.Extensions.FileSystemGlobbing": "2.1.0" + } + }, + "Microsoft.Extensions.FileSystemGlobbing": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "JEwwhwbVTEXJu4W4l/FFx7FG9Fh5R8999mZl6qJImjM/LY4DxQsFYzpSkziMdY022n7TQpNUxJlH9bKZc7TqWw==" + }, + "Microsoft.Extensions.Hosting": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "nqOrLtBqpwRT006vdQ2Vp87uiuYztiZcZAndFqH91ZH4SQgr8wImCVQwzUgTxx1DSrpIW765+xrZTZqsoGtvqg==", + "dependencies": { + "Microsoft.Extensions.Configuration": "2.1.0", + "Microsoft.Extensions.DependencyInjection": "2.1.0", + "Microsoft.Extensions.FileProviders.Physical": "2.1.0", + "Microsoft.Extensions.Hosting.Abstractions": "2.1.0", + "Microsoft.Extensions.Logging": "2.1.0" + } + }, + "Microsoft.Extensions.Hosting.Abstractions": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "BpMaoBxdXr5VD0yk7rYN6R8lAU9X9JbvsPveNdKT+llIn3J5s4sxpWqaSG/NnzTzTLU5eJE5nrecTl7clg/7dQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.1.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0", + "Microsoft.Extensions.FileProviders.Abstractions": "2.1.0", + "Microsoft.Extensions.Logging.Abstractions": "2.1.0" + } + }, + "Microsoft.Extensions.Logging": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "hh+mkOAQDTp6XH80xJt3+wwYVzkbwYQl9XZRCz4Um0JjP/o7N9vHM3rZ6wwwtr+BBe/L6iBO2sz0px6OWBzqZQ==", + "dependencies": { + "Microsoft.Extensions.Configuration.Binder": "2.1.1", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1", + "Microsoft.Extensions.Logging.Abstractions": "2.1.1", + "Microsoft.Extensions.Options": "2.1.1" + } + }, + "Microsoft.Extensions.Logging.Abstractions": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "XRzK7ZF+O6FzdfWrlFTi1Rgj2080ZDsd46vzOjadHUB0Cz5kOvDG8vI7caa5YFrsHQpcfn0DxtjS4E46N4FZsA==" + }, + "Microsoft.Extensions.Logging.Configuration": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "nMAcTACzW37zc3f7n5fIYsRDXtjjQA2U/kiE4xmuSLn7coCIeDfFTpUhJ+wG/3vwb5f1lFWNpyXGyQdlUCIXUw==", + "dependencies": { + "Microsoft.Extensions.Logging": "2.1.0", + "Microsoft.Extensions.Options.ConfigurationExtensions": "2.1.0" + } + }, + "Microsoft.Extensions.ObjectPool": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "SErON45qh4ogDp6lr6UvVmFYW0FERihW+IQ+2JyFv1PUyWktcJytFaWH5zarufJvZwhci7Rf1IyGXr9pVEadTw==" + }, + "Microsoft.Extensions.Options": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "V7lXCU78lAbzaulCGFKojcCyG8RTJicEbiBkPJjFqiqXwndEBBIehdXRMWEVU3UtzQ1yDvphiWUL9th6/4gJ7w==", + "dependencies": { + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1", + "Microsoft.Extensions.Primitives": "2.1.1" + } + }, + "Microsoft.Extensions.Options.ConfigurationExtensions": { + "type": "Transitive", + "resolved": "2.1.0", + "contentHash": "w/MP147fSqlIcCymaNpLbjdJsFVkSJM9Sz+jbWMr1gKMDVxoOS8AuFjJkVyKU/eydYxHIR/K1Hn3wisJBW5gSg==", + "dependencies": { + "Microsoft.Extensions.Configuration.Abstractions": "2.1.0", + "Microsoft.Extensions.Configuration.Binder": "2.1.0", + "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0", + "Microsoft.Extensions.Options": "2.1.0" + } + }, + "Microsoft.Extensions.Primitives": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "scJ1GZNIxMmjpENh0UZ8XCQ6vzr/LzeF9WvEA51Ix2OQGAs9WPgPu8ABVUdvpKPLuor/t05gm6menJK3PwqOXg==", + "dependencies": { + "System.Memory": "4.5.1", + "System.Runtime.CompilerServices.Unsafe": "4.5.1" + } + }, + "Microsoft.Identity.Client": { + "type": "Transitive", + "resolved": "4.30.1", + "contentHash": "xk8tJeGfB2yD3+d7a0DXyV7/HYyEG10IofUHYHoPYKmDbroi/j9t1BqSHgbq1nARDjg7m8Ki6e21AyNU7e/R4Q==" + }, + "Microsoft.Identity.Client.Extensions.Msal": { + "type": "Transitive", + "resolved": "2.18.4", + "contentHash": "HpG4oLwhQsy0ce7OWq9iDdLtJKOvKRStIKoSEOeBMKuohfuOWNDyhg8fMAJkpG/kFeoe4J329fiMHcJmmB+FPw==", + "dependencies": { + "Microsoft.Identity.Client": "4.30.0", + "System.Security.Cryptography.ProtectedData": "4.5.0" + } + }, + "Microsoft.IdentityModel.JsonWebTokens": { + "type": "Transitive", + "resolved": "6.8.0", + "contentHash": "+7JIww64PkMt7NWFxoe4Y/joeF7TAtA/fQ0b2GFGcagzB59sKkTt/sMZWR6aSZht5YC7SdHi3W6yM1yylRGJCQ==", + "dependencies": { + "Microsoft.IdentityModel.Tokens": "6.8.0" + } + }, + "Microsoft.IdentityModel.Logging": { + "type": "Transitive", + "resolved": "6.8.0", + "contentHash": "Rfh/p4MaN4gkmhPxwbu8IjrmoDncGfHHPh1sTnc0AcM/Oc39/fzC9doKNWvUAjzFb8LqA6lgZyblTrIsX/wDXg==" + }, + "Microsoft.IdentityModel.Protocols": { + "type": "Transitive", + "resolved": "6.8.0", + "contentHash": "OJZx5nPdiH+MEkwCkbJrTAUiO/YzLe0VSswNlDxJsJD9bhOIdXHufh650pfm59YH1DNevp3/bXzukKrG57gA1w==", + "dependencies": { + "Microsoft.IdentityModel.Logging": "6.8.0", + "Microsoft.IdentityModel.Tokens": "6.8.0" + } + }, + "Microsoft.IdentityModel.Protocols.OpenIdConnect": { + "type": "Transitive", + "resolved": "6.8.0", + "contentHash": "X/PiV5l3nYYsodtrNMrNQIVlDmHpjQQ5w48E+o/D5H4es2+4niEyQf3l03chvZGWNzBRhfSstaXr25/Ye4AeYw==", + "dependencies": { + "Microsoft.IdentityModel.Protocols": "6.8.0", + "System.IdentityModel.Tokens.Jwt": "6.8.0" + } + }, + "Microsoft.IdentityModel.Tokens": { + "type": "Transitive", + "resolved": "6.8.0", + "contentHash": "gTqzsGcmD13HgtNePPcuVHZ/NXWmyV+InJgalW/FhWpII1D7V1k0obIseGlWMeA4G+tZfeGMfXr0klnWbMR/mQ==", + "dependencies": { + "Microsoft.CSharp": "4.5.0", + "Microsoft.IdentityModel.Logging": "6.8.0", + "System.Security.Cryptography.Cng": "4.5.0" + } + }, + "Microsoft.Net.Http.Headers": { + "type": "Transitive", + "resolved": "2.1.1", + "contentHash": "lPNIphl8b2EuhOE9dMH6EZDmu7pS882O+HMi5BJNsigxHaWlBrYxZHFZgE18cyaPp6SSZcTkKkuzfjV/RRQKlA==", + "dependencies": { + "Microsoft.Extensions.Primitives": "2.1.1", + "System.Buffers": "4.5.0" + } + }, + "Microsoft.NETCore.Platforms": { + "type": "Transitive", + "resolved": "3.1.0", + "contentHash": "z7aeg8oHln2CuNulfhiLYxCVMPEwBl3rzicjvIX+4sUuCwvXw5oXQEtbiU2c0z4qYL5L3Kmx0mMA/+t/SbY67w==" + }, + "Microsoft.NETCore.Targets": { + "type": "Transitive", + "resolved": "1.1.0", + "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg==" + }, + "Microsoft.TestPlatform.ObjectModel": { + "type": "Transitive", + "resolved": "17.0.0", + "contentHash": "WMugCdPkA8U/BsSRc+3RN+DXcaYSDvp/s0MofVld08iF1O5fek4iKecygk6NruNf1rgJsv4LK71mrwbyeqhzHA==", + "dependencies": { + "NuGet.Frameworks": "5.0.0", + "System.Reflection.Metadata": "1.6.0" + } + }, + "Microsoft.TestPlatform.TestHost": { + "type": "Transitive", + "resolved": "17.0.0", + "contentHash": "xkKFzm0hylHF0SlDj78ACYMJC/i8fQ3i16sDDNYoKnjTsstGSQfuSBJ+QT4nqRXk/fOiYTh+iY0KIX5N7HTLuQ==", + "dependencies": { + "Microsoft.TestPlatform.ObjectModel": "17.0.0", + "Newtonsoft.Json": "9.0.1" + } + }, + "Microsoft.Win32.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "9ZQKCWxH7Ijp9BfahvL2Zyf1cJIk8XYLF6Yjzr2yi0b2cOut/HQ31qf1ThHAgCc3WiZMdnWcfJCgN82/0UunxA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "Microsoft.Win32.Registry": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "KSrRMb5vNi0CWSGG1++id2ZOs/1QhRqROt+qgbEAdQuGjGrFcl4AOl4/exGPUYz2wUnU42nvJqon1T3U0kPXLA==", + "dependencies": { + "System.Security.AccessControl": "4.7.0", + "System.Security.Principal.Windows": "4.7.0" + } + }, + "Microsoft.Win32.SystemEvents": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "mtVirZr++rq+XCDITMUdnETD59XoeMxSpLRIII7JRI6Yj0LEDiO1pPn0ktlnIj12Ix8bfvQqQDMMIF9wC98oCA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "3.1.0" + } + }, + "ncrontab.signed": { + "type": "Transitive", + "resolved": "3.3.0", + "contentHash": "w+fVX+uCk3C0nR7BDjWAmUzDQPNAaBusTPljWehx/2cbBTxuKm81sCTebwRnJtHfS+38xbqF7NiiwPWjRMKiFQ==", + "dependencies": { + "System.Collections": "4.0.11", + "System.Diagnostics.Debug": "4.0.11", + "System.Globalization": "4.0.11", + "System.IO": "4.1.0", + "System.Net.Primitives": "4.0.11", + "System.Resources.ResourceManager": "4.0.1" + } + }, + "NETStandard.Library": { + "type": "Transitive", + "resolved": "1.6.1", + "contentHash": "WcSp3+vP+yHNgS8EV5J7pZ9IRpeDuARBPN28by8zqff1wJQXm26PVU8L3/fYLBJVU7BtDyqNVWq2KlCVvSSR4A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.Win32.Primitives": "4.3.0", + "System.AppContext": "4.3.0", + "System.Collections": "4.3.0", + "System.Collections.Concurrent": "4.3.0", + "System.Console": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Calendars": "4.3.0", + "System.IO": "4.3.0", + "System.IO.Compression": "4.3.0", + "System.IO.Compression.ZipFile": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Linq": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "System.Net.Http": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Net.Sockets": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Security.Cryptography.X509Certificates": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Text.Encoding.Extensions": "4.3.0", + "System.Text.RegularExpressions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.Timer": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0", + "System.Xml.XDocument": "4.3.0" + } + }, + "Newtonsoft.Json.Bson": { + "type": "Transitive", + "resolved": "1.0.1", + "contentHash": "5PYT/IqQ+UK31AmZiSS102R6EsTo+LGTSI8bp7WAUqDKaF4wHXD8U9u4WxTI1vc64tYi++8p3dk3WWNqPFgldw==", + "dependencies": { + "NETStandard.Library": "1.6.1", + "Newtonsoft.Json": "10.0.1" + } + }, + "NuGet.Frameworks": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "c5JVjuVAm4f7E9Vj+v09Z9s2ZsqFDjBpcsyS3M9xRo0bEdm/LVZSzLxxNvfvAwRiiE8nwe1h2G4OwiwlzFKXlA==" + }, + "Perfolizer": { + "type": "Transitive", + "resolved": "0.2.1", + "contentHash": "Dt4aCxCT8NPtWBKA8k+FsN/RezOQ2C6omNGm5o/qmYRiIwlQYF93UgFmeF1ezVNsztTnkg7P5P63AE+uNkLfrw==", + "dependencies": { + "System.Memory": "4.5.3" + } + }, + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "HdSSp5MnJSsg08KMfZThpuLPJpPwE5hBXvHwoKWosyHHfe8Mh5WKT0ylEOf6yNzX6Ngjxe4Whkafh5q7Ymac4Q==" + }, + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "+yH1a49wJMy8Zt4yx5RhJrxO/DBDByAiCzNwiETI+1S4mPdCu0OY4djdciC7Vssk0l22wQaDLrXxXkp+3+7bVA==" + }, + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c3YNH1GQJbfIPJeCnr4avseugSqPrxwIqzthYyZDN6EuOyNOzq+y2KSUfRcXauya1sF4foESTgwM5e1A8arAKw==" + }, + "runtime.native.System": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "c/qWt2LieNZIj1jGnVNsE2Kl23Ya2aSTBuXMD6V7k9KWr6l16Tqdwq+hJScEpWER9753NWC8h96PaVNY5Ld7Jw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.IO.Compression": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "INBPonS5QPEgn7naufQFXJEp3zX6L4bwHgJ/ZH78aBTpeNfQMtf7C6VrAFhlq2xxWBveIOWyFzQjJ8XzHMhdOQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ZVuZJqnnegJhd2k/PtAbbIcZ3aZeITq3sj06oKfMBSfphW3HDmk/t4ObvbOk/JA/swGR0LNqMksAh/f7gpTROg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "runtime.native.System.Security.Cryptography.Apple": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "DloMk88juo0OuOWr56QG7MNchmafTLYWvABy36izkrLI5VledI0rq28KGs1i9wbpeT9NPQrx/wTf8U2vazqQ3Q==", + "dependencies": { + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.0" + } + }, + "runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "NS1U+700m4KFRHR5o4vo9DSlTmlCKu/u7dtE5sUHVIPB+xpXxYQvgBgA6wEIeCz6Yfn0Z52/72WYsToCEPJnrw==", + "dependencies": { + "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0", + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "b3pthNgxxFcD+Pc0WSEoC0+md3MyhRS6aCEeenvNE3Fdw1HyJ18ZhRFVJJzIeR/O/jpxPboB805Ho0T3Ul7w8A==" + }, + "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KeLz4HClKf+nFS7p/6Fi/CqyLXh81FpiGzcmuS8DGi9lUqSnZ6Es23/gv2O+1XVGfrbNmviF7CckBpavkBoIFQ==" + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "kVXCuMTrTlxq4XOOMAysuNwsXWpYeboGddNGpIgNSZmv1b6r/s/DPk0fYMB7Q5Qo4bY68o48jt4T4y5BVecbCQ==" + }, + "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "X7IdhILzr4ROXd8mI1BUCQMSHSQwelUlBjF1JyTKCjXaOGn2fB4EKBxQbCK2VjO3WaWIdlXZL3W6TiIVnrhX4g==" + }, + "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "nyFNiCk/r+VOiIqreLix8yN+q3Wga9+SE8BCgkf+2BwEKiNx6DyvFjCgkfV743/grxv8jHJ8gUK4XEQw7yzRYg==" + }, + "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ytoewC6wGorL7KoCAvRfsgoJPJbNq+64k2SqW6JcOAebWsFUvCCYgfzQMrnpvPiEl4OrblUlhF2ji+Q1+SVLrQ==" + }, + "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "I8bKw2I8k58Wx7fMKQJn2R8lamboCAiHfHeV/pS65ScKWMMI0+wJkLYlEKvgW1D/XvSl/221clBoR2q9QNNM7A==" + }, + "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VB5cn/7OzUfzdnC8tqAIMQciVLiq2epm2NrAm1E9OjNRyG4lVhfR61SMcLizejzQP8R8Uf/0l5qOIbUEi+RdEg==" + }, + "System.AppContext": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "fKC+rmaLfeIzUhagxY17Q9siv/sPrjjKcfNg1Ic8IlQkZLipo8ljcaZQu4VtI4Jqbzjc2VTjzGLF6WmsRXAEgA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Buffers": { + "type": "Transitive", + "resolved": "4.5.1", + "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==" + }, + "System.CodeDom": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "gqpR1EeXOuzNQWL7rOzmtdIz3CaXVjSQCiaGOs2ivjPwynKSJYm39X81fdlp7WuojZs/Z5t1k5ni7HtKQurhjw==" + }, + "System.Collections": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3Dcj85/TBdVpL5Zr+gEEBUuFe2icOnLalmEh9hfck1PTYbbyWuZgh4fmm2ysCLTrqLQw6t3TgTyJ+VLp+Qb+Lw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Collections.Concurrent": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ztl69Xp0Y/UXCL+3v3tEU+lIy+bvjKNUmopn1wep/a291pVPK7dxBd6T7WnlQqRog+d1a/hSsgRsmFnIBKTPLQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Collections.Immutable": { + "type": "Transitive", + "resolved": "1.5.0", + "contentHash": "EXKiDFsChZW0RjrZ4FYHu9aW6+P4MCgEDCklsVseRfhoO0F+dXeMSsMRAlVXIo06kGJ/zv+2w1a2uc2+kxxSaQ==" + }, + "System.Collections.NonGeneric": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "prtjIEMhGUnQq6RnPEYLpFt8AtLbp9yq2zxOSrY7KJJZrw25Fi97IzBqY7iqssbM61Ek5b8f3MG/sG1N2sN5KA==", + "dependencies": { + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Collections.Specialized": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Epx8PoVZR0iuOnJJDzp7pWvdfMMOAvpUo95pC4ScH2mJuXkKA2Y4aR3cG9qt2klHgSons1WFh4kcGW7cSXvrxg==", + "dependencies": { + "System.Collections.NonGeneric": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Extensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.ComponentModel": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VyGn1jGRZVfxnh8EdvDCi71v3bMXrsu8aYJOwoV7SNDLVhiEqwP86pPMyRGsDsxhXAm2b3o9OIqeETfN5qfezw==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.ComponentModel.Annotations": { + "type": "Transitive", + "resolved": "4.4.0", + "contentHash": "29K3DQ+IGU7LBaMjTo7SI7T7X/tsMtLvz1p56LJ556Iu0Dw3pKZw5g8yCYCWMRxrOF0Hr0FU0FwW0o42y2sb3A==" + }, + "System.ComponentModel.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "j8GUkCpM8V4d4vhLIIoBLGey2Z5bCkMVNjEZseyAlm4n5arcsJOeI3zkUP+zvZgzsbLTYh4lYeP/ZD/gdIAPrw==", + "dependencies": { + "System.ComponentModel": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.ComponentModel.TypeConverter": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "16pQ6P+EdhcXzPiEK4kbA953Fu0MNG2ovxTZU81/qsCd1zPRsKc3uif5NgvllCY598k6bI0KUyKW8fanlfaDQg==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Collections.NonGeneric": "4.3.0", + "System.Collections.Specialized": "4.3.0", + "System.ComponentModel": "4.3.0", + "System.ComponentModel.Primitives": "4.3.0", + "System.Globalization": "4.3.0", + "System.Linq": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Configuration.ConfigurationManager": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "/anOTeSZCNNI2zDilogWrZ8pNqCmYbzGNexUnNhjW8k0sHqEZ2nHJBp147jBV3hGYswu5lINpNg1vxR7bnqvVA==", + "dependencies": { + "System.Security.Cryptography.ProtectedData": "4.7.0", + "System.Security.Permissions": "4.7.0" + } + }, + "System.Console": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "DHDrIxiqk1h03m6khKWV2X8p/uvN79rgSqpilL6uzpmSfxfU5ng8VcPtW4qsDsQDHiTv6IPV9TmD5M/vElPNLg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.Diagnostics.Debug": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "ZUhUOdqmaG5Jk3Xdb8xi5kIyQYAA4PnTNlHx1mu9ZY3qv4ELIdKbnL/akbGaKi2RnNUWaZsAs31rvzFdewTj2g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Diagnostics.DiagnosticSource": { + "type": "Transitive", + "resolved": "5.0.0", + "contentHash": "tCQTzPsGZh/A9LhhA6zrqCRV4hOHsK90/G7q3Khxmn6tnB1PuNU0cRaKANP2AWcF9bn0zsuOoZOSrHuJk6oNBA==" + }, + "System.Diagnostics.FileVersionInfo": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "omCF64wzQ3Q2CeIqkD6lmmxeMZtGHUmzgFMPjfVaOsyqpR66p/JaZzManMw1s33osoAb5gqpncsjie67+yUPHQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Reflection.Metadata": "1.4.1", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.InteropServices": "4.3.0" + } + }, + "System.Diagnostics.StackTrace": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "BiHg0vgtd35/DM9jvtaC1eKRpWZxr0gcQd643ABG7GnvSlf5pOkY2uyd42mMOJoOmKvnpNj0F4tuoS1pacTwYw==", + "dependencies": { + "System.IO.FileSystem": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Metadata": "1.4.1", + "System.Runtime": "4.3.0" + } + }, + "System.Diagnostics.Tools": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "UUvkJfSYJMM6x527dJg2VyWPSRqIVB0Z7dbjHst1zmwTXz5CcXSYJFWRpuigfbO1Lf7yfZiIaEUesfnl/g5EyA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Diagnostics.TraceSource": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VnYp1NxGx8Ww731y2LJ1vpfb/DKVNKEZ8Jsh5SgQTZREL/YpWRArgh9pI8CDLmgHspZmLL697CaLvH85qQpRiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0" + } + }, + "System.Diagnostics.Tracing": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "rswfv0f/Cqkh78rA5S8eN8Neocz234+emGCtTF3lxPY96F+mmmUen6tbn0glN6PMvlKQb9bPAY5e9u7fgPTkKw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Drawing.Common": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "v+XbyYHaZjDfn0ENmJEV1VYLgGgCTx1gnfOBcppowbpOAriglYgGCvFCPr2EEZyBvXlpxbEsTwkOlInl107ahA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "3.1.0", + "Microsoft.Win32.SystemEvents": "4.7.0" + } + }, + "System.Dynamic.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "SNVi1E/vfWUAs/WYKhE9+qlS6KqK0YVhnlT0HQtr8pMIA8YX3lwy3uPMownDwdYISBdmAF/2holEIldVp85Wag==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Linq": "4.3.0", + "System.Linq.Expressions": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Globalization": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "kYdVd2f2PAdFGblzFswE4hkNANJBKRmsfa2X5LG2AcWE1c7/4t0pYae1L8vfZ5xvE2nK/R9JprtToA61OSHWIg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Globalization.Calendars": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GUlBtdOWT4LTV3I+9/PJW+56AnnChTaOqqTLFtdmype/L500M2LIyXgmtd9X2P2VOkmJd5c67H5SaC2QcL1bFA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Globalization": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Globalization.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "FhKmdR6MPG+pxow6wGtNAWdZh7noIOpdD5TwQ3CprzgIE1bBBoim0vbR1+AWsWjQmU7zXHgQo4TWSP6lCeiWcQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.InteropServices": "4.3.0" + } + }, + "System.IdentityModel.Tokens.Jwt": { + "type": "Transitive", + "resolved": "6.8.0", + "contentHash": "5tBCjAub2Bhd5qmcd0WhR5s354e4oLYa//kOWrkX+6/7ZbDDJjMTfwLSOiZ/MMpWdE4DWPLOfTLOq/juj9CKzA==", + "dependencies": { + "Microsoft.IdentityModel.JsonWebTokens": "6.8.0", + "Microsoft.IdentityModel.Tokens": "6.8.0" + } + }, + "System.IO": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.IO.Compression": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "YHndyoiV90iu4iKG115ibkhrG+S3jBm8Ap9OwoUAzO5oPDAWcr0SFwQFm0HjM8WkEZWo0zvLTyLmbvTkW1bXgg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Buffers": "4.3.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.IO.Compression": "4.3.0" + } + }, + "System.IO.Compression.ZipFile": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "G4HwjEsgIwy3JFBduZ9quBkAu+eUwjIdJleuNSgmUojbH6O3mlvEIme+GHx/cLlTAPcrnnL7GqvB9pTlWRfhOg==", + "dependencies": { + "System.Buffers": "4.3.0", + "System.IO": "4.3.0", + "System.IO.Compression": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.IO.FileSystem": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "3wEMARTnuio+ulnvi+hkRNROYwa1kylvYahhcLk4HSoVdl+xxTFVeVlYOfLwrDPImGls0mDqbMhrza8qnWPTdA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.IO.FileSystem.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "6QOb2XFLch7bEc4lIcJH49nJN2HV+OC3fHDgsLVsBVBk3Y4hFAnOBGzJ2lUu7CyDDFo9IBWkSsnbkT6IBwwiMw==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Linq": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5DbqIUpsDp0dFftytzuMmc0oeMdQwjcP/EWxsksIz/w1TcFRkZ3yKKz0PqiYFMmEwPSWw+qNVqD7PJ889JzHbw==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0" + } + }, + "System.Linq.Expressions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "PGKkrd2khG4CnlyJwxwwaWWiSiWFNBGlgXvJpeO0xCXrZ89ODrQ6tjEWS/kOqZ8GwEOUATtKtzp1eRgmYNfclg==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Linq": "4.3.0", + "System.ObjectModel": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Emit.Lightweight": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Reflection.TypeExtensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Management": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "Z6ac0qPGr3yJtwZEX1SRkhwWa0Kf5NJxx7smLboYsGrApQFECNFdqhGy252T4lrZ5Nwzhd9VQiaifndR3bfHdg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "2.0.0", + "Microsoft.Win32.Registry": "4.5.0", + "System.CodeDom": "4.5.0" + } + }, + "System.Memory": { + "type": "Transitive", + "resolved": "4.5.4", + "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw==" + }, + "System.Memory.Data": { + "type": "Transitive", + "resolved": "1.0.2", + "contentHash": "JGkzeqgBsiZwKJZ1IxPNsDFZDhUvuEdX8L8BDC8N3KOj+6zMcNU28CNN59TpZE/VJYy9cP+5M+sbxtWJx3/xtw==", + "dependencies": { + "System.Text.Encodings.Web": "4.7.2", + "System.Text.Json": "4.6.0" + } + }, + "System.Net.Http": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "sYg+FtILtRQuYWSIAuNOELwVuVsxVyJGWQyOnlAzhV4xvhyFnON1bAzYYC+jjRW8JREM45R0R5Dgi8MTC5sEwA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.DiagnosticSource": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Extensions": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.OpenSsl": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Security.Cryptography.X509Certificates": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Net.Http": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Net.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "qOu+hDwFwoZPbzPvwut2qATe3ygjeQBDQj91xlsaqGFQUI5i4ZnZb8yyQuLGpDGivEPIt8EJkd1BVzVoP31FXA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0" + } + }, + "System.Net.Sockets": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "m6icV6TqQOAdgt5N/9I5KNpjom/5NFtkmGseEH+AK/hny8XrytLH3+b5M8zL/Ycg3fhIocFpUMyl/wpFnVRvdw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Net.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Numerics.Vectors": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ==" + }, + "System.ObjectModel": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "bdX+80eKv9bN6K4N+d77OankKHGn6CH711a6fcOpMQu2Fckp/Ft4L/kW9WznHpyR0NRAvJutzOMHNNlBGvxQzQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Reflection": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.IO": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "228FG0jLcIwTVJyz8CLFKueVqQK36ANazUManGaJHkO0icjiIypKW7YLWLIWahyIkdh5M7mV2dJepllLyA1SKg==", + "dependencies": { + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit.ILGeneration": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "59tBslAk9733NXLrUJrwNZEzbMAcu8k344OYo+wfSVygcgZ9lgBdGIzH/nrg3LYhXceynyvTc8t5/GD4Ri0/ng==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Emit.Lightweight": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "oadVHGSMsTmZsAF864QYN1t1QzZjIcuKU3l2S9cZOwDdDueNTrqq1yRj7koFfIGEnKpt6NjpL3rOzRhs4ryOgA==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Emit.ILGeneration": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "rJkrJD3kBI5B712aRu4DpSIiHRtr6QlfZSQsb0hYHrDCZORXCFjQfoipo2LaMUHoT9i1B7j7MnfaEKWDFmFQNQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.Metadata": { + "type": "Transitive", + "resolved": "1.6.0", + "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==" + }, + "System.Reflection.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Reflection.TypeExtensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7u6ulLcZbyxB5Gq0nMkQttcdBTx57ibzw+4IOXEfR+sXYQoHvjW5LTLyNr8O22UIMrqYbchJQJnos4eooYzYJA==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Resources.ResourceManager": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "/zrcPkkWdZmI4F92gL/TPumP98AVDu/Wxr3CSJGQQ+XN6wbRZcyfSKVoPo17ilb3iOr0cCRqJInGwNMolqhS8A==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Globalization": "4.3.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0" + } + }, + "System.Runtime.CompilerServices.Unsafe": { + "type": "Transitive", + "resolved": "4.5.2", + "contentHash": "wprSFgext8cwqymChhrBLu62LMg/1u92bU+VOwyfBimSPVFXtsNqEWC92Pf9ofzJFlk4IHmJA75EDJn1b2goAQ==" + }, + "System.Runtime.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "guW0uK0fn5fcJJ1tJVXYd7/1h5F+pea1r7FLSOz/f8vPEqbR2ZAknuRDvTQ8PzAilDveOxNjSfr0CHfIQfFk8g==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime.Handles": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "OKiSUN7DmTWeYb3l51A7EYaeNMnvxwE249YtZz7yooT4gOZhmTjIn48KgSsw2k2lYdLgTKNJw/ZIfSElwDRVgg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime.InteropServices": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "uv1ynXqiMK8mp1GM3jDqPCFN66eJ5w5XNomaK2XD+TuCroNTLFGeZ+WCmBMcBDyTFKou3P6cR6J/QsaqDp7fGQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Reflection": "4.3.0", + "System.Reflection.Primitives": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Handles": "4.3.0" + } + }, + "System.Runtime.InteropServices.RuntimeInformation": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbz4YJMqRDR7oLeMRbdYv7mYzc++17lNhScCX0goO2XpGWdvAt60CGN+FHdePUEHCe/Jy9jUlvNAiNdM+7jsOw==", + "dependencies": { + "System.Reflection": "4.3.0", + "System.Reflection.Extensions": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0" + } + }, + "System.Runtime.Loader": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "DHMaRn8D8YCK2GG2pw+UzNxn/OHVfaWx7OTLBD/hPegHZZgcZh3H6seWegrC4BYwsfuGrywIuT+MQs+rPqRLTQ==", + "dependencies": { + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Runtime": "4.3.0" + } + }, + "System.Runtime.Numerics": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "yMH+MfdzHjy17l2KESnPiF2dwq7T+xLnSJar7slyimAkUh/gTrS9/UQOtv7xarskJ2/XDSNvfLGOBQPjL7PaHQ==", + "dependencies": { + "System.Globalization": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0" + } + }, + "System.Security.AccessControl": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "JECvTt5aFF3WT3gHpfofL2MNNP6v84sxtXxpqhLBCcDRzqsPBmHhQ6shv4DwwN2tRlzsUxtb3G9M3763rbXKDg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "3.1.0", + "System.Security.Principal.Windows": "4.7.0" + } + }, + "System.Security.Cryptography.Algorithms": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "W1kd2Y8mYSCgc3ULTAZ0hOP2dSdG5YauTb1089T0/kRcN2MpSAW1izOFROrJgxSlMn3ArsgHXagigyi+ibhevg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.Apple": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.Cng": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "WG3r7EyjUe9CMPFSs6bty5doUqT+q9pbI80hlNzo2SkPkZ4VTuZkGWjpp77JB8+uaL4DFPRdBsAY+DX3dBK92A==" + }, + "System.Security.Cryptography.Csp": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "X4s/FCkEUnRGnwR3aSfVIkldBmtURMhmexALNTwpjklzxWU7yjMk7GHLKOZTNkgnWnE0q7+BCf9N2LVRWxewaA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0" + } + }, + "System.Security.Cryptography.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "1DEWjZZly9ae9C79vFwqaO5kaOlI5q+3/55ohmq/7dpDyDfc8lYe7YVxJUZ5MF/NtbkRjwFRo14yM4OEo9EmDw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Collections.Concurrent": "4.3.0", + "System.Linq": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.OpenSsl": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "h4CEgOgv5PKVF/HwaHzJRiVboL2THYCou97zpmhjghx5frc7fIvlkY1jL+lnIQyChrJDMNEXS6r7byGif8Cy4w==", + "dependencies": { + "System.Collections": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Cryptography.Primitives": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "7bDIyVFNL/xKeFHjhobUAQqSpJq9YTOpbEs6mR233Et01STBMXNAc/V+BM6dwYGc95gVh/Zf+iVXWzj3mE8DWg==", + "dependencies": { + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Security.Cryptography.ProtectedData": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "ehYW0m9ptxpGWvE4zgqongBVWpSDU/JCFD4K7krxkQwSz/sFQjEXCUqpvencjy6DYDbn7Ig09R8GFffu8TtneQ==" + }, + "System.Security.Cryptography.X509Certificates": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "t2Tmu6Y2NtJ2um0RtcuhP7ZdNNxXEgUm2JeoA/0NvlMjAhKCnM1NX07TDl3244mVp3QU6LPEhT3HTtH1uF7IYw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.Globalization.Calendars": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.Handles": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Runtime.Numerics": "4.3.0", + "System.Security.Cryptography.Algorithms": "4.3.0", + "System.Security.Cryptography.Cng": "4.3.0", + "System.Security.Cryptography.Csp": "4.3.0", + "System.Security.Cryptography.Encoding": "4.3.0", + "System.Security.Cryptography.OpenSsl": "4.3.0", + "System.Security.Cryptography.Primitives": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "runtime.native.System": "4.3.0", + "runtime.native.System.Net.Http": "4.3.0", + "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" + } + }, + "System.Security.Permissions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "dkOV6YYVBnYRa15/yv004eCGRBVADXw8qRbbNiCn/XpdJSUXkkUeIvdvFHkvnko4CdKMqG8yRHC4ox83LSlMsQ==", + "dependencies": { + "System.Security.AccessControl": "4.7.0", + "System.Windows.Extensions": "4.7.0" + } + }, + "System.Security.Principal.Windows": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "ojD0PX0XhneCsUbAZVKdb7h/70vyYMDYs85lwEI+LngEONe/17A0cFaRFqZU+sOEidcVswYWikYOQ9PPfjlbtQ==" + }, + "System.Text.Encoding": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Text.Encoding.CodePages": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "aeu4FlaUTemuT1qOd1MyU4T516QR4Fy+9yDbwWMPHOHy7U8FD6SgTzdZFO7gHcfAPHtECqInbwklVvUK4RHcNg==", + "dependencies": { + "Microsoft.NETCore.Platforms": "3.1.0" + } + }, + "System.Text.Encoding.Extensions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "YVMK0Bt/A43RmwizJoZ22ei2nmrhobgeiYwFzC4YAN+nue8RF6djXDMog0UCn+brerQoYVyaS+ghy9P/MUVcmw==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0", + "System.Text.Encoding": "4.3.0" + } + }, + "System.Text.Encodings.Web": { + "type": "Transitive", + "resolved": "4.7.2", + "contentHash": "iTUgB/WtrZ1sWZs84F2hwyQhiRH6QNjQv2DkwrH+WP6RoFga2Q1m3f9/Q7FG8cck8AdHitQkmkXSY8qylcDmuA==" + }, + "System.Text.Json": { + "type": "Transitive", + "resolved": "4.6.0", + "contentHash": "4F8Xe+JIkVoDJ8hDAZ7HqLkjctN/6WItJIzQaifBwClC7wmoLSda/Sv2i6i1kycqDb3hWF4JCVbpAweyOKHEUA==" + }, + "System.Text.RegularExpressions": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "RpT2DA+L660cBt1FssIE9CAGpLFdFPuheB7pLpKpn6ZXNby7jDERe8Ua/Ne2xGiwLVG2JOqziiaVCGDon5sKFA==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Threading": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "VkUS0kOBcUf3Wwm0TSbrevDDZ6BlM+b/HRiapRFWjM5O0NS0LviG0glKmFK+hhPDd1XFeSdU1GmlLhb2CoVpIw==", + "dependencies": { + "System.Runtime": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Threading.Tasks": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.Threading.Tasks.Dataflow": { + "type": "Transitive", + "resolved": "4.8.0", + "contentHash": "PSIdcgbyNv7FZvZ1I9Mqy6XZOwstYYMdZiXuHvIyc0gDyPjEhrrP9OvTGDHp+LAHp1RNSLjPYssyqox9+Kt9Ug==" + }, + "System.Threading.Tasks.Extensions": { + "type": "Transitive", + "resolved": "4.5.2", + "contentHash": "BG/TNxDFv0svAzx8OiMXDlsHfGw623BZ8tCXw4YLhDFDvDhNUEV58jKYMGRnkbJNm7c3JNNJDiN7JBMzxRBR2w==" + }, + "System.Threading.Tasks.Parallel": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "cbjBNZHf/vQCfcdhzx7knsiygoCKgxL8mZOeocXZn5gWhCdzHIq6bYNKWX0LAJCWYP7bds4yBK8p06YkP0oa0g==", + "dependencies": { + "System.Collections.Concurrent": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tracing": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "System.Threading.Tasks": "4.3.0" + } + }, + "System.Threading.Thread": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "OHmbT+Zz065NKII/ZHcH9XO1dEuLGI1L2k7uYss+9C1jLxTC9kTZZuzUOyXHayRk+dft9CiDf3I/QZ0t8JKyBQ==", + "dependencies": { + "System.Runtime": "4.3.0" + } + }, + "System.Threading.Timer": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "Z6YfyYTCg7lOZjJzBjONJTFKGN9/NIYKSxhU5GRd+DTwHSZyvWp1xuI5aR+dLg+ayyC5Xv57KiY4oJ0tMO89fQ==", + "dependencies": { + "Microsoft.NETCore.Platforms": "1.1.0", + "Microsoft.NETCore.Targets": "1.1.0", + "System.Runtime": "4.3.0" + } + }, + "System.ValueTuple": { + "type": "Transitive", + "resolved": "4.5.0", + "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ==" + }, + "System.Windows.Extensions": { + "type": "Transitive", + "resolved": "4.7.0", + "contentHash": "CeWTdRNfRaSh0pm2gDTJFwVaXfTq6Xwv/sA887iwPTneW7oMtMlpvDIO+U60+3GWTB7Aom6oQwv5VZVUhQRdPQ==", + "dependencies": { + "System.Drawing.Common": "4.7.0" + } + }, + "System.Xml.ReaderWriter": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "GrprA+Z0RUXaR4N7/eW71j1rgMnEnEVlgii49GZyAjTH7uliMnrOU3HNFBr6fEDBCJCIdlVNq9hHbaDR621XBA==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.IO.FileSystem": "4.3.0", + "System.IO.FileSystem.Primitives": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Runtime.InteropServices": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Text.Encoding.Extensions": "4.3.0", + "System.Text.RegularExpressions": "4.3.0", + "System.Threading.Tasks": "4.3.0", + "System.Threading.Tasks.Extensions": "4.3.0" + } + }, + "System.Xml.XDocument": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "5zJ0XDxAIg8iy+t4aMnQAu0MqVbqyvfoUVl1yDV61xdo3Vth45oA2FoY4pPkxYAH5f8ixpmTqXeEIya95x0aCQ==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Diagnostics.Tools": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Reflection": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0" + } + }, + "System.Xml.XmlDocument": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "lJ8AxvkX7GQxpC6GFCeBj8ThYVyQczx2+f/cWHJU8tjS7YfI6Cv6bon70jVEgs2CiFbmmM8b9j1oZVx0dSI2Ww==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Text.Encoding": "4.3.0", + "System.Threading": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0" + } + }, + "System.Xml.XPath": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "v1JQ5SETnQusqmS3RwStF7vwQ3L02imIzl++sewmt23VGygix04pEH+FCj1yWb+z4GDzKiljr1W7Wfvrx0YwgA==", + "dependencies": { + "System.Collections": "4.3.0", + "System.Diagnostics.Debug": "4.3.0", + "System.Globalization": "4.3.0", + "System.IO": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0" + } + }, + "System.Xml.XPath.XDocument": { + "type": "Transitive", + "resolved": "4.3.0", + "contentHash": "jw9oHHEIVW53mHY9PgrQa98Xo2IZ0ZjrpdOTmtvk+Rvg4tq7dydmxdNqUvJ5YwjDqhn75mBXWttWjiKhWP53LQ==", + "dependencies": { + "System.Diagnostics.Debug": "4.3.0", + "System.Linq": "4.3.0", + "System.Resources.ResourceManager": "4.3.0", + "System.Runtime": "4.3.0", + "System.Runtime.Extensions": "4.3.0", + "System.Threading": "4.3.0", + "System.Xml.ReaderWriter": "4.3.0", + "System.Xml.XDocument": "4.3.0", + "System.Xml.XPath": "4.3.0" + } + }, + "WindowsAzure.Storage": { + "type": "Transitive", + "resolved": "9.3.1", + "contentHash": "NooNF4glP6BO7U4dno/xSfiEVVIv6OFcFfisX24Us2CZa9NQR3TSVEj9eVUlM5rLat5H9CHxk6M/mNSIaq7Vrw==", + "dependencies": { + "NETStandard.Library": "1.6.1", + "Newtonsoft.Json": "10.0.2" + } + }, + "xunit.abstractions": { + "type": "Transitive", + "resolved": "2.0.2", + "contentHash": "vItLB0WkaKg0426RgWq+ZdXH6D+YV/uH28C0weWMOBnVx7I+luHuEYss9hoOngpkiN5kUpLvh9VZRx1H2sk59A==" + }, + "xunit.analyzers": { + "type": "Transitive", + "resolved": "0.10.0", + "contentHash": "4/IDFCJfIeg6bix9apmUtIMwvOsiwqdEexeO/R2D4GReIGPLIRODTpId/l4LRSrAJk9lEO3Zx1H0Zx6uohJDNg==" + }, + "xunit.assert": { + "type": "Transitive", + "resolved": "2.4.0", + "contentHash": "Swvkm6iTjZr8TiUj5vMnmfG+2dD4s/BIBgsVOzTxxmoq2ndGsmM2WIL4wuqJ8RhxydWIDOPpIaaytjT2pMTEdg==" + }, + "xunit.core": { + "type": "Transitive", + "resolved": "2.4.0", + "contentHash": "BJ/O/tPEcHUCwQYuwqXoYccTMyw6B5dA6yh7WxWWBhKbjqTsG9RWL0nCQXM5yQYJwUuFzBkiXDPN1BO6UdBB4Q==", + "dependencies": { + "xunit.extensibility.core": "[2.4.0]", + "xunit.extensibility.execution": "[2.4.0]" + } + }, + "xunit.extensibility.core": { + "type": "Transitive", + "resolved": "2.4.0", + "contentHash": "qr/KrR6uukHXD9e/lLQjyCPfMEDuvvhNFDzsYzCF2kKlYKiqcADfUvA9Q68rBtKFtwHFeghjWEuv15KoGD2SfA==", + "dependencies": { + "xunit.abstractions": "2.0.2" + } + }, + "xunit.extensibility.execution": { + "type": "Transitive", + "resolved": "2.4.0", + "contentHash": "252Dzn7i5bMPKtAL15aOP3qJhxKd+57I8ldwIQRJa745JxQuiBu5Da0vtIISVTtc3buRSkBwVnD9iUzsEmCzZA==", + "dependencies": { + "xunit.extensibility.core": "[2.4.0]" + } + }, + "microsoft.azure.webjobs.extensions.sql": { + "type": "Project", + "dependencies": { + "Microsoft.ApplicationInsights": "2.17.0", + "Microsoft.AspNetCore.Http": "2.1.22", + "Microsoft.Azure.WebJobs": "3.0.31", + "Microsoft.Data.SqlClient": "3.0.1", + "Newtonsoft.Json": "13.0.1", + "System.Runtime.Caching": "4.7.0", + "morelinq": "3.3.2" + } + }, + "microsoft.azure.webjobs.extensions.sql.samples": { + "type": "Project", + "dependencies": { + "Microsoft.AspNetCore.Http": "2.1.22", + "Microsoft.Azure.WebJobs.Extensions.Sql": "99.99.99", + "Microsoft.Azure.WebJobs.Extensions.Storage": "5.0.0", + "Microsoft.NET.Sdk.Functions": "3.1.1", + "Newtonsoft.Json": "13.0.1" + } + }, + "microsoft.azure.webjobs.extensions.sql.tests": { + "type": "Project", + "dependencies": { + "Microsoft.AspNetCore.Http": "2.1.22", + "Microsoft.Azure.WebJobs.Extensions.Sql": "99.99.99", + "Microsoft.Azure.WebJobs.Extensions.Sql.Samples": "1.0.0", + "Microsoft.NET.Sdk.Functions": "3.1.1", + "Microsoft.NET.Test.Sdk": "17.0.0", + "Moq": "4.14.3", + "Newtonsoft.Json": "13.0.1", + "xunit": "2.4.0", + "xunit.runner.visualstudio": "2.4.0" + } + }, + "Microsoft.ApplicationInsights": { + "type": "CentralTransitive", + "requested": "[2.17.0, )", + "resolved": "2.17.0", + "contentHash": "moAOrjhwiCWdg8I4fXPEd+bnnyCSRxo6wmYQ0HuNrWJUctzZEiyVTbJ8QTS6+dBOFTxpI6x+OY5wHPHrgWOk1Q==", + "dependencies": { + "System.Diagnostics.DiagnosticSource": "5.0.0" + } + }, + "Microsoft.AspNetCore.Http": { + "type": "CentralTransitive", + "requested": "[2.1.22, )", + "resolved": "2.1.22", + "contentHash": "+Blk++1JWqghbl8+3azQmKhiNZA5wAepL9dY2I6KVmu2Ri07MAcvAVC888qUvO7yd7xgRgZOMfihezKg14O/2A==", + "dependencies": { + "Microsoft.AspNetCore.Http.Abstractions": "2.1.1", + "Microsoft.AspNetCore.WebUtilities": "2.1.1", + "Microsoft.Extensions.ObjectPool": "2.1.1", + "Microsoft.Extensions.Options": "2.1.1", + "Microsoft.Net.Http.Headers": "2.1.1" + } + }, + "Microsoft.Azure.WebJobs": { + "type": "CentralTransitive", + "requested": "[3.0.31, )", + "resolved": "3.0.31", + "contentHash": "Jn6E7OgT7LkwVB6lCpjXJcoQIvKrbJT+taVLA4CekEpa21pzZv6nQ2sYRSNzPz5ul3FAcYhmrCQgV7v2iopjgA==", + "dependencies": { + "Microsoft.Azure.WebJobs.Core": "3.0.31", + "Microsoft.Extensions.Configuration": "2.1.1", + "Microsoft.Extensions.Configuration.Abstractions": "2.1.1", + "Microsoft.Extensions.Configuration.EnvironmentVariables": "2.1.0", + "Microsoft.Extensions.Configuration.Json": "2.1.0", + "Microsoft.Extensions.Hosting": "2.1.0", + "Microsoft.Extensions.Logging": "2.1.1", + "Microsoft.Extensions.Logging.Abstractions": "2.1.1", + "Microsoft.Extensions.Logging.Configuration": "2.1.0", + "Newtonsoft.Json": "11.0.2", + "System.Memory.Data": "1.0.1", + "System.Threading.Tasks.Dataflow": "4.8.0" + } + }, + "Microsoft.Azure.WebJobs.Extensions.Storage": { + "type": "CentralTransitive", + "requested": "[5.0.0, )", + "resolved": "5.0.0", + "contentHash": "nu/a8wnkP+dXY6obqRDPoNFcOTElwWQukuAyx5r6bnWi6ybauD2J15dS7sdMb1jHgHQ9LPxWJLLl6W9sYhua/w==", + "dependencies": { + "Microsoft.Azure.WebJobs.Extensions.Storage.Blobs": "5.0.0", + "Microsoft.Azure.WebJobs.Extensions.Storage.Queues": "5.0.0" + } + }, + "Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator": { + "type": "CentralTransitive", + "requested": "[1.2.3, )", + "resolved": "1.2.2", + "contentHash": "vpiNt3JM1pt/WrDIkg7G2DHhIpI4t5I+R9rmXCxIGiby5oPGEolyfiYZdEf2kMMN3SbWzVAbk4Q3jKgFhO9MaQ==", + "dependencies": { + "System.Runtime.Loader": "4.3.0" + } + }, + "Microsoft.Data.SqlClient": { + "type": "CentralTransitive", + "requested": "[3.0.1, )", + "resolved": "3.0.1", + "contentHash": "5Jgcds8yukUeOHvc8S0rGW87rs2uYEM9/YyrYIb/0C+vqzIa2GiqbVPCDVcnApWhs67OSXLTM7lO6jro24v/rA==", + "dependencies": { + "Azure.Identity": "1.3.0", + "Microsoft.Data.SqlClient.SNI.runtime": "3.0.0", + "Microsoft.Identity.Client": "4.22.0", + "Microsoft.IdentityModel.JsonWebTokens": "6.8.0", + "Microsoft.IdentityModel.Protocols.OpenIdConnect": "6.8.0", + "Microsoft.Win32.Registry": "4.7.0", + "System.Configuration.ConfigurationManager": "4.7.0", + "System.Diagnostics.DiagnosticSource": "4.7.0", + "System.Runtime.Caching": "4.7.0", + "System.Security.Principal.Windows": "4.7.0", + "System.Text.Encoding.CodePages": "4.7.0", + "System.Text.Encodings.Web": "4.7.2" + } + }, + "Microsoft.NET.Sdk.Functions": { + "type": "CentralTransitive", + "requested": "[3.1.1, )", + "resolved": "3.1.1", + "contentHash": "sPPLAjDYroeuIDKwff5B1XrwvGzJm9K9GabXurmfpaXa3M4POy7ngLcG5mm+2pwjTA7e870pIjt1N2DqVQS4yA==", + "dependencies": { + "Microsoft.Azure.Functions.Analyzers": "[1.0.0, 2.0.0)", + "Microsoft.Azure.WebJobs": "[3.0.23, 3.1.0)", + "Microsoft.Azure.WebJobs.Extensions": "3.0.6", + "Microsoft.Azure.WebJobs.Extensions.Http": "[3.0.2, 3.1.0)", + "Microsoft.Azure.WebJobs.Script.ExtensionsMetadataGenerator": "1.2.2", + "Newtonsoft.Json": "11.0.2" + } + }, + "Microsoft.NET.Test.Sdk": { + "type": "CentralTransitive", + "requested": "[17.0.0, )", + "resolved": "17.0.0", + "contentHash": "fJcnMY3jX1MzJvhGvUWauRhU5eQsOaHdwlrcnI3NabBhbi8WLAkMFI8d0YnewA/+b9q/U7vbhp8Xmh1vJ05FYQ==", + "dependencies": { + "Microsoft.CodeCoverage": "17.0.0", + "Microsoft.TestPlatform.TestHost": "17.0.0" + } + }, + "Moq": { + "type": "CentralTransitive", + "requested": "[4.14.3, )", + "resolved": "4.14.3", + "contentHash": "1MB/1YJ9irnhsMXoqJVMUSNZpxpfOJFocLebnd3LMZQ8D8cJE22DxSS+dA7trrHbZf+IWVtbbb0AR+xz/RG/Eg==", + "dependencies": { + "Castle.Core": "4.4.0", + "System.Threading.Tasks.Extensions": "4.5.1" + } + }, + "morelinq": { + "type": "CentralTransitive", + "requested": "[3.3.2, )", + "resolved": "3.3.2", + "contentHash": "MQc8GppZJLmjvcpEdf3EkC6ovsp7gRWt2e5mC7dcIOrgwSc+yjFd3JQ0iRqr3XrUT6rb/phv0IkEmBtbfVA7AQ==" + }, + "Newtonsoft.Json": { + "type": "CentralTransitive", + "requested": "[13.0.1, )", + "resolved": "13.0.1", + "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A==" + }, + "System.Runtime.Caching": { + "type": "CentralTransitive", + "requested": "[4.7.0, )", + "resolved": "4.7.0", + "contentHash": "NdvNRjTPxYvIEhXQszT9L9vJhdQoX6AQ0AlhjTU+5NqFQVuacJTfhPVAvtGWNA2OJCqRiR/okBcZgMwI6MqcZg==", + "dependencies": { + "System.Configuration.ConfigurationManager": "4.7.0" + } + }, + "xunit": { + "type": "CentralTransitive", + "requested": "[2.4.0, )", + "resolved": "2.4.0", + "contentHash": "NL00nGsDsyWc1CWxz5FXXjLpW9oFG18WJoTPCyhNv4KGP/e5iLJqAqgM1uaJZyQ6WaTtmWIy4yjYP3RdcaT7Vw==", + "dependencies": { + "xunit.analyzers": "0.10.0", + "xunit.assert": "[2.4.0]", + "xunit.core": "[2.4.0]" + } + }, + "xunit.runner.visualstudio": { + "type": "CentralTransitive", + "requested": "[2.4.0, )", + "resolved": "2.4.0", + "contentHash": "3eq5cGXbEJkqW9nwLuXwtxy9B5gMA8i7HW4rN63AhAvy5UvEcQbZnve23wx/oPrkyg/4CbfNhxkBezS0b1oUdQ==", + "dependencies": { + "Microsoft.NET.Test.Sdk": "15.0.0" + } + } + } + } +} \ No newline at end of file diff --git a/samples/samples-csharp/local.settings.json b/samples/samples-csharp/local.settings.json index 2ee8fdfec..c92046171 100644 --- a/samples/samples-csharp/local.settings.json +++ b/samples/samples-csharp/local.settings.json @@ -3,8 +3,9 @@ "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "dotnet", - "SqlConnectionString": "", + "SqlConnectionString": "Server=localhost\\SQLEXPRESS;Database=Contact;Trusted_Connection=True", "Sp_SelectCost": "SelectProductsCost", - "ProductCost": 100 + "ProductCost": 100, + "AzureWebJobsSecretStorageType": "files" } } \ No newline at end of file diff --git a/test/Common/TestUtils.cs b/test/Common/TestUtils.cs index 1d302a1c1..2ea752ba2 100644 --- a/test/Common/TestUtils.cs +++ b/test/Common/TestUtils.cs @@ -52,6 +52,7 @@ public static int ExecuteNonQuery( { throw new ArgumentNullException(nameof(commandText)); } + Console.WriteLine("USING THIS CONNECTION STRING: " + connection.ConnectionString); using (IDbCommand cmd = connection.CreateCommand()) { diff --git a/test/Integration/IntegrationTestBase.cs b/test/Integration/IntegrationTestBase.cs index fd2ee1b3d..ff8873a3f 100644 --- a/test/Integration/IntegrationTestBase.cs +++ b/test/Integration/IntegrationTestBase.cs @@ -57,7 +57,7 @@ public class IntegrationTestBase : IDisposable /// protected int Port { get; private set; } = 7071; - public IntegrationTestBase(ITestOutputHelper output) + public IntegrationTestBase(ITestOutputHelper output = null) { this.TestOutput = output; this.SetupDatabase(); @@ -72,20 +72,20 @@ public IntegrationTestBase(ITestOutputHelper output) /// By default, integrated authentication will be used to connect to the server, unless the env variable "SA_PASSWORD" is set. /// In this case, connection will be made using SQL login with user "SA" and the provided password. /// - private void SetupDatabase() + protected void SetupDatabase() { // Get the test server name from environment variable "TEST_SERVER", default to localhost if not set string testServer = Environment.GetEnvironmentVariable("TEST_SERVER"); if (string.IsNullOrEmpty(testServer)) { - testServer = "localhost"; + testServer = "localhost\\SQLEXPRESS"; } // First connect to master to create the database var connectionStringBuilder = new SqlConnectionStringBuilder() { DataSource = testServer, - InitialCatalog = "master", + InitialCatalog = "Contact", Pooling = false }; @@ -166,7 +166,10 @@ protected void StartAzurite() /// protected void StartFunctionHost(string functionName, SupportedLanguages language, bool useTestFolder = false) { - string workingDirectory = useTestFolder ? GetPathToBin() : Path.Combine(GetPathToBin(), "SqlExtensionSamples", Enum.GetName(typeof(SupportedLanguages), language)); + // string workingDirectory = useTestFolder ? GetPathToBin() : Path.Combine(GetPathToBin(), "SqlExtensionSamples", Enum.GetName(typeof(SupportedLanguages), language)); + Console.WriteLine("IGNORE: " + language.ToString()); + Console.WriteLine("IGNORE: " + useTestFolder); + string workingDirectory = "C:\\Users\\luczhan\\GitProjects\\azure-functions-sql-extension\\test\\bin\\Debug\\netcoreapp3.1\\SqlExtensionSamples\\CSharp"; if (!Directory.Exists(workingDirectory)) { throw new FileNotFoundException("Working directory not found at " + workingDirectory); @@ -179,11 +182,11 @@ protected void StartFunctionHost(string functionName, SupportedLanguages languag Arguments = $"start --verbose --port {this.Port} --functions {functionName}", WorkingDirectory = workingDirectory, WindowStyle = ProcessWindowStyle.Hidden, - RedirectStandardOutput = true, - RedirectStandardError = true, - UseShellExecute = false + RedirectStandardOutput = false, + RedirectStandardError = false, + UseShellExecute = true }; - this.TestOutput.WriteLine($"Starting {startInfo.FileName} {startInfo.Arguments} in {startInfo.WorkingDirectory}"); + Console.WriteLine($"Starting {startInfo.FileName} {startInfo.Arguments} in {startInfo.WorkingDirectory}"); this.FunctionHost = new Process { StartInfo = startInfo @@ -192,8 +195,8 @@ protected void StartFunctionHost(string functionName, SupportedLanguages languag this.FunctionHost.ErrorDataReceived += this.TestOutputHandler; this.FunctionHost.Start(); - this.FunctionHost.BeginOutputReadLine(); - this.FunctionHost.BeginErrorReadLine(); + // this.FunctionHost.BeginOutputReadLine(); + // this.FunctionHost.BeginErrorReadLine(); var taskCompletionSource = new TaskCompletionSource(); this.FunctionHost.OutputDataReceived += (object sender, DataReceivedEventArgs e) => @@ -203,28 +206,30 @@ protected void StartFunctionHost(string functionName, SupportedLanguages languag if (e != null && !string.IsNullOrEmpty(e.Data) && e.Data.Contains($"http://localhost:{this.Port}/api")) { taskCompletionSource.SetResult(true); + Console.WriteLine("-----Azure Function host started-----"); } }; - this.TestOutput.WriteLine($"Waiting for Azure Function host to start..."); + Console.WriteLine($"Waiting for Azure Function host to start..."); taskCompletionSource.Task.Wait(60000); - this.TestOutput.WriteLine($"Azure Function host started!"); + Console.WriteLine($"Azure Function host started!"); } private static string GetFunctionsCoreToolsPath() { // Determine npm install path from either env var set by pipeline or OS defaults // Pipeline env var is needed as the Windows hosted agents installs to a non-traditional location - string nodeModulesPath = Environment.GetEnvironmentVariable("NODE_MODULES_PATH"); - if (string.IsNullOrEmpty(nodeModulesPath)) - { - nodeModulesPath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"npm\node_modules\") : - @"/usr/local/lib/node_modules"; - } + // string nodeModulesPath = Environment.GetEnvironmentVariable("NODE_MODULES_PATH"); + // if (string.IsNullOrEmpty(nodeModulesPath)) + // { + // nodeModulesPath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? + // Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"npm\node_modules\") : + // @"/usr/local/lib/node_modules"; + // } string funcExe = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "func.exe" : "func"; - string funcPath = Path.Combine(nodeModulesPath, "azure-functions-core-tools", "bin", funcExe); + // string funcPath = Path.Combine(nodeModulesPath, "azure-functions-core-tools", "bin", funcExe); + string funcPath = "C:\\Program Files\\nodejs\\node_modules\\azure-functions-core-tools\\bin\\func.exe"; if (!File.Exists(funcPath)) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -247,14 +252,15 @@ private void TestOutputHandler(object sender, DataReceivedEventArgs e) { if (e != null && !string.IsNullOrEmpty(e.Data)) { - this.TestOutput.WriteLine(e.Data); + this.TestOutput?.WriteLine(e.Data); } } protected async Task SendGetRequest(string requestUri, bool verifySuccess = true) { string timeStamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", System.Globalization.CultureInfo.InvariantCulture); - this.TestOutput.WriteLine($"[{timeStamp}] Sending GET request: {requestUri}"); + this.TestOutput?.WriteLine($"[{timeStamp}] Sending GET request: {requestUri}"); + Console.WriteLine($"[{timeStamp}] Sending GET request: {requestUri}"); if (string.IsNullOrEmpty(requestUri)) { @@ -274,7 +280,7 @@ protected async Task SendGetRequest(string requestUri, bool protected async Task SendPostRequest(string requestUri, string json, bool verifySuccess = true) { - this.TestOutput.WriteLine("Sending POST request: " + requestUri); + this.TestOutput?.WriteLine("Sending POST request: " + requestUri); if (string.IsNullOrEmpty(requestUri)) { @@ -322,7 +328,7 @@ public void Dispose() } catch (Exception e1) { - this.TestOutput.WriteLine($"Failed to close connection. Error: {e1.Message}"); + this.TestOutput?.WriteLine($"Failed to close connection. Error: {e1.Message}"); } try { @@ -333,7 +339,7 @@ public void Dispose() } catch (Exception e2) { - this.TestOutput.WriteLine($"Failed to drop {this.DatabaseName}, Error: {e2.Message}"); + this.TestOutput?.WriteLine($"Failed to drop {this.DatabaseName}, Error: {e2.Message}"); } finally { @@ -347,7 +353,7 @@ public void Dispose() } catch (Exception e3) { - this.TestOutput.WriteLine($"Failed to stop function host, Error: {e3.Message}"); + this.TestOutput?.WriteLine($"Failed to stop function host, Error: {e3.Message}"); } try @@ -357,7 +363,7 @@ public void Dispose() } catch (Exception e4) { - this.TestOutput.WriteLine($"Failed to stop Azurite, Error: {e4.Message}"); + this.TestOutput?.WriteLine($"Failed to stop Azurite, Error: {e4.Message}"); } } GC.SuppressFinalize(this); From e04aed239717458f030b103dca33f77625d63f00 Mon Sep 17 00:00:00 2001 From: luczhan Date: Wed, 27 Jul 2022 15:13:21 -0700 Subject: [PATCH 02/17] fix logger --- benchmark/SqlInputBindingBenchmarks.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/benchmark/SqlInputBindingBenchmarks.cs b/benchmark/SqlInputBindingBenchmarks.cs index b73c1fa5f..5431ef86a 100644 --- a/benchmark/SqlInputBindingBenchmarks.cs +++ b/benchmark/SqlInputBindingBenchmarks.cs @@ -16,10 +16,8 @@ namespace Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark { public class InputBindingBenchmarks : IntegrationTestBase { - [GlobalSetup] - public void GlobalSetup() + public InputBindingBenchmarks() : base() { - this.SetupDatabase(); this.StartFunctionHost(nameof(GetProducts), SupportedLanguages.CSharp); // Generate T-SQL to insert n rows of data with cost From 330eaa50b1d4453f913681fe7ebd8d29701ae978 Mon Sep 17 00:00:00 2001 From: luczhan Date: Wed, 27 Jul 2022 15:13:40 -0700 Subject: [PATCH 03/17] fix logger --- test/Integration/IntegrationTestBase.cs | 51 +++++++++++++++---------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/test/Integration/IntegrationTestBase.cs b/test/Integration/IntegrationTestBase.cs index ff8873a3f..8429d73b8 100644 --- a/test/Integration/IntegrationTestBase.cs +++ b/test/Integration/IntegrationTestBase.cs @@ -72,7 +72,7 @@ public IntegrationTestBase(ITestOutputHelper output = null) /// By default, integrated authentication will be used to connect to the server, unless the env variable "SA_PASSWORD" is set. /// In this case, connection will be made using SQL login with user "SA" and the provided password. /// - protected void SetupDatabase() + private void SetupDatabase() { // Get the test server name from environment variable "TEST_SERVER", default to localhost if not set string testServer = Environment.GetEnvironmentVariable("TEST_SERVER"); @@ -134,7 +134,7 @@ private void ExecuteAllScriptsInFolder(string folder) { foreach (string file in Directory.EnumerateFiles(folder, "*.sql")) { - Console.WriteLine($"Executing script ${file}"); + this.LogOutput($"Executing script ${file}"); this.ExecuteNonQuery(File.ReadAllText(file)); } } @@ -154,7 +154,8 @@ protected void StartAzurite() } }; - this.AzuriteHost.Start(); + // this.AzuriteHost.Start(); + this.LogOutput(this.AzuriteHost.ToString()); } /// @@ -182,11 +183,11 @@ protected void StartFunctionHost(string functionName, SupportedLanguages languag Arguments = $"start --verbose --port {this.Port} --functions {functionName}", WorkingDirectory = workingDirectory, WindowStyle = ProcessWindowStyle.Hidden, - RedirectStandardOutput = false, - RedirectStandardError = false, - UseShellExecute = true + RedirectStandardOutput = true, + RedirectStandardError = true, + UseShellExecute = false }; - Console.WriteLine($"Starting {startInfo.FileName} {startInfo.Arguments} in {startInfo.WorkingDirectory}"); + this.LogOutput($"Starting {startInfo.FileName} {startInfo.Arguments} in {startInfo.WorkingDirectory}"); this.FunctionHost = new Process { StartInfo = startInfo @@ -195,8 +196,8 @@ protected void StartFunctionHost(string functionName, SupportedLanguages languag this.FunctionHost.ErrorDataReceived += this.TestOutputHandler; this.FunctionHost.Start(); - // this.FunctionHost.BeginOutputReadLine(); - // this.FunctionHost.BeginErrorReadLine(); + this.FunctionHost.BeginOutputReadLine(); + this.FunctionHost.BeginErrorReadLine(); var taskCompletionSource = new TaskCompletionSource(); this.FunctionHost.OutputDataReceived += (object sender, DataReceivedEventArgs e) => @@ -206,12 +207,11 @@ protected void StartFunctionHost(string functionName, SupportedLanguages languag if (e != null && !string.IsNullOrEmpty(e.Data) && e.Data.Contains($"http://localhost:{this.Port}/api")) { taskCompletionSource.SetResult(true); - Console.WriteLine("-----Azure Function host started-----"); } }; - Console.WriteLine($"Waiting for Azure Function host to start..."); + this.LogOutput($"Waiting for Azure Function host to start..."); taskCompletionSource.Task.Wait(60000); - Console.WriteLine($"Azure Function host started!"); + this.LogOutput($"Azure Function host started!"); } private static string GetFunctionsCoreToolsPath() @@ -248,19 +248,30 @@ private static string GetFunctionsCoreToolsPath() return funcPath; } + private void LogOutput(String output) + { + if (this.TestOutput != null) + { + this.TestOutput.WriteLine(output); + } + else + { + Console.WriteLine(output); + } + } + private void TestOutputHandler(object sender, DataReceivedEventArgs e) { if (e != null && !string.IsNullOrEmpty(e.Data)) { - this.TestOutput?.WriteLine(e.Data); + this.LogOutput(e.Data); } } protected async Task SendGetRequest(string requestUri, bool verifySuccess = true) { string timeStamp = DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", System.Globalization.CultureInfo.InvariantCulture); - this.TestOutput?.WriteLine($"[{timeStamp}] Sending GET request: {requestUri}"); - Console.WriteLine($"[{timeStamp}] Sending GET request: {requestUri}"); + this.LogOutput($"[{timeStamp}] Sending GET request: {requestUri}"); if (string.IsNullOrEmpty(requestUri)) { @@ -280,7 +291,7 @@ protected async Task SendGetRequest(string requestUri, bool protected async Task SendPostRequest(string requestUri, string json, bool verifySuccess = true) { - this.TestOutput?.WriteLine("Sending POST request: " + requestUri); + this.LogOutput("Sending POST request: " + requestUri); if (string.IsNullOrEmpty(requestUri)) { @@ -328,7 +339,7 @@ public void Dispose() } catch (Exception e1) { - this.TestOutput?.WriteLine($"Failed to close connection. Error: {e1.Message}"); + this.LogOutput($"Failed to close connection. Error: {e1.Message}"); } try { @@ -339,7 +350,7 @@ public void Dispose() } catch (Exception e2) { - this.TestOutput?.WriteLine($"Failed to drop {this.DatabaseName}, Error: {e2.Message}"); + this.LogOutput($"Failed to drop {this.DatabaseName}, Error: {e2.Message}"); } finally { @@ -353,7 +364,7 @@ public void Dispose() } catch (Exception e3) { - this.TestOutput?.WriteLine($"Failed to stop function host, Error: {e3.Message}"); + this.LogOutput($"Failed to stop function host, Error: {e3.Message}"); } try @@ -363,7 +374,7 @@ public void Dispose() } catch (Exception e4) { - this.TestOutput?.WriteLine($"Failed to stop Azurite, Error: {e4.Message}"); + this.LogOutput($"Failed to stop Azurite, Error: {e4.Message}"); } } GC.SuppressFinalize(this); From 152ac4b34179c2b7ad36d94f390ab90739cc30a6 Mon Sep 17 00:00:00 2001 From: luczhan Date: Fri, 29 Jul 2022 10:36:16 -0700 Subject: [PATCH 04/17] copy functions to benchmark output folder --- WebJobs.Extensions.Sql.sln | 9 ++++++ ...re.WebJobs.Extensions.Sql.Benchmark.csproj | 18 +++++++++++- samples/samples-csharp/local.settings.json | 5 ++-- test/Common/TestUtils.cs | 1 - test/Integration/IntegrationTestBase.cs | 28 ++++++++----------- 5 files changed, 39 insertions(+), 22 deletions(-) diff --git a/WebJobs.Extensions.Sql.sln b/WebJobs.Extensions.Sql.sln index b0b16e416..bf7453852 100644 --- a/WebJobs.Extensions.Sql.sln +++ b/WebJobs.Extensions.Sql.sln @@ -20,6 +20,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark", "benchmark\Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj", "{1A5148B7-F877-4813-852C-F94D6EF795E8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmark", "benchmark", "{F0F3562F-9176-4461-98E4-13D38D3DD056}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -38,6 +42,10 @@ Global {A5B55530-71C8-4A9A-92AD-1D33A5E80FC1}.Debug|Any CPU.Build.0 = Debug|Any CPU {A5B55530-71C8-4A9A-92AD-1D33A5E80FC1}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5B55530-71C8-4A9A-92AD-1D33A5E80FC1}.Release|Any CPU.Build.0 = Release|Any CPU + {1A5148B7-F877-4813-852C-F94D6EF795E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1A5148B7-F877-4813-852C-F94D6EF795E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1A5148B7-F877-4813-852C-F94D6EF795E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1A5148B7-F877-4813-852C-F94D6EF795E8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -46,6 +54,7 @@ Global {B2E0EFB9-BEDB-48F7-BD5A-0E1010333475} = {3691FB44-971D-43FD-9B2F-916B8CE689DB} {4453B407-2CA3-4011-BDE5-247FCE5C1974} = {21CDC01C-247B-46B0-A8F5-9D35686C628B} {A5B55530-71C8-4A9A-92AD-1D33A5E80FC1} = {F7E99EB5-47D3-4B50-A6AA-D8D5508A121A} + {1A5148B7-F877-4813-852C-F94D6EF795E8} = {F0F3562F-9176-4461-98E4-13D38D3DD056} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {49902AA5-150F-4567-B562-4AA8549B2CF4} diff --git a/benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj b/benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj index f143eff35..38e7a0eb5 100644 --- a/benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj +++ b/benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj @@ -13,7 +13,23 @@ - + + + + + + <_CSharpCopyItems Include="..\samples\samples-csharp\bin\$(Configuration)\$(TargetFramework)\**\*.*" /> + + + + + + + + PreserveNewest + %(RecursiveDir)\%(Filename)%(Extension) + False + \ No newline at end of file diff --git a/samples/samples-csharp/local.settings.json b/samples/samples-csharp/local.settings.json index c92046171..2ee8fdfec 100644 --- a/samples/samples-csharp/local.settings.json +++ b/samples/samples-csharp/local.settings.json @@ -3,9 +3,8 @@ "Values": { "AzureWebJobsStorage": "UseDevelopmentStorage=true", "FUNCTIONS_WORKER_RUNTIME": "dotnet", - "SqlConnectionString": "Server=localhost\\SQLEXPRESS;Database=Contact;Trusted_Connection=True", + "SqlConnectionString": "", "Sp_SelectCost": "SelectProductsCost", - "ProductCost": 100, - "AzureWebJobsSecretStorageType": "files" + "ProductCost": 100 } } \ No newline at end of file diff --git a/test/Common/TestUtils.cs b/test/Common/TestUtils.cs index 2ea752ba2..1d302a1c1 100644 --- a/test/Common/TestUtils.cs +++ b/test/Common/TestUtils.cs @@ -52,7 +52,6 @@ public static int ExecuteNonQuery( { throw new ArgumentNullException(nameof(commandText)); } - Console.WriteLine("USING THIS CONNECTION STRING: " + connection.ConnectionString); using (IDbCommand cmd = connection.CreateCommand()) { diff --git a/test/Integration/IntegrationTestBase.cs b/test/Integration/IntegrationTestBase.cs index 8429d73b8..09f079aa4 100644 --- a/test/Integration/IntegrationTestBase.cs +++ b/test/Integration/IntegrationTestBase.cs @@ -85,7 +85,7 @@ private void SetupDatabase() var connectionStringBuilder = new SqlConnectionStringBuilder() { DataSource = testServer, - InitialCatalog = "Contact", + InitialCatalog = "master", Pooling = false }; @@ -154,8 +154,7 @@ protected void StartAzurite() } }; - // this.AzuriteHost.Start(); - this.LogOutput(this.AzuriteHost.ToString()); + this.AzuriteHost.Start(); } /// @@ -167,10 +166,7 @@ protected void StartAzurite() /// protected void StartFunctionHost(string functionName, SupportedLanguages language, bool useTestFolder = false) { - // string workingDirectory = useTestFolder ? GetPathToBin() : Path.Combine(GetPathToBin(), "SqlExtensionSamples", Enum.GetName(typeof(SupportedLanguages), language)); - Console.WriteLine("IGNORE: " + language.ToString()); - Console.WriteLine("IGNORE: " + useTestFolder); - string workingDirectory = "C:\\Users\\luczhan\\GitProjects\\azure-functions-sql-extension\\test\\bin\\Debug\\netcoreapp3.1\\SqlExtensionSamples\\CSharp"; + string workingDirectory = useTestFolder ? GetPathToBin() : Path.Combine(GetPathToBin(), "SqlExtensionSamples", Enum.GetName(typeof(SupportedLanguages), language)); if (!Directory.Exists(workingDirectory)) { throw new FileNotFoundException("Working directory not found at " + workingDirectory); @@ -218,18 +214,16 @@ private static string GetFunctionsCoreToolsPath() { // Determine npm install path from either env var set by pipeline or OS defaults // Pipeline env var is needed as the Windows hosted agents installs to a non-traditional location - // string nodeModulesPath = Environment.GetEnvironmentVariable("NODE_MODULES_PATH"); - // if (string.IsNullOrEmpty(nodeModulesPath)) - // { - // nodeModulesPath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? - // Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"npm\node_modules\") : - // @"/usr/local/lib/node_modules"; - // } + string nodeModulesPath = Environment.GetEnvironmentVariable("NODE_MODULES_PATH"); + if (string.IsNullOrEmpty(nodeModulesPath)) + { + nodeModulesPath = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"npm\node_modules\") : + @"/usr/local/lib/node_modules"; + } string funcExe = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "func.exe" : "func"; - // string funcPath = Path.Combine(nodeModulesPath, "azure-functions-core-tools", "bin", funcExe); - - string funcPath = "C:\\Program Files\\nodejs\\node_modules\\azure-functions-core-tools\\bin\\func.exe"; + string funcPath = Path.Combine(nodeModulesPath, "azure-functions-core-tools", "bin", funcExe); if (!File.Exists(funcPath)) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) From 55b67bd80c48dd6c0310bef0fcb90f34cd5abc8a Mon Sep 17 00:00:00 2001 From: luczhan Date: Fri, 29 Jul 2022 10:56:19 -0700 Subject: [PATCH 05/17] cleanup --- ...re.WebJobs.Extensions.Sql.Benchmark.csproj | 1 + benchmark/SqlInputBindingBenchmarks.cs | 20 ------------------- test/Integration/IntegrationTestBase.cs | 19 +++++++++++++++++- .../SqlInputBindingIntegrationTests.cs | 16 --------------- 4 files changed, 19 insertions(+), 37 deletions(-) diff --git a/benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj b/benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj index 38e7a0eb5..9405196d3 100644 --- a/benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj +++ b/benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj @@ -24,6 +24,7 @@ + PreserveNewest diff --git a/benchmark/SqlInputBindingBenchmarks.cs b/benchmark/SqlInputBindingBenchmarks.cs index 5431ef86a..71ade2217 100644 --- a/benchmark/SqlInputBindingBenchmarks.cs +++ b/benchmark/SqlInputBindingBenchmarks.cs @@ -1,9 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using System; using System.Net.Http; -using System.Text; using System.Threading.Tasks; using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common; using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples; @@ -11,7 +9,6 @@ using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common; using BenchmarkDotNet.Attributes; - namespace Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark { public class InputBindingBenchmarks : IntegrationTestBase @@ -36,7 +33,6 @@ public async Task GetProductsTest(string function, string a [GlobalCleanup] public void GlobalCleanup() { - Console.WriteLine("-----GLOBAL CLEANUP CALLED-----"); this.Dispose(); } @@ -61,21 +57,5 @@ private static Product[] GetProductsWithSameCost(int n, int cost) } return result; } - - private void InsertProducts(Product[] products) - { - if (products.Length == 0) - { - return; - } - - var queryBuilder = new StringBuilder(); - foreach (Product p in products) - { - queryBuilder.AppendLine($"INSERT INTO dbo.Products VALUES({p.ProductID}, '{p.Name}', {p.Cost});"); - } - - this.ExecuteNonQuery(queryBuilder.ToString()); - } } } \ No newline at end of file diff --git a/test/Integration/IntegrationTestBase.cs b/test/Integration/IntegrationTestBase.cs index 09f079aa4..3b1e1a62a 100644 --- a/test/Integration/IntegrationTestBase.cs +++ b/test/Integration/IntegrationTestBase.cs @@ -78,7 +78,7 @@ private void SetupDatabase() string testServer = Environment.GetEnvironmentVariable("TEST_SERVER"); if (string.IsNullOrEmpty(testServer)) { - testServer = "localhost\\SQLEXPRESS"; + testServer = "localhost"; } // First connect to master to create the database @@ -224,6 +224,7 @@ private static string GetFunctionsCoreToolsPath() string funcExe = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "func.exe" : "func"; string funcPath = Path.Combine(nodeModulesPath, "azure-functions-core-tools", "bin", funcExe); + if (!File.Exists(funcPath)) { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) @@ -304,6 +305,22 @@ protected async Task SendPostRequest(string requestUri, str return response; } + protected void InsertProducts(Product[] products) + { + if (products.Length == 0) + { + return; + } + + var queryBuilder = new StringBuilder(); + foreach (Product p in products) + { + queryBuilder.AppendLine($"INSERT INTO dbo.Products VALUES({p.ProductID}, '{p.Name}', {p.Cost});"); + } + + this.ExecuteNonQuery(queryBuilder.ToString()); + } + /// /// Executes a command against the current connection. /// diff --git a/test/Integration/SqlInputBindingIntegrationTests.cs b/test/Integration/SqlInputBindingIntegrationTests.cs index d5750daa5..08f26f8fb 100644 --- a/test/Integration/SqlInputBindingIntegrationTests.cs +++ b/test/Integration/SqlInputBindingIntegrationTests.cs @@ -3,7 +3,6 @@ using System; using System.Net.Http; -using System.Text; using System.Threading.Tasks; using Newtonsoft.Json; using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common; @@ -185,20 +184,5 @@ private static Product[] GetProductsWithSameCostAndName(int n, int cost, string return result; } - private void InsertProducts(Product[] products) - { - if (products.Length == 0) - { - return; - } - - var queryBuilder = new StringBuilder(); - foreach (Product p in products) - { - queryBuilder.AppendLine($"INSERT INTO dbo.Products VALUES({p.ProductID}, '{p.Name}', {p.Cost});"); - } - - this.ExecuteNonQuery(queryBuilder.ToString()); - } } } From 05270790a1e572e4b2abef0bc07c78802e6a29ff Mon Sep 17 00:00:00 2001 From: luczhan Date: Fri, 29 Jul 2022 11:38:56 -0700 Subject: [PATCH 06/17] move setup methods to base --- benchmark/SqlInputBindingBenchmarks.cs | 23 ----- test/Integration/IntegrationTestBase.cs | 84 +++++++++++++++---- .../SqlInputBindingIntegrationTests.cs | 54 ------------ 3 files changed, 68 insertions(+), 93 deletions(-) diff --git a/benchmark/SqlInputBindingBenchmarks.cs b/benchmark/SqlInputBindingBenchmarks.cs index 71ade2217..c89705edc 100644 --- a/benchmark/SqlInputBindingBenchmarks.cs +++ b/benchmark/SqlInputBindingBenchmarks.cs @@ -4,7 +4,6 @@ using System.Net.Http; using System.Threading.Tasks; using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common; -using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples; using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Integration; using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common; using BenchmarkDotNet.Attributes; @@ -35,27 +34,5 @@ public void GlobalCleanup() { this.Dispose(); } - - private async Task SendInputRequest(string functionName, string query = "") - { - string requestUri = $"http://localhost:{this.Port}/api/{functionName}/{query}"; - - return await this.SendGetRequest(requestUri); - } - - private static Product[] GetProductsWithSameCost(int n, int cost) - { - var result = new Product[n]; - for (int i = 0; i < n; i++) - { - result[i] = new Product - { - ProductID = i, - Name = "test", - Cost = cost - }; - } - return result; - } } } \ No newline at end of file diff --git a/test/Integration/IntegrationTestBase.cs b/test/Integration/IntegrationTestBase.cs index 3b1e1a62a..7eeee0fb5 100644 --- a/test/Integration/IntegrationTestBase.cs +++ b/test/Integration/IntegrationTestBase.cs @@ -305,22 +305,6 @@ protected async Task SendPostRequest(string requestUri, str return response; } - protected void InsertProducts(Product[] products) - { - if (products.Length == 0) - { - return; - } - - var queryBuilder = new StringBuilder(); - foreach (Product p in products) - { - queryBuilder.AppendLine($"INSERT INTO dbo.Products VALUES({p.ProductID}, '{p.Name}', {p.Cost});"); - } - - this.ExecuteNonQuery(queryBuilder.ToString()); - } - /// /// Executes a command against the current connection. /// @@ -390,5 +374,73 @@ public void Dispose() } GC.SuppressFinalize(this); } + + protected async Task SendInputRequest(string functionName, string query = "") + { + string requestUri = $"http://localhost:{this.Port}/api/{functionName}/{query}"; + + return await this.SendGetRequest(requestUri); + } + + protected void InsertProducts(Product[] products) + { + if (products.Length == 0) + { + return; + } + + var queryBuilder = new StringBuilder(); + foreach (Product p in products) + { + queryBuilder.AppendLine($"INSERT INTO dbo.Products VALUES({p.ProductID}, '{p.Name}', {p.Cost});"); + } + + this.ExecuteNonQuery(queryBuilder.ToString()); + } + + protected static Product[] GetProducts(int n, int cost) + { + var result = new Product[n]; + for (int i = 1; i <= n; i++) + { + result[i - 1] = new Product + { + ProductID = i, + Name = "test", + Cost = cost * i + }; + } + return result; + } + + protected static Product[] GetProductsWithSameCost(int n, int cost) + { + var result = new Product[n]; + for (int i = 0; i < n; i++) + { + result[i] = new Product + { + ProductID = i, + Name = "test", + Cost = cost + }; + } + return result; + } + + protected static Product[] GetProductsWithSameCostAndName(int n, int cost, string name, int offset = 0) + { + var result = new Product[n]; + for (int i = 0; i < n; i++) + { + result[i] = new Product + { + ProductID = i + offset, + Name = name, + Cost = cost + }; + } + return result; + } } } diff --git a/test/Integration/SqlInputBindingIntegrationTests.cs b/test/Integration/SqlInputBindingIntegrationTests.cs index 08f26f8fb..9dc6532b9 100644 --- a/test/Integration/SqlInputBindingIntegrationTests.cs +++ b/test/Integration/SqlInputBindingIntegrationTests.cs @@ -3,7 +3,6 @@ using System; using System.Net.Http; -using System.Threading.Tasks; using Newtonsoft.Json; using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common; using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples; @@ -20,13 +19,6 @@ public SqlInputBindingIntegrationTests(ITestOutputHelper output) : base(output) { } - private async Task SendInputRequest(string functionName, string query = "") - { - string requestUri = $"http://localhost:{this.Port}/api/{functionName}/{query}"; - - return await this.SendGetRequest(requestUri); - } - [Theory] [SqlInlineData(0, 100)] [SqlInlineData(1, -500)] @@ -138,51 +130,5 @@ public async void GetProductNamesViewTest(SupportedLanguages lang) Assert.Equal(expectedResponse, TestUtils.CleanJsonString(actualResponse), StringComparer.OrdinalIgnoreCase); } - - private static Product[] GetProductsWithSameCost(int n, int cost) - { - var result = new Product[n]; - for (int i = 0; i < n; i++) - { - result[i] = new Product - { - ProductID = i, - Name = "test", - Cost = cost - }; - } - return result; - } - - private static Product[] GetProducts(int n, int cost) - { - var result = new Product[n]; - for (int i = 1; i <= n; i++) - { - result[i - 1] = new Product - { - ProductID = i, - Name = "test", - Cost = cost * i - }; - } - return result; - } - - private static Product[] GetProductsWithSameCostAndName(int n, int cost, string name, int offset = 0) - { - var result = new Product[n]; - for (int i = 0; i < n; i++) - { - result[i] = new Product - { - ProductID = i + offset, - Name = name, - Cost = cost - }; - } - return result; - } - } } From 45649cbab0b061692a6460a2037e2503b29257ef Mon Sep 17 00:00:00 2001 From: luczhan Date: Tue, 16 Aug 2022 15:17:02 -0700 Subject: [PATCH 07/17] add output binding perf --- ...re.WebJobs.Extensions.Sql.Benchmark.csproj | 4 +- benchmark/Program.cs | 7 +- benchmark/SqlInputBindingBenchmarks.cs | 24 +++--- benchmark/SqlOutputBindingBenchmarks.cs | 49 +++++++++++ samples/samples-csharp/Common/Invoice.cs | 60 +++++++++++++ .../Database/Tables/Invoices.sql | 27 ++++++ samples/samples-csharp/GlobalSuppressions.cs | 2 + .../InputBindingSamples/GetInvoices.cs | 26 ++++++ .../InputBindingSamples/GetProductsTopN.cs | 27 ++++++ .../OutputBindingSamples/AddInvoicesArray.cs | 25 ++++++ test/Common/TestUtils.cs | 1 + test/Integration/IntegrationTestBase.cs | 85 +++++++++++++------ .../SqlOutputBindingIntegrationTests.cs | 22 ----- 13 files changed, 297 insertions(+), 62 deletions(-) create mode 100644 benchmark/SqlOutputBindingBenchmarks.cs create mode 100644 samples/samples-csharp/Common/Invoice.cs create mode 100644 samples/samples-csharp/Database/Tables/Invoices.sql create mode 100644 samples/samples-csharp/InputBindingSamples/GetInvoices.cs create mode 100644 samples/samples-csharp/InputBindingSamples/GetProductsTopN.cs create mode 100644 samples/samples-csharp/OutputBindingSamples/AddInvoicesArray.cs diff --git a/benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj b/benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj index 9405196d3..19f8e9efe 100644 --- a/benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj +++ b/benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj @@ -27,9 +27,9 @@ - PreserveNewest + Always %(RecursiveDir)\%(Filename)%(Extension) - False + True diff --git a/benchmark/Program.cs b/benchmark/Program.cs index 33681e54c..bf2677b42 100644 --- a/benchmark/Program.cs +++ b/benchmark/Program.cs @@ -10,7 +10,12 @@ public class Program { public static void Main() { - BenchmarkRunner.Run( + BenchmarkRunner.Run( + ManualConfig + .Create(DefaultConfig.Instance) + .WithOptions(ConfigOptions.DisableOptimizationsValidator)); + + BenchmarkRunner.Run( ManualConfig .Create(DefaultConfig.Instance) .WithOptions(ConfigOptions.DisableOptimizationsValidator)); diff --git a/benchmark/SqlInputBindingBenchmarks.cs b/benchmark/SqlInputBindingBenchmarks.cs index c89705edc..37f0aa71e 100644 --- a/benchmark/SqlInputBindingBenchmarks.cs +++ b/benchmark/SqlInputBindingBenchmarks.cs @@ -4,29 +4,33 @@ using System.Net.Http; using System.Threading.Tasks; using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common; -using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Integration; +using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples; using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common; +using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Integration; using BenchmarkDotNet.Attributes; namespace Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark { - public class InputBindingBenchmarks : IntegrationTestBase + public class SqlInputBindingBenchmarks : IntegrationTestBase { - public InputBindingBenchmarks() : base() + [GlobalSetup] + public void GlobalSetup() { - this.StartFunctionHost(nameof(GetProducts), SupportedLanguages.CSharp); - - // Generate T-SQL to insert n rows of data with cost - Product[] products = GetProductsWithSameCost(10, 100); + this.StartFunctionHost(nameof(GetProductsTopN), SupportedLanguages.CSharp); + Product[] products = GetProductsWithSameCost(10000, 100); this.InsertProducts(products); } [Benchmark] - [Arguments("getproducts", "100")] - public async Task GetProductsTest(string function, string args) + [Arguments("1")] + [Arguments("10")] + [Arguments("100")] + [Arguments("1000")] + [Arguments("10000")] + public async Task GetProductsTest(string count) { // Run the function - return await this.SendInputRequest(function, args); + return await this.SendInputRequest("getproductstopn", count); } [GlobalCleanup] diff --git a/benchmark/SqlOutputBindingBenchmarks.cs b/benchmark/SqlOutputBindingBenchmarks.cs new file mode 100644 index 000000000..fda94362d --- /dev/null +++ b/benchmark/SqlOutputBindingBenchmarks.cs @@ -0,0 +1,49 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Net.Http; +using System.Threading.Tasks; +using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Integration; +using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Common; +using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common; +using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.OutputBindingSamples; +using Newtonsoft.Json; +using BenchmarkDotNet.Attributes; + + +namespace Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark +{ + public class SqlOutputBindingBenchmarks : IntegrationTestBase + { + [GlobalSetup] + public void StartAddProductsArrayFunction() + { + this.StartFunctionHost(nameof(AddProductsArray), SupportedLanguages.CSharp); + } + + [Benchmark] + [Arguments(1)] + [Arguments(10)] + [Arguments(100)] + [Arguments(1000)] + public async Task AddProductsArrayTest(int count) + { + Product[] productsToAdd = GetProductsWithSameCost(count, 100); + return await this.SendOutputPostRequest("addproducts-array", JsonConvert.SerializeObject(productsToAdd)); + } + + [IterationCleanup] + public void IterationCleanup() + { + // Delete all rows in Products table after each iteration + this.ExecuteNonQuery("TRUNCATE TABLE Products"); + } + + [GlobalCleanup] + public void GlobalCleanup() + { + // Delete the database + this.Dispose(); + } + } +} \ No newline at end of file diff --git a/samples/samples-csharp/Common/Invoice.cs b/samples/samples-csharp/Common/Invoice.cs new file mode 100644 index 000000000..e05229ba6 --- /dev/null +++ b/samples/samples-csharp/Common/Invoice.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common +{ + public class Invoice + { + public int InvoiceID { get; set; } + + public int CustomerID { get; set; } + + public int BillToCustomerID { get; set; } + + public int OrderID { get; set; } + + public int DeliveryMethodID { get; set; } + + public int ContactPersonID { get; set; } + + public int AccountsPersonID { get; set; } + + public int SalespersonPersonID { get; set; } + + public int PackedByPersonID { get; set; } + + public string InvoiceDate { get; set; } + + public string CustomerPurchaseOrderNumber { get; set; } + + public bool IsCreditNote { get; set; } + + public string CreditNoteReason { get; set; } + + public string Comments { get; set; } + + public string DeliveryInstructions { get; set; } + + public string InternalComments { get; set; } + + public int TotalDryItems { get; set; } + + public int TotalChillerItems { get; set; } + + public string DeliveryRun { get; set; } + + public string RunPosition { get; set; } + + public string ReturnedDeliveryData { get; set; } + + public DateTime ConfirmedDeliveryTime { get; set; } + + public string ConfirmedReceivedBy { get; set; } + + public int LastEditedBy { get; set; } + + public DateTime LastEditedWhen { get; set; } + } +} diff --git a/samples/samples-csharp/Database/Tables/Invoices.sql b/samples/samples-csharp/Database/Tables/Invoices.sql new file mode 100644 index 000000000..60141f9d1 --- /dev/null +++ b/samples/samples-csharp/Database/Tables/Invoices.sql @@ -0,0 +1,27 @@ +CREATE TABLE [Invoices]( + [InvoiceID] [int] NOT NULL PRIMARY KEY, + [CustomerID] [int] NOT NULL, + [BillToCustomerID] [int] NOT NULL, + [OrderID] [int] NULL, + [DeliveryMethodID] [int] NOT NULL, + [ContactPersonID] [int] NOT NULL, + [AccountsPersonID] [int] NOT NULL, + [SalespersonPersonID] [int] NOT NULL, + [PackedByPersonID] [int] NOT NULL, + [InvoiceDate] [string] NOT NULL, + [CustomerPurchaseOrderNumber] [nvarchar](20) NULL, + [IsCreditNote] bit NOT NULL, + [CreditNoteReason] [nvarchar](max) NULL, + [Comments] [nvarchar](max) NULL, + [DeliveryInstructions] [nvarchar](max) NULL, + [InternalComments] [nvarchar](max) NULL, + [TotalDryItems] [int] NOT NULL, + [TotalChillerItems] [int] NOT NULL, + [DeliveryRun] [nvarchar](5) NULL, + [RunPosition] [nvarchar](5) NULL, + [ReturnedDeliveryData] [nvarchar](max) NULL, + [ConfirmedDeliveryTime] [datetime2](7) NULL, + [ConfirmedReceivedBy] [nvarchar](max) NULL, + [LastEditedBy] [int] NOT NULL, + [LastEditedWhen] [datetime2](7) NOT NULL +) \ No newline at end of file diff --git a/samples/samples-csharp/GlobalSuppressions.cs b/samples/samples-csharp/GlobalSuppressions.cs index 16afef56a..7d362cdbc 100644 --- a/samples/samples-csharp/GlobalSuppressions.cs +++ b/samples/samples-csharp/GlobalSuppressions.cs @@ -22,3 +22,5 @@ [assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples.GetProductNamesView.Run(Microsoft.AspNetCore.Http.HttpRequest,System.Collections.Generic.IEnumerable{Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common.ProductName})~Microsoft.AspNetCore.Mvc.IActionResult")] [assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples.GetProductsStoredProcedureFromAppSetting.Run(Microsoft.AspNetCore.Http.HttpRequest,System.Collections.Generic.IEnumerable{Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common.Product})~Microsoft.AspNetCore.Mvc.IActionResult")] [assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:Microsoft.Azure.WebJobs.Extensions.Sql.Samples.OutputBindingSamples.AddProductsWithIdentityColumnArray.Run(Microsoft.AspNetCore.Http.HttpRequest,Microsoft.Azure.WebJobs.Extensions.Sql.Samples.OutputBindingSamples.ProductWithoutId[]@)~Microsoft.AspNetCore.Mvc.IActionResult")] +[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples.GetInvoices.Run(Microsoft.AspNetCore.Http.HttpRequest,System.Collections.Generic.IEnumerable{Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common.Invoice})~Microsoft.AspNetCore.Mvc.IActionResult")] +[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples.GetProductsTopN.Run(Microsoft.AspNetCore.Http.HttpRequest,System.Collections.Generic.IEnumerable{Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common.Product})~Microsoft.AspNetCore.Mvc.IActionResult")] diff --git a/samples/samples-csharp/InputBindingSamples/GetInvoices.cs b/samples/samples-csharp/InputBindingSamples/GetInvoices.cs new file mode 100644 index 000000000..5059ac20c --- /dev/null +++ b/samples/samples-csharp/InputBindingSamples/GetInvoices.cs @@ -0,0 +1,26 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Azure.WebJobs.Extensions.Http; +using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common; + +namespace Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples +{ + public static class GetInvoices + { + [FunctionName("GetInvoices")] + public static IActionResult Run( + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "getinvoices")] + HttpRequest req, + [Sql("SELECT * FROM Invoices", + CommandType = System.Data.CommandType.Text, + ConnectionStringSetting = "SqlConnectionString")] + IEnumerable invoices) + { + return new OkObjectResult(invoices); + } + } +} diff --git a/samples/samples-csharp/InputBindingSamples/GetProductsTopN.cs b/samples/samples-csharp/InputBindingSamples/GetProductsTopN.cs new file mode 100644 index 000000000..6f9acdd2b --- /dev/null +++ b/samples/samples-csharp/InputBindingSamples/GetProductsTopN.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System.Collections.Generic; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Azure.WebJobs.Extensions.Http; +using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common; + +namespace Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples +{ + public static class GetProductsTopN + { + [FunctionName("GetProductsTopN")] + public static IActionResult Run( + [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "getproductstopn/{count}")] + HttpRequest req, + [Sql("SELECT TOP(CAST(@Count AS INT)) * FROM Products", + CommandType = System.Data.CommandType.Text, + Parameters = "@Count={count}", + ConnectionStringSetting = "SqlConnectionString")] + IEnumerable products) + { + return new OkObjectResult(products); + } + } +} diff --git a/samples/samples-csharp/OutputBindingSamples/AddInvoicesArray.cs b/samples/samples-csharp/OutputBindingSamples/AddInvoicesArray.cs new file mode 100644 index 000000000..a9330be2c --- /dev/null +++ b/samples/samples-csharp/OutputBindingSamples/AddInvoicesArray.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Mvc; +using Microsoft.Azure.WebJobs.Extensions.Http; +using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common; +using System.Collections.Generic; + +namespace Microsoft.Azure.WebJobs.Extensions.Sql.Samples.OutputBindingSamples +{ + public static class AddInvoicesArray + { + [FunctionName("AddInvoicesArray")] + public static IActionResult Run( + [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "addinvoices-array")] + [FromBody] List invoices, + [Sql("dbo.Invoices", ConnectionStringSetting = "SqlConnectionString")] out Invoice[] output) + { + // Upsert the invoices, which will insert them into the Invoice table if the primary key (InvoiceID) for that item doesn't exist. + // If it does then update it. + output = invoices.ToArray(); + return new CreatedResult($"/api/addinvoices-array", output); + } + } +} diff --git a/test/Common/TestUtils.cs b/test/Common/TestUtils.cs index 1d302a1c1..935bf6602 100644 --- a/test/Common/TestUtils.cs +++ b/test/Common/TestUtils.cs @@ -60,6 +60,7 @@ public static int ExecuteNonQuery( cmd.CommandText = commandText; cmd.CommandType = CommandType.Text; + cmd.CommandTimeout = 60000; Console.WriteLine($"Executing non-query {commandText}"); return cmd.ExecuteNonQuery(); } diff --git a/test/Integration/IntegrationTestBase.cs b/test/Integration/IntegrationTestBase.cs index 7eeee0fb5..580328bd3 100644 --- a/test/Integration/IntegrationTestBase.cs +++ b/test/Integration/IntegrationTestBase.cs @@ -15,6 +15,8 @@ using Xunit; using Xunit.Abstractions; using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common; +using Microsoft.AspNetCore.WebUtilities; +using System.Collections.Generic; namespace Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Integration { @@ -72,37 +74,46 @@ public IntegrationTestBase(ITestOutputHelper output = null) /// By default, integrated authentication will be used to connect to the server, unless the env variable "SA_PASSWORD" is set. /// In this case, connection will be made using SQL login with user "SA" and the provided password. /// - private void SetupDatabase() + protected void SetupDatabase() { - // Get the test server name from environment variable "TEST_SERVER", default to localhost if not set - string testServer = Environment.GetEnvironmentVariable("TEST_SERVER"); - if (string.IsNullOrEmpty(testServer)) + SqlConnectionStringBuilder connectionStringBuilder; + string connectionString = Environment.GetEnvironmentVariable("PERF_TEST_SERVER"); + if (connectionString != null) { - testServer = "localhost"; - } - - // First connect to master to create the database - var connectionStringBuilder = new SqlConnectionStringBuilder() - { - DataSource = testServer, - InitialCatalog = "master", - Pooling = false - }; - - // Either use integrated auth or SQL login depending if SA_PASSWORD is set - string userId = "SA"; - string password = Environment.GetEnvironmentVariable("SA_PASSWORD"); - if (string.IsNullOrEmpty(password)) - { - connectionStringBuilder.IntegratedSecurity = true; + this.MasterConnectionString = connectionString; + connectionStringBuilder = new SqlConnectionStringBuilder(connectionString); } else { - connectionStringBuilder.UserID = userId; - connectionStringBuilder.Password = password; - } + // Get the test server name from environment variable "TEST_SERVER", default to localhost if not set + string testServer = Environment.GetEnvironmentVariable("TEST_SERVER"); + if (string.IsNullOrEmpty(testServer)) + { + testServer = "localhost"; + } - this.MasterConnectionString = connectionStringBuilder.ToString(); + // First connect to master to create the database + connectionStringBuilder = new SqlConnectionStringBuilder() + { + DataSource = testServer, + InitialCatalog = "master", + Pooling = false + }; + + // Either use integrated auth or SQL login depending if SA_PASSWORD is set + string userId = "SA"; + string password = Environment.GetEnvironmentVariable("SA_PASSWORD"); + if (string.IsNullOrEmpty(password)) + { + connectionStringBuilder.IntegratedSecurity = true; + } + else + { + connectionStringBuilder.UserID = userId; + connectionStringBuilder.Password = password; + } + this.MasterConnectionString = connectionStringBuilder.ToString(); + } // Create database // Retry this in case the server isn't fully initialized yet @@ -154,7 +165,8 @@ protected void StartAzurite() } }; - this.AzuriteHost.Start(); + // this.AzuriteHost.Start(); + Console.WriteLine(this.AzuriteHost.ToString()); } /// @@ -243,7 +255,7 @@ private static string GetFunctionsCoreToolsPath() return funcPath; } - private void LogOutput(String output) + private void LogOutput(string output) { if (this.TestOutput != null) { @@ -382,6 +394,25 @@ protected async Task SendInputRequest(string functionName, return await this.SendGetRequest(requestUri); } + protected Task SendOutputGetRequest(string functionName, IDictionary query = null) + { + string requestUri = $"http://localhost:{this.Port}/api/{functionName}"; + + if (query != null) + { + requestUri = QueryHelpers.AddQueryString(requestUri, query); + } + + return this.SendGetRequest(requestUri); + } + + protected Task SendOutputPostRequest(string functionName, string query) + { + string requestUri = $"http://localhost:{this.Port}/api/{functionName}"; + + return this.SendPostRequest(requestUri, query); + } + protected void InsertProducts(Product[] products) { if (products.Length == 0) diff --git a/test/Integration/SqlOutputBindingIntegrationTests.cs b/test/Integration/SqlOutputBindingIntegrationTests.cs index d8423f5c5..8f2f693c7 100644 --- a/test/Integration/SqlOutputBindingIntegrationTests.cs +++ b/test/Integration/SqlOutputBindingIntegrationTests.cs @@ -3,10 +3,7 @@ using System; using System.Collections.Generic; -using System.Net.Http; using System.Threading; -using System.Threading.Tasks; -using Microsoft.AspNetCore.WebUtilities; using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.OutputBindingSamples; using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common; using Xunit; @@ -23,25 +20,6 @@ public SqlOutputBindingIntegrationTests(ITestOutputHelper output) : base(output) { } - private Task SendOutputGetRequest(string functionName, IDictionary query = null) - { - string requestUri = $"http://localhost:{this.Port}/api/{functionName}"; - - if (query != null) - { - requestUri = QueryHelpers.AddQueryString(requestUri, query); - } - - return this.SendGetRequest(requestUri); - } - - private Task SendOutputPostRequest(string functionName, string query) - { - string requestUri = $"http://localhost:{this.Port}/api/{functionName}"; - - return this.SendPostRequest(requestUri, query); - } - [Theory] [SqlInlineData(1, "Test", 5)] [SqlInlineData(0, "", 0)] From 58b1b043411344ad2f5d5b9544d41d5dece29d60 Mon Sep 17 00:00:00 2001 From: luczhan Date: Tue, 16 Aug 2022 15:35:52 -0700 Subject: [PATCH 08/17] clean up --- samples/samples-csharp/Common/Invoice.cs | 60 ------------------- .../Database/Tables/Invoices.sql | 27 --------- .../InputBindingSamples/GetInvoices.cs | 26 -------- .../OutputBindingSamples/AddInvoicesArray.cs | 25 -------- test/Integration/IntegrationTestBase.cs | 3 +- 5 files changed, 1 insertion(+), 140 deletions(-) delete mode 100644 samples/samples-csharp/Common/Invoice.cs delete mode 100644 samples/samples-csharp/Database/Tables/Invoices.sql delete mode 100644 samples/samples-csharp/InputBindingSamples/GetInvoices.cs delete mode 100644 samples/samples-csharp/OutputBindingSamples/AddInvoicesArray.cs diff --git a/samples/samples-csharp/Common/Invoice.cs b/samples/samples-csharp/Common/Invoice.cs deleted file mode 100644 index e05229ba6..000000000 --- a/samples/samples-csharp/Common/Invoice.cs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System; - -namespace Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common -{ - public class Invoice - { - public int InvoiceID { get; set; } - - public int CustomerID { get; set; } - - public int BillToCustomerID { get; set; } - - public int OrderID { get; set; } - - public int DeliveryMethodID { get; set; } - - public int ContactPersonID { get; set; } - - public int AccountsPersonID { get; set; } - - public int SalespersonPersonID { get; set; } - - public int PackedByPersonID { get; set; } - - public string InvoiceDate { get; set; } - - public string CustomerPurchaseOrderNumber { get; set; } - - public bool IsCreditNote { get; set; } - - public string CreditNoteReason { get; set; } - - public string Comments { get; set; } - - public string DeliveryInstructions { get; set; } - - public string InternalComments { get; set; } - - public int TotalDryItems { get; set; } - - public int TotalChillerItems { get; set; } - - public string DeliveryRun { get; set; } - - public string RunPosition { get; set; } - - public string ReturnedDeliveryData { get; set; } - - public DateTime ConfirmedDeliveryTime { get; set; } - - public string ConfirmedReceivedBy { get; set; } - - public int LastEditedBy { get; set; } - - public DateTime LastEditedWhen { get; set; } - } -} diff --git a/samples/samples-csharp/Database/Tables/Invoices.sql b/samples/samples-csharp/Database/Tables/Invoices.sql deleted file mode 100644 index 60141f9d1..000000000 --- a/samples/samples-csharp/Database/Tables/Invoices.sql +++ /dev/null @@ -1,27 +0,0 @@ -CREATE TABLE [Invoices]( - [InvoiceID] [int] NOT NULL PRIMARY KEY, - [CustomerID] [int] NOT NULL, - [BillToCustomerID] [int] NOT NULL, - [OrderID] [int] NULL, - [DeliveryMethodID] [int] NOT NULL, - [ContactPersonID] [int] NOT NULL, - [AccountsPersonID] [int] NOT NULL, - [SalespersonPersonID] [int] NOT NULL, - [PackedByPersonID] [int] NOT NULL, - [InvoiceDate] [string] NOT NULL, - [CustomerPurchaseOrderNumber] [nvarchar](20) NULL, - [IsCreditNote] bit NOT NULL, - [CreditNoteReason] [nvarchar](max) NULL, - [Comments] [nvarchar](max) NULL, - [DeliveryInstructions] [nvarchar](max) NULL, - [InternalComments] [nvarchar](max) NULL, - [TotalDryItems] [int] NOT NULL, - [TotalChillerItems] [int] NOT NULL, - [DeliveryRun] [nvarchar](5) NULL, - [RunPosition] [nvarchar](5) NULL, - [ReturnedDeliveryData] [nvarchar](max) NULL, - [ConfirmedDeliveryTime] [datetime2](7) NULL, - [ConfirmedReceivedBy] [nvarchar](max) NULL, - [LastEditedBy] [int] NOT NULL, - [LastEditedWhen] [datetime2](7) NOT NULL -) \ No newline at end of file diff --git a/samples/samples-csharp/InputBindingSamples/GetInvoices.cs b/samples/samples-csharp/InputBindingSamples/GetInvoices.cs deleted file mode 100644 index 5059ac20c..000000000 --- a/samples/samples-csharp/InputBindingSamples/GetInvoices.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using System.Collections.Generic; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs.Extensions.Http; -using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common; - -namespace Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples -{ - public static class GetInvoices - { - [FunctionName("GetInvoices")] - public static IActionResult Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "getinvoices")] - HttpRequest req, - [Sql("SELECT * FROM Invoices", - CommandType = System.Data.CommandType.Text, - ConnectionStringSetting = "SqlConnectionString")] - IEnumerable invoices) - { - return new OkObjectResult(invoices); - } - } -} diff --git a/samples/samples-csharp/OutputBindingSamples/AddInvoicesArray.cs b/samples/samples-csharp/OutputBindingSamples/AddInvoicesArray.cs deleted file mode 100644 index a9330be2c..000000000 --- a/samples/samples-csharp/OutputBindingSamples/AddInvoicesArray.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using Microsoft.AspNetCore.Mvc; -using Microsoft.Azure.WebJobs.Extensions.Http; -using Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common; -using System.Collections.Generic; - -namespace Microsoft.Azure.WebJobs.Extensions.Sql.Samples.OutputBindingSamples -{ - public static class AddInvoicesArray - { - [FunctionName("AddInvoicesArray")] - public static IActionResult Run( - [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "addinvoices-array")] - [FromBody] List invoices, - [Sql("dbo.Invoices", ConnectionStringSetting = "SqlConnectionString")] out Invoice[] output) - { - // Upsert the invoices, which will insert them into the Invoice table if the primary key (InvoiceID) for that item doesn't exist. - // If it does then update it. - output = invoices.ToArray(); - return new CreatedResult($"/api/addinvoices-array", output); - } - } -} diff --git a/test/Integration/IntegrationTestBase.cs b/test/Integration/IntegrationTestBase.cs index 580328bd3..a12daff9e 100644 --- a/test/Integration/IntegrationTestBase.cs +++ b/test/Integration/IntegrationTestBase.cs @@ -165,8 +165,7 @@ protected void StartAzurite() } }; - // this.AzuriteHost.Start(); - Console.WriteLine(this.AzuriteHost.ToString()); + this.AzuriteHost.Start(); } /// From 81a78adc8596bda92d1704bb0ad7866b81a3cdb8 Mon Sep 17 00:00:00 2001 From: luczhan Date: Tue, 16 Aug 2022 16:27:12 -0700 Subject: [PATCH 09/17] add README --- benchmark/README.md | 31 ++++++++++++++++++++ samples/samples-csharp/GlobalSuppressions.cs | 1 - test/Integration/IntegrationTestBase.cs | 2 +- 3 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 benchmark/README.md diff --git a/benchmark/README.md b/benchmark/README.md new file mode 100644 index 000000000..c7cf5d8cd --- /dev/null +++ b/benchmark/README.md @@ -0,0 +1,31 @@ +# Running Performance Tests +To run performance tests, you will need +1. [Azure Functions Core Tools](https://docs.microsoft.com/azure/azure-functions/functions-run-local#install-the-azure-functions-core-tools) - This is used to start the functions runtime. + + Installation with npm: + ``` + npm install -g azure-functions-core-tools + ``` +2. [Azurite Emulator for Local Azure Storage](https://docs.microsoft.com/azure/storage/common/storage-use-azurite?tabs=npm#install-and-run-azurite) - This is required to run non-HTTP binding functions. + + Installation with npm: + ``` + npm install -g azurite + ``` +3. A local SQL Server instance or an Azure SQL Database. You just need the server to be up and running, the tests will create the database and tables which will be cleaned up afterwards. + + - You can either have a SQL Server installation with `localhost` available for connection via integrated security, or + - Start a SQL Server instance with Docker + ``` + docker pull mcr.microsoft.com/mssql/server:2019-latest + docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD={your_password}" -e "MSSQL_PID=Express" -p 1433:1433 --name sql1 -h sql1 -d mcr.microsoft.com/mssql/server:2019-latest + ``` + After the Docker image is running, you just need to set `SA_PASSWORD` environment variable to `{your_password}` and can run tests normally. + + Note: If `SA_PASSWORD` is not set, the tests will assume you're using a local MSSQL installation and default to using integrated auth. MSSQL on Docker does not support integrated auth by default. + - To use a Azure SQL Database, set 'AZURE_SQL_DB_CONNECTION_STRING' environment variable to your Azure SQL Database Connection String. +Run the performance tests +``` +dotnet run +``` +The test results will be generated in the BenchmarDotNet.Artifacts folder. diff --git a/samples/samples-csharp/GlobalSuppressions.cs b/samples/samples-csharp/GlobalSuppressions.cs index 7d362cdbc..24a32bec9 100644 --- a/samples/samples-csharp/GlobalSuppressions.cs +++ b/samples/samples-csharp/GlobalSuppressions.cs @@ -22,5 +22,4 @@ [assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples.GetProductNamesView.Run(Microsoft.AspNetCore.Http.HttpRequest,System.Collections.Generic.IEnumerable{Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common.ProductName})~Microsoft.AspNetCore.Mvc.IActionResult")] [assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples.GetProductsStoredProcedureFromAppSetting.Run(Microsoft.AspNetCore.Http.HttpRequest,System.Collections.Generic.IEnumerable{Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common.Product})~Microsoft.AspNetCore.Mvc.IActionResult")] [assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:Microsoft.Azure.WebJobs.Extensions.Sql.Samples.OutputBindingSamples.AddProductsWithIdentityColumnArray.Run(Microsoft.AspNetCore.Http.HttpRequest,Microsoft.Azure.WebJobs.Extensions.Sql.Samples.OutputBindingSamples.ProductWithoutId[]@)~Microsoft.AspNetCore.Mvc.IActionResult")] -[assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples.GetInvoices.Run(Microsoft.AspNetCore.Http.HttpRequest,System.Collections.Generic.IEnumerable{Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common.Invoice})~Microsoft.AspNetCore.Mvc.IActionResult")] [assembly: SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "Unused parameter is required by functions binding", Scope = "member", Target = "~M:Microsoft.Azure.WebJobs.Extensions.Sql.Samples.InputBindingSamples.GetProductsTopN.Run(Microsoft.AspNetCore.Http.HttpRequest,System.Collections.Generic.IEnumerable{Microsoft.Azure.WebJobs.Extensions.Sql.Samples.Common.Product})~Microsoft.AspNetCore.Mvc.IActionResult")] diff --git a/test/Integration/IntegrationTestBase.cs b/test/Integration/IntegrationTestBase.cs index a12daff9e..5bc0a7e08 100644 --- a/test/Integration/IntegrationTestBase.cs +++ b/test/Integration/IntegrationTestBase.cs @@ -77,7 +77,7 @@ public IntegrationTestBase(ITestOutputHelper output = null) protected void SetupDatabase() { SqlConnectionStringBuilder connectionStringBuilder; - string connectionString = Environment.GetEnvironmentVariable("PERF_TEST_SERVER"); + string connectionString = Environment.GetEnvironmentVariable("AZURE_SQL_DB_CONNECTION_STRING"); if (connectionString != null) { this.MasterConnectionString = connectionString; From b917abc9df77bd944552ccde76c065ea712ab37a Mon Sep 17 00:00:00 2001 From: luczhan Date: Tue, 16 Aug 2022 16:45:28 -0700 Subject: [PATCH 10/17] fix readme --- benchmark/README.md | 30 +++++-------------- .../{Program.cs => SqlBindingBenchmarks.cs} | 2 +- test/README.md | 3 +- 3 files changed, 10 insertions(+), 25 deletions(-) rename benchmark/{Program.cs => SqlBindingBenchmarks.cs} (95%) diff --git a/benchmark/README.md b/benchmark/README.md index c7cf5d8cd..765e9f1f6 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -1,31 +1,15 @@ # Running Performance Tests -To run performance tests, you will need -1. [Azure Functions Core Tools](https://docs.microsoft.com/azure/azure-functions/functions-run-local#install-the-azure-functions-core-tools) - This is used to start the functions runtime. - Installation with npm: - ``` - npm install -g azure-functions-core-tools - ``` -2. [Azurite Emulator for Local Azure Storage](https://docs.microsoft.com/azure/storage/common/storage-use-azurite?tabs=npm#install-and-run-azurite) - This is required to run non-HTTP binding functions. +## Pre-requisites +The performance tests are based on the IntegrationTestBase class. Follow the instructions to set up the pre-requisites for integration tests [here](). - Installation with npm: - ``` - npm install -g azurite - ``` -3. A local SQL Server instance or an Azure SQL Database. You just need the server to be up and running, the tests will create the database and tables which will be cleaned up afterwards. +## Run +The performance tests use BenchmarkDotNet to benchmark performance for input and output bindings. - - You can either have a SQL Server installation with `localhost` available for connection via integrated security, or - - Start a SQL Server instance with Docker - ``` - docker pull mcr.microsoft.com/mssql/server:2019-latest - docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD={your_password}" -e "MSSQL_PID=Express" -p 1433:1433 --name sql1 -h sql1 -d mcr.microsoft.com/mssql/server:2019-latest - ``` - After the Docker image is running, you just need to set `SA_PASSWORD` environment variable to `{your_password}` and can run tests normally. - - Note: If `SA_PASSWORD` is not set, the tests will assume you're using a local MSSQL installation and default to using integrated auth. MSSQL on Docker does not support integrated auth by default. - - To use a Azure SQL Database, set 'AZURE_SQL_DB_CONNECTION_STRING' environment variable to your Azure SQL Database Connection String. -Run the performance tests +Run the tests from the terminal. ``` dotnet run ``` + +## Results The test results will be generated in the BenchmarDotNet.Artifacts folder. diff --git a/benchmark/Program.cs b/benchmark/SqlBindingBenchmarks.cs similarity index 95% rename from benchmark/Program.cs rename to benchmark/SqlBindingBenchmarks.cs index bf2677b42..1ab172c93 100644 --- a/benchmark/Program.cs +++ b/benchmark/SqlBindingBenchmarks.cs @@ -6,7 +6,7 @@ namespace Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark { - public class Program + public class SqlBindingBenchmarks { public static void Main() { diff --git a/test/README.md b/test/README.md index 1824c49c2..1dd82f263 100644 --- a/test/README.md +++ b/test/README.md @@ -14,7 +14,7 @@ Our integration tests are based on functions from the samples project. To run in ``` npm install -g azurite ``` -3. A local SQL Server instance - This is used by tests to verify that data is correctly added/fetched from the database when a test Function is run. You just need the server to be up and running, the tests will create the database and tables which will be cleaned up afterwards. +3. A local SQL Server instance or an Azure SQL Database - This is used by tests to verify that data is correctly added/fetched from the database when a test Function is run. You just need the server to be up and running, the tests will create the database and tables which will be cleaned up afterwards. - You can either have a SQL Server installation with `localhost` available for connection via integrated security, or - Start a SQL Server instance with Docker @@ -25,6 +25,7 @@ Our integration tests are based on functions from the samples project. To run in After the Docker image is running, you just need to set `SA_PASSWORD` environment variable to `{your_password}` and can run tests normally. Note: If `SA_PASSWORD` is not set, the tests will assume you're using a local MSSQL installation and default to using integrated auth. MSSQL on Docker does not support integrated auth by default. + - To use an Azure SQL Database, set the `AZURE_SQL_DB_CONNECTION_STRING` environment variable to your Azure SQL Datbase connection string. ## Adding New Integration Tests When adding a new integration test for a function follow these steps: From 3282f0eca7ae26f5491ced32df8ff8555ac271c5 Mon Sep 17 00:00:00 2001 From: luczhan Date: Tue, 16 Aug 2022 16:47:49 -0700 Subject: [PATCH 11/17] add link to integration tests --- benchmark/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/benchmark/README.md b/benchmark/README.md index 765e9f1f6..d1b7e7b5e 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -1,7 +1,7 @@ # Running Performance Tests ## Pre-requisites -The performance tests are based on the IntegrationTestBase class. Follow the instructions to set up the pre-requisites for integration tests [here](). +The performance tests are based on the IntegrationTestBase class. Follow the instructions to set up the pre-requisites for integration tests [here](https://github.com/Azure/azure-functions-sql-extension/tree/main/test#running-integration-tests). ## Run The performance tests use BenchmarkDotNet to benchmark performance for input and output bindings. From 8e4130b73bb4622667974aa0f97bbdb738a64fd6 Mon Sep 17 00:00:00 2001 From: luczhan Date: Tue, 16 Aug 2022 16:49:52 -0700 Subject: [PATCH 12/17] typo --- test/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/README.md b/test/README.md index 1dd82f263..b67bea1d3 100644 --- a/test/README.md +++ b/test/README.md @@ -25,7 +25,7 @@ Our integration tests are based on functions from the samples project. To run in After the Docker image is running, you just need to set `SA_PASSWORD` environment variable to `{your_password}` and can run tests normally. Note: If `SA_PASSWORD` is not set, the tests will assume you're using a local MSSQL installation and default to using integrated auth. MSSQL on Docker does not support integrated auth by default. - - To use an Azure SQL Database, set the `AZURE_SQL_DB_CONNECTION_STRING` environment variable to your Azure SQL Datbase connection string. + - To use an Azure SQL Database, set the `AZURE_SQL_DB_CONNECTION_STRING` environment variable to your Azure SQL Database connection string. ## Adding New Integration Tests When adding a new integration test for a function follow these steps: From 7839c008a3558d7b9aca1e6d9b15bf069639dd5c Mon Sep 17 00:00:00 2001 From: luczhan Date: Tue, 16 Aug 2022 17:20:37 -0700 Subject: [PATCH 13/17] cleanup + comments --- benchmark/README.md | 2 +- benchmark/SqlInputBindingBenchmarks.cs | 1 - test/Common/TestUtils.cs | 2 +- test/Integration/IntegrationTestBase.cs | 2 +- 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/benchmark/README.md b/benchmark/README.md index d1b7e7b5e..42b057d85 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -12,4 +12,4 @@ dotnet run ``` ## Results -The test results will be generated in the BenchmarDotNet.Artifacts folder. +The test results will be generated in the BenchmarkDotNet.Artifacts folder. diff --git a/benchmark/SqlInputBindingBenchmarks.cs b/benchmark/SqlInputBindingBenchmarks.cs index 37f0aa71e..397a43a8b 100644 --- a/benchmark/SqlInputBindingBenchmarks.cs +++ b/benchmark/SqlInputBindingBenchmarks.cs @@ -29,7 +29,6 @@ public void GlobalSetup() [Arguments("10000")] public async Task GetProductsTest(string count) { - // Run the function return await this.SendInputRequest("getproductstopn", count); } diff --git a/test/Common/TestUtils.cs b/test/Common/TestUtils.cs index 935bf6602..38330e33d 100644 --- a/test/Common/TestUtils.cs +++ b/test/Common/TestUtils.cs @@ -60,7 +60,7 @@ public static int ExecuteNonQuery( cmd.CommandText = commandText; cmd.CommandType = CommandType.Text; - cmd.CommandTimeout = 60000; + cmd.CommandTimeout = 60000; // Increase from default 30s to prevent timeouts while connecting to Azure SQL DB Console.WriteLine($"Executing non-query {commandText}"); return cmd.ExecuteNonQuery(); } diff --git a/test/Integration/IntegrationTestBase.cs b/test/Integration/IntegrationTestBase.cs index 5bc0a7e08..024be9a83 100644 --- a/test/Integration/IntegrationTestBase.cs +++ b/test/Integration/IntegrationTestBase.cs @@ -74,7 +74,7 @@ public IntegrationTestBase(ITestOutputHelper output = null) /// By default, integrated authentication will be used to connect to the server, unless the env variable "SA_PASSWORD" is set. /// In this case, connection will be made using SQL login with user "SA" and the provided password. /// - protected void SetupDatabase() + private void SetupDatabase() { SqlConnectionStringBuilder connectionStringBuilder; string connectionString = Environment.GetEnvironmentVariable("AZURE_SQL_DB_CONNECTION_STRING"); From cf141c595e683c561e670abc0d169e33c1637417 Mon Sep 17 00:00:00 2001 From: luczhan Date: Fri, 26 Aug 2022 08:58:47 -0700 Subject: [PATCH 14/17] address pr comments --- WebJobs.Extensions.Sql.sln | 4 +-- benchmark/SqlBindingBenchmarks.cs | 24 ------------------ ....WebJobs.Extensions.Sql.Performance.csproj | 0 {benchmark => performance}/README.md | 3 ++- performance/SqlBindingBenchmarks.cs | 16 ++++++++++++ .../SqlInputBindingPerformance.cs | 4 +-- .../SqlOutputBindingPerformance.cs | 4 +-- {benchmark => performance}/packages.lock.json | 0 test/README.md | 25 +++++++++++-------- 9 files changed, 39 insertions(+), 41 deletions(-) delete mode 100644 benchmark/SqlBindingBenchmarks.cs rename benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj => performance/Microsoft.Azure.WebJobs.Extensions.Sql.Performance.csproj (100%) rename {benchmark => performance}/README.md (93%) create mode 100644 performance/SqlBindingBenchmarks.cs rename benchmark/SqlInputBindingBenchmarks.cs => performance/SqlInputBindingPerformance.cs (90%) rename benchmark/SqlOutputBindingBenchmarks.cs => performance/SqlOutputBindingPerformance.cs (92%) rename {benchmark => performance}/packages.lock.json (100%) diff --git a/WebJobs.Extensions.Sql.sln b/WebJobs.Extensions.Sql.sln index bf7453852..7600df817 100644 --- a/WebJobs.Extensions.Sql.sln +++ b/WebJobs.Extensions.Sql.sln @@ -20,9 +20,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark", "benchmark\Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj", "{1A5148B7-F877-4813-852C-F94D6EF795E8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.WebJobs.Extensions.Sql.Performance", "performance\Microsoft.Azure.WebJobs.Extensions.Sql.Performance.csproj", "{1A5148B7-F877-4813-852C-F94D6EF795E8}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmark", "benchmark", "{F0F3562F-9176-4461-98E4-13D38D3DD056}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "performance", "performance", "{F0F3562F-9176-4461-98E4-13D38D3DD056}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/benchmark/SqlBindingBenchmarks.cs b/benchmark/SqlBindingBenchmarks.cs deleted file mode 100644 index 1ab172c93..000000000 --- a/benchmark/SqlBindingBenchmarks.cs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. See License.txt in the project root for license information. - -using BenchmarkDotNet.Running; -using BenchmarkDotNet.Configs; - -namespace Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark -{ - public class SqlBindingBenchmarks - { - public static void Main() - { - BenchmarkRunner.Run( - ManualConfig - .Create(DefaultConfig.Instance) - .WithOptions(ConfigOptions.DisableOptimizationsValidator)); - - BenchmarkRunner.Run( - ManualConfig - .Create(DefaultConfig.Instance) - .WithOptions(ConfigOptions.DisableOptimizationsValidator)); - } - } -} \ No newline at end of file diff --git a/benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj b/performance/Microsoft.Azure.WebJobs.Extensions.Sql.Performance.csproj similarity index 100% rename from benchmark/Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark.csproj rename to performance/Microsoft.Azure.WebJobs.Extensions.Sql.Performance.csproj diff --git a/benchmark/README.md b/performance/README.md similarity index 93% rename from benchmark/README.md rename to performance/README.md index 42b057d85..2b68333e9 100644 --- a/benchmark/README.md +++ b/performance/README.md @@ -8,7 +8,8 @@ The performance tests use BenchmarkDotNet to benchmark performance for input and Run the tests from the terminal. ``` -dotnet run +cd performance +dotnet run -c Release ``` ## Results diff --git a/performance/SqlBindingBenchmarks.cs b/performance/SqlBindingBenchmarks.cs new file mode 100644 index 000000000..b56563db3 --- /dev/null +++ b/performance/SqlBindingBenchmarks.cs @@ -0,0 +1,16 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using BenchmarkDotNet.Running; + +namespace Microsoft.Azure.WebJobs.Extensions.Sql.Performance +{ + public class SqlBindingPerformance + { + public static void Main() + { + BenchmarkRunner.Run(); + BenchmarkRunner.Run(); + } + } +} \ No newline at end of file diff --git a/benchmark/SqlInputBindingBenchmarks.cs b/performance/SqlInputBindingPerformance.cs similarity index 90% rename from benchmark/SqlInputBindingBenchmarks.cs rename to performance/SqlInputBindingPerformance.cs index 397a43a8b..d657ca3de 100644 --- a/benchmark/SqlInputBindingBenchmarks.cs +++ b/performance/SqlInputBindingPerformance.cs @@ -9,9 +9,9 @@ using Microsoft.Azure.WebJobs.Extensions.Sql.Tests.Integration; using BenchmarkDotNet.Attributes; -namespace Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark +namespace Microsoft.Azure.WebJobs.Extensions.Sql.Performance { - public class SqlInputBindingBenchmarks : IntegrationTestBase + public class SqlInputBindingPerformance : IntegrationTestBase { [GlobalSetup] public void GlobalSetup() diff --git a/benchmark/SqlOutputBindingBenchmarks.cs b/performance/SqlOutputBindingPerformance.cs similarity index 92% rename from benchmark/SqlOutputBindingBenchmarks.cs rename to performance/SqlOutputBindingPerformance.cs index fda94362d..e1317b137 100644 --- a/benchmark/SqlOutputBindingBenchmarks.cs +++ b/performance/SqlOutputBindingPerformance.cs @@ -11,9 +11,9 @@ using BenchmarkDotNet.Attributes; -namespace Microsoft.Azure.WebJobs.Extensions.Sql.Benchmark +namespace Microsoft.Azure.WebJobs.Extensions.Sql.Performance { - public class SqlOutputBindingBenchmarks : IntegrationTestBase + public class SqlOutputBindingPerformance : IntegrationTestBase { [GlobalSetup] public void StartAddProductsArrayFunction() diff --git a/benchmark/packages.lock.json b/performance/packages.lock.json similarity index 100% rename from benchmark/packages.lock.json rename to performance/packages.lock.json diff --git a/test/README.md b/test/README.md index b67bea1d3..1c05302b9 100644 --- a/test/README.md +++ b/test/README.md @@ -16,16 +16,21 @@ Our integration tests are based on functions from the samples project. To run in ``` 3. A local SQL Server instance or an Azure SQL Database - This is used by tests to verify that data is correctly added/fetched from the database when a test Function is run. You just need the server to be up and running, the tests will create the database and tables which will be cleaned up afterwards. - - You can either have a SQL Server installation with `localhost` available for connection via integrated security, or - - Start a SQL Server instance with Docker - ``` - docker pull mcr.microsoft.com/mssql/server:2019-latest - docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD={your_password}" -e "MSSQL_PID=Express" -p 1433:1433 --name sql1 -h sql1 -d mcr.microsoft.com/mssql/server:2019-latest - ``` - After the Docker image is running, you just need to set `SA_PASSWORD` environment variable to `{your_password}` and can run tests normally. - - Note: If `SA_PASSWORD` is not set, the tests will assume you're using a local MSSQL installation and default to using integrated auth. MSSQL on Docker does not support integrated auth by default. - - To use an Azure SQL Database, set the `AZURE_SQL_DB_CONNECTION_STRING` environment variable to your Azure SQL Database connection string. +### Local Install +To use a SQL Server installation, ensure `localhost` is available for connection via integrated security. + +### Docker Container +Start a SQL Server instance with Docker +``` +docker pull mcr.microsoft.com/mssql/server:2019-latest +docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD={your_password}" -e "MSSQL_PID=Express" -p 1433:1433 --name sql1 -h sql1 -d mcr.microsoft.com/mssql/server:2019-latest +``` +After the Docker image is running, you just need to set `SA_PASSWORD` environment variable to `{your_password}` and can run tests normally. + +Note: If `SA_PASSWORD` is not set, the tests will assume you're using a local MSSQL installation and default to using integrated auth. MSSQL on Docker does not support integrated auth by default. + +### Azure SQL Database +To use an Azure SQL Database, set the `AZURE_SQL_DB_CONNECTION_STRING` environment variable to your Azure SQL Database connection string. ## Adding New Integration Tests When adding a new integration test for a function follow these steps: From 1e1e0598c88bcc6aaf3f3259458bde347d6dc4a8 Mon Sep 17 00:00:00 2001 From: Lucy Zhang Date: Tue, 30 Aug 2022 11:07:38 -0700 Subject: [PATCH 15/17] fix readme --- performance/README.md | 2 +- test/README.md | 26 +++++++++++++------------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/performance/README.md b/performance/README.md index 2b68333e9..f40ec53dd 100644 --- a/performance/README.md +++ b/performance/README.md @@ -1,7 +1,7 @@ # Running Performance Tests ## Pre-requisites -The performance tests are based on the IntegrationTestBase class. Follow the instructions to set up the pre-requisites for integration tests [here](https://github.com/Azure/azure-functions-sql-extension/tree/main/test#running-integration-tests). +The performance tests are based on the IntegrationTestBase class. Follow the instructions to set up the pre-requisites for integration tests [here](../test/README.md#running-integration-tests). ## Run The performance tests use BenchmarkDotNet to benchmark performance for input and output bindings. diff --git a/test/README.md b/test/README.md index 1c05302b9..30bebca3a 100644 --- a/test/README.md +++ b/test/README.md @@ -16,21 +16,21 @@ Our integration tests are based on functions from the samples project. To run in ``` 3. A local SQL Server instance or an Azure SQL Database - This is used by tests to verify that data is correctly added/fetched from the database when a test Function is run. You just need the server to be up and running, the tests will create the database and tables which will be cleaned up afterwards. -### Local Install -To use a SQL Server installation, ensure `localhost` is available for connection via integrated security. + ### Local Install + To use a SQL Server installation, ensure `localhost` is available for connection via integrated security. -### Docker Container -Start a SQL Server instance with Docker -``` -docker pull mcr.microsoft.com/mssql/server:2019-latest -docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD={your_password}" -e "MSSQL_PID=Express" -p 1433:1433 --name sql1 -h sql1 -d mcr.microsoft.com/mssql/server:2019-latest -``` -After the Docker image is running, you just need to set `SA_PASSWORD` environment variable to `{your_password}` and can run tests normally. + ### Docker Container + Start a SQL Server instance with Docker + ``` + docker pull mcr.microsoft.com/mssql/server:2019-latest + docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD={your_password}" -e "MSSQL_PID=Express" -p 1433:1433 --name sql1 -h sql1 -d mcr.microsoft.com/mssql/server:2019-latest + ``` + After the Docker image is running, you just need to set `SA_PASSWORD` environment variable to `{your_password}` and can run tests normally. -Note: If `SA_PASSWORD` is not set, the tests will assume you're using a local MSSQL installation and default to using integrated auth. MSSQL on Docker does not support integrated auth by default. + Note: If `SA_PASSWORD` is not set, the tests will assume you're using a local MSSQL installation and default to using integrated auth. MSSQL on Docker does not support integrated auth by default. -### Azure SQL Database -To use an Azure SQL Database, set the `AZURE_SQL_DB_CONNECTION_STRING` environment variable to your Azure SQL Database connection string. + ### Azure SQL Database + To use an Azure SQL Database, set the `AZURE_SQL_DB_CONNECTION_STRING` environment variable to your Azure SQL Database connection string. ## Adding New Integration Tests When adding a new integration test for a function follow these steps: @@ -41,7 +41,7 @@ To use an Azure SQL Database, set the `AZURE_SQL_DB_CONNECTION_STRING` environme 4. After the functions are created then add the test itself to either SqlInputBindingIntegrationTests.ts or SqlOutputBindingIntegrationTests.ts. See below for the various attributes, parameters and setup that are required for each test ### SqlInlineData attribute: - + SqlInlineData attribute is a custom attribute derived from Xunit Data attribute and it supplies the SupportedLanguage parameter to the test for the test to run against in addition to any other data parameters included. By default any test decorated with the [SqlInlineData] attribute will be run against each supported language in the SupportedLanguages enum. How to use: Add [Theory] and [SqlInlineData] attributes over the test and pass in the test variables except the language variable. From 2c23040f77b61872b4b47e4541a0638d5b0b06df Mon Sep 17 00:00:00 2001 From: luczhan Date: Wed, 31 Aug 2022 09:08:38 -0700 Subject: [PATCH 16/17] fix integration test base --- test/Integration/IntegrationTestBase.cs | 50 ++++++++++++------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/test/Integration/IntegrationTestBase.cs b/test/Integration/IntegrationTestBase.cs index f511e1b64..3431037c0 100644 --- a/test/Integration/IntegrationTestBase.cs +++ b/test/Integration/IntegrationTestBase.cs @@ -50,7 +50,7 @@ public class IntegrationTestBase : IDisposable /// /// Output redirect for XUnit tests. - /// Please use LogOutput.WriteLine() instead of Console or Debug. + /// Please use LogOutput() instead of Console or Debug. /// protected ITestOutputHelper TestOutput { get; private set; } @@ -212,7 +212,7 @@ protected void StartFunctionHost(string functionName, SupportedLanguages languag this.FunctionHost.BeginOutputReadLine(); this.FunctionHost.BeginErrorReadLine(); - this.LogOutput.WriteLine($"Waiting for Azure Function host to start..."); + this.LogOutput($"Waiting for Azure Function host to start..."); const int FunctionHostStartupTimeoutInSeconds = 60; bool isCompleted = taskCompletionSource.Task.Wait(TimeSpan.FromSeconds(FunctionHostStartupTimeoutInSeconds)); @@ -223,7 +223,7 @@ protected void StartFunctionHost(string functionName, SupportedLanguages languag const int BufferTimeInSeconds = 5; Task.Delay(TimeSpan.FromSeconds(BufferTimeInSeconds)).Wait(); - this.LogOutput.WriteLine($"Azure Function host started!"); + this.LogOutput($"Azure Function host started!"); this.FunctionHost.OutputDataReceived -= SignalStartupHandler; void SignalStartupHandler(object sender, DataReceivedEventArgs e) @@ -366,6 +366,27 @@ public void Dispose() { this.LogOutput($"Failed to close connection. Error: {e1.Message}"); } + + try + { + this.FunctionHost?.Kill(); + this.FunctionHost?.Dispose(); + } + catch (Exception e2) + { + this.LogOutput($"Failed to stop function host, Error: {e2.Message}"); + } + + try + { + this.AzuriteHost?.Kill(); + this.AzuriteHost?.Dispose(); + } + catch (Exception e3) + { + this.LogOutput($"Failed to stop Azurite, Error: {e3.Message}"); + } + try { // Drop the test database @@ -375,30 +396,9 @@ public void Dispose() } catch (Exception e4) { - this.LogOutput.WriteLine($"Failed to drop {this.DatabaseName}, Error: {e2.Message}"); + this.LogOutput($"Failed to drop {this.DatabaseName}, Error: {e4.Message}"); } - // Try to clean up after test run, but don't consider it a failure if we can't for some reason - try - { - this.FunctionHost?.Kill(); - this.FunctionHost?.Dispose(); - } - catch (Exception e3) - { - this.LogOutput.WriteLine($"Failed to stop function host, Error: {e3.Message}"); - } - - try - { - this.AzuriteHost?.Kill(); - this.AzuriteHost?.Dispose(); - } - catch (Exception e4) - { - this.LogOutput.WriteLine($"Failed to stop Azurite, Error: {e4.Message}"); - } - } GC.SuppressFinalize(this); } From 40b41f4fe96660bd4cdcc0565bab8c0d67037717 Mon Sep 17 00:00:00 2001 From: luczhan Date: Wed, 31 Aug 2022 11:38:13 -0700 Subject: [PATCH 17/17] pr comments --- test/Integration/IntegrationTestBase.cs | 2 +- test/README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/Integration/IntegrationTestBase.cs b/test/Integration/IntegrationTestBase.cs index 3431037c0..665fbf3fa 100644 --- a/test/Integration/IntegrationTestBase.cs +++ b/test/Integration/IntegrationTestBase.cs @@ -77,7 +77,7 @@ public IntegrationTestBase(ITestOutputHelper output = null) private void SetupDatabase() { SqlConnectionStringBuilder connectionStringBuilder; - string connectionString = Environment.GetEnvironmentVariable("AZURE_SQL_DB_CONNECTION_STRING"); + string connectionString = Environment.GetEnvironmentVariable("TEST_CONNECTION_STRING"); if (connectionString != null) { this.MasterConnectionString = connectionString; diff --git a/test/README.md b/test/README.md index 30bebca3a..7be921e56 100644 --- a/test/README.md +++ b/test/README.md @@ -14,7 +14,7 @@ Our integration tests are based on functions from the samples project. To run in ``` npm install -g azurite ``` -3. A local SQL Server instance or an Azure SQL Database - This is used by tests to verify that data is correctly added/fetched from the database when a test Function is run. You just need the server to be up and running, the tests will create the database and tables which will be cleaned up afterwards. +3. A SQL Server instance - This is used by tests to verify that data is correctly added/fetched from the database when a test Function is run. You just need the server to be up and running, the tests will create the database and tables which will be cleaned up afterwards. ### Local Install To use a SQL Server installation, ensure `localhost` is available for connection via integrated security. @@ -30,7 +30,7 @@ Our integration tests are based on functions from the samples project. To run in Note: If `SA_PASSWORD` is not set, the tests will assume you're using a local MSSQL installation and default to using integrated auth. MSSQL on Docker does not support integrated auth by default. ### Azure SQL Database - To use an Azure SQL Database, set the `AZURE_SQL_DB_CONNECTION_STRING` environment variable to your Azure SQL Database connection string. + To use an Azure SQL Database, set the `TEST_CONNECTION_STRING` environment variable to your Azure SQL Database connection string. ## Adding New Integration Tests When adding a new integration test for a function follow these steps: