diff --git a/common/src/Logging.Common.cs b/common/src/Logging.Common.cs index 5854203881..953a824062 100644 --- a/common/src/Logging.Common.cs +++ b/common/src/Logging.Common.cs @@ -399,7 +399,6 @@ public static void DumpBuffer(object thisOrContextObject, byte[] buffer, int off /// The number of bytes to log. /// The calling member. [NonEvent] - [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "Parameters 'thisOrContextObject' and 'memberName' used in NET451; remove when no longer supported.")] public static unsafe void DumpBuffer(object thisOrContextObject, IntPtr bufferPtr, int count, [CallerMemberName] string memberName = null) { Debug.Assert(bufferPtr != IntPtr.Zero); diff --git a/common/src/device/provisioning/transport/ProvisioningSasBuilder.cs b/common/src/device/provisioning/transport/ProvisioningSasBuilder.cs index ddb53688b8..15a502a765 100644 --- a/common/src/device/provisioning/transport/ProvisioningSasBuilder.cs +++ b/common/src/device/provisioning/transport/ProvisioningSasBuilder.cs @@ -103,7 +103,7 @@ public static string BuildSasSignature(string keyName, string key, string target public static string BuildExpiresOn(TimeSpan timeToLive) { - DateTime epochTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); + var epochTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); DateTime expiresOn = DateTime.UtcNow.Add(timeToLive); TimeSpan secondsFromBaseTime = expiresOn.Subtract(epochTime); long seconds = Convert.ToInt64(secondsFromBaseTime.TotalSeconds, CultureInfo.InvariantCulture); diff --git a/common/src/service/HttpClientHelper.cs b/common/src/service/HttpClientHelper.cs index 5cddb9f422..55563952a8 100644 --- a/common/src/service/HttpClientHelper.cs +++ b/common/src/service/HttpClientHelper.cs @@ -882,10 +882,10 @@ internal static HttpMessageHandler CreateDefaultHttpMessageHandler(IWebProxy web #pragma warning disable CA2000 // Dispose objects before losing scope (object is returned by this method, so the caller is responsible for disposing it) #if NETCOREAPP && !NETCOREAPP2_0 && !NETCOREAPP1_0 && !NETCOREAPP1_1 // SocketsHttpHandler is only available in netcoreapp2.1 and onwards - SocketsHttpHandler httpMessageHandler = new SocketsHttpHandler(); + var httpMessageHandler = new SocketsHttpHandler(); httpMessageHandler.SslOptions.EnabledSslProtocols = TlsVersions.Instance.Preferred; #else - HttpClientHandler httpMessageHandler = new HttpClientHandler(); + var httpMessageHandler = new HttpClientHandler(); #if !NET451 httpMessageHandler.SslProtocols = TlsVersions.Instance.Preferred; httpMessageHandler.CheckCertificateRevocationList = TlsVersions.Instance.CertificateRevocationCheck; diff --git a/common/src/service/IAuthorizationHeaderProvider.cs b/common/src/service/IAuthorizationHeaderProvider.cs index 207526a168..798ce5bfdb 100644 --- a/common/src/service/IAuthorizationHeaderProvider.cs +++ b/common/src/service/IAuthorizationHeaderProvider.cs @@ -3,7 +3,10 @@ namespace Microsoft.Azure.Devices { - interface IAuthorizationHeaderProvider + /// + /// Gets the authorization header for authenticated requests, regardless of choice of authentication. + /// + internal interface IAuthorizationHeaderProvider { string GetAuthorizationHeader(); } diff --git a/common/src/service/StringValidationHelper.cs b/common/src/service/StringValidationHelper.cs index 3529463e18..09482b20ed 100644 --- a/common/src/service/StringValidationHelper.cs +++ b/common/src/service/StringValidationHelper.cs @@ -66,9 +66,9 @@ public static bool IsBase64String(string value) return false; } - var lengthNoPadding = value.Length; + int lengthNoPadding = value.Length; value = value.TrimEnd(Base64Padding); - var lengthPadding = value.Length; + int lengthPadding = value.Length; if ((lengthNoPadding - lengthPadding) > 2) { diff --git a/e2e/test/GlobalSuppressions.cs b/e2e/test/GlobalSuppressions.cs new file mode 100644 index 0000000000..2a5b12c82b --- /dev/null +++ b/e2e/test/GlobalSuppressions.cs @@ -0,0 +1,17 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage( + "Style", + "IDE1006:Naming Styles", + Justification = "Missing Async suffix on test method names. Test method names may be misleading when they have the Async suffix. Additionally, not changing test names help to maintain ADO history.", + Scope = "module")] +[assembly: SuppressMessage( + "CodeQuality", + "IDE0079:Remove unnecessary suppression", + Justification = "Each frameworks consider certain suppressions required by other frameworks unnecessary.", + Scope = "module")] diff --git a/e2e/test/config/TestConfiguration.IoTHub.cs b/e2e/test/config/TestConfiguration.IoTHub.cs index 2e2a9264ec..a94b6f093b 100644 --- a/e2e/test/config/TestConfiguration.IoTHub.cs +++ b/e2e/test/config/TestConfiguration.IoTHub.cs @@ -59,7 +59,7 @@ public static string GetIotHubSharedAccessSignature(TimeSpan timeToLive) public static X509Certificate2 GetCertificateWithPrivateKey() { const string hubPfxCert = "IOTHUB_X509_PFX_CERTIFICATE"; - var cert = GetBase64EncodedCertificate(hubPfxCert, defaultValue: string.Empty); + X509Certificate2 cert = GetBase64EncodedCertificate(hubPfxCert, defaultValue: string.Empty); Assert.IsTrue(cert.NotAfter > DateTime.UtcNow, $"The X509 cert from {hubPfxCert} has expired."); return cert; } @@ -67,7 +67,7 @@ public static X509Certificate2 GetCertificateWithPrivateKey() public static X509Certificate2 GetChainDeviceCertificateWithPrivateKey() { const string hubPfxCert = "HUB_CHAIN_DEVICE_PFX_CERTIFICATE"; - var cert = GetBase64EncodedCertificate(hubPfxCert, defaultValue: string.Empty); + X509Certificate2 cert = GetBase64EncodedCertificate(hubPfxCert, defaultValue: string.Empty); Assert.IsTrue(cert.NotAfter > DateTime.UtcNow, $"The X509 cert from {hubPfxCert} has expired."); return cert; } @@ -75,7 +75,7 @@ public static X509Certificate2 GetChainDeviceCertificateWithPrivateKey() public static X509Certificate2 GetRootCACertificate() { const string hubCert = "HUB_CHAIN_ROOT_CA_CERTIFICATE"; - var cert = GetBase64EncodedCertificate(hubCert); + X509Certificate2 cert = GetBase64EncodedCertificate(hubCert); Assert.IsTrue(cert.NotAfter > DateTime.UtcNow, $"The X509 cert from {hubCert} has expired."); return cert; } @@ -83,7 +83,7 @@ public static X509Certificate2 GetRootCACertificate() public static X509Certificate2 GetIntermediate1Certificate() { const string hubCert = "HUB_CHAIN_INTERMEDIATE1_CERTIFICATE"; - var cert = GetBase64EncodedCertificate(hubCert); + X509Certificate2 cert = GetBase64EncodedCertificate(hubCert); Assert.IsTrue(cert.NotAfter > DateTime.UtcNow, $"The X509 cert from {hubCert} has expired."); return cert; } @@ -91,7 +91,7 @@ public static X509Certificate2 GetIntermediate1Certificate() public static X509Certificate2 GetIntermediate2Certificate() { const string hubCert = "HUB_CHAIN_INTERMEDIATE2_CERTIFICATE"; - var cert = GetBase64EncodedCertificate(hubCert); + X509Certificate2 cert = GetBase64EncodedCertificate(hubCert); Assert.IsTrue(cert.NotAfter > DateTime.UtcNow, $"The X509 cert from {hubCert} has expired."); return cert; } @@ -111,7 +111,7 @@ public static X509Certificate2 GetIntermediate2Certificate() private static string GenerateSasToken(string resourceUri, string sharedAccessKey, TimeSpan timeToLive, string policyName = default) { - DateTime epochTime = new DateTime(1970, 1, 1); + var epochTime = new DateTime(1970, 1, 1); DateTime expiresOn = DateTime.UtcNow.Add(timeToLive); TimeSpan secondsFromEpochTime = expiresOn.Subtract(epochTime); long seconds = Convert.ToInt64(secondsFromEpochTime.TotalSeconds, CultureInfo.InvariantCulture); diff --git a/e2e/test/helpers/ImportExportDevicesHelpers.cs b/e2e/test/helpers/ImportExportDevicesHelpers.cs deleted file mode 100644 index 7e753b4455..0000000000 --- a/e2e/test/helpers/ImportExportDevicesHelpers.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Collections.Generic; -using System.IO; -using System.Text; -using Newtonsoft.Json; - -namespace Microsoft.Azure.Devices.E2ETests.Helpers -{ - internal static class ImportExportDevicesHelpers - { - /// - /// Makes a stream compatible for writing to a storage blob of serialized, newline-delimited rows of the specified devices - /// - /// The devices to serialize - public static Stream BuildDevicesStream(IReadOnlyList devices) - { - var devicesFileSb = new StringBuilder(); - - foreach (ExportImportDevice device in devices) - { - devicesFileSb.AppendLine(JsonConvert.SerializeObject(device)); - } - - byte[] devicesFileInBytes = Encoding.Default.GetBytes(devicesFileSb.ToString()); - return new MemoryStream(devicesFileInBytes); - } - } -} diff --git a/e2e/test/helpers/ImportExportHelpers.cs b/e2e/test/helpers/ImportExportHelpers.cs new file mode 100644 index 0000000000..d762ede683 --- /dev/null +++ b/e2e/test/helpers/ImportExportHelpers.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Collections.Generic; +using System.IO; +using System.Text; +using Newtonsoft.Json; + +namespace Microsoft.Azure.Devices.E2ETests.Helpers +{ + internal static class ImportExportHelpers + { + /// + /// Makes a stream compatible for writing to a storage blob of serialized, newline-delimited rows of the specified objects. + /// + /// The objects to serialize. + public static Stream BuildImportStream(IReadOnlyList items) + { + var itemsFileSb = new StringBuilder(); + + foreach (T item in items) + { + itemsFileSb.AppendLine(JsonConvert.SerializeObject(item)); + } + + byte[] itemsFileInBytes = Encoding.Default.GetBytes(itemsFileSb.ToString()); + return new MemoryStream(itemsFileInBytes); + } + } +} diff --git a/e2e/test/helpers/StorageContainer.cs b/e2e/test/helpers/StorageContainer.cs index d763128d2f..28b7b24330 100644 --- a/e2e/test/helpers/StorageContainer.cs +++ b/e2e/test/helpers/StorageContainer.cs @@ -136,7 +136,7 @@ protected virtual void Dispose(bool disposing) private async Task InitializeAsync() { - CloudStorageAccount storageAccount = CloudStorageAccount.Parse(TestConfiguration.Storage.ConnectionString); + var storageAccount = CloudStorageAccount.Parse(TestConfiguration.Storage.ConnectionString); CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient(); CloudBlobContainer = cloudBlobClient.GetContainerReference(ContainerName); await CloudBlobContainer.CreateIfNotExistsAsync().ConfigureAwait(false); diff --git a/e2e/test/helpers/TestDevice.cs b/e2e/test/helpers/TestDevice.cs index 659e5fd770..ef3be3a394 100644 --- a/e2e/test/helpers/TestDevice.cs +++ b/e2e/test/helpers/TestDevice.cs @@ -75,7 +75,7 @@ private static async Task CreateDeviceAsync(TestDeviceType type, str string deviceName = "E2E_" + prefix + Guid.NewGuid(); // Delete existing devices named this way and create a new one. - using RegistryManager rm = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var rm = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); s_logger.Trace($"{nameof(GetTestDeviceAsync)}: Creating device {deviceName} with type {type}."); Client.IAuthenticationMethod auth = null; diff --git a/e2e/test/helpers/TestDeviceCallbackHandler.cs b/e2e/test/helpers/TestDeviceCallbackHandler.cs index 02d2fbb24f..071f613804 100644 --- a/e2e/test/helpers/TestDeviceCallbackHandler.cs +++ b/e2e/test/helpers/TestDeviceCallbackHandler.cs @@ -177,13 +177,11 @@ public async Task WaitForReceiveMessageCallbackAsync(CancellationToken ct) public async Task SetClientPropertyUpdateCallbackHandlerAsync(string expectedPropName, string componentName = default) { - string userContext = "myContext"; - await _deviceClient .SubscribeToWritablePropertyUpdateRequestsAsync( - (patch, context) => + patch => { - _logger.Trace($"{nameof(SetClientPropertyUpdateCallbackHandlerAsync)}: DeviceClient {_testDevice.Id} callback property: WritableProperty: {patch}, {context}"); + _logger.Trace($"{nameof(SetClientPropertyUpdateCallbackHandlerAsync)}: DeviceClient {_testDevice.Id} callback property: WritableProperty: {patch}."); try { @@ -193,7 +191,6 @@ await _deviceClient isPropertyPresent.Should().BeTrue(); propertyFromCollection.Should().BeEquivalentTo((T)ExpectedClientPropertyValue); - context.Should().Be(userContext); } catch (Exception ex) { @@ -206,8 +203,7 @@ await _deviceClient } return Task.FromResult(true); - }, - userContext) + }) .ConfigureAwait(false); } diff --git a/e2e/test/iothub/AuthenticationWithTokenRefreshDisposalTests.cs b/e2e/test/iothub/AuthenticationWithTokenRefreshDisposalTests.cs index 9554bf001c..b133b8c474 100644 --- a/e2e/test/iothub/AuthenticationWithTokenRefreshDisposalTests.cs +++ b/e2e/test/iothub/AuthenticationWithTokenRefreshDisposalTests.cs @@ -101,69 +101,53 @@ public async Task DeviceClient_AuthenticationMethodDisposesTokenRefresher_MqttWs private async Task AuthenticationMethodDisposesTokenRefresher(Client.TransportType transport) { - AuthenticationWithTokenRefresh authenticationMethod = null; - DeviceClient testClient = null; - try - { - TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - authenticationMethod = new DeviceAuthenticationSasToken(testDevice.ConnectionString, disposeWithClient: true); - - // Create an instance of the device client, send a test message and then close and dispose it. - testClient = DeviceClient.Create(testDevice.IoTHubHostName, authenticationMethod, transport); - using var message1 = new Client.Message(); - await testClient.SendEventAsync(message1).ConfigureAwait(false); - await testClient.CloseAsync(); - testClient.Dispose(); - testClient = null; - Logger.Trace("Test with instance 1 completed"); - - // Perform the same steps again, reusing the previously created authentication method instance. - // Since the default behavior is to dispose AuthenticationWithTokenRefresh authentication method on DeviceClient disposal, - // this should now throw an ObjectDisposedException. - testClient = DeviceClient.Create(testDevice.IoTHubHostName, authenticationMethod, transport); - using var message2 = new Client.Message(); - - Func act = async () => await testClient.SendEventAsync(message2).ConfigureAwait(false); - await act.Should().ThrowAsync(); - } - finally - { - testClient?.Dispose(); - authenticationMethod?.Dispose(); - } + TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); + var authenticationMethod = new DeviceAuthenticationSasToken(testDevice.ConnectionString, disposeWithClient: true); + + // Create an instance of the device client, send a test message and then close and dispose it. + var deviceClient = DeviceClient.Create(testDevice.IoTHubHostName, authenticationMethod, transport); + using var message1 = new Client.Message(); + await deviceClient.SendEventAsync(message1).ConfigureAwait(false); + await deviceClient.CloseAsync(); + deviceClient.Dispose(); + Logger.Trace("Test with instance 1 completed"); + + // Perform the same steps again, reusing the previously created authentication method instance. + // Since the default behavior is to dispose AuthenticationWithTokenRefresh authentication method on DeviceClient disposal, + // this should now throw an ObjectDisposedException. + var deviceClient2 = DeviceClient.Create(testDevice.IoTHubHostName, authenticationMethod, transport); + using var message2 = new Client.Message(); + + Func act = async () => await deviceClient2.SendEventAsync(message2).ConfigureAwait(false); + await act.Should().ThrowAsync(); + + authenticationMethod.Dispose(); + deviceClient2.Dispose(); } private async Task ReuseAuthenticationMethod_SingleDevice(Client.TransportType transport) { - AuthenticationWithTokenRefresh authenticationMethod = null; - DeviceClient testClient = null; - try - { - TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - authenticationMethod = new DeviceAuthenticationSasToken(testDevice.ConnectionString, disposeWithClient: false); - - // Create an instance of the device client, send a test message and then close and dispose it. - testClient = DeviceClient.Create(testDevice.IoTHubHostName, authenticationMethod, transport); - using var message1 = new Client.Message(); - await testClient.SendEventAsync(message1).ConfigureAwait(false); - await testClient.CloseAsync(); - testClient.Dispose(); - testClient = null; - Logger.Trace("Test with instance 1 completed"); - - // Perform the same steps again, reusing the previously created authentication method instance to ensure - // that the sdk did not dispose the user supplied authentication method instance. - testClient = DeviceClient.Create(testDevice.IoTHubHostName, authenticationMethod, transport); - using var message2 = new Client.Message(); - await testClient.SendEventAsync(message2).ConfigureAwait(false); - await testClient.CloseAsync(); - Logger.Trace("Test with instance 2 completed, reused the previously created authentication method instance for the device client."); - } - finally - { - authenticationMethod?.Dispose(); - testClient?.Dispose(); - } + TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); + var authenticationMethod = new DeviceAuthenticationSasToken(testDevice.ConnectionString, disposeWithClient: false); + + // Create an instance of the device client, send a test message and then close and dispose it. + var deviceClient = DeviceClient.Create(testDevice.IoTHubHostName, authenticationMethod, transport); + using var message1 = new Client.Message(); + await deviceClient.SendEventAsync(message1).ConfigureAwait(false); + await deviceClient.CloseAsync(); + deviceClient.Dispose(); + Logger.Trace("Test with instance 1 completed"); + + // Perform the same steps again, reusing the previously created authentication method instance to ensure + // that the SDK did not dispose the user supplied authentication method instance. + var deviceClient2 = DeviceClient.Create(testDevice.IoTHubHostName, authenticationMethod, transport); + using var message2 = new Client.Message(); + await deviceClient2.SendEventAsync(message2).ConfigureAwait(false); + await deviceClient2.CloseAsync(); + deviceClient2.Dispose(); + Logger.Trace("Test with instance 2 completed, reused the previously created authentication method instance for the device client."); + + authenticationMethod.Dispose(); } private async Task ReuseAuthenticationMethod_MuxedDevices(Client.TransportType transport, int devicesCount) @@ -198,7 +182,7 @@ private async Task ReuseAuthenticationMethod_MuxedDevices(Client.TransportType t for (int i = 0; i < devicesCount; i++) { #pragma warning disable CA2000 // Dispose objects before losing scope - the client instance is disposed during the course of the test. - DeviceClient deviceClient = DeviceClient.Create(testDevices[i].IoTHubHostName, authenticationMethods[i], new ITransportSettings[] { amqpTransportSettings }); + var deviceClient = DeviceClient.Create(testDevices[i].IoTHubHostName, authenticationMethods[i], new ITransportSettings[] { amqpTransportSettings }); #pragma warning restore CA2000 // Dispose objects before losing scope var amqpConnectionStatusChange = new AmqpConnectionStatusChange(testDevices[i].Id, Logger); @@ -262,7 +246,13 @@ private async Task ReuseAuthenticationMethod_MuxedDevices(Client.TransportType t for (int i = 0; i < devicesCount; i++) { #pragma warning disable CA2000 // Dispose objects before losing scope - the client instance is disposed at the end of the test. - DeviceClient deviceClient = DeviceClient.Create(testDevices[i].IoTHubHostName, authenticationMethods[i], new ITransportSettings[] { amqpTransportSettings }); + var deviceClient = DeviceClient.Create( + testDevices[i].IoTHubHostName, + authenticationMethods[i], + new ITransportSettings[] + { + amqpTransportSettings + }); #pragma warning restore CA2000 // Dispose objects before losing scope var amqpConnectionStatusChange = new AmqpConnectionStatusChange(testDevices[i].Id, Logger); diff --git a/e2e/test/iothub/ConnectionStatusChangeHandlerTests.cs b/e2e/test/iothub/ConnectionStatusChangeHandlerTests.cs index 92e1cca9ae..b044d2356f 100644 --- a/e2e/test/iothub/ConnectionStatusChangeHandlerTests.cs +++ b/e2e/test/iothub/ConnectionStatusChangeHandlerTests.cs @@ -116,7 +116,7 @@ public async Task ModuleClient_DeviceDisabled_Gives_ConnectionStatus_DeviceDisab ConnectionStatusChangeReason? statusChangeReason = null; int deviceDisabledReceivedCount = 0; - using (DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(deviceConnectionString, protocol)) + using (var deviceClient = DeviceClient.CreateFromConnectionString(deviceConnectionString, protocol)) { ConnectionStatusChangesHandler statusChangeHandler = (s, r) => { @@ -135,11 +135,11 @@ public async Task ModuleClient_DeviceDisabled_Gives_ConnectionStatus_DeviceDisab // Receiving the module twin should succeed right now. Logger.Trace($"{nameof(DeviceClient_Gives_ConnectionStatus_DeviceDisabled_Base)}: DeviceClient GetTwinAsync."); - var twin = await deviceClient.GetTwinAsync().ConfigureAwait(false); + Shared.Twin twin = await deviceClient.GetTwinAsync().ConfigureAwait(false); Assert.IsNotNull(twin); // Delete/disable the device in IoT Hub. This should trigger the ConnectionStatusChangesHandler. - using (RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString)) + using (var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString)) { await registryManagerOperation(registryManager, deviceId).ConfigureAwait(false); } @@ -168,8 +168,8 @@ public async Task ModuleClient_DeviceDisabled_Gives_ConnectionStatus_DeviceDisab private async Task ModuleClient_Gives_ConnectionStatus_DeviceDisabled_Base( Client.TransportType protocol, Func registryManagerOperation) { - AmqpTransportSettings amqpTransportSettings = new AmqpTransportSettings(protocol); - ITransportSettings[] transportSettings = new ITransportSettings[] { amqpTransportSettings }; + var amqpTransportSettings = new AmqpTransportSettings(protocol); + var transportSettings = new ITransportSettings[] { amqpTransportSettings }; TestModule testModule = await TestModule.GetTestModuleAsync(DevicePrefix + $"_{Guid.NewGuid()}", ModulePrefix, Logger).ConfigureAwait(false); ConnectionStatus? status = null; @@ -185,7 +185,7 @@ public async Task ModuleClient_DeviceDisabled_Gives_ConnectionStatus_DeviceDisab } }; - using (ModuleClient moduleClient = ModuleClient.CreateFromConnectionString(testModule.ConnectionString, transportSettings)) + using (var moduleClient = ModuleClient.CreateFromConnectionString(testModule.ConnectionString, transportSettings)) { moduleClient.SetConnectionStatusChangesHandler(statusChangeHandler); Logger.Trace($"{nameof(ModuleClient_Gives_ConnectionStatus_DeviceDisabled_Base)}: Created {nameof(ModuleClient)} with moduleId={testModule.Id}"); @@ -194,11 +194,11 @@ public async Task ModuleClient_DeviceDisabled_Gives_ConnectionStatus_DeviceDisab // Receiving the module twin should succeed right now. Logger.Trace($"{nameof(ModuleClient_Gives_ConnectionStatus_DeviceDisabled_Base)}: ModuleClient GetTwinAsync."); - var twin = await moduleClient.GetTwinAsync().ConfigureAwait(false); + Shared.Twin twin = await moduleClient.GetTwinAsync().ConfigureAwait(false); Assert.IsNotNull(twin); // Delete/disable the device in IoT Hub. - using (RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString)) + using (var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString)) { await registryManagerOperation(registryManager, testModule.DeviceId).ConfigureAwait(false); } diff --git a/e2e/test/iothub/DeviceClientX509AuthenticationE2ETests.cs b/e2e/test/iothub/DeviceClientX509AuthenticationE2ETests.cs index 9f367eded3..fc7d5de7fe 100644 --- a/e2e/test/iothub/DeviceClientX509AuthenticationE2ETests.cs +++ b/e2e/test/iothub/DeviceClientX509AuthenticationE2ETests.cs @@ -159,7 +159,7 @@ public async Task X509_Cert_Chain_Install_Test_MQTT_TCP() TestConfiguration.IoTHub.X509ChainDeviceName, s_chainCertificateWithPrivateKey, chainCerts); - using DeviceClient deviceClient = DeviceClient.Create( + using var deviceClient = DeviceClient.Create( _hostName, auth, DeviceTransportType.Mqtt_Tcp_Only); @@ -186,7 +186,7 @@ public async Task X509_Cert_Chain_Install_Test_AMQP_TCP() TestConfiguration.IoTHub.X509ChainDeviceName, s_chainCertificateWithPrivateKey, chainCerts); - using DeviceClient deviceClient = DeviceClient.Create( + using var deviceClient = DeviceClient.Create( _hostName, auth, DeviceTransportType.Amqp_Tcp_Only); @@ -253,7 +253,7 @@ private async Task X509InvalidDeviceIdOpenAsyncTest(Client.TransportType transpo { string deviceName = $"DEVICE_NOT_EXIST_{Guid.NewGuid()}"; using var auth = new DeviceAuthenticationWithX509Certificate(deviceName, s_selfSignedCertificateWithPrivateKey); - using DeviceClient deviceClient = DeviceClient.Create(_hostName, auth, transportType); + using var deviceClient = DeviceClient.Create(_hostName, auth, transportType); try { @@ -274,7 +274,7 @@ private async Task X509InvalidDeviceIdOpenAsyncTwiceTest(Client.TransportType tr { string deviceName = $"DEVICE_NOT_EXIST_{Guid.NewGuid()}"; using var auth = new DeviceAuthenticationWithX509Certificate(deviceName, s_selfSignedCertificateWithPrivateKey); - using DeviceClient deviceClient = DeviceClient.Create(_hostName, auth, transportType); + using var deviceClient = DeviceClient.Create(_hostName, auth, transportType); for (int i = 0; i < 2; i++) { diff --git a/e2e/test/iothub/DeviceTokenRefreshE2ETests.cs b/e2e/test/iothub/DeviceTokenRefreshE2ETests.cs index 96f308eacc..601228c2d3 100644 --- a/e2e/test/iothub/DeviceTokenRefreshE2ETests.cs +++ b/e2e/test/iothub/DeviceTokenRefreshE2ETests.cs @@ -31,7 +31,7 @@ public async Task DeviceClient_Not_Exist_AMQP() using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix).ConfigureAwait(false); var config = new TestConfiguration.IoTHub.ConnectionStringParser(testDevice.ConnectionString); - using (DeviceClient deviceClient = DeviceClient.CreateFromConnectionString($"HostName={config.IotHubHostName};DeviceId=device_id_not_exist;SharedAccessKey={config.SharedAccessKey}", Client.TransportType.Amqp_Tcp_Only)) + using (var deviceClient = DeviceClient.CreateFromConnectionString($"HostName={config.IotHubHostName};DeviceId=device_id_not_exist;SharedAccessKey={config.SharedAccessKey}", Client.TransportType.Amqp_Tcp_Only)) { await deviceClient.OpenAsync().ConfigureAwait(false); } @@ -45,7 +45,7 @@ public async Task DeviceClient_Bad_Credentials_AMQP() var config = new TestConfiguration.IoTHub.ConnectionStringParser(testDevice.ConnectionString); string invalidKey = Convert.ToBase64String(Encoding.UTF8.GetBytes("invalid_key")); - using (DeviceClient deviceClient = DeviceClient.CreateFromConnectionString($"HostName={config.IotHubHostName};DeviceId={config.DeviceID};SharedAccessKey={invalidKey}", Client.TransportType.Amqp_Tcp_Only)) + using (var deviceClient = DeviceClient.CreateFromConnectionString($"HostName={config.IotHubHostName};DeviceId={config.DeviceID};SharedAccessKey={invalidKey}", Client.TransportType.Amqp_Tcp_Only)) { await deviceClient.OpenAsync().ConfigureAwait(false); } @@ -96,7 +96,7 @@ public async Task DeviceClient_TokenConnectionDoubleRelease_Ok() var auth = new DeviceAuthenticationWithToken(deviceId, builder.ToSignature()); - using DeviceClient deviceClient = DeviceClient.Create(iotHub, auth, Client.TransportType.Amqp_Tcp_Only); + using var deviceClient = DeviceClient.Create(iotHub, auth, Client.TransportType.Amqp_Tcp_Only); Logger.Trace($"{deviceId}: Created {nameof(DeviceClient)} ID={TestLogger.IdOf(deviceClient)}"); Logger.Trace($"{deviceId}: DeviceClient OpenAsync."); @@ -183,7 +183,7 @@ private async Task DeviceClient_TokenIsRefreshed_Internal(Client.TransportType t transport, Logger); - using DeviceClient deviceClient = DeviceClient.Create(testDevice.IoTHubHostName, refresher, transport); + using var deviceClient = DeviceClient.Create(testDevice.IoTHubHostName, refresher, transport); Logger.Trace($"Created {nameof(DeviceClient)} ID={TestLogger.IdOf(deviceClient)}"); if (transport == Client.TransportType.Mqtt) diff --git a/e2e/test/iothub/FileUploadFaultInjectionTests.cs b/e2e/test/iothub/FileUploadFaultInjectionTests.cs index 8e261f4312..35a20bfaf0 100644 --- a/e2e/test/iothub/FileUploadFaultInjectionTests.cs +++ b/e2e/test/iothub/FileUploadFaultInjectionTests.cs @@ -87,7 +87,7 @@ public async Task FileUploadSuccess_QuotaExceed_Amqp() { using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix).ConfigureAwait(false); - using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); + using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); TimeSpan operationTimeout = retryDurationInMilliSec == TimeSpan.Zero ? FaultInjection.RecoveryTime : retryDurationInMilliSec; deviceClient.OperationTimeoutInMilliseconds = (uint)operationTimeout.TotalMilliseconds; diff --git a/e2e/test/iothub/SasCredentialAuthenticationTests.cs b/e2e/test/iothub/SasCredentialAuthenticationTests.cs index 827f05ec2b..decc7947ef 100644 --- a/e2e/test/iothub/SasCredentialAuthenticationTests.cs +++ b/e2e/test/iothub/SasCredentialAuthenticationTests.cs @@ -101,7 +101,7 @@ public async Task JobClient_Http_SasCredentialAuth_Success() string jobId = "JOBSAMPLE" + Guid.NewGuid().ToString(); string jobDeviceId = "JobsSample_Device"; string query = $"DeviceId IN ['{jobDeviceId}']"; - Twin twin = new Twin(jobDeviceId); + var twin = new Twin(jobDeviceId); try { diff --git a/e2e/test/iothub/TokenCredentialAuthenticationTests.cs b/e2e/test/iothub/TokenCredentialAuthenticationTests.cs index cbbcc0633c..6c1e687b76 100644 --- a/e2e/test/iothub/TokenCredentialAuthenticationTests.cs +++ b/e2e/test/iothub/TokenCredentialAuthenticationTests.cs @@ -66,7 +66,7 @@ public async Task JobClient_Http_TokenCredentialAuth_Success() string jobId = "JOBSAMPLE" + Guid.NewGuid().ToString(); string jobDeviceId = "JobsSample_Device"; string query = $"DeviceId IN ['{jobDeviceId}']"; - Twin twin = new Twin(jobDeviceId); + var twin = new Twin(jobDeviceId); try { @@ -124,22 +124,19 @@ public async Task DigitalTwinClient_Http_TokenCredentialAuth_Success() public async Task Service_Amqp_TokenCredentialAuth_Success() { // arrange - TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using DeviceClient deviceClient = testDevice.CreateDeviceClient(Client.TransportType.Mqtt); - await deviceClient.OpenAsync().ConfigureAwait(false); - + string ghostDevice = $"{nameof(Service_Amqp_TokenCredentialAuth_Success)}_{Guid.NewGuid()}"; using var serviceClient = ServiceClient.Create( TestConfiguration.IoTHub.GetIotHubHostName(), TestConfiguration.IoTHub.GetClientSecretCredential(), TransportType.Amqp); - - // act await serviceClient.OpenAsync().ConfigureAwait(false); using var message = new Message(Encoding.ASCII.GetBytes("Hello, Cloud!")); - await serviceClient.SendAsync(testDevice.Id, message); - // cleanup - await testDevice.RemoveDeviceAsync().ConfigureAwait(false); + // act + Func act = async () => await serviceClient.SendAsync(ghostDevice, message).ConfigureAwait(false); + + // assert + await act.Should().ThrowAsync(); } #endif diff --git a/e2e/test/iothub/command/CommandE2ETests.cs b/e2e/test/iothub/command/CommandE2ETests.cs index 8be5c30240..a160831709 100644 --- a/e2e/test/iothub/command/CommandE2ETests.cs +++ b/e2e/test/iothub/command/CommandE2ETests.cs @@ -3,6 +3,7 @@ using System; using System.Threading.Tasks; +using FluentAssertions; using Microsoft.Azure.Devices.Client; using Microsoft.Azure.Devices.E2ETests.Helpers; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -120,44 +121,45 @@ public static async Task DigitalTwinsSendCommandAndVerifyResponseAsync(string de digitalTwinClient.Dispose(); #endif logger.Trace($"{nameof(DigitalTwinsSendCommandAndVerifyResponseAsync)}: Command status: {statusCode}."); - Assert.AreEqual(200, statusCode, $"The expected response status should be 200 but was {statusCode}"); - Assert.AreEqual(responseExpected, payloadReceived, $"The expected response payload should be {responseExpected} but was {payloadReceived}"); + statusCode.Should().Be(200, "The expected response status should be 200."); + payloadReceived.Should().Be(responseExpected); } public static async Task SetDeviceReceiveCommandAsync(DeviceClient deviceClient, string componentName, string commandName, MsTestLogger logger) { var commandCallReceived = new TaskCompletionSource(); - await deviceClient.SubscribeToCommandsAsync( - (request, context) => - { - logger.Trace($"{nameof(SetDeviceReceiveCommandAsync)}: DeviceClient command: {request.CommandName}."); - - try + await deviceClient + .SubscribeToCommandsAsync( + request => { - var valueToTest = request.GetData(); - if (string.IsNullOrEmpty(componentName)) + logger.Trace($"{nameof(SetDeviceReceiveCommandAsync)}: DeviceClient command: {request.CommandName}."); + + try { - Assert.AreEqual(null, request.ComponentName, $"The expected component name should be null but was {request.ComponentName}"); + var valueToTest = request.GetPayload(); + if (string.IsNullOrEmpty(componentName)) + { + request.ComponentName.Should().BeNull(); + } + else + { + request.ComponentName.Should().Be(componentName); + } + var assertionObject = new ServiceCommandRequestAssertion(); + string responseExpected = JsonConvert.SerializeObject(assertionObject); + request.GetPayloadAsString().Should().Be(responseExpected); + valueToTest.A.Should().Be(assertionObject.A, "The expected response object should decode properly."); + commandCallReceived.SetResult(true); } - else + catch (Exception ex) { - Assert.AreEqual(componentName, request.ComponentName, $"The expected component name should be {componentName} but was {request.ComponentName}"); + commandCallReceived.SetException(ex); } - var assertionObject = new ServiceCommandRequestAssertion(); - string responseExpected = JsonConvert.SerializeObject(assertionObject); - Assert.AreEqual(responseExpected, request.DataAsJson, $"The expected response payload should be {responseExpected} but was {request.DataAsJson}"); - Assert.AreEqual(assertionObject.A, valueToTest.A, $"The expected response object did not decode properly. Value a should be {assertionObject.A} but was {valueToTest?.A ?? int.MinValue}"); - commandCallReceived.SetResult(true); - } - catch (Exception ex) - { - commandCallReceived.SetException(ex); - } - return Task.FromResult(new CommandResponse(new DeviceCommandResponse(), 200)); - }, - null).ConfigureAwait(false); + return Task.FromResult(new CommandResponse(new DeviceCommandResponse(), 200)); + }) + .ConfigureAwait(false); // Return the task that tells us we have received the callback. return commandCallReceived.Task; diff --git a/e2e/test/iothub/messaging/AzureSecurityCenterForIoTSecurityMessageE2ETests.cs b/e2e/test/iothub/messaging/AzureSecurityCenterForIoTSecurityMessageE2ETests.cs index a1927d5160..608ff56129 100644 --- a/e2e/test/iothub/messaging/AzureSecurityCenterForIoTSecurityMessageE2ETests.cs +++ b/e2e/test/iothub/messaging/AzureSecurityCenterForIoTSecurityMessageE2ETests.cs @@ -92,7 +92,7 @@ private Client.Message ComposeD2CSecurityTestMessage() private JObject ComposeAzureSecurityCenterForIoTSecurityMessagePayload(string eventId) { - var now = DateTime.UtcNow; + DateTime now = DateTime.UtcNow; return new JObject { { "AgentVersion", "0.0.1" }, diff --git a/e2e/test/iothub/messaging/FaultInjectionPoolAmqpTests.MessageReceiveFaultInjectionPoolAmqpTests.cs b/e2e/test/iothub/messaging/FaultInjectionPoolAmqpTests.MessageReceiveFaultInjectionPoolAmqpTests.cs index e23297a804..9247448d96 100644 --- a/e2e/test/iothub/messaging/FaultInjectionPoolAmqpTests.MessageReceiveFaultInjectionPoolAmqpTests.cs +++ b/e2e/test/iothub/messaging/FaultInjectionPoolAmqpTests.MessageReceiveFaultInjectionPoolAmqpTests.cs @@ -953,7 +953,7 @@ await FaultInjectionPoolingOverAmqp string proxyAddress = null) { // Initialize the service client - ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); async Task InitOperationAsync(DeviceClient deviceClient, TestDevice testDevice, TestDeviceCallbackHandler testDeviceCallbackHandler) { diff --git a/e2e/test/iothub/messaging/MessageFeedbackE2ETests.cs b/e2e/test/iothub/messaging/MessageFeedbackE2ETests.cs index 8f0ab42de0..239f3aad9c 100644 --- a/e2e/test/iothub/messaging/MessageFeedbackE2ETests.cs +++ b/e2e/test/iothub/messaging/MessageFeedbackE2ETests.cs @@ -33,7 +33,7 @@ private static async Task CompleteMessageMixOrder(TestDeviceType type, Client.Tr { using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(logger, s_devicePrefix, type).ConfigureAwait(false); using (DeviceClient deviceClient = testDevice.CreateDeviceClient(transport)) - using (ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString)) + using (var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString)) { await deviceClient.OpenAsync().ConfigureAwait(false); @@ -61,7 +61,7 @@ private static async Task CompleteMessageMixOrder(TestDeviceType type, Client.Tr for (int i = 0; i < MESSAGE_COUNT; i++) { - Stopwatch stopwatch = new Stopwatch(); + var stopwatch = new Stopwatch(); stopwatch.Start(); await deviceClient.CompleteAsync(messages[MESSAGE_COUNT - 1 - i]).ConfigureAwait(false); stopwatch.Stop(); diff --git a/e2e/test/iothub/messaging/MessageReceiveE2ETests.cs b/e2e/test/iothub/messaging/MessageReceiveE2ETests.cs index e3e58f40f1..98762ff820 100644 --- a/e2e/test/iothub/messaging/MessageReceiveE2ETests.cs +++ b/e2e/test/iothub/messaging/MessageReceiveE2ETests.cs @@ -623,7 +623,7 @@ private async Task ReceiveSingleMessageAsync(TestDeviceType type, Client.Transpo { using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, s_devicePrefix, type).ConfigureAwait(false); using DeviceClient deviceClient = testDevice.CreateDeviceClient(transport); - using ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); await deviceClient.OpenAsync().ConfigureAwait(false); await serviceClient.OpenAsync().ConfigureAwait(false); @@ -667,7 +667,7 @@ private async Task ReceiveSingleMessageWithCancellationTokenAsync(TestDeviceType { using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, s_devicePrefix, type).ConfigureAwait(false); using DeviceClient deviceClient = testDevice.CreateDeviceClient(transport); - using ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); await deviceClient.OpenAsync().ConfigureAwait(false); await serviceClient.OpenAsync().ConfigureAwait(false); @@ -733,7 +733,7 @@ private async Task ReceiveSingleMessageUsingCallbackAsync(TestDeviceType type, C using DeviceClient deviceClient = testDevice.CreateDeviceClient(transport); using var testDeviceCallbackHandler = new TestDeviceCallbackHandler(deviceClient, testDevice, Logger); - using ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); (Message msg, string payload, string p1Value) = ComposeC2dTestMessage(Logger); using (msg) @@ -760,7 +760,7 @@ private async Task ReceiveMessageUsingCallbackAndUnsubscribeAsync(TestDeviceType using DeviceClient deviceClient = testDevice.CreateDeviceClient(transport); using var testDeviceCallbackHandler = new TestDeviceCallbackHandler(deviceClient, testDevice, Logger); - using ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); // For Mqtt - we will need to subscribe to the Mqtt receive telemetry topic // before the device can begin receiving c2d messages. @@ -837,7 +837,7 @@ private async Task ReceiveMessageUsingCallbackUpdateHandlerAsync(TestDeviceType using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, s_devicePrefix, type).ConfigureAwait(false); using DeviceClient deviceClient = testDevice.CreateDeviceClient(transport); - using ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); // Set the first C2D message handler. await deviceClient.SetReceiveMessageHandlerAsync( @@ -896,7 +896,7 @@ private async Task ReceiveMessagesSentBeforeSubscriptionAsync(TestDeviceType typ DeviceClient deviceClient = testDevice.CreateDeviceClient(transport); var testDeviceCallbackHandler = new TestDeviceCallbackHandler(deviceClient, testDevice, Logger); - using ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); (Message msg, string payload, string p1Value) = ComposeC2dTestMessage(Logger); @@ -939,7 +939,7 @@ private async Task DoNotReceiveMessagesSentBeforeSubscriptionAsync(TestDeviceTyp DeviceClient deviceClient = testDevice.CreateDeviceClient(settings); var testDeviceCallbackHandler = new TestDeviceCallbackHandler(deviceClient, testDevice, Logger); - using ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); (Message msg, string payload, string p1Value) = ComposeC2dTestMessage(Logger); diff --git a/e2e/test/iothub/messaging/MessageSendE2ETests.cs b/e2e/test/iothub/messaging/MessageSendE2ETests.cs index 5872b277e7..4ff3f6024c 100644 --- a/e2e/test/iothub/messaging/MessageSendE2ETests.cs +++ b/e2e/test/iothub/messaging/MessageSendE2ETests.cs @@ -68,18 +68,18 @@ public async Task Message_DeviceSendSingleMessage_Http() [LoggedTestMethod] public async Task Message_DeviceSendSingleMessage_Amqp_WithHeartbeats() { - Client.AmqpTransportSettings amqpTransportSettings = new Client.AmqpTransportSettings(Client.TransportType.Amqp_Tcp_Only); + var amqpTransportSettings = new Client.AmqpTransportSettings(Client.TransportType.Amqp_Tcp_Only); amqpTransportSettings.IdleTimeout = TimeSpan.FromMinutes(2); - ITransportSettings[] transportSettings = new ITransportSettings[] { amqpTransportSettings }; + var transportSettings = new ITransportSettings[] { amqpTransportSettings }; await SendSingleMessage(TestDeviceType.Sasl, transportSettings).ConfigureAwait(false); } [LoggedTestMethod] public async Task Message_DeviceSendSingleMessage_AmqpWs_WithHeartbeats() { - Client.AmqpTransportSettings amqpTransportSettings = new Client.AmqpTransportSettings(Client.TransportType.Amqp_WebSocket_Only); + var amqpTransportSettings = new Client.AmqpTransportSettings(Client.TransportType.Amqp_WebSocket_Only); amqpTransportSettings.IdleTimeout = TimeSpan.FromMinutes(2); - ITransportSettings[] transportSettings = new ITransportSettings[] { amqpTransportSettings }; + var transportSettings = new ITransportSettings[] { amqpTransportSettings }; await SendSingleMessage(TestDeviceType.Sasl, transportSettings).ConfigureAwait(false); } @@ -89,9 +89,9 @@ public async Task Message_DeviceSendSingleMessage_AmqpWs_WithHeartbeats() [TestCategory("LongRunning")] public async Task Message_DeviceSendSingleMessage_Http_WithProxy() { - Client.Http1TransportSettings httpTransportSettings = new Client.Http1TransportSettings(); + var httpTransportSettings = new Client.Http1TransportSettings(); httpTransportSettings.Proxy = new WebProxy(s_proxyServerAddress); - ITransportSettings[] transportSettings = new ITransportSettings[] { httpTransportSettings }; + var transportSettings = new ITransportSettings[] { httpTransportSettings }; await SendSingleMessage(TestDeviceType.Sasl, transportSettings).ConfigureAwait(false); } @@ -100,10 +100,10 @@ public async Task Message_DeviceSendSingleMessage_Http_WithProxy() [TestCategory("Proxy")] public async Task Message_DeviceSendSingleMessage_Http_WithCustomProxy() { - Http1TransportSettings httpTransportSettings = new Http1TransportSettings(); - CustomWebProxy proxy = new CustomWebProxy(Logger); + var httpTransportSettings = new Http1TransportSettings(); + var proxy = new CustomWebProxy(Logger); httpTransportSettings.Proxy = proxy; - ITransportSettings[] transportSettings = new ITransportSettings[] { httpTransportSettings }; + var transportSettings = new ITransportSettings[] { httpTransportSettings }; await SendSingleMessage(TestDeviceType.Sasl, transportSettings).ConfigureAwait(false); Assert.AreNotEqual(proxy.Counter, 0); @@ -114,9 +114,9 @@ public async Task Message_DeviceSendSingleMessage_Http_WithCustomProxy() [TestCategory("LongRunning")] public async Task Message_DeviceSendSingleMessage_AmqpWs_WithProxy() { - Client.AmqpTransportSettings amqpTransportSettings = new Client.AmqpTransportSettings(Client.TransportType.Amqp_WebSocket_Only); + var amqpTransportSettings = new Client.AmqpTransportSettings(Client.TransportType.Amqp_WebSocket_Only); amqpTransportSettings.Proxy = new WebProxy(s_proxyServerAddress); - ITransportSettings[] transportSettings = new ITransportSettings[] { amqpTransportSettings }; + var transportSettings = new ITransportSettings[] { amqpTransportSettings }; await SendSingleMessage(TestDeviceType.Sasl, transportSettings).ConfigureAwait(false); } @@ -125,10 +125,10 @@ public async Task Message_DeviceSendSingleMessage_AmqpWs_WithProxy() [TestCategory("Proxy")] public async Task Message_DeviceSendSingleMessage_MqttWs_WithProxy() { - Client.Transport.Mqtt.MqttTransportSettings mqttTransportSettings = + var mqttTransportSettings = new Client.Transport.Mqtt.MqttTransportSettings(Client.TransportType.Mqtt_WebSocket_Only); mqttTransportSettings.Proxy = new WebProxy(s_proxyServerAddress); - ITransportSettings[] transportSettings = new ITransportSettings[] { mqttTransportSettings }; + var transportSettings = new ITransportSettings[] { mqttTransportSettings }; await SendSingleMessage(TestDeviceType.Sasl, transportSettings).ConfigureAwait(false); } @@ -137,9 +137,9 @@ public async Task Message_DeviceSendSingleMessage_MqttWs_WithProxy() [TestCategory("Proxy")] public async Task Message_ModuleSendSingleMessage_AmqpWs_WithProxy() { - Client.AmqpTransportSettings amqpTransportSettings = new Client.AmqpTransportSettings(Client.TransportType.Amqp_WebSocket_Only); + var amqpTransportSettings = new Client.AmqpTransportSettings(Client.TransportType.Amqp_WebSocket_Only); amqpTransportSettings.Proxy = new WebProxy(s_proxyServerAddress); - ITransportSettings[] transportSettings = new ITransportSettings[] { amqpTransportSettings }; + var transportSettings = new ITransportSettings[] { amqpTransportSettings }; await SendSingleMessageModule(transportSettings).ConfigureAwait(false); } @@ -148,10 +148,10 @@ public async Task Message_ModuleSendSingleMessage_AmqpWs_WithProxy() [TestCategory("Proxy")] public async Task Message_ModuleSendSingleMessage_MqttWs_WithProxy() { - Client.Transport.Mqtt.MqttTransportSettings mqttTransportSettings = + var mqttTransportSettings = new Client.Transport.Mqtt.MqttTransportSettings(Client.TransportType.Mqtt_WebSocket_Only); mqttTransportSettings.Proxy = new WebProxy(s_proxyServerAddress); - ITransportSettings[] transportSettings = new ITransportSettings[] { mqttTransportSettings }; + var transportSettings = new ITransportSettings[] { mqttTransportSettings }; await SendSingleMessageModule(transportSettings).ConfigureAwait(false); } diff --git a/e2e/test/iothub/method/MethodE2ETests.cs b/e2e/test/iothub/method/MethodE2ETests.cs index 973b2dcfbb..5717254ff6 100644 --- a/e2e/test/iothub/method/MethodE2ETests.cs +++ b/e2e/test/iothub/method/MethodE2ETests.cs @@ -159,7 +159,7 @@ public async Task Method_ServiceSendsMethodThroughProxyWithCustomTimeout() public async Task Method_ServiceInvokeDeviceMethodWithUnknownDeviceThrows() { // setup - using ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); var methodInvocation = new CloudToDeviceMethod("SetTelemetryInterval"); methodInvocation.SetPayloadJson("10"); @@ -233,7 +233,7 @@ public async Task Method_ServiceInvokeDeviceMethodWithUnknownModuleThrows() { // setup using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, "ModuleNotFoundTest").ConfigureAwait(false); - using ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); var methodInvocation = new CloudToDeviceMethod("SetTelemetryInterval"); methodInvocation.SetPayloadJson("10"); @@ -283,7 +283,7 @@ await deviceClient .ConfigureAwait(false); using var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); - var c2dMethod = new CloudToDeviceMethod(commandName, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)).SetPayloadJson(null); + CloudToDeviceMethod c2dMethod = new CloudToDeviceMethod(commandName, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)).SetPayloadJson(null); // act @@ -572,7 +572,7 @@ public static async Task SetModuleReceiveMethodDefaultHandlerAsync(ModuleC ServiceClientTransportSettings serviceClientTransportSettings = default) { using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); + using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); await subscribeAndUnsubscribeMethod(deviceClient, MethodName, Logger).ConfigureAwait(false); @@ -588,7 +588,7 @@ public static async Task SetModuleReceiveMethodDefaultHandlerAsync(ModuleC ServiceClientTransportSettings serviceClientTransportSettings = default) { using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); + using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); Task methodReceivedTask = await setDeviceReceiveMethod(deviceClient, MethodName, Logger).ConfigureAwait(false); Task serviceSendTask = ServiceSendMethodAndVerifyResponseAsync( @@ -608,7 +608,7 @@ public static async Task SetModuleReceiveMethodDefaultHandlerAsync(ModuleC private async Task SendMethodAndRespondAsync(Client.TransportType transport, Func> setDeviceReceiveMethod, TimeSpan responseTimeout = default, ServiceClientTransportSettings serviceClientTransportSettings = default) { TestModule testModule = await TestModule.GetTestModuleAsync(_devicePrefix, _modulePrefix, Logger).ConfigureAwait(false); - using ModuleClient moduleClient = ModuleClient.CreateFromConnectionString(testModule.ConnectionString, transport); + using var moduleClient = ModuleClient.CreateFromConnectionString(testModule.ConnectionString, transport); Task methodReceivedTask = await setDeviceReceiveMethod(moduleClient, MethodName, Logger).ConfigureAwait(false); diff --git a/e2e/test/iothub/method/MethodFaultInjectionTests.cs b/e2e/test/iothub/method/MethodFaultInjectionTests.cs index 4b33787d59..1f08a07cfd 100644 --- a/e2e/test/iothub/method/MethodFaultInjectionTests.cs +++ b/e2e/test/iothub/method/MethodFaultInjectionTests.cs @@ -214,7 +214,7 @@ private async Task ServiceSendMethodAndVerifyResponseAsync(string deviceName, st attempt++; try { - using ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); Logger.Trace($"{nameof(ServiceSendMethodAndVerifyResponseAsync)}: Invoke method {methodName}."); CloudToDeviceMethodResult response = diff --git a/e2e/test/iothub/properties/PropertiesE2ETests.cs b/e2e/test/iothub/properties/PropertiesE2ETests.cs index 6d68028541..580a394e4c 100644 --- a/e2e/test/iothub/properties/PropertiesE2ETests.cs +++ b/e2e/test/iothub/properties/PropertiesE2ETests.cs @@ -206,19 +206,18 @@ private async Task Properties_ServiceSetsWritablePropertyAndDeviceUnsubscribes(C // Set a callback await deviceClient. SubscribeToWritablePropertyUpdateRequestsAsync( - (patch, context) => + patch => { Assert.Fail("After having unsubscribed from receiving client property update notifications " + "this callback should not have been invoked."); return Task.FromResult(true); - }, - null) + }) .ConfigureAwait(false); // Unsubscribe await deviceClient - .SubscribeToWritablePropertyUpdateRequestsAsync(null, null) + .SubscribeToWritablePropertyUpdateRequestsAsync(null) .ConfigureAwait(false); await RegistryManagerUpdateWritablePropertyAsync(testDevice.Id, propName, propValue) @@ -261,7 +260,7 @@ await Task string serializedActualPropertyValue = JsonConvert.SerializeObject(actualProp); serializedActualPropertyValue.Should().Be(JsonConvert.SerializeObject(propValue)); - await deviceClient.SubscribeToWritablePropertyUpdateRequestsAsync(null, null).ConfigureAwait(false); + await deviceClient.SubscribeToWritablePropertyUpdateRequestsAsync(null).ConfigureAwait(false); await deviceClient.CloseAsync().ConfigureAwait(false); } @@ -278,7 +277,7 @@ private async Task Properties_ServiceSetsWritablePropertyAndDeviceReceivesEventA using var writablePropertyCallbackSemaphore = new SemaphoreSlim(0, 1); await deviceClient .SubscribeToWritablePropertyUpdateRequestsAsync( - async (writableProperties, userContext) => + async (writableProperties) => { try { @@ -286,7 +285,6 @@ await deviceClient isPropertyPresent.Should().BeTrue(); propertyFromCollection.Should().BeEquivalentTo(propValue); - userContext.Should().BeNull(); var writablePropertyAcks = new ClientPropertyCollection(); foreach (KeyValuePair writableProperty in writableProperties) @@ -304,7 +302,6 @@ await deviceClient writablePropertyCallbackSemaphore.Release(); } }, - null, cts.Token) .ConfigureAwait(false); diff --git a/e2e/test/iothub/properties/PropertiesWithComponentsE2ETests.cs b/e2e/test/iothub/properties/PropertiesWithComponentsE2ETests.cs index 31db30de03..a970dc28c8 100644 --- a/e2e/test/iothub/properties/PropertiesWithComponentsE2ETests.cs +++ b/e2e/test/iothub/properties/PropertiesWithComponentsE2ETests.cs @@ -215,19 +215,18 @@ private async Task PropertiesWithComponents_ServiceSetsWritablePropertyAndDevice // Set a callback await deviceClient. SubscribeToWritablePropertyUpdateRequestsAsync( - (patch, context) => + patch => { Assert.Fail("After having unsubscribed from receiving client property update notifications " + "this callback should not have been invoked."); return Task.FromResult(true); - }, - null) + }) .ConfigureAwait(false); // Unsubscribe await deviceClient - .SubscribeToWritablePropertyUpdateRequestsAsync(null, null) + .SubscribeToWritablePropertyUpdateRequestsAsync(null) .ConfigureAwait(false); await RegistryManagerUpdateWritablePropertyAsync(testDevice.Id, ComponentName, propName, propValue) @@ -270,7 +269,7 @@ await Task string serializedActualPropertyValue = JsonConvert.SerializeObject(actualProp); serializedActualPropertyValue.Should().Be(JsonConvert.SerializeObject(propValue)); - await deviceClient.SubscribeToWritablePropertyUpdateRequestsAsync(null, null).ConfigureAwait(false); + await deviceClient.SubscribeToWritablePropertyUpdateRequestsAsync(null).ConfigureAwait(false); await deviceClient.CloseAsync().ConfigureAwait(false); } @@ -287,7 +286,7 @@ private async Task PropertiesWithComponents_ServiceSetsWritablePropertyAndDevice using var writablePropertyCallbackSemaphore = new SemaphoreSlim(0, 1); await deviceClient .SubscribeToWritablePropertyUpdateRequestsAsync( - async (writableProperties, userContext) => + async (writableProperties) => { try { @@ -295,7 +294,6 @@ await deviceClient isPropertyPresent.Should().BeTrue(); propertyFromCollection.Should().BeEquivalentTo(propValue); - userContext.Should().BeNull(); var writablePropertyAcks = new ClientPropertyCollection(); foreach (KeyValuePair writableProperty in writableProperties) @@ -319,7 +317,6 @@ await deviceClient writablePropertyCallbackSemaphore.Release(); } }, - null, cts.Token) .ConfigureAwait(false); diff --git a/e2e/test/iothub/service/BulkOperationsE2ETests.cs b/e2e/test/iothub/service/BulkOperationsE2ETests.cs index 53ebc5c813..bf686384f0 100644 --- a/e2e/test/iothub/service/BulkOperationsE2ETests.cs +++ b/e2e/test/iothub/service/BulkOperationsE2ETests.cs @@ -21,18 +21,18 @@ public class BulkOperationsE2ETests : E2EMsTestBase [LoggedTestMethod] public async Task BulkOperations_UpdateTwins2Device_Ok() { - var tagName = Guid.NewGuid().ToString(); - var tagValue = Guid.NewGuid().ToString(); + string tagName = Guid.NewGuid().ToString(); + string tagValue = Guid.NewGuid().ToString(); using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix).ConfigureAwait(false); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); Twin twin = await registryManager.GetTwinAsync(testDevice.Id).ConfigureAwait(false); twin.Tags = new TwinCollection(); twin.Tags[tagName] = tagValue; - var result = await registryManager.UpdateTwins2Async(new List { twin }, true).ConfigureAwait(false); + BulkRegistryOperationResult result = await registryManager.UpdateTwins2Async(new List { twin }, true).ConfigureAwait(false); Assert.IsTrue(result.IsSuccessful, $"UpdateTwins2Async error:\n{ResultErrorsToString(result)}"); Twin twinUpd = await registryManager.GetTwinAsync(testDevice.Id).ConfigureAwait(false); @@ -48,18 +48,18 @@ public async Task BulkOperations_UpdateTwins2Device_Ok() [LoggedTestMethod] public async Task BulkOperations_UpdateTwins2DevicePatch_Ok() { - var tagName = Guid.NewGuid().ToString(); - var tagValue = Guid.NewGuid().ToString(); + string tagName = Guid.NewGuid().ToString(); + string tagValue = Guid.NewGuid().ToString(); using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix).ConfigureAwait(false); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); - Twin twin = new Twin(); + var twin = new Twin(); twin.DeviceId = testDevice.Id; twin.Tags = new TwinCollection(); twin.Tags[tagName] = tagValue; - var result = await registryManager.UpdateTwins2Async(new List { twin }, true).ConfigureAwait(false); + BulkRegistryOperationResult result = await registryManager.UpdateTwins2Async(new List { twin }, true).ConfigureAwait(false); Assert.IsTrue(result.IsSuccessful, $"UpdateTwins2Async error:\n{ResultErrorsToString(result)}"); Twin twinUpd = await registryManager.GetTwinAsync(testDevice.Id).ConfigureAwait(false); @@ -75,18 +75,18 @@ public async Task BulkOperations_UpdateTwins2DevicePatch_Ok() [LoggedTestMethod] public async Task BulkOperations_UpdateTwins2Module_Ok() { - var tagName = Guid.NewGuid().ToString(); - var tagValue = Guid.NewGuid().ToString(); + string tagName = Guid.NewGuid().ToString(); + string tagValue = Guid.NewGuid().ToString(); TestModule testModule = await TestModule.GetTestModuleAsync(DevicePrefix, ModulePrefix, Logger).ConfigureAwait(false); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); Twin twin = await registryManager.GetTwinAsync(testModule.DeviceId, testModule.Id).ConfigureAwait(false); twin.Tags = new TwinCollection(); twin.Tags[tagName] = tagValue; - var result = await registryManager.UpdateTwins2Async(new List { twin }, true).ConfigureAwait(false); + BulkRegistryOperationResult result = await registryManager.UpdateTwins2Async(new List { twin }, true).ConfigureAwait(false); Assert.IsTrue(result.IsSuccessful, $"UpdateTwins2Async error:\n{ResultErrorsToString(result)}"); Twin twinUpd = await registryManager.GetTwinAsync(testModule.DeviceId, testModule.Id).ConfigureAwait(false); @@ -103,19 +103,19 @@ public async Task BulkOperations_UpdateTwins2Module_Ok() [LoggedTestMethod] public async Task BulkOperations_UpdateTwins2ModulePatch_Ok() { - var tagName = Guid.NewGuid().ToString(); - var tagValue = Guid.NewGuid().ToString(); + string tagName = Guid.NewGuid().ToString(); + string tagValue = Guid.NewGuid().ToString(); TestModule testModule = await TestModule.GetTestModuleAsync(DevicePrefix, ModulePrefix, Logger).ConfigureAwait(false); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); var twin = new Twin(); twin.DeviceId = testModule.DeviceId; twin.ModuleId = testModule.Id; twin.Tags = new TwinCollection(); twin.Tags[tagName] = tagValue; - var result = await registryManager.UpdateTwins2Async(new List { twin }, true).ConfigureAwait(false); + BulkRegistryOperationResult result = await registryManager.UpdateTwins2Async(new List { twin }, true).ConfigureAwait(false); Assert.IsTrue(result.IsSuccessful, $"UpdateTwins2Async error:\n{ResultErrorsToString(result)}"); Twin twinUpd = await registryManager.GetTwinAsync(testModule.DeviceId, testModule.Id).ConfigureAwait(false); @@ -131,9 +131,9 @@ public async Task BulkOperations_UpdateTwins2ModulePatch_Ok() private string ResultErrorsToString(BulkRegistryOperationResult result) { - var errorString = ""; + string errorString = ""; - foreach (var error in result.Errors) + foreach (DeviceRegistryOperationError error in result.Errors) { errorString += $"\t{error.ErrorCode} : {error.ErrorStatus}\n"; } diff --git a/e2e/test/iothub/service/IoTHubCertificateValidationE2ETest.cs b/e2e/test/iothub/service/IoTHubCertificateValidationE2ETest.cs index 0047b1bc78..932020154a 100644 --- a/e2e/test/iothub/service/IoTHubCertificateValidationE2ETest.cs +++ b/e2e/test/iothub/service/IoTHubCertificateValidationE2ETest.cs @@ -19,7 +19,7 @@ public class IoTHubCertificateValidationE2ETest : E2EMsTestBase [LoggedTestMethod] public async Task RegistryManager_QueryDevicesInvalidServiceCertificateHttp_Fails() { - using RegistryManager rm = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionStringInvalidServiceCertificate); + using var rm = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionStringInvalidServiceCertificate); IQuery query = rm.CreateQuery("select * from devices"); IotHubCommunicationException exception = await Assert.ThrowsExceptionAsync( () => query.GetNextAsTwinAsync()).ConfigureAwait(false); @@ -34,7 +34,7 @@ public async Task RegistryManager_QueryDevicesInvalidServiceCertificateHttp_Fail [LoggedTestMethod] public async Task ServiceClient_SendMessageToDeviceInvalidServiceCertificateAmqpTcp_Fails() { - var transport = TransportType.Amqp; + TransportType transport = TransportType.Amqp; await Assert.ThrowsExceptionAsync( () => TestServiceClientInvalidServiceCertificate(transport)).ConfigureAwait(false); } @@ -42,8 +42,8 @@ public async Task ServiceClient_SendMessageToDeviceInvalidServiceCertificateAmqp [LoggedTestMethod] public async Task ServiceClient_SendMessageToDeviceInvalidServiceCertificateAmqpWs_Fails() { - var transport = TransportType.Amqp_WebSocket_Only; - var exception = await Assert.ThrowsExceptionAsync( + TransportType transport = TransportType.Amqp_WebSocket_Only; + WebSocketException exception = await Assert.ThrowsExceptionAsync( () => TestServiceClientInvalidServiceCertificate(transport)).ConfigureAwait(false); Assert.IsInstanceOfType(exception.InnerException.InnerException, typeof(AuthenticationException)); @@ -51,7 +51,7 @@ public async Task ServiceClient_SendMessageToDeviceInvalidServiceCertificateAmqp private static async Task TestServiceClientInvalidServiceCertificate(TransportType transport) { - using ServiceClient service = ServiceClient.CreateFromConnectionString( + using var service = ServiceClient.CreateFromConnectionString( TestConfiguration.IoTHub.ConnectionStringInvalidServiceCertificate, transport); using var testMessage = new Message(); @@ -61,8 +61,8 @@ private static async Task TestServiceClientInvalidServiceCertificate(TransportTy [LoggedTestMethod] public async Task JobClient_ScheduleTwinUpdateInvalidServiceCertificateHttp_Fails() { - using JobClient jobClient = JobClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionStringInvalidServiceCertificate); - var exception = await Assert.ThrowsExceptionAsync( + using var jobClient = JobClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionStringInvalidServiceCertificate); + IotHubCommunicationException exception = await Assert.ThrowsExceptionAsync( () => jobClient.ScheduleTwinUpdateAsync( "testDevice", "DeviceId IN ['testDevice']", @@ -80,7 +80,7 @@ public async Task JobClient_ScheduleTwinUpdateInvalidServiceCertificateHttp_Fail [LoggedTestMethod] public async Task DeviceClient_SendAsyncInvalidServiceCertificateAmqpTcp_Fails() { - var transport = Client.TransportType.Amqp_Tcp_Only; + Client.TransportType transport = Client.TransportType.Amqp_Tcp_Only; await Assert.ThrowsExceptionAsync( () => TestDeviceClientInvalidServiceCertificate(transport)).ConfigureAwait(false); } @@ -88,7 +88,7 @@ public async Task DeviceClient_SendAsyncInvalidServiceCertificateAmqpTcp_Fails() [LoggedTestMethod] public async Task DeviceClient_SendAsyncInvalidServiceCertificateMqttTcp_Fails() { - var transport = Client.TransportType.Mqtt_Tcp_Only; + Client.TransportType transport = Client.TransportType.Mqtt_Tcp_Only; await Assert.ThrowsExceptionAsync( () => TestDeviceClientInvalidServiceCertificate(transport)).ConfigureAwait(false); } @@ -96,8 +96,8 @@ public async Task DeviceClient_SendAsyncInvalidServiceCertificateMqttTcp_Fails() [LoggedTestMethod] public async Task DeviceClient_SendAsyncInvalidServiceCertificateHttp_Fails() { - var transport = Client.TransportType.Http1; - var exception = await Assert.ThrowsExceptionAsync( + Client.TransportType transport = Client.TransportType.Http1; + AuthenticationException exception = await Assert.ThrowsExceptionAsync( () => TestDeviceClientInvalidServiceCertificate(transport)).ConfigureAwait(false); #if NET451 || NET472 @@ -110,8 +110,8 @@ public async Task DeviceClient_SendAsyncInvalidServiceCertificateHttp_Fails() [LoggedTestMethod] public async Task DeviceClient_SendAsyncInvalidServiceCertificateAmqpWs_Fails() { - var transport = Client.TransportType.Amqp_WebSocket_Only; - var exception = await Assert.ThrowsExceptionAsync( + Client.TransportType transport = Client.TransportType.Amqp_WebSocket_Only; + AuthenticationException exception = await Assert.ThrowsExceptionAsync( () => TestDeviceClientInvalidServiceCertificate(transport)).ConfigureAwait(false); Assert.IsInstanceOfType(exception.InnerException.InnerException.InnerException, typeof(AuthenticationException)); @@ -120,8 +120,8 @@ public async Task DeviceClient_SendAsyncInvalidServiceCertificateAmqpWs_Fails() [LoggedTestMethod] public async Task DeviceClient_SendAsyncInvalidServiceCertificateMqttWs_Fails() { - var transport = Client.TransportType.Mqtt_WebSocket_Only; - var exception = await Assert.ThrowsExceptionAsync( + Client.TransportType transport = Client.TransportType.Mqtt_WebSocket_Only; + AuthenticationException exception = await Assert.ThrowsExceptionAsync( () => TestDeviceClientInvalidServiceCertificate(transport)).ConfigureAwait(false); Assert.IsInstanceOfType(exception.InnerException.InnerException.InnerException, typeof(AuthenticationException)); @@ -129,15 +129,13 @@ public async Task DeviceClient_SendAsyncInvalidServiceCertificateMqttWs_Fails() private static async Task TestDeviceClientInvalidServiceCertificate(Client.TransportType transport) { - using (DeviceClient deviceClient = + using var deviceClient = DeviceClient.CreateFromConnectionString( TestConfiguration.IoTHub.DeviceConnectionStringInvalidServiceCertificate, - transport)) - { - using var testMessage = new Client.Message(); - await deviceClient.SendEventAsync(testMessage).ConfigureAwait(false); - await deviceClient.CloseAsync().ConfigureAwait(false); - } + transport); + using var testMessage = new Client.Message(); + await deviceClient.SendEventAsync(testMessage).ConfigureAwait(false); + await deviceClient.CloseAsync().ConfigureAwait(false); } } } diff --git a/e2e/test/iothub/service/IoTHubServiceProxyE2ETests.cs b/e2e/test/iothub/service/IoTHubServiceProxyE2ETests.cs index 0cf3a69c07..24a2d778e6 100644 --- a/e2e/test/iothub/service/IoTHubServiceProxyE2ETests.cs +++ b/e2e/test/iothub/service/IoTHubServiceProxyE2ETests.cs @@ -30,7 +30,7 @@ public class IoTHubServiceProxyE2ETests : E2EMsTestBase [LoggedTestMethod] public async Task ServiceClient_Message_SendSingleMessage_WithProxy() { - ServiceClientTransportSettings transportSettings = new ServiceClientTransportSettings(); + var transportSettings = new ServiceClientTransportSettings(); transportSettings.AmqpProxy = new WebProxy(s_proxyServerAddress); transportSettings.HttpProxy = new WebProxy(s_proxyServerAddress); @@ -40,7 +40,7 @@ public async Task ServiceClient_Message_SendSingleMessage_WithProxy() [LoggedTestMethod] public async Task RegistryManager_AddAndRemoveDevice_WithProxy() { - HttpTransportSettings httpTransportSettings = new HttpTransportSettings(); + var httpTransportSettings = new HttpTransportSettings(); httpTransportSettings.Proxy = new WebProxy(s_proxyServerAddress); await RegistryManager_AddDevice(httpTransportSettings).ConfigureAwait(false); @@ -49,7 +49,7 @@ public async Task RegistryManager_AddAndRemoveDevice_WithProxy() [LoggedTestMethod] public async Task JobClient_ScheduleAndRunTwinJob_WithProxy() { - HttpTransportSettings httpTransportSettings = new HttpTransportSettings(); + var httpTransportSettings = new HttpTransportSettings(); httpTransportSettings.Proxy = new WebProxy(s_proxyServerAddress); await JobClient_ScheduleAndRunTwinJob(httpTransportSettings).ConfigureAwait(false); @@ -58,8 +58,8 @@ public async Task JobClient_ScheduleAndRunTwinJob_WithProxy() private async Task SendSingleMessageService(ServiceClientTransportSettings transportSettings) { using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix).ConfigureAwait(false); - using (DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString)) - using (ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(s_connectionString, TransportType.Amqp, transportSettings)) + using (var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString)) + using (var serviceClient = ServiceClient.CreateFromConnectionString(s_connectionString, TransportType.Amqp, transportSettings)) { (Message testMessage, string messageId, string payload, string p1Value) = ComposeD2CTestMessage(); await serviceClient.SendAsync(testDevice.Id, testMessage).ConfigureAwait(false); @@ -73,7 +73,7 @@ private async Task RegistryManager_AddDevice(HttpTransportSettings httpTransport { string deviceName = DevicePrefix + Guid.NewGuid(); - using (RegistryManager registryManager = RegistryManager.CreateFromConnectionString(s_connectionString, httpTransportSettings)) + using (var registryManager = RegistryManager.CreateFromConnectionString(s_connectionString, httpTransportSettings)) { await registryManager.AddDeviceAsync(new Device(deviceName)).ConfigureAwait(false); await registryManager.RemoveDeviceAsync(deviceName).ConfigureAwait(false); @@ -82,11 +82,11 @@ private async Task RegistryManager_AddDevice(HttpTransportSettings httpTransport private async Task JobClient_ScheduleAndRunTwinJob(HttpTransportSettings httpTransportSettings) { - Twin twin = new Twin(JobDeviceId); + var twin = new Twin(JobDeviceId); twin.Tags = new TwinCollection(); twin.Tags[JobTestTagName] = JobDeviceId; - using (JobClient jobClient = JobClient.CreateFromConnectionString(s_connectionString, httpTransportSettings)) + using (var jobClient = JobClient.CreateFromConnectionString(s_connectionString, httpTransportSettings)) { int tryCount = 0; while (true) @@ -111,9 +111,9 @@ private async Task JobClient_ScheduleAndRunTwinJob(HttpTransportSettings httpTra private (Message message, string messageId, string payload, string p1Value) ComposeD2CTestMessage() { - var messageId = Guid.NewGuid().ToString(); - var payload = Guid.NewGuid().ToString(); - var p1Value = Guid.NewGuid().ToString(); + string messageId = Guid.NewGuid().ToString(); + string payload = Guid.NewGuid().ToString(); + string p1Value = Guid.NewGuid().ToString(); Logger.Trace($"{nameof(ComposeD2CTestMessage)}: messageId='{messageId}' payload='{payload}' p1Value='{p1Value}'"); var message = new Message(Encoding.UTF8.GetBytes(payload)) diff --git a/e2e/test/iothub/service/PnpServiceTests.cs b/e2e/test/iothub/service/PnpServiceTests.cs index 631309c795..bd8f9c918f 100644 --- a/e2e/test/iothub/service/PnpServiceTests.cs +++ b/e2e/test/iothub/service/PnpServiceTests.cs @@ -38,13 +38,13 @@ public async Task DeviceTwin_Contains_ModelId() { ModelId = TestModelId, }; - using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, Client.TransportType.Mqtt_Tcp_Only, options); + using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, Client.TransportType.Mqtt_Tcp_Only, options); await deviceClient.OpenAsync().ConfigureAwait(false); // Act // Get device twin. - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); Twin twin = await registryManager.GetTwinAsync(testDevice.Device.Id).ConfigureAwait(false); // Assert @@ -69,13 +69,13 @@ public async Task DeviceTwin_Contains_ModelId_X509() string hostName = HostNameHelper.GetHostName(TestConfiguration.IoTHub.ConnectionString); X509Certificate2 authCertificate = TestConfiguration.IoTHub.GetCertificateWithPrivateKey(); using var auth = new DeviceAuthenticationWithX509Certificate(testDevice.Id, authCertificate); - using DeviceClient deviceClient = DeviceClient.Create(hostName, auth, Client.TransportType.Mqtt_Tcp_Only, options); + using var deviceClient = DeviceClient.Create(hostName, auth, Client.TransportType.Mqtt_Tcp_Only, options); await deviceClient.OpenAsync().ConfigureAwait(false); // Act // Get device twin. - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); Twin twin = await registryManager.GetTwinAsync(testDevice.Device.Id).ConfigureAwait(false); // Assert @@ -104,13 +104,13 @@ public async Task ModuleTwin_Contains_ModelId() { ModelId = TestModelId, }; - using ModuleClient moduleClient = ModuleClient.CreateFromConnectionString(testModule.ConnectionString, Client.TransportType.Mqtt_Tcp_Only, options); + using var moduleClient = ModuleClient.CreateFromConnectionString(testModule.ConnectionString, Client.TransportType.Mqtt_Tcp_Only, options); await moduleClient.OpenAsync().ConfigureAwait(false); // Act // Get module twin. - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); Twin twin = await registryManager.GetTwinAsync(testModule.DeviceId, testModule.Id).ConfigureAwait(false); // Assert diff --git a/e2e/test/iothub/service/RegistryManagerE2ETests.cs b/e2e/test/iothub/service/RegistryManagerE2ETests.cs index 5729f9b7b4..e67c6ca723 100644 --- a/e2e/test/iothub/service/RegistryManagerE2ETests.cs +++ b/e2e/test/iothub/service/RegistryManagerE2ETests.cs @@ -26,7 +26,7 @@ public class RegistryManagerE2ETests : E2EMsTestBase public async Task RegistryManager_BadProxy_ThrowsException() { // arrange - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString( + using var registryManager = RegistryManager.CreateFromConnectionString( TestConfiguration.IoTHub.ConnectionString, new HttpTransportSettings { @@ -94,7 +94,7 @@ public async Task RegistryManager_AddDeviceWithTwinWithDeviceCapabilities() { string deviceId = _idPrefix + Guid.NewGuid(); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); var twin = new Twin { Tags = new TwinCollection(@"{ companyId: 1234 }"), @@ -114,63 +114,195 @@ public async Task RegistryManager_AddDeviceWithTwinWithDeviceCapabilities() } [LoggedTestMethod] - public async Task RegistryManager_BulkLifecycle() + public async Task RegistryManager_AddDevices2Async_Works() { - int bulkCount = 50; - var devices = new List(); - for (int i = 0; i < bulkCount; i++) + // arrange + + var edge = new Device(_idPrefix + Guid.NewGuid()) { - var device = new Device(_idPrefix + Guid.NewGuid()); - device.Scope = "someScope" + Guid.NewGuid(); - device.ParentScopes.Add("someParentScope" + Guid.NewGuid()); - devices.Add(device); - } + Scope = "someScope" + Guid.NewGuid(), + }; + var device = new Device(_idPrefix + Guid.NewGuid()) + { + Scope = edge.Scope, + }; - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); - // Test that you can create devices in bulk - BulkRegistryOperationResult bulkAddResult = await registryManager.AddDevices2Async(devices).ConfigureAwait(false); - Assert.IsTrue(bulkAddResult.IsSuccessful); + try + { + // act + BulkRegistryOperationResult bulkAddResult = await registryManager + .AddDevices2Async(new List { edge, device }) + .ConfigureAwait(false); + + // assert - foreach (Device device in devices) + bulkAddResult.IsSuccessful.Should().BeTrue(); + + Device actualEdge = await registryManager.GetDeviceAsync(edge.Id).ConfigureAwait(false); + actualEdge.Id.Should().Be(edge.Id); + actualEdge.Scope.Should().Be(edge.Scope); + + Device actualDevice = await registryManager.GetDeviceAsync(device.Id).ConfigureAwait(false); + actualDevice.Id.Should().Be(device.Id); + actualDevice.Scope.Should().Be(device.Scope); + actualDevice.ParentScopes.Count.Should().Be(1); + actualDevice.ParentScopes.First().Should().Be(edge.Scope); + } + finally { - // After a bulk add, every device should be able to be retrieved - Device retrievedDevice = await registryManager.GetDeviceAsync(device.Id).ConfigureAwait(false); - Assert.IsNotNull(retrievedDevice.Id); - Assert.AreEqual(device.Scope, retrievedDevice.Scope); - Assert.AreEqual(1, retrievedDevice.ParentScopes.Count); - Assert.AreEqual(device.ParentScopes.ElementAt(0), retrievedDevice.ParentScopes.ElementAt(0)); + try + { + await registryManager.RemoveDeviceAsync(device.Id).ConfigureAwait(false); + await registryManager.RemoveDeviceAsync(edge.Id).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.Trace($"Failed to clean up devices due to {ex}"); + } } + } + + [LoggedTestMethod] + public async Task RegistryManager_UpdateDevices2Async_Works() + { + // arrange - var twins = new List(); - string expectedProperty = "someNewProperty"; - string expectedPropertyValue = "someNewPropertyValue"; - foreach (Device device in devices) + var device1 = new Device(_idPrefix + Guid.NewGuid()); + var device2 = new Device(_idPrefix + Guid.NewGuid()); + var edge = new Device(_idPrefix + Guid.NewGuid()); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + + try { - Twin twin = await registryManager.GetTwinAsync(device.Id).ConfigureAwait(false); - twin.Properties.Desired[expectedProperty] = expectedPropertyValue; - twins.Add(twin); + Device addedDevice1 = await registryManager.AddDeviceAsync(device1).ConfigureAwait(false); + Device addedDevice2 = await registryManager.AddDeviceAsync(device2).ConfigureAwait(false); + Device addedEdge = await registryManager.AddDeviceAsync(edge).ConfigureAwait(false); + + // act + + addedDevice1.Scope = addedEdge.Scope; + addedDevice2.Scope = addedEdge.Scope; + BulkRegistryOperationResult result = await registryManager + .UpdateDevices2Async(new[] { addedDevice1, addedDevice2 }) + .ConfigureAwait(false); + + // assert + + result.IsSuccessful.Should().BeTrue(); + + Device actualDevice1 = await registryManager.GetDeviceAsync(device1.Id).ConfigureAwait(false); + actualDevice1.Scope.Should().Be(addedEdge.Scope); + + Device actualDevice2 = await registryManager.GetDeviceAsync(device2.Id).ConfigureAwait(false); + actualDevice2.Scope.Should().Be(addedEdge.Scope); } + finally + { + try + { + await registryManager.RemoveDeviceAsync(device1.Id).ConfigureAwait(false); + await registryManager.RemoveDeviceAsync(device2.Id).ConfigureAwait(false); + await registryManager.RemoveDeviceAsync(edge.Id).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.Trace($"Failed to clean up devices due to {ex}"); + } + } + } - // Test that you can update twins in bulk - await registryManager.UpdateTwins2Async(twins).ConfigureAwait(false); + [LoggedTestMethod] + public async Task RegistryManager_UpdateTwins2Async_Works() + { + // arrange + + var device1 = new Device(_idPrefix + Guid.NewGuid()); + var device2 = new Device(_idPrefix + Guid.NewGuid()); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); - foreach (Device device in devices) + try { - Twin twin = await registryManager.GetTwinAsync(device.Id).ConfigureAwait(false); - Assert.IsNotNull(twin.Properties.Desired[expectedProperty]); - Assert.AreEqual(expectedPropertyValue, (string)twin.Properties.Desired[expectedProperty]); + await registryManager.AddDeviceAsync(device1).ConfigureAwait(false); + Twin twin1 = await registryManager.GetTwinAsync(device1.Id).ConfigureAwait(false); + await registryManager.AddDeviceAsync(device2).ConfigureAwait(false); + Twin twin2 = await registryManager.GetTwinAsync(device2.Id).ConfigureAwait(false); + + // act + + const string expectedProperty = "someNewProperty"; + const string expectedPropertyValue = "someNewPropertyValue"; + + twin1.Properties.Desired[expectedProperty] = expectedPropertyValue; + twin2.Properties.Desired[expectedProperty] = expectedPropertyValue; + + BulkRegistryOperationResult result = await registryManager + .UpdateTwins2Async(new[] { twin1, twin2 }) + .ConfigureAwait(false); + + // assert + + result.IsSuccessful.Should().BeTrue(); + + var actualTwin1 = await registryManager.GetTwinAsync(device1.Id).ConfigureAwait(false); + ((string)actualTwin1.Properties.Desired[expectedProperty]).Should().Be(expectedPropertyValue); + var actualTwin2 = await registryManager.GetTwinAsync(device2.Id).ConfigureAwait(false); + ((string)(actualTwin2.Properties.Desired[expectedProperty])).Should().Be(expectedPropertyValue); } + finally + { + try + { + await registryManager.RemoveDeviceAsync(device1.Id).ConfigureAwait(false); + await registryManager.RemoveDeviceAsync(device2.Id).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.Trace($"Failed to clean up devices due to {ex}"); + } + } + } - // Test that you can delete device identities in bulk - BulkRegistryOperationResult bulkDeleteResult = await registryManager.RemoveDevices2Async(devices, true, default).ConfigureAwait(false); + [LoggedTestMethod] + public async Task RegistryManager_RemoveDevices2Async_Works() + { + // arrange - Assert.IsTrue(bulkDeleteResult.IsSuccessful); + var device1 = new Device(_idPrefix + Guid.NewGuid()); + var device2 = new Device(_idPrefix + Guid.NewGuid()); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); - foreach (Device device in devices) + try { - // After a bulk delete, every device should not be found - Assert.IsNull(await registryManager.GetDeviceAsync(device.Id).ConfigureAwait(false)); + await registryManager.AddDeviceAsync(device1).ConfigureAwait(false); + await registryManager.AddDeviceAsync(device2).ConfigureAwait(false); + + // act + + BulkRegistryOperationResult bulkDeleteResult = await registryManager + .RemoveDevices2Async(new[] { device1, device2 }, true, default) + .ConfigureAwait(false); + + // assert + + bulkDeleteResult.IsSuccessful.Should().BeTrue(); + Device actualDevice1 = await registryManager.GetDeviceAsync(device1.Id).ConfigureAwait(false); + actualDevice1.Should().BeNull(); + Device actualDevice2 = await registryManager.GetDeviceAsync(device1.Id).ConfigureAwait(false); + actualDevice2.Should().BeNull(); + } + finally + { + try + { + await registryManager.RemoveDeviceAsync(device1.Id).ConfigureAwait(false); + await registryManager.RemoveDeviceAsync(device2.Id).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.Trace($"Failed to clean up devices due to {ex}"); + } } } @@ -183,7 +315,7 @@ public async Task RegistryManager_AddDeviceWithProxy() Proxy = new WebProxy(TestConfiguration.IoTHub.ProxyServerAddress) }; - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString, transportSettings); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString, transportSettings); var device = new Device(deviceId); await registryManager.AddDeviceAsync(device).ConfigureAwait(false); } @@ -195,7 +327,7 @@ public async Task RegistryManager_ConfigurationOperations_Work() bool configCreated = false; string configurationId = (_idPrefix + Guid.NewGuid()).ToLower(); // Configuration Id characters must be all lower-case. - using RegistryManager client = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var client = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); try { @@ -260,50 +392,32 @@ public async Task RegistryManager_ConfigurationOperations_Work() public async Task RegistryManager_Query_Works() { // arrange - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); - string deviceId = $"{_idPrefix}{Guid.NewGuid()}"; - - try - { - Device device = await registryManager - .AddDeviceAsync(new Device(deviceId)) - .ConfigureAwait(false); - // act + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + string deviceId = TestConfiguration.IoTHub.X509ChainDeviceName; - IQuery query = null; - IEnumerable twins = null; - for (int i = 0; i < 30; ++i) - { - string queryText = $"select * from devices where deviceId = '{deviceId}'"; - query = registryManager.CreateQuery(queryText); + Device device = await registryManager + .GetDeviceAsync(deviceId) + .ConfigureAwait(false); + device.Should().NotBeNull($"Device {deviceId} should already exist in hub setup for E2E tests"); - twins = await query.GetNextAsTwinAsync().ConfigureAwait(false); + // act - if (twins.Any()) - { - break; - } + string queryText = $"select * from devices where deviceId = '{deviceId}'"; + IQuery query = registryManager.CreateQuery(queryText); + IEnumerable twins = await query.GetNextAsTwinAsync().ConfigureAwait(false); - // A new device may not return immediately from a query, so give it some time and some retries to appear - await Task.Delay(250).ConfigureAwait(false); - } + // assert - // assert - twins.Count().Should().Be(1, "only asked for 1 device by its Id"); - twins.First().DeviceId.Should().Be(deviceId, "The Id of the device returned should match"); - query.HasMoreResults.Should().BeFalse("We've processed the single, expected result"); - } - finally - { - await registryManager.RemoveDeviceAsync(deviceId).ConfigureAwait(false); - } + twins.Count().Should().Be(1, "only asked for 1 device by its Id"); + twins.First().DeviceId.Should().Be(deviceId, "The Id of the device returned should match"); + query.HasMoreResults.Should().BeFalse("We've processed the single, expected result"); } [LoggedTestMethod] public async Task ModulesClient_GetModulesOnDevice() { - int moduleCount = 5; + const int moduleCount = 2; string testDeviceId = $"IdentityLifecycleDevice{Guid.NewGuid()}"; string[] testModuleIds = new string[moduleCount]; for (int i = 0; i < moduleCount; i++) @@ -312,7 +426,7 @@ public async Task ModulesClient_GetModulesOnDevice() } Device device = null; - using RegistryManager client = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var client = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); try { @@ -326,6 +440,9 @@ public async Task ModulesClient_GetModulesOnDevice() new Module(testDeviceId, testModuleIds[i])).ConfigureAwait(false); } + // Give the hub a moment + await Task.Delay(250).ConfigureAwait(false); + // List the modules on the test device IEnumerable modulesOnDevice = await client.GetModulesOnDeviceAsync(testDeviceId).ConfigureAwait(false); @@ -336,7 +453,7 @@ public async Task ModulesClient_GetModulesOnDevice() Assert.AreEqual(moduleCount, moduleIdsOnDevice.Count); for (int i = 0; i < moduleCount; i++) { - Assert.IsTrue(moduleIdsOnDevice.Contains(testModuleIds[i])); + moduleIdsOnDevice.Should().Contain(testModuleIds[i]); } } finally @@ -355,7 +472,7 @@ public async Task ModulesClient_IdentityLifecycle() string testDeviceId = $"IdentityLifecycleDevice{Guid.NewGuid()}"; string testModuleId = $"IdentityLifecycleModule{Guid.NewGuid()}"; - using RegistryManager client = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var client = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); try { @@ -398,7 +515,7 @@ public async Task ModulesClient_IdentityLifecycle() [LoggedTestMethod] public async Task ModulesClient_DeviceTwinLifecycle() { - using RegistryManager client = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var client = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); TestModule module = await TestModule.GetTestModuleAsync(_idPrefix, _idPrefix, Logger).ConfigureAwait(false); try @@ -427,7 +544,7 @@ public async Task ModulesClient_DeviceTwinLifecycle() } } - private async Task CleanupAsync(RegistryManager client, string deviceId) + private static async Task CleanupAsync(RegistryManager client, string deviceId) { // cleanup try diff --git a/e2e/test/iothub/service/RegistryManagerExportDevicesTests.cs b/e2e/test/iothub/service/RegistryManagerExportDevicesTests.cs index c779d0099a..de45b2783c 100644 --- a/e2e/test/iothub/service/RegistryManagerExportDevicesTests.cs +++ b/e2e/test/iothub/service/RegistryManagerExportDevicesTests.cs @@ -1,8 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; -using System.Diagnostics.Tracing; using System.Linq; using System.Threading.Tasks; using FluentAssertions; @@ -24,7 +24,7 @@ public class RegistryManagerExportDevicesTests : E2EMsTestBase // https://github.com/Azure/azure-sdk-for-net/issues/10476 private const string ExportFileNameDefault = "devices.txt"; - private const int MaxIterationWait = 30; + private const int MaxIterationWait = 60; private static readonly TimeSpan s_waitDuration = TimeSpan.FromSeconds(3); private static readonly char[] s_newlines = new char[] @@ -53,17 +53,22 @@ public async Task RegistryManager_ExportDevices(StorageAuthenticationType storag { // arrange - string edgeId1 = $"{nameof(RegistryManager_ExportDevices)}-Edge-{StorageContainer.GetRandomSuffix(4)}"; - string edgeId2 = $"{nameof(RegistryManager_ExportDevices)}-Edge-{StorageContainer.GetRandomSuffix(4)}"; - string deviceId = $"{nameof(RegistryManager_ExportDevices)}-{StorageContainer.GetRandomSuffix(4)}"; - string devicesFileName = $"{nameof(RegistryManager_ExportDevices)}-devicesexport-{StorageContainer.GetRandomSuffix(4)}.txt"; - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + const string idPrefix = nameof(RegistryManager_ExportDevices); + + string edgeId1 = $"{idPrefix}-Edge-{StorageContainer.GetRandomSuffix(4)}"; + string edgeId2 = $"{idPrefix}-Edge-{StorageContainer.GetRandomSuffix(4)}"; + string deviceId = $"{idPrefix}-{StorageContainer.GetRandomSuffix(4)}"; + string configurationId = (idPrefix + Guid.NewGuid()).ToLower(); // Configuration Id characters must be all lower-case. + Logger.Trace($"Using Ids {deviceId}, {edgeId1}, {edgeId2}, and {configurationId}"); + + string devicesFileName = $"{idPrefix}-devicesexport-{StorageContainer.GetRandomSuffix(4)}.txt"; + string configsFileName = $"{idPrefix}-configsexport-{StorageContainer.GetRandomSuffix(4)}.txt"; - Logger.Trace($"Using deviceId {deviceId}"); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); try { - string containerName = StorageContainer.BuildContainerName(nameof(RegistryManager_ExportDevices)); + string containerName = StorageContainer.BuildContainerName(idPrefix); using StorageContainer storageContainer = await StorageContainer .GetInstanceAsync(containerName) .ConfigureAwait(false); @@ -73,7 +78,7 @@ public async Task RegistryManager_ExportDevices(StorageAuthenticationType storag ? storageContainer.SasUri : storageContainer.Uri; - var edge1 = await registryManager + Device edge1 = await registryManager .AddDeviceAsync( new Device(edgeId1) { @@ -82,7 +87,7 @@ public async Task RegistryManager_ExportDevices(StorageAuthenticationType storag }) .ConfigureAwait(false); - var edge2 = await registryManager + Device edge2 = await registryManager .AddDeviceAsync( new Device(edgeId2) { @@ -92,7 +97,7 @@ public async Task RegistryManager_ExportDevices(StorageAuthenticationType storag }) .ConfigureAwait(false); - await registryManager + Device device = await registryManager .AddDeviceAsync( new Device(deviceId) { @@ -101,123 +106,221 @@ await registryManager }) .ConfigureAwait(false); + //Configuration configuration = await registryManager + // .AddConfigurationAsync( + // new Configuration(configurationId) + // { + // Priority = 2, + // Labels = { { "labelName", "labelValue" } }, + // TargetCondition = "*", + // Content = + // { + // DeviceContent = { { "properties.desired.x", 4L } }, + // }, + // Metrics = + // { + // Queries = { { "successfullyConfigured", "select deviceId from devices where properties.reported.x = 4" } } + // }, + // }) + // .ConfigureAwait(false); + // act - JobProperties exportJobResponse = null; - int tryCount = 0; - while (true) + JobProperties exportJobResponse = await CreateAndWaitForJobAsync( + storageAuthenticationType, + isUserAssignedMsi, + devicesFileName, + configsFileName, + registryManager, + containerUri) + .ConfigureAwait(false); + + // assert + await ValidateDevicesAsync( + devicesFileName, + storageContainer, + edge1, + edge2, + device) + .ConfigureAwait(false); + //await ValidateConfigurationsAsync( + // configsFileName, + // storageContainer, + // configuration) + // .ConfigureAwait(false); + } + finally + { + await CleanUpDevicesAsync(edgeId1, edgeId2, deviceId, configurationId, registryManager).ConfigureAwait(false); + } + } + + private async Task CreateAndWaitForJobAsync( + StorageAuthenticationType storageAuthenticationType, + bool isUserAssignedMsi, + string devicesFileName, + string configsFileName, + RegistryManager registryManager, + Uri containerUri) + { + int tryCount = 0; + + ManagedIdentity identity = isUserAssignedMsi + ? new ManagedIdentity { - try - { - ManagedIdentity identity = null; - if (isUserAssignedMsi) - { - string userAssignedMsiResourceId = TestConfiguration.IoTHub.UserAssignedMsiResourceId; - identity = new ManagedIdentity - { - userAssignedIdentity = userAssignedMsiResourceId - }; - } - - JobProperties jobProperties = JobProperties.CreateForExportJob( - containerUri.ToString(), - true, - devicesFileName, - storageAuthenticationType, - identity); - exportJobResponse = await registryManager.ExportDevicesAsync(jobProperties).ConfigureAwait(false); - break; - } - // Concurrent jobs can be rejected, so implement a retry mechanism to handle conflicts with other tests - catch (JobQuotaExceededException) when (++tryCount < MaxIterationWait) - { - Logger.Trace($"JobQuotaExceededException... waiting."); - await Task.Delay(s_waitDuration).ConfigureAwait(false); - continue; - } + UserAssignedIdentity = TestConfiguration.IoTHub.UserAssignedMsiResourceId } + : null; - // Wait for job to complete - for (int i = 0; i < MaxIterationWait; ++i) + JobProperties exportJobResponse = JobProperties.CreateForExportJob( + containerUri.ToString(), + true, + devicesFileName, + storageAuthenticationType, + identity); + //exportJobResponse.IncludeConfigurations = true; + //exportJobResponse.ConfigurationsBlobName = configsFileName; + + while (tryCount < MaxIterationWait) + { + try + { + exportJobResponse = await registryManager.ExportDevicesAsync(exportJobResponse).ConfigureAwait(false); + break; + } + // Concurrent jobs can be rejected, so implement a retry mechanism to handle conflicts with other tests + catch (JobQuotaExceededException) when (++tryCount < MaxIterationWait) { + Logger.Trace($"JobQuotaExceededException... waiting."); await Task.Delay(s_waitDuration).ConfigureAwait(false); - exportJobResponse = await registryManager.GetJobAsync(exportJobResponse.JobId).ConfigureAwait(false); - Logger.Trace($"Job {exportJobResponse.JobId} is {exportJobResponse.Status} with progress {exportJobResponse.Progress}%"); - if (!s_incompleteJobs.Contains(exportJobResponse.Status)) - { - break; - } + continue; + } + } + + for (int i = 0; i < MaxIterationWait; ++i) + { + await Task.Delay(s_waitDuration).ConfigureAwait(false); + exportJobResponse = await registryManager.GetJobAsync(exportJobResponse.JobId).ConfigureAwait(false); + Logger.Trace($"Job {exportJobResponse.JobId} is {exportJobResponse.Status} with progress {exportJobResponse.Progress}%"); + if (!s_incompleteJobs.Contains(exportJobResponse.Status)) + { + break; } + } - // assert + exportJobResponse.Status.Should().Be(JobStatus.Completed, "Otherwise import failed"); + exportJobResponse.FailureReason.Should().BeNullOrEmpty("Otherwise import failed"); - exportJobResponse.Status.Should().Be(JobStatus.Completed, "Otherwise import failed"); - exportJobResponse.FailureReason.Should().BeNullOrEmpty("Otherwise import failed"); + return exportJobResponse; + } - string devicesContent = await DownloadFileAsync(storageContainer, devicesFileName).ConfigureAwait(false); - string[] serializedDevices = devicesContent.Split(s_newlines, StringSplitOptions.RemoveEmptyEntries); + private async Task ValidateDevicesAsync( + string devicesFileName, + StorageContainer storageContainer, + Device edge1, + Device edge2, + Device device) + { + string devicesContent = await DownloadFileAsync(storageContainer, devicesFileName).ConfigureAwait(false); + string[] serializedDevices = devicesContent.Split(s_newlines, StringSplitOptions.RemoveEmptyEntries); - bool foundDeviceInExport = false; - bool foundEdgeInExport = false; - foreach (string serializedDevice in serializedDevices) + bool foundEdge1InExport = false; + bool foundEdge2InExport = false; + bool foundDeviceInExport = false; + + foreach (string serializedDevice in serializedDevices) + { + // The first line may be a comment to the user, so skip any lines that don't start with a json object initial character: curly brace + if (serializedDevice[0] != '{') { - // The first line may be a comment to the user, so skip any lines that don't start with a json object initial character: curly brace - if (serializedDevice[0] != '{') - { - continue; - } - - if (foundEdgeInExport && foundDeviceInExport) - { - // we're done - break; - } - - ExportImportDevice exportedDevice = JsonConvert.DeserializeObject(serializedDevice); - if (StringComparer.Ordinal.Equals(exportedDevice.Id, edgeId2) && exportedDevice.Capabilities.IotEdge) - { - Logger.Trace($"Found edge2 in export as [{serializedDevice}]"); - foundEdgeInExport = true; - exportedDevice.DeviceScope.Should().Be(edge2.Scope, "Edges retain their own scope"); - - // This is broken. The export doesn't include the ParentScopes property. - // Disable this assert until it is fixed in the service. - //exportedDevice.ParentScopes.First().Should().Be(edge1.Scope); - continue; - } - - if (StringComparer.Ordinal.Equals(exportedDevice.Id, deviceId)) - { - Logger.Trace($"Found device in export as [{serializedDevice}]"); - foundDeviceInExport = true; - exportedDevice.DeviceScope.Should().Be(edge1.Scope); - continue; - } + continue; } - foundEdgeInExport.Should().BeTrue("Expected edge did not appear in the export"); - foundDeviceInExport.Should().BeTrue("Expected device did not appear in the export"); - } - finally - { - try + + if (foundEdge1InExport + && foundEdge2InExport + && foundDeviceInExport) + { + // we're done + break; + } + + ExportImportDevice exportedDevice = JsonConvert.DeserializeObject(serializedDevice); + + if (StringComparer.Ordinal.Equals(exportedDevice.Id, edge1.Id) && exportedDevice.Capabilities.IotEdge) + { + Logger.Trace($"Found edge1 in export as [{serializedDevice}]"); + foundEdge1InExport = true; + exportedDevice.DeviceScope.Should().Be(edge1.Scope, "Edges retain their own scope"); + continue; + } + + if (StringComparer.Ordinal.Equals(exportedDevice.Id, edge2.Id) && exportedDevice.Capabilities.IotEdge) { - await registryManager.RemoveDeviceAsync(deviceId).ConfigureAwait(false); - await registryManager.RemoveDeviceAsync(edgeId2).ConfigureAwait(false); - await registryManager.RemoveDeviceAsync(edgeId1).ConfigureAwait(false); + Logger.Trace($"Found edge2 in export as [{serializedDevice}]"); + foundEdge2InExport = true; + exportedDevice.DeviceScope.Should().Be(edge2.Scope, "Edges retain their own scope"); + continue; } - catch (Exception ex) + + if (StringComparer.Ordinal.Equals(exportedDevice.Id, device.Id)) { - Logger.Trace($"Failed to remove device during cleanup due to {ex}"); + Logger.Trace($"Found device in export as [{serializedDevice}]"); + foundDeviceInExport = true; + exportedDevice.DeviceScope.Should().Be(edge1.Scope); + continue; } } + foundEdge1InExport.Should().BeTrue("Expected edge did not appear in the export"); + foundEdge2InExport.Should().BeTrue("Expected edge did not appear in the export"); + foundDeviceInExport.Should().BeTrue("Expected device did not appear in the export"); + } + + private async Task ValidateConfigurationsAsync( + string configsFileName, + StorageContainer storageContainer, + Configuration configuration) + { + string configsContent = await DownloadFileAsync(storageContainer, configsFileName).ConfigureAwait(false); + string[] serializedConfigs = configsContent.Split(s_newlines, StringSplitOptions.RemoveEmptyEntries); + + bool foundConfig = false; + foreach (string serializedConfig in serializedConfigs) + { + Configuration exportedConfig = JsonConvert.DeserializeObject(serializedConfig); + if (StringComparer.Ordinal.Equals(exportedConfig.Id, configuration.Id)) + { + Logger.Trace($"Found config in export as [{serializedConfig}]"); + foundConfig = true; + } + } + + foundConfig.Should().BeTrue(); } private static async Task DownloadFileAsync(StorageContainer storageContainer, string fileName) { CloudBlockBlob exportFile = storageContainer.CloudBlobContainer.GetBlockBlobReference(fileName); - string fileContents = await exportFile.DownloadTextAsync().ConfigureAwait(false); + return await exportFile.DownloadTextAsync().ConfigureAwait(false); + } - return fileContents; + private async Task CleanUpDevicesAsync( + string edgeId1, + string edgeId2, + string deviceId, + string configurationId, + RegistryManager registryManager) + { + try + { + await registryManager.RemoveDeviceAsync(deviceId).ConfigureAwait(false); + await registryManager.RemoveDeviceAsync(edgeId2).ConfigureAwait(false); + await registryManager.RemoveDeviceAsync(edgeId1).ConfigureAwait(false); + //await registryManager.RemoveConfigurationAsync(configurationId).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.Trace($"Failed to remove device/config during cleanup due to {ex}"); + } } } } diff --git a/e2e/test/iothub/service/RegistryManagerImportDevicesTests.cs b/e2e/test/iothub/service/RegistryManagerImportDevicesTests.cs index 8134adc813..19c7f842c3 100644 --- a/e2e/test/iothub/service/RegistryManagerImportDevicesTests.cs +++ b/e2e/test/iothub/service/RegistryManagerImportDevicesTests.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.IO; @@ -46,23 +47,28 @@ public async Task RegistryManager_ImportDevices(StorageAuthenticationType storag { // arrange - string deviceId = $"{nameof(RegistryManager_ImportDevices)}-device-{StorageContainer.GetRandomSuffix(4)}"; - string devicesFileName = $"{nameof(RegistryManager_ImportDevices)}-{StorageContainer.GetRandomSuffix(4)}.txt"; - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + const string idPrefix = nameof(RegistryManager_ImportDevices); + + string deviceId = $"{idPrefix}-device-{StorageContainer.GetRandomSuffix(4)}"; + string configId = $"{idPrefix}-config-{StorageContainer.GetRandomSuffix(4)}".ToLower(); // Configuration Id characters must be all lower-case. + Logger.Trace($"Using Ids {deviceId} and {configId}."); - Logger.Trace($"Using deviceId {deviceId}."); + string devicesFileName = $"{idPrefix}-devices-{StorageContainer.GetRandomSuffix(4)}.txt"; + string configsFileName = $"{idPrefix}-configs-{StorageContainer.GetRandomSuffix(4)}.txt"; + + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); try { string containerName = StorageContainer.BuildContainerName(nameof(RegistryManager_ImportDevices)); using StorageContainer storageContainer = await StorageContainer.GetInstanceAsync(containerName).ConfigureAwait(false); - Logger.Trace($"Using container {storageContainer.Uri}"); + Logger.Trace($"Using devices container {storageContainer.Uri}"); Uri containerUri = storageAuthenticationType == StorageAuthenticationType.KeyBased ? storageContainer.SasUri : storageContainer.Uri; - using Stream devicesStream = ImportExportDevicesHelpers.BuildDevicesStream( + using Stream devicesStream = ImportExportHelpers.BuildImportStream( new List { new ExportImportDevice( @@ -74,54 +80,44 @@ public async Task RegistryManager_ImportDevices(StorageAuthenticationType storag }); await UploadFileAndConfirmAsync(storageContainer, devicesStream, devicesFileName).ConfigureAwait(false); - // act - - JobProperties importJobResponse = null; - int tryCount = 0; - while (true) - { - try + //using Stream configsStream = ImportExportHelpers.BuildImportStream( + // new List + // { + // new ImportConfiguration(configId) + // { + // ImportMode = ConfigurationImportMode.CreateOrUpdateIfMatchETag, + // Priority = 3, + // Labels = { { "labelName", "labelValue" } }, + // TargetCondition = "*", + // Content = + // { + // DeviceContent = { { "properties.desired.x", 5L } }, + // }, + // Metrics = + // { + // Queries = { { "successfullyConfigured", "select deviceId from devices where properties.reported.x = 5" } } + // }, + // }, + // }); + //await UploadFileAndConfirmAsync(storageContainer, configsStream, configsFileName).ConfigureAwait(false); + + ManagedIdentity identity = isUserAssignedMsi + ? new ManagedIdentity { - ManagedIdentity identity = null; - if (isUserAssignedMsi) - { - string userAssignedMsiResourceId = TestConfiguration.IoTHub.UserAssignedMsiResourceId; - identity = new ManagedIdentity - { - userAssignedIdentity = userAssignedMsiResourceId - }; - } - - importJobResponse = await registryManager - .ImportDevicesAsync( - JobProperties.CreateForImportJob( - containerUri.ToString(), - containerUri.ToString(), - devicesFileName, - storageAuthenticationType)) - .ConfigureAwait(false); - break; + UserAssignedIdentity = TestConfiguration.IoTHub.UserAssignedMsiResourceId } - // Concurrent jobs can be rejected, so implement a retry mechanism to handle conflicts with other tests - catch (JobQuotaExceededException) when (++tryCount < MaxIterationWait) - { - Logger.Trace($"JobQuotaExceededException... waiting."); - await Task.Delay(s_waitDuration).ConfigureAwait(false); - continue; - } - } + : null; - // wait for job to complete - for (int i = 0; i < MaxIterationWait; ++i) - { - await Task.Delay(1000).ConfigureAwait(false); - importJobResponse = await registryManager.GetJobAsync(importJobResponse.JobId).ConfigureAwait(false); - Logger.Trace($"Job {importJobResponse.JobId} is {importJobResponse.Status} with progress {importJobResponse.Progress}%"); - if (!s_incompleteJobs.Contains(importJobResponse.Status)) - { - break; - } - } + // act + + JobProperties importJobResponse = await CreateAndWaitForJobAsync( + storageAuthenticationType, + devicesFileName, + configsFileName, + registryManager, + containerUri, + identity) + .ConfigureAwait(false); // assert @@ -130,38 +126,48 @@ public async Task RegistryManager_ImportDevices(StorageAuthenticationType storag // should not throw due to 404, but device may not immediately appear in registry Device device = null; + //Configuration config = null; for (int i = 0; i < MaxIterationWait; ++i) { await Task.Delay(s_waitDuration).ConfigureAwait(false); try { device = await registryManager.GetDeviceAsync(deviceId).ConfigureAwait(false); + //config = await registryManager.GetConfigurationAsync(configId).ConfigureAwait(false); break; } catch (Exception ex) { - Logger.Trace($"Could not find device on iteration {i} due to [{ex.Message}]"); + Logger.Trace($"Could not find device/config on iteration {i} due to [{ex.Message}]"); } } if (device == null) { Assert.Fail($"Device {deviceId} not found in registry manager"); } + //if (config == null) + //{ + // Assert.Fail($"Config {configId} not found in registry manager"); + //} } finally { try { await registryManager.RemoveDeviceAsync(deviceId).ConfigureAwait(false); + //await registryManager.RemoveConfigurationAsync(configId).ConfigureAwait(false); + } + catch (Exception ex) + { + Logger.Trace($"Failed to clean up device/config due to {ex}"); } - catch { } } } - private static async Task UploadFileAndConfirmAsync(StorageContainer storageContainer, Stream devicesFile, string fileName) + private static async Task UploadFileAndConfirmAsync(StorageContainer storageContainer, Stream fileContents, string fileName) { CloudBlockBlob cloudBlob = storageContainer.CloudBlobContainer.GetBlockBlobReference(fileName); - await cloudBlob.UploadFromStreamAsync(devicesFile).ConfigureAwait(false); + await cloudBlob.UploadFromStreamAsync(fileContents).ConfigureAwait(false); // wait for blob to be written bool foundBlob = false; @@ -174,7 +180,62 @@ private static async Task UploadFileAndConfirmAsync(StorageContainer storageCont break; } } - foundBlob.Should().BeTrue($"Failed to find {fileName} in storage container, required for test."); + foundBlob.Should().BeTrue($"Failed to find {fileName} in storage container - required for test."); + } + + private async Task CreateAndWaitForJobAsync( + StorageAuthenticationType storageAuthenticationType, + string devicesFileName, + string configsFileName, + RegistryManager registryManager, + Uri containerUri, + ManagedIdentity identity) + { + int tryCount = 0; + JobProperties importJobResponse = null; + + JobProperties jobProperties = JobProperties.CreateForImportJob( + containerUri.ToString(), + containerUri.ToString(), + devicesFileName, + storageAuthenticationType, + identity); + //jobProperties.ConfigurationsBlobName = configsFileName; + //jobProperties.IncludeConfigurations = true; + + while (tryCount < MaxIterationWait) + { + try + { + importJobResponse = await registryManager.ImportDevicesAsync(jobProperties).ConfigureAwait(false); + if (!string.IsNullOrWhiteSpace(importJobResponse.FailureReason)) + { + Logger.Trace($"Job failed due to {importJobResponse.FailureReason}"); + } + break; + } + // Concurrent jobs can be rejected, so implement a retry mechanism to handle conflicts with other tests + catch (JobQuotaExceededException) when (++tryCount < MaxIterationWait) + { + Logger.Trace($"JobQuotaExceededException... waiting."); + await Task.Delay(s_waitDuration).ConfigureAwait(false); + continue; + } + } + + // wait for job to complete + for (int i = 0; i < MaxIterationWait; ++i) + { + await Task.Delay(1000).ConfigureAwait(false); + importJobResponse = await registryManager.GetJobAsync(importJobResponse?.JobId).ConfigureAwait(false); + Logger.Trace($"Job {importJobResponse.JobId} is {importJobResponse.Status} with progress {importJobResponse.Progress}%"); + if (!s_incompleteJobs.Contains(importJobResponse.Status)) + { + break; + } + } + + return importJobResponse; } } } diff --git a/e2e/test/iothub/service/ServiceClientE2ETests.cs b/e2e/test/iothub/service/ServiceClientE2ETests.cs index ebad2565b7..d18fdda791 100644 --- a/e2e/test/iothub/service/ServiceClientE2ETests.cs +++ b/e2e/test/iothub/service/ServiceClientE2ETests.cs @@ -50,7 +50,7 @@ private async Task TestTimeout(TimeSpan? timeout) using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix).ConfigureAwait(false); using var sender = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); - Stopwatch sw = new Stopwatch(); + var sw = new Stopwatch(); sw.Start(); Logger.Trace($"Testing ServiceClient SendAsync() timeout in ticks={timeout?.Ticks}"); @@ -73,7 +73,7 @@ public async Task ServiceClient_SendsMessage(TransportType transportType) { // arrange using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix).ConfigureAwait(false); - using ServiceClient sender = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString, transportType); + using var sender = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString, transportType); string messageId = Guid.NewGuid().ToString(); // act and expect no exception @@ -92,7 +92,7 @@ public async Task MessageIdDefaultNotSet_SendEventDoesNotSetMessageId() { // arrange using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix).ConfigureAwait(false); - using ServiceClient sender = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var sender = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); string messageId = Guid.NewGuid().ToString(); // act @@ -121,7 +121,7 @@ public async Task MessageIdDefaultSetToNull_SendEventDoesNotSetMessageId() { SdkAssignsMessageId = Shared.SdkAssignsMessageId.Never, }; - using ServiceClient sender = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString, options); + using var sender = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString, options); string messageId = Guid.NewGuid().ToString(); // act @@ -150,7 +150,7 @@ public async Task MessageIdDefaultSetToGuid_SendEventSetMessageIdIfNotSet() { SdkAssignsMessageId = Shared.SdkAssignsMessageId.WhenUnset, }; - using ServiceClient sender = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString, options); + using var sender = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString, options); string messageId = Guid.NewGuid().ToString(); // act diff --git a/e2e/test/iothub/telemetry/TelemetrySendE2ETests.cs b/e2e/test/iothub/telemetry/TelemetrySendE2ETests.cs index fac19320d7..23b1b1ff25 100644 --- a/e2e/test/iothub/telemetry/TelemetrySendE2ETests.cs +++ b/e2e/test/iothub/telemetry/TelemetrySendE2ETests.cs @@ -2,7 +2,6 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Net; using System.Threading.Tasks; using Microsoft.Azure.Devices.Client; using Microsoft.Azure.Devices.E2ETests.Helpers; diff --git a/e2e/test/iothub/twin/FaultInjectionPoolAmqpTests.TwinFaultInjectionPoolAmqpTests.cs b/e2e/test/iothub/twin/FaultInjectionPoolAmqpTests.TwinFaultInjectionPoolAmqpTests.cs index 53736ec57c..419708e302 100644 --- a/e2e/test/iothub/twin/FaultInjectionPoolAmqpTests.TwinFaultInjectionPoolAmqpTests.cs +++ b/e2e/test/iothub/twin/FaultInjectionPoolAmqpTests.TwinFaultInjectionPoolAmqpTests.cs @@ -1481,8 +1481,8 @@ await FaultInjectionPoolingOverAmqp async Task InitOperationAsync(DeviceClient deviceClient, TestDevice testDevice, TestDeviceCallbackHandler testDeviceCallbackHandler) { - var propName = Guid.NewGuid().ToString(); - var propValue = Guid.NewGuid().ToString(); + string propName = Guid.NewGuid().ToString(); + string propValue = Guid.NewGuid().ToString(); twinPropertyMap.Add(testDevice.Id, new List { propName, propValue }); Logger.Trace($"{nameof(FaultInjectionPoolAmqpTests)}: Setting desired propery callback for device {testDevice.Id}"); @@ -1495,8 +1495,8 @@ async Task TestOperationAsync(DeviceClient deviceClient, TestDevice testDevice, using var cts = new CancellationTokenSource(FaultInjection.RecoveryTime); List twinProperties = twinPropertyMap[testDevice.Id]; - var propName = twinProperties[0]; - var propValue = twinProperties[1]; + string propName = twinProperties[0]; + string propValue = twinProperties[1]; testDeviceCallbackHandler.ExpectedTwinPropertyValue = propValue; Logger.Trace($"{nameof(FaultInjectionPoolAmqpTests)}: Updating the desired properties for device {testDevice.Id}"); diff --git a/e2e/test/iothub/twin/TwinE2ETests.cs b/e2e/test/iothub/twin/TwinE2ETests.cs index be9d0aecca..27e4915ee3 100644 --- a/e2e/test/iothub/twin/TwinE2ETests.cs +++ b/e2e/test/iothub/twin/TwinE2ETests.cs @@ -392,7 +392,7 @@ public async Task Twin_ClientSetsReportedPropertyWithoutDesiredPropertyCallback( // arrange using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transportType); + using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transportType); await Twin_DeviceSetsReportedPropertyAndGetsItBackAsync(deviceClient, testDevice.Id, Guid.NewGuid().ToString(), Logger).ConfigureAwait(false); @@ -418,7 +418,7 @@ public async Task Twin_ClientSetsReportedPropertyWithoutDesiredPropertyCallback( private async Task Twin_DeviceSetsReportedPropertyAndGetsItBackSingleDeviceAsync(Client.TransportType transport) { using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); + using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); await Twin_DeviceSetsReportedPropertyAndGetsItBackAsync(deviceClient, testDevice.Id, Guid.NewGuid().ToString(), Logger).ConfigureAwait(false); } @@ -426,14 +426,14 @@ private async Task Twin_DeviceSetsReportedPropertyAndGetsItBackSingleDeviceAsync private async Task Twin_DeviceSetsReportedPropertyArrayAndGetsItBackSingleDeviceAsync(Client.TransportType transport) { using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); + using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); await Twin_DeviceSetsReportedPropertyAndGetsItBackAsync(deviceClient, testDevice.Id, s_listOfPropertyValues, Logger).ConfigureAwait(false); } public static async Task Twin_DeviceSetsReportedPropertyAndGetsItBackAsync(DeviceClient deviceClient, string deviceId, object propValue, MsTestLogger logger) { - var propName = Guid.NewGuid().ToString(); + string propName = Guid.NewGuid().ToString(); logger.Trace($"{nameof(Twin_DeviceSetsReportedPropertyAndGetsItBackAsync)}: name={propName}, value={propValue}"); @@ -443,12 +443,12 @@ public static async Task Twin_DeviceSetsReportedPropertyAndGetsItBackAsync(Devic // Validate the updated twin from the device-client Twin deviceTwin = await deviceClient.GetTwinAsync().ConfigureAwait(false); - var actual = deviceTwin.Properties.Reported[propName]; + dynamic actual = deviceTwin.Properties.Reported[propName]; Assert.AreEqual(JsonConvert.SerializeObject(actual), JsonConvert.SerializeObject(propValue)); // Validate the updated twin from the service-client Twin completeTwin = await _registryManager.GetTwinAsync(deviceId).ConfigureAwait(false); - var actualProp = completeTwin.Properties.Reported[propName]; + dynamic actualProp = completeTwin.Properties.Reported[propName]; Assert.AreEqual(JsonConvert.SerializeObject(actualProp), JsonConvert.SerializeObject(propValue)); } @@ -523,7 +523,7 @@ await deviceClient public static async Task RegistryManagerUpdateDesiredPropertyAsync(string deviceId, string propName, object propValue) { - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); var twinPatch = new Twin(); twinPatch.Properties.Desired[propName] = propValue; @@ -534,12 +534,12 @@ public static async Task RegistryManagerUpdateDesiredPropertyAsync(string device private async Task Twin_ServiceSetsDesiredPropertyAndDeviceUnsubscribes(Client.TransportType transport, object propValue) { - var propName = Guid.NewGuid().ToString(); + string propName = Guid.NewGuid().ToString(); Logger.Trace($"{nameof(Twin_ServiceSetsDesiredPropertyAndDeviceReceivesEventAsync)}: name={propName}, value={propValue}"); using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); + using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); // Set a callback await deviceClient. @@ -569,12 +569,12 @@ await RegistryManagerUpdateDesiredPropertyAsync(testDevice.Id, propName, propVal private async Task Twin_ServiceSetsDesiredPropertyAndDeviceReceivesEventAsync(Client.TransportType transport, Func> setTwinPropertyUpdateCallbackAsync, object propValue) { - var propName = Guid.NewGuid().ToString(); + string propName = Guid.NewGuid().ToString(); Logger.Trace($"{nameof(Twin_ServiceSetsDesiredPropertyAndDeviceReceivesEventAsync)}: name={propName}, value={propValue}"); using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); + using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); Task updateReceivedTask = await setTwinPropertyUpdateCallbackAsync(deviceClient, propName, propValue, Logger).ConfigureAwait(false); @@ -584,12 +584,12 @@ private async Task Twin_ServiceSetsDesiredPropertyAndDeviceReceivesEventAsync(Cl // Validate the updated twin from the device-client Twin deviceTwin = await deviceClient.GetTwinAsync().ConfigureAwait(false); - var actual = deviceTwin.Properties.Desired[propName]; + dynamic actual = deviceTwin.Properties.Desired[propName]; Assert.AreEqual(JsonConvert.SerializeObject(actual), JsonConvert.SerializeObject(propValue)); // Validate the updated twin from the service-client Twin completeTwin = await _registryManager.GetTwinAsync(testDevice.Id).ConfigureAwait(false); - var actualProp = completeTwin.Properties.Desired[propName]; + dynamic actualProp = completeTwin.Properties.Desired[propName]; Assert.AreEqual(JsonConvert.SerializeObject(actualProp), JsonConvert.SerializeObject(propValue)); await deviceClient.SetDesiredPropertyUpdateCallbackAsync(null, null).ConfigureAwait(false); @@ -598,12 +598,12 @@ private async Task Twin_ServiceSetsDesiredPropertyAndDeviceReceivesEventAsync(Cl private async Task Twin_ServiceSetsDesiredPropertyAndDeviceReceivesItOnNextGetAsync(Client.TransportType transport) { - var propName = Guid.NewGuid().ToString(); - var propValue = Guid.NewGuid().ToString(); + string propName = Guid.NewGuid().ToString(); + string propValue = Guid.NewGuid().ToString(); using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); - using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); var twinPatch = new Twin(); twinPatch.Properties.Desired[propName] = propValue; @@ -618,12 +618,12 @@ private async Task Twin_ServiceSetsDesiredPropertyAndDeviceReceivesItOnNextGetAs private async Task Twin_DeviceSetsReportedPropertyAndServiceReceivesItAsync(Client.TransportType transport) { - var propName = Guid.NewGuid().ToString(); - var propValue = Guid.NewGuid().ToString(); + string propName = Guid.NewGuid().ToString(); + string propValue = Guid.NewGuid().ToString(); using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); - using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); var patch = new TwinCollection(); patch[propName] = propValue; @@ -638,13 +638,13 @@ private async Task Twin_DeviceSetsReportedPropertyAndServiceReceivesItAsync(Clie private async Task Twin_ServiceDoesNotCreateNullPropertyInCollectionAsync(Client.TransportType transport) { - var propName1 = Guid.NewGuid().ToString(); - var propName2 = Guid.NewGuid().ToString(); - var propEmptyValue = "{}"; + string propName1 = Guid.NewGuid().ToString(); + string propName2 = Guid.NewGuid().ToString(); + string propEmptyValue = "{}"; using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); - using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); await deviceClient .UpdateReportedPropertiesAsync( @@ -690,14 +690,14 @@ await deviceClient private async Task Twin_ClientHandlesRejectionInvalidPropertyNameAsync(Client.TransportType transport) { - var propName1 = "$" + Guid.NewGuid().ToString(); - var propName2 = Guid.NewGuid().ToString(); + string propName1 = "$" + Guid.NewGuid().ToString(); + string propName2 = Guid.NewGuid().ToString(); using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); - using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); - var exceptionThrown = false; + bool exceptionThrown = false; try { await deviceClient diff --git a/e2e/test/iothub/twin/TwinFaultInjectionTests.cs b/e2e/test/iothub/twin/TwinFaultInjectionTests.cs index 0d76a46aec..eb94dd3fbc 100644 --- a/e2e/test/iothub/twin/TwinFaultInjectionTests.cs +++ b/e2e/test/iothub/twin/TwinFaultInjectionTests.cs @@ -219,12 +219,12 @@ public async Task Twin_DeviceDesiredPropertyUpdateGracefulShutdownRecovery_AmqpW TimeSpan delayInSec, string proxyAddress = null) { - var propName = Guid.NewGuid().ToString(); + string propName = Guid.NewGuid().ToString(); var props = new TwinCollection(); Func testOperation = async (deviceClient, testDevice) => { - var propValue = Guid.NewGuid().ToString(); + string propValue = Guid.NewGuid().ToString(); props[propName] = propValue; await deviceClient.UpdateReportedPropertiesAsync(props).ConfigureAwait(false); @@ -256,7 +256,7 @@ await FaultInjection private async Task RegistryManagerUpdateDesiredPropertyAsync(string deviceId, string propName, string propValue) { - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); var twinPatch = new Twin(); twinPatch.Properties.Desired[propName] = propValue; @@ -273,10 +273,10 @@ private async Task RegistryManagerUpdateDesiredPropertyAsync(string deviceId, st string proxyAddress = null) { TestDeviceCallbackHandler testDeviceCallbackHandler = null; - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); using var cts = new CancellationTokenSource(FaultInjection.RecoveryTime); - var propName = Guid.NewGuid().ToString(); + string propName = Guid.NewGuid().ToString(); var props = new TwinCollection(); // Configure the callback and start accepting twin changes. @@ -289,7 +289,7 @@ async Task InitOperationAsync(DeviceClient deviceClient, TestDevice testDevice) // Change the twin from the service side and verify the device received it. async Task TestOperationAsync(DeviceClient deviceClient, TestDevice testDevice) { - var propValue = Guid.NewGuid().ToString(); + string propValue = Guid.NewGuid().ToString(); testDeviceCallbackHandler.ExpectedTwinPropertyValue = propValue; Logger.Trace($"{nameof(Twin_DeviceDesiredPropertyUpdateRecoveryAsync)}: name={propName}, value={propValue}"); diff --git a/e2e/test/prerequisites/E2ETestsSetup/e2eTestsSetup.ps1 b/e2e/test/prerequisites/E2ETestsSetup/e2eTestsSetup.ps1 index a2f83cf513..0ac2a2e319 100644 --- a/e2e/test/prerequisites/E2ETestsSetup/e2eTestsSetup.ps1 +++ b/e2e/test/prerequisites/E2ETestsSetup/e2eTestsSetup.ps1 @@ -144,10 +144,9 @@ $certificateHashAlgorithm = "SHA256" if ($GenerateResourcesForDevOpsPipeline) { - $iothubUnitsToBeCreated = 3; + $iothubUnitsToBeCreated = 5; } - ################################################################################################# # Get Function App contents to pass to deployment ################################################################################################# @@ -208,6 +207,7 @@ $groupCertChainPath = "$PSScriptRoot/GroupCertChain.p7b"; ############################################################################################################################ # Cleanup old certs and files that can cause a conflict ############################################################################################################################ + CleanUp-Certs # Generate self signed Root and Intermediate CA cert, expiring in 2 years @@ -434,28 +434,43 @@ $instrumentationKey = az deployment group show -g $ResourceGroup -n $deploymentN $iotHubName = az deployment group show -g $ResourceGroup -n $deploymentName --query 'properties.outputs.hubName.value' --output tsv ################################################################################################################################################# -# Configure an AAD app and create self signed certs and get the bytes to generate more content info. +# Configure an AAD app to authenticate Log Analytics Workspace, if specified ################################################################################################################################################# -Write-Host "`nCreating app registration $logAnalyticsAppRegnName" -$logAnalyticsAppRegUrl = "http://$logAnalyticsAppRegnName" -$logAnalyticsAppId = az ad sp create-for-rbac -n $logAnalyticsAppRegUrl --role "Reader" --scope $resourceGroupId --query "appId" --output tsv -Write-Host "`nCreated application $logAnalyticsAppRegnName with Id $logAnalyticsAppId." + +if ($EnableIotHubSecuritySolution) +{ + Write-Host "`nCreating app registration $logAnalyticsAppRegnName" + $logAnalyticsAppRegUrl = "http://$logAnalyticsAppRegnName" + $logAnalyticsAppId = az ad sp create-for-rbac -n $logAnalyticsAppRegUrl --role "Reader" --scope $resourceGroupId --query "appId" --output tsv + Write-Host "`nCreated application $logAnalyticsAppRegnName with Id $logAnalyticsAppId." +} ################################################################################################################################################# # Configure an AAD app to perform IoT hub data actions. ################################################################################################################################################# + Write-Host "`nCreating app registration $iotHubAadTestAppRegName for IoT hub data actions" -$iotHubAadTestAppRegUrl = "http://$iotHubAadTestAppRegName" $iotHubDataContributorRoleId = "4fc6c259987e4a07842ec321cc9d413f" $iotHubScope = "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroup/providers/Microsoft.Devices/IotHubs/$iotHubName" -$iotHubAadTestAppInfo = az ad sp create-for-rbac -n $iotHubAadTestAppRegUrl --role $iotHubDataContributorRoleId --scope $iotHubScope --query '{appId:appId, password:password}' | ConvertFrom-Json -$iotHubAadTestAppPassword = $iotHubAadTestAppInfo.password +$iotHubAadTestAppInfo = az ad sp create-for-rbac -n $iotHubAadTestAppRegName --role $iotHubDataContributorRoleId --scope $iotHubScope --query '{appId:appId, password:password}' | ConvertFrom-Json + $iotHubAadTestAppId = $iotHubAadTestAppInfo.appId +$iotHubAadTestAppPassword = $iotHubAadTestAppInfo.password Write-Host "`nCreated application $iotHubAadTestAppRegName with Id $iotHubAadTestAppId." +################################################################################################################################################# +# Configure AAD app to perform DPS data actions. +################################################################################################################################################# + +Write-Host "`nGiving app registration $iotHubAadTestAppRegName data contributor permission on DPS instance $dpsName" +$dpsContributorId = "dfce44e4-17b7-4bd1-a6d1-04996ec95633" +$dpsScope = "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroup/providers/Microsoft.Devices/ProvisioningServices/$dpsName" +az role assignment create --role $dpsContributorId --assignee $iotHubAadTestAppId --scope $dpsScope + ################################################################################################################################################# # Add role assignement for User assinged managed identity to be able to perform import and export jobs on the IoT hub. ################################################################################################################################################# + Write-Host "`nGranting the user assigned managed identity $managedIdentityName Storage Blob Data Contributor permissions on resource group: $ResourceGroup." $msiPrincipalId = az identity show -n $managedIdentityName -g $ResourceGroup --query principalId --output tsv $msiResourceId = "/subscriptions/$SubscriptionId/resourceGroups/$ResourceGroup/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$managedIdentityName" @@ -464,6 +479,7 @@ az role assignment create --assignee $msiPrincipalId --role 'Storage Blob Data C ################################################################################################################################## # Granting the IoT hub system identity storage blob contributor access on the resoruce group ################################################################################################################################## + Write-Host "`nGranting the system identity on the hub $iotHubName Storage Blob Data Contributor permissions on resource group: $ResourceGroup." $systemIdentityPrincipal = az resource list -n $iotHubName --query [0].identity.principalId --out tsv az role assignment create --assignee $systemIdentityPrincipal --role "Storage Blob Data Contributor" --scope $resourceGroupId --output none @@ -472,8 +488,8 @@ az role assignment create --assignee $systemIdentityPrincipal --role "Storage Bl # Uploading root CA certificate to IoT hub and verifying ################################################################################################################################## -$certExits = az iot hub certificate list -g $ResourceGroup --hub-name $iotHubName --query "value[?name=='$hubUploadCertificateName']" --output tsv -if ($certExits) +$certExists = az iot hub certificate list -g $ResourceGroup --hub-name $iotHubName --query "value[?name=='$hubUploadCertificateName']" --output tsv +if ($certExists) { Write-Host "`nDeleting existing certificate from IoT hub." $etag = az iot hub certificate show -g $ResourceGroup --hub-name $iotHubName --name $hubUploadCertificateName --query 'etag' @@ -505,6 +521,7 @@ if ($isVerified -eq 'false') ################################################################################################################################## # Fetch the iothubowner policy details ################################################################################################################################## + $iothubownerSasPolicy = "iothubowner" $iothubownerSasPrimaryKey = az iot hub policy show --hub-name $iotHubName --name $iothubownerSasPolicy --query 'primaryKey' @@ -523,6 +540,7 @@ if (-not $iotHubCertChainDevice) ################################################################################################################################## # Create the IoT devices and modules that are used by the .NET samples ################################################################################################################################## + $iotHubSasBasedDeviceId = "DoNotDeleteDevice1" $iotHubSasBasedDevice = az iot hub device-identity list -g $ResourceGroup --hub-name $iotHubName --query "[?deviceId=='$iotHubSasBasedDeviceId'].deviceId" --output tsv @@ -584,8 +602,8 @@ $symmetricKeySampleEnrollmentPrimaryKey = az iot dps enrollment show -g $Resourc ################################################################################################################################## $dpsIdScope = az iot dps show -g $ResourceGroup --name $dpsName --query 'properties.idScope' --output tsv -$certExits = az iot dps certificate list -g $ResourceGroup --dps-name $dpsName --query "value[?name=='$uploadCertificateName']" --output tsv -if ($certExits) +$certExists = az iot dps certificate list -g $ResourceGroup --dps-name $dpsName --query "value[?name=='$uploadCertificateName']" --output tsv +if ($certExists) { Write-Host "`nDeleting existing certificate from DPS." $etag = az iot dps certificate show -g $ResourceGroup --dps-name $dpsName --certificate-name $uploadCertificateName --query 'etag' @@ -642,90 +660,109 @@ az iot dps enrollment create ` --certificate-path $individualDeviceCertPath ` --output none -Write-Host "`nCreating a self-signed certificate and placing it in $ResourceGroup." -az ad app credential reset --id $logAnalyticsAppId --create-cert --keyvault $keyVaultName --cert $ResourceGroup --output none -Write-Host "`nSuccessfully created a self signed certificate for your application $logAnalyticsAppRegnName in $keyVaultName key vault with cert name $ResourceGroup." - -Write-Host "`nFetching the certificate binary." -$selfSignedCerts = "$PSScriptRoot\selfSignedCerts" -if (Test-Path $selfSignedCerts -PathType Leaf) +if ($EnableIotHubSecuritySolution) { - Remove-Item -r $selfSignedCerts -} + Write-Host "`nCreating a self-signed certificate for LA and placing it in $ResourceGroup." + az ad app credential reset --id $logAnalyticsAppId --create-cert --keyvault $keyVaultName --cert $ResourceGroup --output none + Write-Host "`nSuccessfully created a self signed certificate for your application $logAnalyticsAppRegnName in $keyVaultName key vault with cert name $ResourceGroup." -az keyvault secret download --file $selfSignedCerts --vault-name $keyVaultName -n $ResourceGroup --encoding base64 -$fileContent = Get-Content $selfSignedCerts -AsByteStream -$fileContentB64String = [System.Convert]::ToBase64String($fileContent); + Write-Host "`nFetching the certificate binary for LA." + $selfSignedCerts = "$PSScriptRoot\selfSignedCerts" + if (Test-Path $selfSignedCerts -PathType Leaf) + { + Remove-Item -r $selfSignedCerts + } + + az keyvault secret download --file $selfSignedCerts --vault-name $keyVaultName -n $ResourceGroup --encoding base64 + $fileContent = Get-Content $selfSignedCerts -AsByteStream + $fileContentB64String = [System.Convert]::ToBase64String($fileContent); -Write-Host "`nSuccessfully fetched the certificate bytes. Removing the cert file from the disk." -Remove-Item -r $selfSignedCerts + Write-Host "`nSuccessfully fetched the certificate bytes for LA. Removing the cert file from the disk." + Remove-Item -r $selfSignedCerts +} ################################################################################################################################### # Store all secrets in a KeyVault - Values will be pulled down from here to configure environment variables ################################################################################################################################### -Write-Host "`nWriting secrets to KeyVault $keyVaultName." -az keyvault set-policy -g $ResourceGroup --name $keyVaultName --object-id $userObjectId --secret-permissions delete get list set --output none -az keyvault secret set --vault-name $keyVaultName --name "IOTHUB-CONNECTION-STRING" --value $iotHubConnectionString --output none -az keyvault secret set --vault-name $keyVaultName --name "IOTHUB-PFX-X509-THUMBPRINT" --value $iotHubThumbprint --output none -az keyvault secret set --vault-name $keyVaultName --name "IOTHUB-PROXY-SERVER-ADDRESS" --value $proxyServerAddress --output none -az keyvault secret set --vault-name $keyVaultName --name "FAR-AWAY-IOTHUB-HOSTNAME" --value $farHubHostName --output none -az keyvault secret set --vault-name $keyVaultName --name "DPS-IDSCOPE" --value $dpsIdScope --output none -az keyvault secret set --vault-name $keyVaultName --name "PROVISIONING-CONNECTION-STRING" --value $dpsConnectionString --output none -az keyvault secret set --vault-name $keyVaultName --name "CUSTOM-ALLOCATION-POLICY-WEBHOOK" --value $customAllocationPolicyWebhook --output none - $dpsEndpoint = "global.azure-devices-provisioning.net" if ($Region.EndsWith('euap', 'CurrentCultureIgnoreCase')) { $dpsEndpoint = "global-canary.azure-devices-provisioning.net" } -az keyvault secret set --vault-name $keyVaultName --name "DPS-GLOBALDEVICEENDPOINT" --value $dpsEndpoint --output none - -az keyvault secret set --vault-name $keyVaultName --name "DPS-X509-PFX-CERTIFICATE-PASSWORD" --value $dpsX509PfxCertificatePassword --output none -az keyvault secret set --vault-name $keyVaultName --name "IOTHUB-X509-PFX-CERTIFICATE" --value $iothubX509PfxCertificate --output none -az keyvault secret set --vault-name $keyVaultName --name "DPS-INDIVIDUALX509-PFX-CERTIFICATE" --value $dpsIndividualX509PfxCertificate --output none -az keyvault secret set --vault-name $keyVaultName --name "DPS-GROUPX509-PFX-CERTIFICATE" --value $dpsGroupX509PfxCertificate --output none -az keyvault secret set --vault-name $keyVaultName --name "DPS-GROUPX509-CERTIFICATE-CHAIN" --value $dpsGroupX509CertificateChain --output none -az keyvault secret set --vault-name $keyVaultName --name "STORAGE-ACCOUNT-CONNECTION-STRING" --value $storageAccountConnectionString --output none -az keyvault secret set --vault-name $keyVaultName --name "LA-WORKSPACE-ID" --value $workspaceId --output none -az keyvault secret set --vault-name $keyVaultName --name "MSFT-TENANT-ID" --value "72f988bf-86f1-41af-91ab-2d7cd011db47" --output none -az keyvault secret set --vault-name $keyVaultName --name "LA-AAD-APP-ID" --value $logAnalyticsAppId --output none -az keyvault secret set --vault-name $keyVaultName --name "IOTHUB-CLIENT-ID" --value $iotHubAadTestAppId --output none -az keyvault secret set --vault-name $keyVaultName --name "IOTHUB-CLIENT-SECRET" --value $iotHubAadTestAppPassword --output none -az keyvault secret set --vault-name $keyVaultName --name "LA-AAD-APP-CERT-BASE64" --value $fileContentB64String --output none -az keyvault secret set --vault-name $keyVaultName --name "DPS-GLOBALDEVICEENDPOINT-INVALIDCERT" --value "invalidcertgde1.westus.cloudapp.azure.com" --output none -az keyvault secret set --vault-name $keyVaultName --name "PIPELINE-ENVIRONMENT" --value "prod" --output none -az keyvault secret set --vault-name $keyVaultName --name "HUB-CHAIN-DEVICE-PFX-CERTIFICATE" --value $iothubX509ChainDevicePfxCertificate --output none -az keyvault secret set --vault-name $keyVaultName --name "HUB-CHAIN-ROOT-CA-CERTIFICATE" --value $iothubX509RootCACertificate --output none -az keyvault secret set --vault-name $keyVaultName --name "HUB-CHAIN-INTERMEDIATE1-CERTIFICATE" --value $iothubX509Intermediate1Certificate --output none -az keyvault secret set --vault-name $keyVaultName --name "HUB-CHAIN-INTERMEDIATE2-CERTIFICATE" --value $iothubX509Intermediate2Certificate --output none -az keyvault secret set --vault-name $keyVaultName --name "IOTHUB-X509-CHAIN-DEVICE-NAME" --value $iotHubCertChainDeviceCommonName --output none -az keyvault secret set --vault-name $keyVaultName --name "IOTHUB-USER-ASSIGNED-MSI-RESOURCE-ID" --value $msiResourceId --output none - -# These environment variables are only used in Java -az keyvault secret set --vault-name $keyVaultName --name "IOT-DPS-CONNECTION-STRING" --value $dpsConnectionString --output none # DPS Connection string Environment variable for Java -az keyvault secret set --vault-name $keyVaultName --name "IOT-DPS-ID-SCOPE" --value $dpsIdScope --output none # DPS ID Scope Environment variable for Java -az keyvault secret set --vault-name $keyVaultName --name "FAR-AWAY-IOTHUB-CONNECTION-STRING" --value $farHubConnectionString --output none -az keyvault secret set --vault-name $keyVaultName --name "IS-BASIC-TIER-HUB" --value "false" --output none - -<#[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="fake shared access token")]#> -az keyvault secret set --vault-name $keyVaultName --name "IOTHUB-DEVICE-CONN-STRING-INVALIDCERT" --value "HostName=invalidcertiothub1.westus.cloudapp.azure.com;DeviceId=DoNotDelete1;SharedAccessKey=zWmeTGWmjcgDG1dpuSCVjc5ZY4TqVnKso5+g1wt/K3E=" --output none -<#[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="fake shared access token")]#> -az keyvault secret set --vault-name $keyVaultName --name "IOTHUB-CONN-STRING-INVALIDCERT" --value "HostName=invalidcertiothub1.westus.cloudapp.azure.com;SharedAccessKeyName=iothubowner;SharedAccessKey=Fk1H0asPeeAwlRkUMTybJasksTYTd13cgI7SsteB05U=" --output none -<#[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="fake shared access token")]#> -az keyvault secret set --vault-name $keyVaultName --name "PROVISIONING-CONNECTION-STRING-INVALIDCERT" --value "HostName=invalidcertdps1.westus.cloudapp.azure.com;SharedAccessKeyName=provisioningserviceowner;SharedAccessKey=lGO7OlXNhXlFyYV1rh9F/lUCQC1Owuh5f/1P0I1AFSY=" --output none - -az keyvault secret set --vault-name $keyVaultName --name "E2E-IKEY" --value $instrumentationKey --output none - -# These environment variables are used by .NET samples -az keyvault secret set --vault-name $keyVaultName --name "IOTHUB-DEVICE-CONN-STRING" --value $iotHubSasBasedDeviceConnectionString --output none -az keyvault secret set --vault-name $keyVaultName --name "IOTHUB-MODULE-CONN-STRING" --value $iotHubSasBasedModuleConnectionString --output none -az keyvault secret set --vault-name $keyVaultName --name "PNP-TC-DEVICE-CONN-STRING" --value $temperatureControllerSampleDeviceConnectionString --output none -az keyvault secret set --vault-name $keyVaultName --name "PNP-THERMOSTAT-DEVICE-CONN-STRING" --value $thermostatSampleDeviceConnectionString --output none -az keyvault secret set --vault-name $keyVaultName --name "IOTHUB-SAS-KEY" --value $iothubownerSasPrimaryKey --output none -az keyvault secret set --vault-name $keyVaultName --name "IOTHUB-SAS-KEY-NAME" --value $iothubownerSasPolicy --output none -az keyvault secret set --vault-name $keyVaultName --name "DPS-SYMMETRIC-KEY-INDIVIDUAL-ENROLLMENT-REGISTRATION-ID" --value $symmetricKeySampleEnrollmentRegistrationId --output none -az keyvault secret set --vault-name $keyVaultName --name "DPS-SYMMETRIC-KEY-INDIVIDUAL-ENROLLEMNT-PRIMARY-KEY" --value $symmetricKeySampleEnrollmentPrimaryKey --output none + +$keyvaultKvps = @{ + "IOTHUB-CONNECTION-STRING" = $iotHubConnectionString; + "IOTHUB-PFX-X509-THUMBPRINT" = $iotHubThumbprint; + "IOTHUB-PROXY-SERVER-ADDRESS" = $proxyServerAddress; + "FAR-AWAY-IOTHUB-HOSTNAME" = $farHubHostName; + "DPS-IDSCOPE" = $dpsIdScope; + "PROVISIONING-CONNECTION-STRING" = $dpsConnectionString; + "CUSTOM-ALLOCATION-POLICY-WEBHOOK" = $customAllocationPolicyWebhook; + "DPS-GLOBALDEVICEENDPOINT" = $dpsEndpoint; + "DPS-X509-PFX-CERTIFICATE-PASSWORD" = $dpsX509PfxCertificatePassword; + "IOTHUB-X509-PFX-CERTIFICATE" = $iothubX509PfxCertificate; + "DPS-INDIVIDUALX509-PFX-CERTIFICATE" = $dpsIndividualX509PfxCertificate; + "DPS-GROUPX509-PFX-CERTIFICATE" = $dpsGroupX509PfxCertificate; + "DPS-GROUPX509-CERTIFICATE-CHAIN" = $dpsGroupX509CertificateChain; + "STORAGE-ACCOUNT-CONNECTION-STRING" = $storageAccountConnectionString; + "MSFT-TENANT-ID" = "72f988bf-86f1-41af-91ab-2d7cd011db47"; + "IOTHUB-CLIENT-ID" = $iotHubAadTestAppId; + "IOTHUB-CLIENT-SECRET" = $iotHubAadTestAppPassword; + "DPS-GLOBALDEVICEENDPOINT-INVALIDCERT" = "invalidcertgde1.westus.cloudapp.azure.com"; + "PIPELINE-ENVIRONMENT" = "prod"; + "HUB-CHAIN-DEVICE-PFX-CERTIFICATE" = $iothubX509ChainDevicePfxCertificate; + "HUB-CHAIN-ROOT-CA-CERTIFICATE" = $iothubX509RootCACertificate; + "HUB-CHAIN-INTERMEDIATE1-CERTIFICATE" = $iothubX509Intermediate1Certificate; + "HUB-CHAIN-INTERMEDIATE2-CERTIFICATE" = $iothubX509Intermediate2Certificate; + "IOTHUB-X509-CHAIN-DEVICE-NAME" = $iotHubCertChainDeviceCommonName; + "IOTHUB-USER-ASSIGNED-MSI-RESOURCE-ID" = $msiResourceId; + "E2E-IKEY" = $instrumentationKey; + + <#[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="fake shared access token")]#> + "IOTHUB-DEVICE-CONN-STRING-INVALIDCERT" = "HostName=invalidcertiothub1.westus.cloudapp.azure.com;DeviceId=DoNotDelete1;SharedAccessKey=zWmeTGWmjcgDG1dpuSCVjc5ZY4TqVnKso5+g1wt/K3E="; + <#[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="fake shared access token")]#> + "IOTHUB-CONN-STRING-INVALIDCERT" = "HostName=invalidcertiothub1.westus.cloudapp.azure.com;SharedAccessKeyName=iothubowner;SharedAccessKey=Fk1H0asPeeAwlRkUMTybJasksTYTd13cgI7SsteB05U="; + <#[SuppressMessage("Microsoft.Security", "CS002:SecretInNextLine", Justification="fake shared access token")]#> + "PROVISIONING-CONNECTION-STRING-INVALIDCERT" = "HostName=invalidcertdps1.westus.cloudapp.azure.com;SharedAccessKeyName=provisioningserviceowner;SharedAccessKey=lGO7OlXNhXlFyYV1rh9F/lUCQC1Owuh5f/1P0I1AFSY="; + + # These environment variables are only used in Java + + "IOT-DPS-CONNECTION-STRING" = $dpsConnectionString; # DPS Connection string Environment variable for Java + "IOT-DPS-ID-SCOPE" = $dpsIdScope; # DPS ID Scope Environment variable for Java + "FAR-AWAY-IOTHUB-CONNECTION-STRING" = $farHubConnectionString; + "IS-BASIC-TIER-HUB" = "false"; + + # These environment variables are used by .NET samples + + "IOTHUB-DEVICE-CONN-STRING" = $iotHubSasBasedDeviceConnectionString; + "IOTHUB-MODULE-CONN-STRING" = $iotHubSasBasedModuleConnectionString; + "PNP-TC-DEVICE-CONN-STRING" = $temperatureControllerSampleDeviceConnectionString; + "PNP-THERMOSTAT-DEVICE-CONN-STRING" = $thermostatSampleDeviceConnectionString; + "IOTHUB-SAS-KEY" = $iothubownerSasPrimaryKey; + "IOTHUB-SAS-KEY-NAME" = $iothubownerSasPolicy; + "DPS-SYMMETRIC-KEY-INDIVIDUAL-ENROLLMENT-REGISTRATION-ID" = $symmetricKeySampleEnrollmentRegistrationId; + "DPS-SYMMETRIC-KEY-INDIVIDUAL-ENROLLEMNT-PRIMARY-KEY" = $symmetricKeySampleEnrollmentPrimaryKey; +} + +if ($EnableIotHubSecuritySolution) +{ + $keyvaultKvps.Add("LA-WORKSPACE-ID", $workspaceId) + $keyvaultKvps.Add("LA-AAD-APP-CERT-BASE64", $fileContentB64String) + $keyvaultKvps.Add("LA-AAD-APP-ID", $logAnalyticsAppId) +} + +Write-Host "`nWriting secrets to KeyVault $keyVaultName." +az keyvault set-policy -g $ResourceGroup --name $keyVaultName --object-id "$userObjectId" --output none --show-only-errors --secret-permissions delete get list set; +foreach ($kvp in $keyvaultKvps.GetEnumerator()) +{ + Write-Host "`tWriting $($kvp.Name)." + if ($null -eq $kvp.Value) + { + Write-Warning "`t`tValue is unexpectedly null!"; + } + az keyvault secret set --vault-name $keyVaultName --name $kvp.Name --value "$($kvp.Value)" --output none --only-show-errors +} ################################################################################################################################### # Run docker containers for TPM simulators and proxy @@ -746,6 +783,7 @@ if (-not (docker images -q aziotbld/testproxy)) ############################################################################################################################ # Clean up certs and files created by the script ############################################################################################################################ + CleanUp-Certs # Creating a file to run to load environment variables @@ -758,6 +796,7 @@ Add-Content -Path $file.PSPath -Value "$PSScriptRoot\LoadEnvironmentVariablesFro ############################################################################################################################ # Configure environment variables ############################################################################################################################ + Invoke-Expression "$loadScriptDir\$loadScriptName" $endTime = (Get-Date) diff --git a/e2e/test/provisioning/ProvisioningCertificateValidationE2ETest.cs b/e2e/test/provisioning/ProvisioningCertificateValidationE2ETest.cs index 31d790610c..ca3afe1125 100644 --- a/e2e/test/provisioning/ProvisioningCertificateValidationE2ETest.cs +++ b/e2e/test/provisioning/ProvisioningCertificateValidationE2ETest.cs @@ -20,12 +20,12 @@ public class ProvisioningCertificateValidationE2ETest : E2EMsTestBase [LoggedTestMethod] public async Task ProvisioningServiceClient_QueryInvalidServiceCertificateHttp_Fails() { - using ProvisioningServiceClient provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString( + using var provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString( TestConfiguration.Provisioning.ConnectionStringInvalidServiceCertificate); Query q = provisioningServiceClient.CreateEnrollmentGroupQuery( new QuerySpecification("SELECT * FROM enrollmentGroups")); - var exception = await Assert.ThrowsExceptionAsync( + ProvisioningServiceClientTransportException exception = await Assert.ThrowsExceptionAsync( () => q.NextAsync()).ConfigureAwait(false); #if NET472 || NET451 @@ -39,7 +39,7 @@ public async Task ProvisioningServiceClient_QueryInvalidServiceCertificateHttp_F public async Task ProvisioningDeviceClient_RegisterAsyncInvalidServiceCertificateAmqpTcp_Fails() { using var transport = new ProvisioningTransportHandlerAmqp(TransportFallbackType.TcpOnly); - var exception = await Assert.ThrowsExceptionAsync( + ProvisioningTransportException exception = await Assert.ThrowsExceptionAsync( () => TestInvalidServiceCertificate(transport)).ConfigureAwait(false); Assert.IsInstanceOfType(exception.InnerException, typeof(AuthenticationException)); @@ -49,7 +49,7 @@ public async Task ProvisioningDeviceClient_RegisterAsyncInvalidServiceCertificat public async Task ProvisioningDeviceClient_RegisterAsyncInvalidServiceCertificateMqttTcp_Fails() { using var transport = new ProvisioningTransportHandlerMqtt(TransportFallbackType.TcpOnly); - var exception = await Assert.ThrowsExceptionAsync( + ProvisioningTransportException exception = await Assert.ThrowsExceptionAsync( () => TestInvalidServiceCertificate(transport)).ConfigureAwait(false); if (exception.InnerException == null) @@ -66,7 +66,7 @@ public async Task ProvisioningDeviceClient_RegisterAsyncInvalidServiceCertificat public async Task ProvisioningDeviceClient_RegisterAsyncInvalidServiceCertificateHttp_Fails() { using var transport = new ProvisioningTransportHandlerHttp(); - var exception = await Assert.ThrowsExceptionAsync( + ProvisioningTransportException exception = await Assert.ThrowsExceptionAsync( () => TestInvalidServiceCertificate(transport)).ConfigureAwait(false); #if NET472 || NET451 @@ -80,7 +80,7 @@ public async Task ProvisioningDeviceClient_RegisterAsyncInvalidServiceCertificat public async Task ProvisioningDeviceClient_RegisterAsyncInvalidServiceCertificateAmqpWs_Fails() { using var transport = new ProvisioningTransportHandlerAmqp(TransportFallbackType.WebSocketOnly); - var exception = await Assert.ThrowsExceptionAsync( + ProvisioningTransportException exception = await Assert.ThrowsExceptionAsync( () => TestInvalidServiceCertificate(transport)).ConfigureAwait(false); Assert.IsInstanceOfType(exception.InnerException.InnerException.InnerException, typeof(AuthenticationException)); @@ -90,7 +90,7 @@ public async Task ProvisioningDeviceClient_RegisterAsyncInvalidServiceCertificat public async Task ProvisioningDeviceClient_RegisterAsyncInvalidServiceCertificateMqttWs_Fails() { using var transport = new ProvisioningTransportHandlerMqtt(TransportFallbackType.WebSocketOnly); - var exception = await Assert.ThrowsExceptionAsync( + ProvisioningTransportException exception = await Assert.ThrowsExceptionAsync( () => TestInvalidServiceCertificate(transport)).ConfigureAwait(false); Assert.IsInstanceOfType(exception.InnerException.InnerException.InnerException, typeof(AuthenticationException)); @@ -100,7 +100,7 @@ private static async Task TestInvalidServiceCertificate(ProvisioningTransportHan { using X509Certificate2 cert = TestConfiguration.Provisioning.GetIndividualEnrollmentCertificate(); using var security = new SecurityProviderX509Certificate(cert); - ProvisioningDeviceClient provisioningDeviceClient = ProvisioningDeviceClient.Create( + var provisioningDeviceClient = ProvisioningDeviceClient.Create( TestConfiguration.Provisioning.GlobalDeviceEndpointInvalidServiceCertificate, "0ne00000001", security, diff --git a/e2e/test/provisioning/ProvisioningE2ETests.cs b/e2e/test/provisioning/ProvisioningE2ETests.cs index 5f56f48c22..61c73f1698 100644 --- a/e2e/test/provisioning/ProvisioningE2ETests.cs +++ b/e2e/test/provisioning/ProvisioningE2ETests.cs @@ -4,14 +4,12 @@ using System; using System.Collections.Generic; using System.Net; -using System.Net.Sockets; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Devices.Client; -using Microsoft.Azure.Devices.E2ETests.Helpers; using Microsoft.Azure.Devices.Provisioning.Client; using Microsoft.Azure.Devices.Provisioning.Client.Transport; using Microsoft.Azure.Devices.Provisioning.Security.Samples; @@ -559,7 +557,7 @@ public async Task ProvisioningDeviceClient_ValidRegistrationId_TimeSpanTimeoutRe transport.Proxy = (proxyServerAddress != null) ? new WebProxy(s_proxyServerAddress) : null; } - ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create( + var provClient = ProvisioningDeviceClient.Create( s_globalDeviceEndpoint, TestConfiguration.Provisioning.IdScope, security, @@ -577,14 +575,9 @@ public async Task ProvisioningDeviceClient_ValidRegistrationId_TimeSpanTimeoutRe { try { - if (timeout != TimeSpan.MaxValue) - { - result = await provClient.RegisterAsync(timeout).ConfigureAwait(false); - } - else - { - result = await provClient.RegisterAsync(cts.Token).ConfigureAwait(false); - } + result = timeout != TimeSpan.MaxValue + ? await provClient.RegisterAsync(timeout).ConfigureAwait(false) + : await provClient.RegisterAsync(cts.Token).ConfigureAwait(false); break; } // Catching all ProvisioningTransportException as the status code is not the same for Mqtt, Amqp and Http. @@ -653,7 +646,7 @@ public async Task ProvisioningDeviceClient_ValidRegistrationId_TimeSpanTimeoutRe transport.Proxy = (proxyServerAddress != null) ? new WebProxy(s_proxyServerAddress) : null; } - ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create( + var provClient = ProvisioningDeviceClient.Create( s_globalDeviceEndpoint, TestConfiguration.Provisioning.IdScope, security, @@ -720,7 +713,7 @@ public async Task ProvisioningDeviceClient_InvalidRegistrationId_TpmRegister_Fai { using ProvisioningTransportHandler transport = CreateTransportHandlerFromName(transportProtocol); using SecurityProvider security = new SecurityProviderTpmSimulator("invalidregistrationid"); - ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create( + var provClient = ProvisioningDeviceClient.Create( s_globalDeviceEndpoint, TestConfiguration.Provisioning.IdScope, security, @@ -829,7 +822,7 @@ public async Task ProvisioningDeviceClient_InvalidIdScope_Register_MqttWs_X509Gr { using ProvisioningTransportHandler transport = CreateTransportHandlerFromName(transportProtocol); using SecurityProvider security = await CreateSecurityProviderFromNameAsync(attestationType, enrollmentType, groupId, null, AllocationPolicy.Hashed, null, null).ConfigureAwait(false); - ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create( + var provClient = ProvisioningDeviceClient.Create( s_globalDeviceEndpoint, InvalidIdScope, security, @@ -837,7 +830,7 @@ public async Task ProvisioningDeviceClient_InvalidIdScope_Register_MqttWs_X509Gr using var cts = new CancellationTokenSource(FailingTimeoutMiliseconds); - var exception = await Assert.ThrowsExceptionAsync( + ProvisioningTransportException exception = await Assert.ThrowsExceptionAsync( () => provClient.RegisterAsync(cts.Token)).ConfigureAwait(false); Logger.Trace($"Exception: {exception}"); @@ -887,7 +880,7 @@ public async Task ProvisioningDeviceClient_InvalidGlobalAddress_Register_MqttWs_ using ProvisioningTransportHandler transport = CreateTransportHandlerFromName(transportProtocol); using SecurityProvider security = await CreateSecurityProviderFromNameAsync(attestationType, enrollmentType, groupId, null, AllocationPolicy.Hashed, null, null).ConfigureAwait(false); - ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create( + var provClient = ProvisioningDeviceClient.Create( InvalidGlobalAddress, TestConfiguration.Provisioning.IdScope, security, @@ -936,7 +929,7 @@ public static ProvisioningTransportHandler CreateTransportHandlerFromName(Client /// CreateSecurityProviderFromNameAsync(Attesta { _verboseLog.WriteLine($"{nameof(CreateSecurityProviderFromNameAsync)}({attestationType})"); - using ProvisioningServiceClient provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString(TestConfiguration.Provisioning.ConnectionString); + using var provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString(TestConfiguration.Provisioning.ConnectionString); switch (attestationType) { @@ -1016,7 +1009,7 @@ private async Task CreateSecurityProviderFromNameAsync(Attesta case EnrollmentType.Group: EnrollmentGroup symmetricKeyEnrollmentGroup = await CreateEnrollmentGroup(provisioningServiceClient, AttestationMechanismType.SymmetricKey, groupId, reprovisionPolicy, allocationPolicy, customAllocationDefinition, iothubs, capabilities).ConfigureAwait(false); Assert.IsTrue(symmetricKeyEnrollmentGroup.Attestation is SymmetricKeyAttestation); - SymmetricKeyAttestation symmetricKeyAttestation = (SymmetricKeyAttestation)symmetricKeyEnrollmentGroup.Attestation; + var symmetricKeyAttestation = (SymmetricKeyAttestation)symmetricKeyEnrollmentGroup.Attestation; string registrationIdSymmetricKey = _idPrefix + Guid.NewGuid(); string primaryKeyEnrollmentGroup = symmetricKeyAttestation.PrimaryKey; string secondaryKeyEnrollmentGroup = symmetricKeyAttestation.SecondaryKey; diff --git a/e2e/test/provisioning/ProvisioningServiceClientE2ETests.cs b/e2e/test/provisioning/ProvisioningServiceClientE2ETests.cs index 3757ae6825..1ada9be022 100644 --- a/e2e/test/provisioning/ProvisioningServiceClientE2ETests.cs +++ b/e2e/test/provisioning/ProvisioningServiceClientE2ETests.cs @@ -76,7 +76,7 @@ public async Task ProvisioningServiceClient_SymmetricKey_GroupEnrollments_Create //This webhook won't actually work for reprovisioning, but this test is only testing that the field is accepted by the service var customAllocationDefinition = new CustomAllocationDefinition { ApiVersion = "2019-03-31", WebhookUrl = "https://www.microsoft.com" }; var reprovisionPolicy = new ReprovisionPolicy { MigrateDeviceData = false, UpdateHubAssignment = true }; - var allocationPolicy = AllocationPolicy.GeoLatency; + AllocationPolicy allocationPolicy = AllocationPolicy.GeoLatency; await ProvisioningServiceClient_GroupEnrollments_Create_Ok("", AttestationMechanismType.SymmetricKey, reprovisionPolicy, allocationPolicy, customAllocationDefinition, null).ConfigureAwait(false); } @@ -112,7 +112,7 @@ public async Task ProvisioningServiceClient_GetEnrollmentGroupAttestation_Symmet public async Task ProvisioningServiceClient_GetIndividualEnrollmentAttestation(AttestationMechanismType attestationType) { - using ProvisioningServiceClient provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString(TestConfiguration.Provisioning.ConnectionString); + using var provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString(TestConfiguration.Provisioning.ConnectionString); IndividualEnrollment individualEnrollment = await CreateIndividualEnrollment(provisioningServiceClient, attestationType, null, AllocationPolicy.Static, null, null, null); AttestationMechanism attestationMechanism = await provisioningServiceClient.GetIndividualEnrollmentAttestationAsync(individualEnrollment.RegistrationId); @@ -120,21 +120,21 @@ public async Task ProvisioningServiceClient_GetIndividualEnrollmentAttestation(A if (attestationType == AttestationMechanismType.SymmetricKey) { Assert.AreEqual(AttestationMechanismType.SymmetricKey, attestationMechanism.Type); - SymmetricKeyAttestation symmetricKeyAttestation = (SymmetricKeyAttestation)attestationMechanism.GetAttestation(); + var symmetricKeyAttestation = (SymmetricKeyAttestation)attestationMechanism.GetAttestation(); Assert.AreEqual(((SymmetricKeyAttestation)individualEnrollment.Attestation).PrimaryKey, symmetricKeyAttestation.PrimaryKey); Assert.AreEqual(((SymmetricKeyAttestation)individualEnrollment.Attestation).SecondaryKey, symmetricKeyAttestation.SecondaryKey); } else if (attestationType == AttestationMechanismType.X509) { Assert.AreEqual(AttestationMechanismType.X509, attestationMechanism.Type); - X509Attestation x509Attestation = (X509Attestation)attestationMechanism.GetAttestation(); + var x509Attestation = (X509Attestation)attestationMechanism.GetAttestation(); Assert.AreEqual(((X509Attestation)individualEnrollment.Attestation).GetPrimaryX509CertificateInfo().SHA1Thumbprint, x509Attestation.GetPrimaryX509CertificateInfo().SHA1Thumbprint); Assert.AreEqual(((X509Attestation)individualEnrollment.Attestation).GetSecondaryX509CertificateInfo().SHA1Thumbprint, x509Attestation.GetSecondaryX509CertificateInfo().SHA1Thumbprint); } else { Assert.AreEqual(AttestationMechanismType.Tpm, attestationMechanism.Type); - TpmAttestation tpmAttestation = (TpmAttestation)attestationMechanism.GetAttestation(); + var tpmAttestation = (TpmAttestation)attestationMechanism.GetAttestation(); Assert.AreEqual(((TpmAttestation)individualEnrollment.Attestation).EndorsementKey, tpmAttestation.EndorsementKey); Assert.AreEqual(((TpmAttestation)individualEnrollment.Attestation).StorageRootKey, tpmAttestation.StorageRootKey); } @@ -142,7 +142,7 @@ public async Task ProvisioningServiceClient_GetIndividualEnrollmentAttestation(A public async Task ProvisioningServiceClient_GetEnrollmentGroupAttestation(AttestationMechanismType attestationType) { - using ProvisioningServiceClient provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString(TestConfiguration.Provisioning.ConnectionString); + using var provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString(TestConfiguration.Provisioning.ConnectionString); string groupId = AttestationTypeToString(attestationType) + "-" + Guid.NewGuid(); EnrollmentGroup enrollmentGroup = await CreateEnrollmentGroup(provisioningServiceClient, attestationType, groupId, null, AllocationPolicy.Static, null, null, null); @@ -152,14 +152,14 @@ public async Task ProvisioningServiceClient_GetEnrollmentGroupAttestation(Attest if (attestationType == AttestationMechanismType.SymmetricKey) { Assert.AreEqual(AttestationMechanismType.SymmetricKey, attestationMechanism.Type); - SymmetricKeyAttestation symmetricKeyAttestation = (SymmetricKeyAttestation)attestationMechanism.GetAttestation(); + var symmetricKeyAttestation = (SymmetricKeyAttestation)attestationMechanism.GetAttestation(); Assert.AreEqual(((SymmetricKeyAttestation)enrollmentGroup.Attestation).PrimaryKey, symmetricKeyAttestation.PrimaryKey); Assert.AreEqual(((SymmetricKeyAttestation)enrollmentGroup.Attestation).SecondaryKey, symmetricKeyAttestation.SecondaryKey); } else if (attestationType == AttestationMechanismType.X509) { Assert.AreEqual(AttestationMechanismType.X509, attestationMechanism.Type); - X509Attestation x509Attestation = (X509Attestation)attestationMechanism.GetAttestation(); + var x509Attestation = (X509Attestation)attestationMechanism.GetAttestation(); Assert.AreEqual(((X509Attestation)enrollmentGroup.Attestation).GetPrimaryX509CertificateInfo().SHA1Thumbprint, x509Attestation.GetPrimaryX509CertificateInfo().SHA1Thumbprint); Assert.AreEqual(((X509Attestation)enrollmentGroup.Attestation).GetSecondaryX509CertificateInfo().SHA1Thumbprint, x509Attestation.GetSecondaryX509CertificateInfo().SHA1Thumbprint); } @@ -266,7 +266,7 @@ public static async Task CreateIndividualEnrollment(Provis using (var tpmSim = new SecurityProviderTpmSimulator(registrationId)) { string base64Ek = Convert.ToBase64String(tpmSim.GetEndorsementKey()); - using ProvisioningServiceClient provisioningService = ProvisioningServiceClient.CreateFromConnectionString(TestConfiguration.Provisioning.ConnectionString); + using var provisioningService = ProvisioningServiceClient.CreateFromConnectionString(TestConfiguration.Provisioning.ConnectionString); individualEnrollment = new IndividualEnrollment(registrationId, new TpmAttestation(base64Ek)) { Capabilities = capabilities, diff --git a/e2e/test/provisioning/ReprovisioningE2ETests.cs b/e2e/test/provisioning/ReprovisioningE2ETests.cs index f647a52d02..d80d2170ef 100644 --- a/e2e/test/provisioning/ReprovisioningE2ETests.cs +++ b/e2e/test/provisioning/ReprovisioningE2ETests.cs @@ -211,7 +211,7 @@ public async Task ProvisioningDeviceClient_ReprovisioningBlockingWorks_MqttWs_Sy /// private async Task ProvisioningDeviceClient_ReprovisioningFlow_ResetTwin(Client.TransportType transportProtocol, AttestationMechanismType attestationType, EnrollmentType enrollmentType, bool setCustomProxy, string customServerProxy = null) { - IotHubConnectionStringBuilder connectionString = IotHubConnectionStringBuilder.Create(TestConfiguration.IoTHub.ConnectionString); + var connectionString = IotHubConnectionStringBuilder.Create(TestConfiguration.IoTHub.ConnectionString); ICollection iotHubsToStartAt = new List() { TestConfiguration.Provisioning.FarAwayIotHubHostName }; ICollection iotHubsToReprovisionTo = new List() { connectionString.HostName }; await ProvisioningDeviceClient_ReprovisioningFlow(transportProtocol, attestationType, enrollmentType, setCustomProxy, new ReprovisionPolicy { MigrateDeviceData = false, UpdateHubAssignment = true }, AllocationPolicy.Hashed, null, iotHubsToStartAt, iotHubsToReprovisionTo, customServerProxy).ConfigureAwait(false); @@ -223,7 +223,7 @@ private async Task ProvisioningDeviceClient_ReprovisioningFlow_ResetTwin(Client. /// private async Task ProvisioningDeviceClient_ReprovisioningFlow_KeepTwin(Client.TransportType transportProtocol, AttestationMechanismType attestationType, EnrollmentType enrollmentType, bool setCustomProxy, string customServerProxy = null) { - IotHubConnectionStringBuilder connectionString = IotHubConnectionStringBuilder.Create(TestConfiguration.IoTHub.ConnectionString); + var connectionString = IotHubConnectionStringBuilder.Create(TestConfiguration.IoTHub.ConnectionString); ICollection iotHubsToStartAt = new List() { TestConfiguration.Provisioning.FarAwayIotHubHostName }; ICollection iotHubsToReprovisionTo = new List() { connectionString.HostName }; await ProvisioningDeviceClient_ReprovisioningFlow(transportProtocol, attestationType, enrollmentType, setCustomProxy, new ReprovisionPolicy { MigrateDeviceData = true, UpdateHubAssignment = true }, AllocationPolicy.Hashed, null, iotHubsToStartAt, iotHubsToReprovisionTo, customServerProxy).ConfigureAwait(false); @@ -234,7 +234,7 @@ private async Task ProvisioningDeviceClient_ReprovisioningFlow_KeepTwin(Client.T /// private async Task ProvisioningDeviceClient_ReprovisioningFlow_DoNotReprovision(Client.TransportType transportProtocol, AttestationMechanismType attestationType, EnrollmentType enrollmentType, bool setCustomProxy, string customServerProxy = null) { - IotHubConnectionStringBuilder connectionString = IotHubConnectionStringBuilder.Create(TestConfiguration.IoTHub.ConnectionString); + var connectionString = IotHubConnectionStringBuilder.Create(TestConfiguration.IoTHub.ConnectionString); ICollection iotHubsToStartAt = new List() { TestConfiguration.Provisioning.FarAwayIotHubHostName }; ICollection iotHubsToReprovisionTo = new List() { connectionString.HostName }; await ProvisioningDeviceClient_ReprovisioningFlow(transportProtocol, attestationType, enrollmentType, setCustomProxy, new ReprovisionPolicy { MigrateDeviceData = false, UpdateHubAssignment = false }, AllocationPolicy.Hashed, null, iotHubsToStartAt, iotHubsToReprovisionTo, customServerProxy).ConfigureAwait(false); @@ -281,7 +281,7 @@ private async Task ProvisioningDeviceClient_ReprovisioningFlow_DoNotReprovision( transport.Proxy = (proxyServerAddress != null) ? new WebProxy(s_proxyServerAddress) : null; } - ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create( + var provClient = ProvisioningDeviceClient.Create( s_globalDeviceEndpoint, TestConfiguration.Provisioning.IdScope, security, @@ -320,7 +320,7 @@ private async Task ProvisioningDeviceClient_ReprovisioningFlow_DoNotReprovision( /// private async Task ConfirmRegisteredDeviceWorks(DeviceRegistrationResult result, Client.IAuthenticationMethod auth, Client.TransportType transportProtocol, bool sendReportedPropertiesUpdate) { - using (DeviceClient iotClient = DeviceClient.Create(result.AssignedHub, auth, transportProtocol)) + using (var iotClient = DeviceClient.Create(result.AssignedHub, auth, transportProtocol)) { Logger.Trace("DeviceClient OpenAsync."); await iotClient.OpenAsync().ConfigureAwait(false); @@ -346,7 +346,7 @@ private async Task ConfirmExpectedDeviceCapabilities(DeviceRegistrationResult re if (capabilities != null) { //hardcoding amqp since http does not support twin, but tests that call into this may use http - using (DeviceClient iotClient = DeviceClient.Create(result.AssignedHub, auth, Client.TransportType.Amqp)) + using (var iotClient = DeviceClient.Create(result.AssignedHub, auth, Client.TransportType.Amqp)) { //Confirm that the device twin reflects what the enrollment dictated Twin twin = await iotClient.GetTwinAsync().ConfigureAwait(false); @@ -369,7 +369,7 @@ private async Task CreateSecurityProviderFromName(AttestationM string base64Ek = Convert.ToBase64String(tpmSim.GetEndorsementKey()); - using (ProvisioningServiceClient provisioningService = ProvisioningServiceClient.CreateFromConnectionString(TestConfiguration.Provisioning.ConnectionString)) + using (var provisioningService = ProvisioningServiceClient.CreateFromConnectionString(TestConfiguration.Provisioning.ConnectionString)) { Logger.Trace($"Getting enrollment: RegistrationID = {registrationId}"); var individualEnrollment = new IndividualEnrollment(registrationId, new TpmAttestation(base64Ek)) { AllocationPolicy = allocationPolicy, ReprovisionPolicy = reprovisionPolicy, IotHubs = iothubs, CustomAllocationDefinition = customAllocationDefinition, Capabilities = capabilities }; @@ -409,7 +409,7 @@ private async Task CreateSecurityProviderFromName(AttestationM case EnrollmentType.Group: EnrollmentGroup symmetricKeyEnrollmentGroup = await CreateEnrollmentGroup(provisioningServiceClient, AttestationMechanismType.SymmetricKey, groupId, reprovisionPolicy, allocationPolicy, customAllocationDefinition, iothubs, capabilities).ConfigureAwait(false); Assert.IsTrue(symmetricKeyEnrollmentGroup.Attestation is SymmetricKeyAttestation); - SymmetricKeyAttestation symmetricKeyAttestation = (SymmetricKeyAttestation)symmetricKeyEnrollmentGroup.Attestation; + var symmetricKeyAttestation = (SymmetricKeyAttestation)symmetricKeyEnrollmentGroup.Attestation; string registrationIdSymmetricKey = _devicePrefix + Guid.NewGuid(); string primaryKeyEnrollmentGroup = symmetricKeyAttestation.PrimaryKey; string secondaryKeyEnrollmentGroup = symmetricKeyAttestation.SecondaryKey; @@ -525,7 +525,7 @@ private void ConfirmDeviceInExpectedHub(DeviceRegistrationResult result, Reprovi private async Task ConfirmDeviceWorksAfterReprovisioning(DeviceRegistrationResult result, Client.IAuthenticationMethod auth, Client.TransportType transportProtocol, ReprovisionPolicy reprovisionPolicy, bool twinOperationsAllowed) { - using (DeviceClient iotClient = DeviceClient.Create(result.AssignedHub, auth, transportProtocol)) + using (var iotClient = DeviceClient.Create(result.AssignedHub, auth, transportProtocol)) { Logger.Trace("DeviceClient OpenAsync."); await iotClient.OpenAsync().ConfigureAwait(false); diff --git a/iothub/device/devdoc/Convention-based operations.md b/iothub/device/devdoc/Convention-based operations.md index 49216ecc89..43321f4828 100644 --- a/iothub/device/devdoc/Convention-based operations.md +++ b/iothub/device/devdoc/Convention-based operations.md @@ -43,23 +43,40 @@ public abstract class PayloadSerializer { } public sealed class DefaultPayloadConvention : PayloadConvention { +<<<<<<< HEAD public static readonly DefaultPayloadConvention Instance; public DefaultPayloadConvention(); +======= + public DefaultPayloadConvention(); + public static DefaultPayloadConvention Instance { get; } +>>>>>>> previews/pnpApi public override PayloadEncoder PayloadEncoder { get; } public override PayloadSerializer PayloadSerializer { get; } } public class Utf8PayloadEncoder : PayloadEncoder { +<<<<<<< HEAD public static readonly Utf8PayloadEncoder Instance; public Utf8PayloadEncoder(); public override Encoding ContentEncoding { get; } +======= + public Utf8PayloadEncoder(); + public override Encoding ContentEncoding { get; } + public static Utf8PayloadEncoder Instance { get; } +>>>>>>> previews/pnpApi public override byte[] EncodeStringToByteArray(string contentPayload); } public class NewtonsoftJsonPayloadSerializer : PayloadSerializer { +<<<<<<< HEAD public static readonly NewtonsoftJsonPayloadSerializer Instance; public NewtonsoftJsonPayloadSerializer(); public override string ContentType { get; } +======= + public NewtonsoftJsonPayloadSerializer(); + public override string ContentType { get; } + public static NewtonsoftJsonPayloadSerializer Instance { get; } +>>>>>>> previews/pnpApi public override T ConvertFromObject(object objectToConvert); public override IWritablePropertyResponse CreateWritablePropertyResponse(object value, int statusCode, long version, string description = null); public override T DeserializeToType(string stringToDeserialize); @@ -69,7 +86,10 @@ public class NewtonsoftJsonPayloadSerializer : PayloadSerializer { public abstract class PayloadCollection : IEnumerable, IEnumerable> { protected PayloadCollection(); +<<<<<<< HEAD public IDictionary Collection { get; private set; } +======= +>>>>>>> previews/pnpApi public PayloadConvention Convention { get; internal set; } public virtual object this[string key] { get; set; } public virtual void Add(string key, object value); @@ -121,12 +141,11 @@ public Task GetClientPropertiesAsync(CancellationToken cancell public Task UpdateClientPropertiesAsync(ClientPropertyCollection propertyCollection, CancellationToken cancellationToken = default); /// -/// Sets the global listener for Writable properties +/// Sets the listener for writable property update events. /// -/// The global call back to handle all writable property updates. -/// Generic parameter to be interpreted by the client code. +/// The callback to handle all writable property updates for the client. /// A cancellation token to cancel the operation. -public Task SubscribeToWritablePropertyUpdateRequestsAsync(Func callback, object userContext, CancellationToken cancellationToken = default); +public Task SubscribeToWritablePropertyUpdateRequestsAsync(Func callback, CancellationToken cancellationToken = default); ``` #### All related types @@ -134,8 +153,8 @@ public Task SubscribeToWritablePropertyUpdateRequestsAsync(Func telemetryValues); public override void Add(string telemetryName, object telemetryValue); + public void AddOrUpdate(IDictionary telemetryValues); public override void AddOrUpdate(string telemetryName, object telemetryValue); } @@ -214,28 +235,29 @@ public sealed class TelemetryMessage : MessageBase { ```csharp /// -/// Set the global command callback handler. +/// Sets the listener for command invocation requests. /// -/// A method implementation that will handle the incoming command. -/// Generic parameter to be interpreted by the client code. +/// The callback to handle all incoming commands for the client. /// A cancellation token to cancel the operation. -public Task SubscribeToCommandsAsync(Func> callback, object userContext, CancellationToken cancellationToken = default); +public Task SubscribeToCommandsAsync(Func> callback, CancellationToken cancellationToken = default); ``` #### All related types ```csharp public sealed class CommandRequest { - public string CommandName { get; private set; } - public string ComponentName { get; private set; } - public string DataAsJson { get; } - public T GetData(); - public byte[] GetDataAsBytes(); + public CommandRequest(); + public string CommandName { get; } + public string ComponentName { get; } + public T GetPayload(); + public ReadOnlyCollection GetPayloadAsBytes(); + public string GetPayloadAsString(); } public sealed class CommandResponse { + public CommandResponse(); public CommandResponse(int status); - public CommandResponse(object result, int status); - public string ResultAsJson { get; } - public int Status { get; private set; } + public CommandResponse(object payload, int status); + public object Payload { get; } + public int Status { get; } } ``` diff --git a/iothub/device/src/AmqpTransportSettings.cs b/iothub/device/src/AmqpTransportSettings.cs index 59b56192bf..84a427e4eb 100644 --- a/iothub/device/src/AmqpTransportSettings.cs +++ b/iothub/device/src/AmqpTransportSettings.cs @@ -156,9 +156,7 @@ public TimeSpan OpenTimeout /// public X509Certificate2 ClientCertificate { get; set; } - /// - /// The proxy - /// + /// public IWebProxy Proxy { get; set; } /// diff --git a/iothub/device/src/AuthenticationMethodFactory.cs b/iothub/device/src/AuthenticationMethodFactory.cs index 85f68a908d..c371040a20 100644 --- a/iothub/device/src/AuthenticationMethodFactory.cs +++ b/iothub/device/src/AuthenticationMethodFactory.cs @@ -33,16 +33,14 @@ internal static IAuthenticationMethod GetAuthenticationMethod(IotHubConnectionSt } else if (iotHubConnectionStringBuilder.SharedAccessSignature != null) { - if (iotHubConnectionStringBuilder.ModuleId != null) - { - return new ModuleAuthenticationWithToken( - iotHubConnectionStringBuilder.DeviceId, iotHubConnectionStringBuilder.ModuleId, iotHubConnectionStringBuilder.SharedAccessSignature); - } - else - { - return new DeviceAuthenticationWithToken( - iotHubConnectionStringBuilder.DeviceId, iotHubConnectionStringBuilder.SharedAccessSignature); - } + return iotHubConnectionStringBuilder.ModuleId != null + ? new ModuleAuthenticationWithToken( + iotHubConnectionStringBuilder.DeviceId, + iotHubConnectionStringBuilder.ModuleId, + iotHubConnectionStringBuilder.SharedAccessSignature) + : (IAuthenticationMethod)new DeviceAuthenticationWithToken( + iotHubConnectionStringBuilder.DeviceId, + iotHubConnectionStringBuilder.SharedAccessSignature); } else if (iotHubConnectionStringBuilder.UsingX509Cert) { diff --git a/iothub/device/src/ClientFactory.cs b/iothub/device/src/ClientFactory.cs index 3532c5516c..3ab6d9b710 100644 --- a/iothub/device/src/ClientFactory.cs +++ b/iothub/device/src/ClientFactory.cs @@ -122,12 +122,11 @@ internal static InternalClient Create(string hostname, IAuthenticationMethod aut throw new ArgumentException("No certificate was found. To use certificate authentication certificate must be present."); } -#pragma warning disable CA2000 // This is returned to client so cannot be disposed here. InternalClient dc = CreateFromConnectionString( connectionStringBuilder.ToString(), PopulateCertificateInTransportSettings(connectionStringBuilder, transportType), options); -#pragma warning restore CA2000 + dc.Certificate = connectionStringBuilder.Certificate; // Install all the intermediate certificates in the chain if specified. @@ -438,7 +437,7 @@ internal static ITransportSettings[] GetTransportSettings(TransportType transpor builder.SasTokenRenewalBuffer = options?.SasTokenRenewalBuffer ?? default; } - IotHubConnectionString iotHubConnectionString = builder.ToIotHubConnectionString(); + var iotHubConnectionString = builder.ToIotHubConnectionString(); foreach (ITransportSettings transportSetting in transportSettings) { @@ -612,7 +611,7 @@ private static IDeviceClientPipelineBuilder BuildPipeline() private static ITransportSettings[] PopulateCertificateInTransportSettings(IotHubConnectionStringBuilder connectionStringBuilder, ITransportSettings[] transportSettings) { - foreach (var transportSetting in transportSettings) + foreach (ITransportSettings transportSetting in transportSettings) { switch (transportSetting.GetTransportType()) { diff --git a/iothub/device/src/ClientProperties.cs b/iothub/device/src/ClientProperties.cs index 31c8fe4966..8a3247fbb4 100644 --- a/iothub/device/src/ClientProperties.cs +++ b/iothub/device/src/ClientProperties.cs @@ -40,7 +40,7 @@ internal ClientProperties(ClientPropertyCollection writablePropertyRequestCollec /// /// See the Writable properties documentation for more information. /// - public ClientPropertyCollection WritablePropertyRequests { get; private set; } + public ClientPropertyCollection WritablePropertyRequests { get; } /// /// The collection of properties reported by the client. @@ -49,6 +49,6 @@ internal ClientProperties(ClientPropertyCollection writablePropertyRequestCollec /// Client reported properties can either be Read-only properties /// or they can be Writable properties. /// - public ClientPropertyCollection ReportedFromClient { get; private set; } + public ClientPropertyCollection ReportedFromClient { get; } } } diff --git a/iothub/device/src/CommandRequest.cs b/iothub/device/src/CommandRequest.cs index 9b434f34a6..3c14956010 100644 --- a/iothub/device/src/CommandRequest.cs +++ b/iothub/device/src/CommandRequest.cs @@ -1,7 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text; +using System.Collections.ObjectModel; +using System.Linq; using Microsoft.Azure.Devices.Shared; namespace Microsoft.Azure.Devices.Client @@ -11,35 +12,42 @@ namespace Microsoft.Azure.Devices.Client /// public sealed class CommandRequest { - private readonly byte[] _data; + private readonly ReadOnlyCollection _payload; private readonly PayloadConvention _payloadConvention; + /// + /// Public constructor provided only for mocking purposes. + /// + public CommandRequest() + { + } + internal CommandRequest(PayloadConvention payloadConvention, string commandName, string componentName = default, byte[] data = default) { CommandName = commandName; ComponentName = componentName; - _data = data; + _payload = new ReadOnlyCollection(data); _payloadConvention = payloadConvention; } /// /// The name of the component that is command is invoked on. /// - public string ComponentName { get; private set; } + public string ComponentName { get; } /// /// The command name. /// - public string CommandName { get; private set; } + public string CommandName { get; } /// /// The command request data. /// /// The type to cast the command request data to. /// The command request data. - public T GetData() + public T GetPayload() { - string dataAsJson = DataAsJson; + string dataAsJson = GetPayloadAsString(); return dataAsJson == null ? default @@ -52,16 +60,21 @@ public T GetData() /// /// The command request data bytes. /// - public byte[] GetDataAsBytes() + public ReadOnlyCollection GetPayloadAsBytes() { // Need to return a clone of the array so that consumers // of this library cannot change its contents. - return (byte[])_data.Clone(); + return _payload; } /// - /// The command data in Json format. + /// The command data as a string. /// - public string DataAsJson => (_data == null || _data.Length == 0) ? null : Encoding.UTF8.GetString(_data); + public string GetPayloadAsString() + { + return _payload.Count == 0 + ? null + : _payloadConvention.PayloadEncoder.ContentEncoding.GetString(GetPayloadAsBytes().ToArray()); + } } } diff --git a/iothub/device/src/CommandResponse.cs b/iothub/device/src/CommandResponse.cs index 465cecc78e..ec41a62fee 100644 --- a/iothub/device/src/CommandResponse.cs +++ b/iothub/device/src/CommandResponse.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System.Text; using Microsoft.Azure.Devices.Shared; namespace Microsoft.Azure.Devices.Client @@ -11,18 +10,25 @@ namespace Microsoft.Azure.Devices.Client /// public sealed class CommandResponse { - private readonly object _result; + private readonly object _payload; internal PayloadConvention PayloadConvention { get; set; } + /// + /// Public constructor provided only for mocking purposes. + /// + public CommandResponse() + { + } + /// /// Creates a new instance of the class with the associated command response data and a status code. /// - /// The command response data. + /// The command response payload. /// A status code indicating success or failure. - public CommandResponse(object result, int status) + public CommandResponse(object payload, int status) { - _result = result; + _payload = payload; Status = status; } @@ -38,13 +44,28 @@ public CommandResponse(int status) /// /// The command response status code indicating success or failure. /// - public int Status { get; private set; } + public int Status { get; } + + /// + /// The command response payload. + /// + public object Payload { get; } /// /// The serialized command response data. /// - public string ResultAsJson => _result == null ? null : PayloadConvention.PayloadSerializer.SerializeToString(_result); + internal string GetPayloadAsString() + { + return _payload == null + ? null + : PayloadConvention.PayloadSerializer.SerializeToString(_payload); + } - internal byte[] ResultAsBytes => _result == null ? null : Encoding.UTF8.GetBytes(ResultAsJson); + internal byte[] GetPayloadAsBytes() + { + return _payload == null + ? null + : PayloadConvention.PayloadEncoder.ContentEncoding.GetBytes(GetPayloadAsString()); + } } } diff --git a/iothub/device/src/Common/Amqp/ClientWebSocketTransport.cs b/iothub/device/src/Common/Amqp/ClientWebSocketTransport.cs index e17018693b..da7e9f7eb3 100644 --- a/iothub/device/src/Common/Amqp/ClientWebSocketTransport.cs +++ b/iothub/device/src/Common/Amqp/ClientWebSocketTransport.cs @@ -249,7 +249,7 @@ private static void OnReadComplete(IAsyncResult result) private static void HandleReadComplete(IAsyncResult result) { - Task taskResult = (Task)result; + var taskResult = (Task)result; var args = (TransportAsyncCallbackArgs)taskResult.AsyncState; ReadTaskDone(taskResult, args); @@ -291,7 +291,7 @@ private static void OnWriteComplete(IAsyncResult result) private static void HandleWriteComplete(IAsyncResult result) { - Task taskResult = (Task)result; + var taskResult = (Task)result; var args = (TransportAsyncCallbackArgs)taskResult.AsyncState; WriteTaskDone(taskResult, args); args.CompletedCallback(args); diff --git a/iothub/device/src/Common/Extensions/CommonExtensions.cs b/iothub/device/src/Common/Extensions/CommonExtensions.cs index 95f2c6ddb1..944e21eda8 100644 --- a/iothub/device/src/Common/Extensions/CommonExtensions.cs +++ b/iothub/device/src/Common/Extensions/CommonExtensions.cs @@ -121,7 +121,7 @@ public static string GetMaskedClientIpAddress(this HttpRequestMessage requestMes // note that this only works if we are hosted as an OWIN app if (requestMessage.Properties.ContainsKey("MS_OwinContext")) { - OwinContext owinContext = requestMessage.Properties["MS_OwinContext"] as OwinContext; + var owinContext = requestMessage.Properties["MS_OwinContext"] as OwinContext; if (owinContext != null) { string remoteIpAddress = owinContext.Request.RemoteIpAddress; diff --git a/iothub/device/src/Common/Fx.cs b/iothub/device/src/Common/Fx.cs index 14dc31e8ec..12d03b05b6 100644 --- a/iothub/device/src/Common/Fx.cs +++ b/iothub/device/src/Common/Fx.cs @@ -212,7 +212,7 @@ internal static Type[] BreakOnExceptionTypes string[] typeNames = value as string[]; if (typeNames != null && typeNames.Length > 0) { - List types = new List(typeNames.Length); + var types = new List(typeNames.Length); for (int i = 0; i < typeNames.Length; i++) { types.Add(Type.GetType(typeNames[i], false)); diff --git a/iothub/device/src/Common/PartialTrustHelpers.cs b/iothub/device/src/Common/PartialTrustHelpers.cs index fbd857815e..0d7976e9bd 100644 --- a/iothub/device/src/Common/PartialTrustHelpers.cs +++ b/iothub/device/src/Common/PartialTrustHelpers.cs @@ -173,7 +173,7 @@ internal static bool HasEtwPermissions() throw new NotImplementedException(); #else //Currently unrestricted permissions are required to create Etw provider. - PermissionSet permissions = new PermissionSet(PermissionState.Unrestricted); + var permissions = new PermissionSet(PermissionState.Unrestricted); return CheckAppDomainPermissions(permissions); #endif } diff --git a/iothub/device/src/Common/ReadOnlyDictionary45.cs b/iothub/device/src/Common/ReadOnlyDictionary45.cs index 7faf42c1a6..f63baf8af2 100644 --- a/iothub/device/src/Common/ReadOnlyDictionary45.cs +++ b/iothub/device/src/Common/ReadOnlyDictionary45.cs @@ -283,7 +283,7 @@ bool IDictionary.Contains(object key) IDictionaryEnumerator IDictionary.GetEnumerator() { - IDictionary d = Dictionary as IDictionary; + var d = Dictionary as IDictionary; if (d != null) { return d.GetEnumerator(); @@ -352,17 +352,17 @@ void ICollection.CopyTo(Array array, int index) throw Fx.Exception.Argument(nameof(array), Resources.InvalidBufferSize); } - KeyValuePair[] pairs = array as KeyValuePair[]; + var pairs = array as KeyValuePair[]; if (pairs != null) { Dictionary.CopyTo(pairs, index); } else { - DictionaryEntry[] dictEntryArray = array as DictionaryEntry[]; + var dictEntryArray = array as DictionaryEntry[]; if (dictEntryArray != null) { - foreach (var item in Dictionary) + foreach (KeyValuePair item in Dictionary) { dictEntryArray[index++] = new DictionaryEntry(item.Key, item.Value); } @@ -377,7 +377,7 @@ void ICollection.CopyTo(Array array, int index) try { - foreach (var item in Dictionary) + foreach (KeyValuePair item in Dictionary) { objects[index++] = new KeyValuePair(item.Key, item.Value); } @@ -401,7 +401,7 @@ object ICollection.SyncRoot { if (_syncRoot == null) { - ICollection c = Dictionary as ICollection; + var c = Dictionary as ICollection; if (c != null) { _syncRoot = c.SyncRoot; @@ -568,7 +568,7 @@ object ICollection.SyncRoot { if (_syncRoot == null) { - ICollection c = _collection as ICollection; + var c = _collection as ICollection; if (c != null) { _syncRoot = c.SyncRoot; @@ -715,7 +715,7 @@ object ICollection.SyncRoot { if (m_syncRoot == null) { - ICollection c = m_collection as ICollection; + var c = m_collection as ICollection; if (c != null) { m_syncRoot = c.SyncRoot; diff --git a/iothub/device/src/Common/SynchronizedPool.cs b/iothub/device/src/Common/SynchronizedPool.cs index 5fce4fc1b4..25ca74a7bd 100644 --- a/iothub/device/src/Common/SynchronizedPool.cs +++ b/iothub/device/src/Common/SynchronizedPool.cs @@ -201,7 +201,7 @@ private void RecordTakeFromGlobalPool(int thisThreadID) } else { - PendingEntry[] newPending = new PendingEntry[localPending.Length * 2]; + var newPending = new PendingEntry[localPending.Length * 2]; Array.Copy(localPending, newPending, localPending.Length); this.pending = newPending; } diff --git a/iothub/device/src/Common/TaskHelpers.cs b/iothub/device/src/Common/TaskHelpers.cs index a258da2980..6fffea7e20 100644 --- a/iothub/device/src/Common/TaskHelpers.cs +++ b/iothub/device/src/Common/TaskHelpers.cs @@ -122,7 +122,7 @@ internal static void MarshalTaskResults(Task source, TaskCompletionSour break; case TaskStatus.RanToCompletion: - Task castedSource = source as Task; + var castedSource = source as Task; proxy.TrySetResult( castedSource == null ? default(TResult) : // source is a Task castedSource.Result); // source is a Task diff --git a/iothub/device/src/Common/Utils.cs b/iothub/device/src/Common/Utils.cs index 723bdfc762..7b0784d23a 100644 --- a/iothub/device/src/Common/Utils.cs +++ b/iothub/device/src/Common/Utils.cs @@ -118,7 +118,7 @@ public static void ValidateDataIsEmptyOrJson(byte[] data) throw new ArgumentNullException(nameof(dictionaries), "Provided dictionaries should not be null"); } - Dictionary result = dictionaries.SelectMany(dict => dict) + var result = dictionaries.SelectMany(dict => dict) .ToLookup(pair => pair.Key, pair => pair.Value) .ToDictionary(group => group.Key, group => group.First()); diff --git a/iothub/device/src/DeviceClient.ConventionBasedOperations.cs b/iothub/device/src/DeviceClient.ConventionBasedOperations.cs index 4b22036d85..0aa2922054 100644 --- a/iothub/device/src/DeviceClient.ConventionBasedOperations.cs +++ b/iothub/device/src/DeviceClient.ConventionBasedOperations.cs @@ -19,7 +19,7 @@ public partial class DeviceClient : IDisposable #endif { /// - /// The that the client uses for convention-based operations. + /// The payload convention implementation that the client uses for convention-based operations. /// public PayloadConvention PayloadConvention => InternalClient.PayloadConvention; @@ -28,7 +28,7 @@ public partial class DeviceClient : IDisposable /// /// /// Use the constructor to pass in the optional component name - /// that the telemetry message is from. + /// that the telemetry message belongs to. /// /// The telemetry message. /// A cancellation token to cancel the operation. @@ -36,21 +36,18 @@ public Task SendTelemetryAsync(TelemetryMessage telemetryMessage, CancellationTo => InternalClient.SendTelemetryAsync(telemetryMessage, cancellationToken); /// - /// Set the global command callback handler. + /// Sets the listener for command invocation requests. /// - /// A method implementation that will handle the incoming command. - /// Generic parameter to be interpreted by the client code. + /// The callback to handle all incoming commands for the client. /// A cancellation token to cancel the operation. - public Task SubscribeToCommandsAsync( - Func> callback, object userContext, - CancellationToken cancellationToken = default) - => InternalClient.SubscribeToCommandsAsync(callback, userContext, cancellationToken); + public Task SubscribeToCommandsAsync(Func> callback, CancellationToken cancellationToken = default) + => InternalClient.SubscribeToCommandsAsync(callback, cancellationToken); /// /// Retrieve the client properties. /// /// A cancellation token to cancel the operation. - /// The device properties. + /// The client properties. public Task GetClientPropertiesAsync(CancellationToken cancellationToken = default) => InternalClient.GetClientTwinPropertiesAsync(cancellationToken); @@ -61,16 +58,17 @@ public Task GetClientPropertiesAsync(CancellationToken cancell /// Reported properties to push. /// A cancellation token to cancel the operation. /// The response of the update operation. - public Task UpdateClientPropertiesAsync(ClientPropertyCollection propertyCollection, CancellationToken cancellationToken = default) + public Task UpdateClientPropertiesAsync( + ClientPropertyCollection propertyCollection, + CancellationToken cancellationToken = default) => InternalClient.UpdateClientPropertiesAsync(propertyCollection, cancellationToken); /// - /// Sets the global listener for writable property update events. + /// Sets the listener for writable property update events. /// - /// The global call back to handle all writable property updates. - /// Generic parameter to be interpreted by the client code. + /// The callback to handle all writable property updates for the client. /// A cancellation token to cancel the operation. - public Task SubscribeToWritablePropertyUpdateRequestsAsync(Func callback, object userContext, CancellationToken cancellationToken = default) - => InternalClient.SubscribeToWritablePropertyUpdateRequestsAsync(callback, userContext, cancellationToken); + public Task SubscribeToWritablePropertyUpdateRequestsAsync(Func callback, CancellationToken cancellationToken = default) + => InternalClient.SubscribeToWritablePropertyUpdateRequestsAsync(callback, cancellationToken); } } diff --git a/iothub/device/src/DeviceClient.cs b/iothub/device/src/DeviceClient.cs index 3da5d4d3e3..01910c541c 100644 --- a/iothub/device/src/DeviceClient.cs +++ b/iothub/device/src/DeviceClient.cs @@ -289,7 +289,7 @@ public void SetRetryPolicy(IRetryPolicy retryPolicy) public Task OpenAsync(CancellationToken cancellationToken) => InternalClient.OpenAsync(cancellationToken); /// - /// Close the DeviceClient instance + /// Close the DeviceClient instance. /// public Task CloseAsync() => InternalClient.CloseAsync(); @@ -298,7 +298,6 @@ public void SetRetryPolicy(IRetryPolicy retryPolicy) /// /// A cancellation token to cancel the operation. /// Thrown when the operation has been canceled. - /// public Task CloseAsync(CancellationToken cancellationToken) => InternalClient.CloseAsync(cancellationToken); /// @@ -319,12 +318,12 @@ public void SetRetryPolicy(IRetryPolicy retryPolicy) /// , or , and then dispose the message. /// /// - /// You cannot Reject or Abandon messages over MQTT protocol. + /// You cannot reject or abandon messages over MQTT protocol. /// For more details, see https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messages-c2d#the-cloud-to-device-message-life-cycle. /// /// A cancellation token to cancel the operation. - /// Thrown when the operation has been canceled. - /// The receive message or null if there was no message until CancellationToken Expired + /// Thrown when the operation has been canceled. The inner exception will be . + /// The received message or null if there was no message until cancellation token has expired public Task ReceiveAsync(CancellationToken cancellationToken) => InternalClient.ReceiveAsync(cancellationToken); /// @@ -333,10 +332,10 @@ public void SetRetryPolicy(IRetryPolicy retryPolicy) /// , or , and then dispose the message. /// /// - /// You cannot Reject or Abandon messages over MQTT protocol. + /// You cannot reject or abandon messages over MQTT protocol. /// For more details, see https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messages-c2d#the-cloud-to-device-message-life-cycle. /// - /// The receive message or null if there was no message until the specified time has elapsed + /// The received message or null if there was no message until the specified time has elapsed. public Task ReceiveAsync(TimeSpan timeout) => InternalClient.ReceiveAsync(timeout); /// @@ -352,94 +351,85 @@ public void SetRetryPolicy(IRetryPolicy retryPolicy) InternalClient.SetReceiveMessageHandlerAsync(messageHandler, userContext, cancellationToken); /// - /// Deletes a received message from the device queue + /// Deletes a received message from the device queue. /// /// The message lockToken. - /// The lock identifier for the previously received message public Task CompleteAsync(string lockToken) => InternalClient.CompleteAsync(lockToken); /// - /// Deletes a received message from the device queue + /// Deletes a received message from the device queue. /// /// The message lockToken. /// A cancellation token to cancel the operation. - /// The lock identifier for the previously received message - /// Thrown when the operation has been canceled. + /// Thrown when the operation has been canceled. The inner exception will be . public Task CompleteAsync(string lockToken, CancellationToken cancellationToken) => InternalClient.CompleteAsync(lockToken, cancellationToken); /// - /// Deletes a received message from the device queue + /// Deletes a received message from the device queue. /// /// The message. - /// The previously received message public Task CompleteAsync(Message message) => InternalClient.CompleteAsync(message); /// - /// Deletes a received message from the device queue + /// Deletes a received message from the device queue. /// /// The message. /// A cancellation token to cancel the operation. - /// Thrown when the operation has been canceled. - /// The previously received message + /// Thrown when the operation has been canceled. The inner exception will be . public Task CompleteAsync(Message message, CancellationToken cancellationToken) => InternalClient.CompleteAsync(message, cancellationToken); /// - /// Puts a received message back onto the device queue + /// Puts a received message back onto the device queue. /// /// - /// You cannot Abandon a message over MQTT protocol. + /// You cannot abandon a message over MQTT protocol. /// For more details, see https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messages-c2d#the-cloud-to-device-message-life-cycle. /// /// The message lockToken. - /// The previously received message public Task AbandonAsync(string lockToken) => InternalClient.AbandonAsync(lockToken); /// /// Puts a received message back onto the device queue /// /// - /// You cannot Abandon a message over MQTT protocol. + /// You cannot abandon a message over MQTT protocol. /// For more details, see https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messages-c2d#the-cloud-to-device-message-life-cycle. /// /// The message lockToken. /// A cancellation token to cancel the operation. - /// Thrown when the operation has been canceled. - /// The previously received message + /// Thrown when the operation has been canceled. The inner exception will be . public Task AbandonAsync(string lockToken, CancellationToken cancellationToken) => InternalClient.AbandonAsync(lockToken, cancellationToken); /// - /// Puts a received message back onto the device queue + /// Puts a received message back onto the device queue. /// /// - /// You cannot Abandon a message over MQTT protocol. + /// You cannot abandon a message over MQTT protocol. /// For more details, see https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messages-c2d#the-cloud-to-device-message-life-cycle. /// - /// The message. - /// The lock identifier for the previously received message + /// The message to abandon. public Task AbandonAsync(Message message) => InternalClient.AbandonAsync(message); /// - /// Puts a received message back onto the device queue + /// Puts a received message back onto the device queue. /// /// - /// You cannot Abandon a message over MQTT protocol. + /// You cannot abandon a message over MQTT protocol. /// For more details, see https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messages-c2d#the-cloud-to-device-message-life-cycle. /// - /// The message. + /// The message to abandon. /// A cancellation token to cancel the operation. - /// Thrown when the operation has been canceled. - /// The lock identifier for the previously received message + /// Thrown when the operation has been canceled. The inner exception will be . public Task AbandonAsync(Message message, CancellationToken cancellationToken) => InternalClient.AbandonAsync(message, cancellationToken); /// /// Deletes a received message from the device queue and indicates to the server that the message could not be processed. /// /// - /// You cannot Reject a message over MQTT protocol. + /// You cannot reject a message over MQTT protocol. /// For more details, see https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messages-c2d#the-cloud-to-device-message-life-cycle. /// /// The message lockToken. - /// The previously received message public Task RejectAsync(string lockToken) => InternalClient.RejectAsync(lockToken); /// @@ -451,32 +441,29 @@ public void SetRetryPolicy(IRetryPolicy retryPolicy) /// /// A cancellation token to cancel the operation. /// The message lockToken. - /// Thrown when the operation has been canceled. - /// The previously received message + /// Thrown when the operation has been canceled. The inner exception will be . public Task RejectAsync(string lockToken, CancellationToken cancellationToken) => InternalClient.RejectAsync(lockToken, cancellationToken); /// /// Deletes a received message from the device queue and indicates to the server that the message could not be processed. /// /// - /// You cannot Reject a message over MQTT protocol. + /// You cannot reject a message over MQTT protocol. /// For more details, see https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messages-c2d#the-cloud-to-device-message-life-cycle. /// /// The message. - /// The lock identifier for the previously received message public Task RejectAsync(Message message) => InternalClient.RejectAsync(message); /// /// Deletes a received message from the device queue and indicates to the server that the message could not be processed. /// /// - /// You cannot Reject a message over MQTT protocol. + /// You cannot reject a message over MQTT protocol. /// For more details, see https://docs.microsoft.com/azure/iot-hub/iot-hub-devguide-messages-c2d#the-cloud-to-device-message-life-cycle. /// - /// The message. + /// The message to reject. /// A cancellation token to cancel the operation. - /// Thrown when the operation has been canceled. - /// The lock identifier for the previously received message + /// Thrown when the operation has been canceled. The inner exception will be . public Task RejectAsync(Message message, CancellationToken cancellationToken) => InternalClient.RejectAsync(message, cancellationToken); /// @@ -488,6 +475,7 @@ public void SetRetryPolicy(IRetryPolicy retryPolicy) /// The timeout values are largely transport protocol specific. Check the corresponding transport settings to see if they can be configured. /// The operation timeout for the client can be set using . /// Thrown if the client encounters a transient retryable exception. + /// Thrown when the operation has been canceled. The inner exception will be . /// Thrown if a socket error occurs. /// Thrown if an error occurs when performing an operation on a WebSocket connection. /// Thrown if an I/O error occurs. @@ -499,7 +487,6 @@ public void SetRetryPolicy(IRetryPolicy retryPolicy) /// In case of a transient issue, retrying the operation should work. In case of a non-transient issue, inspect the error details and take steps accordingly. /// Please note that the list of exceptions is not exhaustive. /// - /// The task to await public Task SendEventAsync(Message message) => InternalClient.SendEventAsync(message); /// @@ -512,6 +499,7 @@ public void SetRetryPolicy(IRetryPolicy retryPolicy) /// If a cancellation token is not supplied to the operation call, a cancellation token with an expiration time of 4 minutes is used. /// /// Thrown if the client encounters a transient retryable exception. + /// Thrown when the operation has been canceled. The inner exception will be . /// Thrown if a socket error occurs. /// Thrown if an error occurs when performing an operation on a WebSocket connection. /// Thrown if an I/O error occurs. @@ -523,23 +511,20 @@ public void SetRetryPolicy(IRetryPolicy retryPolicy) /// In case of a transient issue, retrying the operation should work. In case of a non-transient issue, inspect the error details and take steps accordingly. /// Please note that the list of exceptions is not exhaustive. /// - /// The task to await public Task SendEventAsync(Message message, CancellationToken cancellationToken) => InternalClient.SendEventAsync(message, cancellationToken); /// /// Sends a batch of events to IoT hub. Use AMQP or HTTPs for a true batch operation. MQTT will just send the messages one after the other. /// /// A list of one or more messages to send. The messages should be disposed after sending. - /// The task to await public Task SendEventBatchAsync(IEnumerable messages) => InternalClient.SendEventBatchAsync(messages); /// /// Sends a batch of events to IoT hub. Use AMQP or HTTPs for a true batch operation. MQTT will just send the messages one after the other. /// - /// An IEnumerable set of Message objects. + /// An set of message objects. /// A cancellation token to cancel the operation. - /// Thrown when the operation has been canceled. - /// The task to await + /// Thrown when the operation has been canceled. The inner exception will be . public Task SendEventBatchAsync(IEnumerable messages, CancellationToken cancellationToken) => InternalClient.SendEventBatchAsync(messages, cancellationToken); /// @@ -548,7 +533,6 @@ public void SetRetryPolicy(IRetryPolicy retryPolicy) /// /// The name of the blob to upload. /// A stream with blob contents. Should be disposed after upload completes. - /// AsncTask [Obsolete("This API has been split into three APIs: GetFileUploadSasUri, uploading to blob directly using the Azure Storage SDK, and CompleteFileUploadAsync")] public Task UploadToBlobAsync(string blobName, Stream source) => InternalClient.UploadToBlobAsync(blobName, source); @@ -560,7 +544,6 @@ public void SetRetryPolicy(IRetryPolicy retryPolicy) /// A stream with blob contents.. Should be disposed after upload completes. /// A cancellation token to cancel the operation. /// Thrown when the operation has been canceled. - /// The task to await [Obsolete("This API has been split into three APIs: GetFileUploadSasUri, uploading to blob directly using the Azure Storage SDK, and CompleteFileUploadAsync")] public Task UploadToBlobAsync(string blobName, Stream source, CancellationToken cancellationToken) => InternalClient.UploadToBlobAsync(blobName, source, cancellationToken); @@ -579,7 +562,6 @@ public void SetRetryPolicy(IRetryPolicy retryPolicy) /// /// The notification details, including if the file upload succeeded. /// The cancellation token. - /// The task to await. public Task CompleteFileUploadAsync(FileUploadCompletionNotification notification, CancellationToken cancellationToken = default) => InternalClient.CompleteFileUploadAsync(notification, cancellationToken); @@ -603,6 +585,7 @@ public void SetRetryPolicy(IRetryPolicy retryPolicy) /// generic parameter to be interpreted by the client code. /// A cancellation token to cancel the operation. /// Thrown when the operation has been canceled. + /// Thrown when the operation has been canceled. /// public Task SetMethodHandlerAsync(string methodName, MethodCallback methodHandler, object userContext, CancellationToken cancellationToken) => InternalClient.SetMethodHandlerAsync(methodName, methodHandler, userContext, cancellationToken); @@ -626,6 +609,7 @@ public void SetRetryPolicy(IRetryPolicy retryPolicy) /// Generic parameter to be interpreted by the client code. /// A cancellation token to cancel the operation. /// Thrown when the operation has been canceled. + /// Thrown when the operation has been canceled. public Task SetMethodDefaultHandlerAsync(MethodCallback methodHandler, object userContext, CancellationToken cancellationToken) => InternalClient.SetMethodDefaultHandlerAsync(methodHandler, userContext, cancellationToken); @@ -665,7 +649,7 @@ public void Dispose() // IAsyncDisposable is available in .NET Standard 2.1 and above /// - /// Disposes the client in an async way. See for more information. + /// Disposes the client in an asynchronous way. See for more information. /// /// /// Includes a call to . @@ -707,7 +691,6 @@ protected virtual void Dispose(bool disposing) if (disposing) { InternalClient?.Dispose(); - InternalClient = null; } } @@ -743,10 +726,12 @@ protected virtual void Dispose(bool disposing) /// /// This has the side-effect of subscribing to the PATCH topic on the service. /// - /// Callback to call after the state update has been received and applied - /// Context object that will be passed into callback + /// Callback to call after the state update has been received and applied. + /// Context object that will be passed into callback. /// A cancellation token to cancel the operation. + /// TODO:azabbasi /// Thrown when the operation has been canceled. + /// Thrown when the operation has been canceled. public Task SetDesiredPropertyUpdateCallbackAsync(DesiredPropertyUpdateCallback callback, object userContext, CancellationToken cancellationToken) => InternalClient.SetDesiredPropertyUpdateCallbackAsync(callback, userContext, cancellationToken); @@ -762,7 +747,7 @@ protected virtual void Dispose(bool disposing) /// For the complete device twin object, use Microsoft.Azure.Devices.RegistryManager.GetTwinAsync(string deviceId). /// /// A cancellation token to cancel the operation. - /// Thrown when the operation has been canceled. + /// Thrown when the operation has been canceled. The inner exception will be . /// The device twin object for the current device public Task GetTwinAsync(CancellationToken cancellationToken) => InternalClient.GetTwinAsync(cancellationToken); @@ -778,7 +763,7 @@ protected virtual void Dispose(bool disposing) /// /// Reported properties to push /// A cancellation token to cancel the operation. - /// Thrown when the operation has been canceled. + /// Thrown when the operation has been canceled. The inner exception will be . public Task UpdateReportedPropertiesAsync(TwinCollection reportedProperties, CancellationToken cancellationToken) => InternalClient.UpdateReportedPropertiesAsync(reportedProperties, cancellationToken); } diff --git a/iothub/device/src/GlobalSuppressions.cs b/iothub/device/src/GlobalSuppressions.cs index 30c80222e1..0dc33d307d 100644 --- a/iothub/device/src/GlobalSuppressions.cs +++ b/iothub/device/src/GlobalSuppressions.cs @@ -7,7 +7,35 @@ using System.Diagnostics.CodeAnalysis; -[assembly: SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Not localizing", Scope = "module")] -[assembly: SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "SDK hides non-actionable errors from user", Scope = "module")] -[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Public API cannot be changed.", Scope = "type", Target = "~T:Microsoft.Azure.Devices.Client.ConnectionStatusChangeReason")] -[assembly: SuppressMessage("Naming", "CA1707:Identifiers should not contain underscores", Justification = "Public API cannot be changed.", Scope = "type", Target = "~T:Microsoft.Azure.Devices.Client.ConnectionStatus")] +[assembly: SuppressMessage( + "Globalization", + "CA1303:Do not pass literals as localized parameters", + Justification = "Not localizing", + Scope = "module")] +[assembly: SuppressMessage( + "Design", + "CA1031:Do not catch general exception types", + Justification = "SDK hides non-actionable errors from user", + Scope = "module")] +[assembly: SuppressMessage( + "Naming", + "CA1707:Identifiers should not contain underscores", + Justification = "Public API cannot be changed.", + Scope = "type", + Target = "~T:Microsoft.Azure.Devices.Client.ConnectionStatusChangeReason")] +[assembly: SuppressMessage( + "Naming", + "CA1707:Identifiers should not contain underscores", + Justification = "Public API cannot be changed.", + Scope = "type", + Target = "~T:Microsoft.Azure.Devices.Client.ConnectionStatus")] +[assembly: SuppressMessage( + "Style", + "IDE0011:Add braces", + Justification = "Agreement in the team to keep this style as is for logging methods only.", + Scope = "module")] +[assembly: SuppressMessage( + "CodeQuality", + "IDE0079:Remove unnecessary suppression", + Justification = "Each frameworks consider certain suppressions required by other frameworks unnecessary.", + Scope = "module")] diff --git a/iothub/device/src/Http1TransportSettings.cs b/iothub/device/src/Http1TransportSettings.cs index d8d638d30e..cd8db8a784 100644 --- a/iothub/device/src/Http1TransportSettings.cs +++ b/iothub/device/src/Http1TransportSettings.cs @@ -45,9 +45,7 @@ public TransportType GetTransportType() /// public TimeSpan DefaultReceiveTimeout => s_defaultOperationTimeout; - /// - /// Proxy information. - /// + /// public IWebProxy Proxy { get; set; } } } diff --git a/iothub/device/src/IDelegatingHandler.cs b/iothub/device/src/IDelegatingHandler.cs index 41e925ed0a..e437ce1e59 100644 --- a/iothub/device/src/IDelegatingHandler.cs +++ b/iothub/device/src/IDelegatingHandler.cs @@ -72,11 +72,5 @@ internal interface IDelegatingHandler : IContinuationProvider GetPropertiesAsync(PayloadConvention payloadConvention, CancellationToken cancellationToken); - - Task SendPropertyPatchAsync(ClientPropertyCollection reportedProperties, CancellationToken cancellationToken); } } diff --git a/iothub/device/src/ITransportSettings.cs b/iothub/device/src/ITransportSettings.cs index e4fccefdc3..2d064751c9 100644 --- a/iothub/device/src/ITransportSettings.cs +++ b/iothub/device/src/ITransportSettings.cs @@ -2,6 +2,8 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Net; +using Microsoft.Azure.Devices.Shared; namespace Microsoft.Azure.Devices.Client { @@ -20,5 +22,41 @@ public interface ITransportSettings /// The time to wait for a receive operation. /// TimeSpan DefaultReceiveTimeout { get; } + + /// + /// The web proxy that will be used to connect to IoT hub using a web socket connection for AMQP, MQTT, or when using the HTTP protocol. + /// + /// + /// An instance of a class that implements . + /// + /// + /// This setting will be used when the client attempts to connect over web sockets. For example, if the client attempts to connect to IoT hub using or the client will first try over TCP. If that fails, the client will fall back to using web sockets and will use the proxy setting. The setting will also be used when , , or is specified. + /// + /// + /// To set a proxy you must instantiate an instance of the class--or any class that derives from . The snippet below shows a method that returns a device using a proxy that connects to localhost on port 8888. + /// + /// static DeviceClient GetClientWithProxy() + /// { + /// try + /// { + /// var proxyHost = "localhost"; + /// var proxyPort = 8888; + /// // Specify the WebProxy to be used for the web socket connection + /// var transportSettings = new AmqpTransportSettings(Microsoft.Azure.Devices.Client.TransportType.Amqp_WebSocket_Only) + /// { + /// Proxy = new WebProxy(proxyHost, proxyPort) + /// }; + /// var deviceClient = DeviceClient.CreateFromConnectionString("a connection string", new ITransportSettings[] { transportSettings }); + /// return deviceClient; + /// } + /// catch (Exception) + /// { + /// Console.WriteLine("Error creating client."); + /// throw; + /// } + /// } + /// + /// + IWebProxy Proxy { get; set; } } } diff --git a/iothub/device/src/InternalClient.ConventionBasedOperations.cs b/iothub/device/src/InternalClient.ConventionBasedOperations.cs index a8e19cb9cc..c6c721bde1 100644 --- a/iothub/device/src/InternalClient.ConventionBasedOperations.cs +++ b/iothub/device/src/InternalClient.ConventionBasedOperations.cs @@ -37,7 +37,7 @@ internal Task SendTelemetryAsync(TelemetryMessage telemetryMessage, Cancellation return SendEventAsync(telemetryMessage, cancellationToken); } - internal Task SubscribeToCommandsAsync(Func> callback, object userContext, CancellationToken cancellationToken) + internal Task SubscribeToCommandsAsync(Func> callback, CancellationToken cancellationToken) { // Subscribe to methods default handler internally and use the callback received internally to invoke the user supplied command callback. var methodDefaultCallback = new MethodCallback(async (methodRequest, userContext) => @@ -56,14 +56,19 @@ internal Task SubscribeToCommandsAsync(Func GetClientTwinPropertiesAsync(CancellationToken cancellationToken) @@ -105,19 +110,22 @@ internal async Task UpdateClientPropertiesAsync( } } - internal Task SubscribeToWritablePropertyUpdateRequestsAsync(Func callback, object userContext, CancellationToken cancellationToken) + internal Task SubscribeToWritablePropertyUpdateRequestsAsync(Func callback, CancellationToken cancellationToken) { // Subscribe to DesiredPropertyUpdateCallback internally and use the callback received internally to invoke the user supplied Property callback. - var desiredPropertyUpdateCallback = new DesiredPropertyUpdateCallback((twinCollection, userContext) => + var desiredPropertyUpdateCallback = new DesiredPropertyUpdateCallback(async (twinCollection, userContext) => { // convert a TwinCollection to PropertyCollection var propertyCollection = ClientPropertyCollection.WritablePropertyUpdateRequestsFromTwinCollection(twinCollection, PayloadConvention); - callback.Invoke(propertyCollection, userContext); - - return TaskHelpers.CompletedTask; + await callback.Invoke(propertyCollection).ConfigureAwait(false); }); - return SetDesiredPropertyUpdateCallbackAsync(desiredPropertyUpdateCallback, userContext, cancellationToken); + // We pass in a null context to the internal API because the updated SubscribeToWritablePropertyUpdateRequestsAsync API + // does not require you to pass in a user context. + // Since SubscribeToWritablePropertyUpdateRequestsAsync callback is invoked for all property update events, + // the user context passed in would be the same for all scenarios. + // This user context can be set at a class level instead. + return SetDesiredPropertyUpdateCallbackAsync(desiredPropertyUpdateCallback, null, cancellationToken); } } } diff --git a/iothub/device/src/InternalClient.cs b/iothub/device/src/InternalClient.cs index 5d6a9953fc..f5c0b2a08c 100644 --- a/iothub/device/src/InternalClient.cs +++ b/iothub/device/src/InternalClient.cs @@ -1894,7 +1894,6 @@ internal void ValidateModuleTransportHandler(string apiName) public void Dispose() { InnerHandler?.Dispose(); - InnerHandler = null; _methodsSemaphore?.Dispose(); _moduleReceiveMessageSemaphore?.Dispose(); _fileUploadHttpTransportHandler?.Dispose(); diff --git a/iothub/device/src/IotHubClientDiagnostic.cs b/iothub/device/src/IotHubClientDiagnostic.cs index 0917efc570..9e27bea66d 100644 --- a/iothub/device/src/IotHubClientDiagnostic.cs +++ b/iothub/device/src/IotHubClientDiagnostic.cs @@ -34,7 +34,7 @@ internal static bool HasDiagnosticProperties(MessageBase message) private static string GenerateEightRandomCharacters() { - var stringChars = new char[8]; + char[] stringChars = new char[8]; var random = new Random(); for (int i = 0; i < stringChars.Length; i++) { diff --git a/iothub/device/src/IotHubConnectionString.Core.cs b/iothub/device/src/IotHubConnectionString.Core.cs index 70edd63dd0..543ea47c5f 100644 --- a/iothub/device/src/IotHubConnectionString.Core.cs +++ b/iothub/device/src/IotHubConnectionString.Core.cs @@ -19,11 +19,14 @@ Task IAuthorizationProvider.GetPasswordAsync() if (Logging.IsEnabled) Logging.Enter(this, $"{nameof(IotHubConnectionString)}.{nameof(IAuthorizationProvider.GetPasswordAsync)}"); - Debug.Assert(TokenRefresher != null); + Debug.Assert( + !string.IsNullOrWhiteSpace(SharedAccessSignature) + || TokenRefresher != null, + "The token refresher and the shared access signature can't both be null"); return !string.IsNullOrWhiteSpace(SharedAccessSignature) ? Task.FromResult(SharedAccessSignature) - : TokenRefresher.GetTokenAsync(Audience); + : TokenRefresher?.GetTokenAsync(Audience); } finally { diff --git a/iothub/device/src/IotHubConnectionString.cs b/iothub/device/src/IotHubConnectionString.cs index 3d8a7349fc..b41bad43e6 100644 --- a/iothub/device/src/IotHubConnectionString.cs +++ b/iothub/device/src/IotHubConnectionString.cs @@ -92,6 +92,33 @@ public IotHubConnectionString(IotHubConnectionStringBuilder builder) } } + // This constructor is only used for unit testing. + internal IotHubConnectionString( + string ioTHubName = null, + string deviceId = null, + string moduleId = null, + string hostName = null, + Uri httpsEndpoint = null, + Uri amqpEndpoint = null, + string audience = null, + string sharedAccessKeyName = null, + string sharedAccessKey = null, + string sharedAccessSignature = null, + bool isUsingGateway = false) + { + IotHubName = ioTHubName; + DeviceId = deviceId; + ModuleId = moduleId; + HostName = hostName; + HttpsEndpoint = httpsEndpoint; + AmqpEndpoint = amqpEndpoint; + Audience = audience; + SharedAccessKeyName = sharedAccessKeyName; + SharedAccessKey = sharedAccessKey; + SharedAccessSignature = sharedAccessSignature; + IsUsingGateway = isUsingGateway; + } + public string IotHubName { get; private set; } public string DeviceId { get; private set; } diff --git a/iothub/device/src/IotHubConnectionStringBuilder.cs b/iothub/device/src/IotHubConnectionStringBuilder.cs index 8c133f9e04..181cd642f6 100644 --- a/iothub/device/src/IotHubConnectionStringBuilder.cs +++ b/iothub/device/src/IotHubConnectionStringBuilder.cs @@ -53,7 +53,7 @@ public sealed class IotHubConnectionStringBuilder /// /// Initializes a new instance of the class. /// - private IotHubConnectionStringBuilder() + internal IotHubConnectionStringBuilder() { } diff --git a/iothub/device/src/Microsoft.Azure.Devices.Client.csproj b/iothub/device/src/Microsoft.Azure.Devices.Client.csproj index 9dc7f14c8c..a8a79410a5 100644 --- a/iothub/device/src/Microsoft.Azure.Devices.Client.csproj +++ b/iothub/device/src/Microsoft.Azure.Devices.Client.csproj @@ -79,6 +79,8 @@ + + diff --git a/iothub/device/src/ModernDotNet/HsmAuthentication/Transport/HttpUdsMessageHandler.cs b/iothub/device/src/ModernDotNet/HsmAuthentication/Transport/HttpUdsMessageHandler.cs index 66a0ea0372..62889cf2d3 100644 --- a/iothub/device/src/ModernDotNet/HsmAuthentication/Transport/HttpUdsMessageHandler.cs +++ b/iothub/device/src/ModernDotNet/HsmAuthentication/Transport/HttpUdsMessageHandler.cs @@ -40,7 +40,7 @@ protected override async Task SendAsync(HttpRequestMessage private async Task GetConnectedSocketAsync() { - Socket socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); + var socket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.Unspecified); // The Edge Agent uses unix sockets for communication with the modules deployed in docker for HSM. // For netstandard 2.0 there was no implementation for a Unix Domain Socket (UDS) so we used a version diff --git a/iothub/device/src/ModuleClient.ConventionBasedOperations.cs b/iothub/device/src/ModuleClient.ConventionBasedOperations.cs index 59cbf76061..ca1a6a669d 100644 --- a/iothub/device/src/ModuleClient.ConventionBasedOperations.cs +++ b/iothub/device/src/ModuleClient.ConventionBasedOperations.cs @@ -36,15 +36,12 @@ public Task SendTelemetryAsync(TelemetryMessage telemetryMessage, CancellationTo => InternalClient.SendTelemetryAsync(telemetryMessage, cancellationToken); /// - /// Set the global command callback handler. + /// Sets the listener for command invocation requests. /// - /// A method implementation that will handle the incoming command. - /// Generic parameter to be interpreted by the client code. + /// The callback to handle all incoming commands for the client. /// A cancellation token to cancel the operation. - public Task SubscribeToCommandsAsync( - Func> callback, object userContext, - CancellationToken cancellationToken = default) - => InternalClient.SubscribeToCommandsAsync(callback, userContext, cancellationToken); + public Task SubscribeToCommandsAsync(Func> callback, CancellationToken cancellationToken = default) + => InternalClient.SubscribeToCommandsAsync(callback, cancellationToken); /// /// Retrieve the client properties. @@ -65,12 +62,11 @@ public Task UpdateClientPropertiesAsync(ClientPr => InternalClient.UpdateClientPropertiesAsync(propertyCollection, cancellationToken); /// - /// Sets the global listener for writable property update events. + /// Sets the listener for writable property update events. /// - /// The global call back to handle all writable property updates. - /// Generic parameter to be interpreted by the client code. + /// The callback to handle all writable property updates for the client. /// A cancellation token to cancel the operation. - public Task SubscribeToWritablePropertyUpdateRequestsAsync(Func callback, object userContext, CancellationToken cancellationToken = default) - => InternalClient.SubscribeToWritablePropertyUpdateRequestsAsync(callback, userContext, cancellationToken); + public Task SubscribeToWritablePropertyUpdateRequestsAsync(Func callback, CancellationToken cancellationToken = default) + => InternalClient.SubscribeToWritablePropertyUpdateRequestsAsync(callback, cancellationToken); } } diff --git a/iothub/device/src/PayloadCollection.cs b/iothub/device/src/PayloadCollection.cs index d4dcbc2295..c86c2e1957 100644 --- a/iothub/device/src/PayloadCollection.cs +++ b/iothub/device/src/PayloadCollection.cs @@ -20,7 +20,7 @@ public abstract class PayloadCollection : IEnumerable /// The underlying collection for the payload. /// - public IDictionary Collection { get; private set; } = new Dictionary(); + internal IDictionary Collection { get; } = new Dictionary(); /// /// The convention to use with this payload. @@ -28,18 +28,16 @@ public abstract class PayloadCollection : IEnumerable - /// Get the value at the specified key. + /// Gets the value with the specified key. /// /// /// This accessor is best used to access and cast to simple types. /// It is recommended to use to deserialize to a complex type. /// - /// /// For setting component-level property values see - /// and instead. - /// These convenience methods ensure that component-level properties include the component identifier markers { "__t": "c" }. + /// and instead + /// as these convenience methods ensure that component-level properties include the component identifier markers: { "__t": "c" }. /// For more information see . - /// /// /// /// Key of value. @@ -73,8 +71,8 @@ public virtual void Add(string key, object value) /// For property operations see /// and instead. /// - /// The name of the telemetry. - /// The value of the telemetry. + /// The name of the key to be added to the collection. + /// The value to be added to the collection. /// is null. public virtual void AddOrUpdate(string key, object value) { @@ -95,10 +93,10 @@ public virtual byte[] GetPayloadObjectBytes() } /// - /// Determines whether the specified property is present. + /// Determines whether the specified key is present. /// /// The key in the collection to locate. - /// true if the specified property is present; otherwise, false. + /// true if the specified property is present, otherwise false. public bool Contains(string key) { return Collection.ContainsKey(key); diff --git a/iothub/device/src/TransientFaultHandling/AsyncExecution[T].cs b/iothub/device/src/TransientFaultHandling/AsyncExecution[T].cs index 7645591a68..278ca97689 100644 --- a/iothub/device/src/TransientFaultHandling/AsyncExecution[T].cs +++ b/iothub/device/src/TransientFaultHandling/AsyncExecution[T].cs @@ -79,7 +79,7 @@ private Task ExecuteAsyncImpl(Task ignore) { return this.previousTask; } - TaskCompletionSource taskCompletionSource = new TaskCompletionSource(); + var taskCompletionSource = new TaskCompletionSource(); taskCompletionSource.TrySetCanceled(); return taskCompletionSource.Task; } @@ -96,7 +96,7 @@ private Task ExecuteAsyncImpl(Task ignore) { throw; } - TaskCompletionSource taskCompletionSource2 = new TaskCompletionSource(); + var taskCompletionSource2 = new TaskCompletionSource(); taskCompletionSource2.TrySetException(ex); task = taskCompletionSource2.Task; } diff --git a/iothub/device/src/Transport/Amqp/AmqpAuthenticationRefresher.cs b/iothub/device/src/Transport/Amqp/AmqpAuthenticationRefresher.cs index 4d9a8d6cad..0b9c09b1b7 100644 --- a/iothub/device/src/Transport/Amqp/AmqpAuthenticationRefresher.cs +++ b/iothub/device/src/Transport/Amqp/AmqpAuthenticationRefresher.cs @@ -21,7 +21,7 @@ internal class AmqpAuthenticationRefresher : IAmqpAuthenticationRefresher, IDisp private Task _refreshLoop; private bool _disposed; - internal AmqpAuthenticationRefresher(DeviceIdentity deviceIdentity, AmqpIotCbsLink amqpCbsLink) + internal AmqpAuthenticationRefresher(IDeviceIdentity deviceIdentity, AmqpIotCbsLink amqpCbsLink) { _amqpIotCbsLink = amqpCbsLink; _connectionString = deviceIdentity.IotHubConnectionString; diff --git a/iothub/device/src/Transport/Amqp/AmqpConnectionHolder.cs b/iothub/device/src/Transport/Amqp/AmqpConnectionHolder.cs index 691410b851..5d9681be70 100644 --- a/iothub/device/src/Transport/Amqp/AmqpConnectionHolder.cs +++ b/iothub/device/src/Transport/Amqp/AmqpConnectionHolder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Amqp; @@ -15,7 +16,7 @@ namespace Microsoft.Azure.Devices.Client.Transport.Amqp { internal class AmqpConnectionHolder : IAmqpConnectionHolder, IAmqpUnitManager { - private readonly DeviceIdentity _deviceIdentity; + private readonly IDeviceIdentity _deviceIdentity; private readonly AmqpIotConnector _amqpIotConnector; private readonly SemaphoreSlim _lock = new SemaphoreSlim(1, 1); private readonly HashSet _amqpUnits = new HashSet(); @@ -24,7 +25,7 @@ internal class AmqpConnectionHolder : IAmqpConnectionHolder, IAmqpUnitManager private IAmqpAuthenticationRefresher _amqpAuthenticationRefresher; private volatile bool _disposed; - public AmqpConnectionHolder(DeviceIdentity deviceIdentity) + public AmqpConnectionHolder(IDeviceIdentity deviceIdentity) { _deviceIdentity = deviceIdentity; _amqpIotConnector = new AmqpIotConnector(deviceIdentity.AmqpTransportSettings, deviceIdentity.IotHubConnectionString.HostName); @@ -35,7 +36,7 @@ public AmqpConnectionHolder(DeviceIdentity deviceIdentity) } public AmqpUnit CreateAmqpUnit( - DeviceIdentity deviceIdentity, + IDeviceIdentity deviceIdentity, Func onMethodCallback, Action twinMessageListener, Func onModuleMessageReceivedCallback, @@ -141,7 +142,7 @@ private void Dispose(bool disposing) _disposed = true; } - public async Task CreateRefresherAsync(DeviceIdentity deviceIdentity, CancellationToken cancellationToken) + public async Task CreateRefresherAsync(IDeviceIdentity deviceIdentity, CancellationToken cancellationToken) { if (Logging.IsEnabled) { @@ -160,7 +161,7 @@ public async Task CreateRefresherAsync(DeviceIdent return amqpAuthenticator; } - public async Task OpenSessionAsync(DeviceIdentity deviceIdentity, CancellationToken cancellationToken) + public async Task OpenSessionAsync(IDeviceIdentity deviceIdentity, CancellationToken cancellationToken) { if (Logging.IsEnabled) { @@ -275,9 +276,14 @@ public void RemoveAmqpUnit(AmqpUnit amqpUnit) } } - internal DeviceIdentity GetDeviceIdentityOfAuthenticationProvider() + internal IDeviceIdentity GetDeviceIdentityOfAuthenticationProvider() { return _deviceIdentity; } + + internal bool IsEmpty() + { + return !_amqpUnits.Any(); + } } } diff --git a/iothub/device/src/Transport/Amqp/AmqpConnectionPool.cs b/iothub/device/src/Transport/Amqp/AmqpConnectionPool.cs index d682ffc51a..25cffd1482 100644 --- a/iothub/device/src/Transport/Amqp/AmqpConnectionPool.cs +++ b/iothub/device/src/Transport/Amqp/AmqpConnectionPool.cs @@ -17,8 +17,13 @@ internal class AmqpConnectionPool : IAmqpUnitManager private readonly IDictionary _amqpSasGroupedPool = new Dictionary(); private readonly object _lock = new object(); + protected virtual IDictionary GetAmqpSasGroupedPoolDictionary() + { + return _amqpSasGroupedPool; + } + public AmqpUnit CreateAmqpUnit( - DeviceIdentity deviceIdentity, + IDeviceIdentity deviceIdentity, Func onMethodCallback, Action twinMessageListener, Func onModuleMessageReceivedCallback, @@ -37,21 +42,6 @@ internal class AmqpConnectionPool : IAmqpUnitManager { AmqpConnectionHolder[] amqpConnectionHolders = ResolveConnectionGroup(deviceIdentity); amqpConnectionHolder = ResolveConnectionByHashing(amqpConnectionHolders, deviceIdentity); - - // For group sas token authenticated devices over a multiplexed connection, the TokenRefresher - // of the first client connecting will be used for generating the group sas tokens - // and will be associated with the connection itself. - // For this reason, if the device identity of the client is not the one associated with the - // connection, the associated TokenRefresher can be safely disposed. - // Note - This does not cause any identity related issues since the group sas tokens are generated - // against the hub host as the intended audience (without the "device Id"). - if (deviceIdentity.AuthenticationModel == AuthenticationModel.SasGrouped - && !ReferenceEquals(amqpConnectionHolder.GetDeviceIdentityOfAuthenticationProvider(), deviceIdentity) - && deviceIdentity.IotHubConnectionString?.TokenRefresher != null - && deviceIdentity.IotHubConnectionString.TokenRefresher.DisposalWithClient) - { - deviceIdentity.IotHubConnectionString.TokenRefresher.Dispose(); - } } if (Logging.IsEnabled) @@ -92,7 +82,7 @@ public void RemoveAmqpUnit(AmqpUnit amqpUnit) Logging.Enter(this, amqpUnit, nameof(RemoveAmqpUnit)); } - DeviceIdentity deviceIdentity = amqpUnit.GetDeviceIdentity(); + IDeviceIdentity deviceIdentity = amqpUnit.GetDeviceIdentity(); if (deviceIdentity.IsPooling()) { AmqpConnectionHolder amqpConnectionHolder; @@ -100,8 +90,17 @@ public void RemoveAmqpUnit(AmqpUnit amqpUnit) { AmqpConnectionHolder[] amqpConnectionHolders = ResolveConnectionGroup(deviceIdentity); amqpConnectionHolder = ResolveConnectionByHashing(amqpConnectionHolders, deviceIdentity); + + amqpConnectionHolder.RemoveAmqpUnit(amqpUnit); + + // If the connection holder does not have any more units, the entry needs to be nullified. + if (amqpConnectionHolder.IsEmpty()) + { + int index = GetDeviceIdentityIndex(deviceIdentity, amqpConnectionHolders.Length); + amqpConnectionHolders[index] = null; + amqpConnectionHolder?.Dispose(); + } } - amqpConnectionHolder.RemoveAmqpUnit(amqpUnit); } if (Logging.IsEnabled) @@ -110,7 +109,7 @@ public void RemoveAmqpUnit(AmqpUnit amqpUnit) } } - private AmqpConnectionHolder[] ResolveConnectionGroup(DeviceIdentity deviceIdentity) + private AmqpConnectionHolder[] ResolveConnectionGroup(IDeviceIdentity deviceIdentity) { if (deviceIdentity.AuthenticationModel == AuthenticationModel.SasIndividual) { @@ -124,25 +123,26 @@ private AmqpConnectionHolder[] ResolveConnectionGroup(DeviceIdentity deviceIdent else { string scope = deviceIdentity.IotHubConnectionString.SharedAccessKeyName; - _amqpSasGroupedPool.TryGetValue(scope, out AmqpConnectionHolder[] amqpConnectionHolders); + GetAmqpSasGroupedPoolDictionary().TryGetValue(scope, out AmqpConnectionHolder[] amqpConnectionHolders); if (amqpConnectionHolders == null) { amqpConnectionHolders = new AmqpConnectionHolder[deviceIdentity.AmqpTransportSettings.AmqpConnectionPoolSettings.MaxPoolSize]; - _amqpSasGroupedPool.Add(scope, amqpConnectionHolders); + GetAmqpSasGroupedPoolDictionary().Add(scope, amqpConnectionHolders); } return amqpConnectionHolders; } } - private AmqpConnectionHolder ResolveConnectionByHashing(AmqpConnectionHolder[] pool, DeviceIdentity deviceIdentity) + private AmqpConnectionHolder ResolveConnectionByHashing(AmqpConnectionHolder[] pool, IDeviceIdentity deviceIdentity) { if (Logging.IsEnabled) { Logging.Enter(this, deviceIdentity, nameof(ResolveConnectionByHashing)); } - int index = Math.Abs(deviceIdentity.GetHashCode()) % pool.Length; + int index = GetDeviceIdentityIndex(deviceIdentity, pool.Length); + if (pool[index] == null) { pool[index] = new AmqpConnectionHolder(deviceIdentity); @@ -155,5 +155,12 @@ private AmqpConnectionHolder ResolveConnectionByHashing(AmqpConnectionHolder[] p return pool[index]; } + + private static int GetDeviceIdentityIndex(IDeviceIdentity deviceIdentity, int poolLength) + { + return deviceIdentity == null + ? throw new ArgumentNullException(nameof(deviceIdentity)) + : Math.Abs(deviceIdentity.GetHashCode()) % poolLength; + } } } diff --git a/iothub/device/src/Transport/Amqp/AmqpTransportHandler.cs b/iothub/device/src/Transport/Amqp/AmqpTransportHandler.cs index 01e33d5671..67430620d5 100644 --- a/iothub/device/src/Transport/Amqp/AmqpTransportHandler.cs +++ b/iothub/device/src/Transport/Amqp/AmqpTransportHandler.cs @@ -53,7 +53,7 @@ static AmqpTransportHandler() { _operationTimeout = transportSettings.OperationTimeout; _onDesiredStatePatchListener = onDesiredStatePatchReceivedCallback; - var deviceIdentity = new DeviceIdentity(connectionString, transportSettings, context.Get(), context.Get()); + IDeviceIdentity deviceIdentity = new DeviceIdentity(connectionString, transportSettings, context.Get(), context.Get()); _amqpUnit = AmqpUnitManager.GetInstance().CreateAmqpUnit( deviceIdentity, onMethodCallback, @@ -509,12 +509,11 @@ public override async Task EnableEventReceiveAsync(bool isAnEdgeModule, Cancella { Logging.Exit(this, cancellationToken, nameof(EnableEventReceiveAsync)); } - } + } else { await EnableReceiveMessageAsync(cancellationToken).ConfigureAwait(false); } - } #endregion Events @@ -592,26 +591,6 @@ private async Task DisposeMessageAsync(string lockToken, AmqpIotDisposeActions o #endregion Accept-Dispose - #region Convention-based operations - - public override Task GetPropertiesAsync(PayloadConvention payloadConvention, CancellationToken cancellationToken) - { - throw new NotImplementedException("This operation is currently not supported over AMQP, please use MQTT protocol instead. " + - "Note that you can still retrieve a client's properties using DeviceClient.GetTwinAsync(CancellationToken cancellationToken) or " + - "ModuleClient.GetTwinAsync(CancellationToken cancellationToken) operations, but the properties will not be formatted " + - "as per DTDL terminology."); - } - - public override Task SendPropertyPatchAsync(ClientPropertyCollection reportedProperties, CancellationToken cancellationToken) - { - throw new NotImplementedException("This operation is currently not supported over AMQP, please use MQTT protocol instead. " + - "Note that you can still retrieve a client's properties using DeviceClient.GetTwinAsync(CancellationToken cancellationToken) or " + - "ModuleClient.GetTwinAsync(CancellationToken cancellationToken) operations, but the properties will not be formatted " + - "as per DTDL terminology."); - } - - #endregion Convention-based operations - #region Helpers private void TwinMessageListener(AmqpMessage responseFromService, string correlationId, IotHubException ex = default) diff --git a/iothub/device/src/Transport/Amqp/AmqpUnit.cs b/iothub/device/src/Transport/Amqp/AmqpUnit.cs index 8fc5ea5f09..f8b0d32708 100644 --- a/iothub/device/src/Transport/Amqp/AmqpUnit.cs +++ b/iothub/device/src/Transport/Amqp/AmqpUnit.cs @@ -18,7 +18,7 @@ namespace Microsoft.Azure.Devices.Client.Transport.AmqpIot internal class AmqpUnit : IDisposable { // If the first argument is set to true, we are disconnecting gracefully via CloseAsync. - private readonly DeviceIdentity _deviceIdentity; + private readonly IDeviceIdentity _deviceIdentity; private readonly Func _onMethodCallback; private readonly Action _twinMessageListener; @@ -53,7 +53,7 @@ internal class AmqpUnit : IDisposable private IAmqpAuthenticationRefresher _amqpAuthenticationRefresher; public AmqpUnit( - DeviceIdentity deviceIdentity, + IDeviceIdentity deviceIdentity, IAmqpConnectionHolder amqpConnectionHolder, Func onMethodCallback, Action twinMessageListener, @@ -72,7 +72,7 @@ internal class AmqpUnit : IDisposable Logging.Associate(this, _deviceIdentity, nameof(_deviceIdentity)); } - internal DeviceIdentity GetDeviceIdentity() + internal IDeviceIdentity GetDeviceIdentity() { return _deviceIdentity; } @@ -801,11 +801,7 @@ private void OnDesiredPropertyReceived(AmqpMessage responseFromService, string c } } - public async Task SendTwinMessageAsync( - AmqpTwinMessageType amqpTwinMessageType, - string correlationId, - Stream reportedProperties, - CancellationToken cancellationToken) + public async Task SendTwinMessageAsync(AmqpTwinMessageType amqpTwinMessageType, string correlationId, Stream reportedProperties, CancellationToken cancellationToken) { Logging.Enter(this, nameof(SendTwinMessageAsync)); diff --git a/iothub/device/src/Transport/Amqp/AmqpUnitManager.cs b/iothub/device/src/Transport/Amqp/AmqpUnitManager.cs index a94dfe6829..64bea200c5 100644 --- a/iothub/device/src/Transport/Amqp/AmqpUnitManager.cs +++ b/iothub/device/src/Transport/Amqp/AmqpUnitManager.cs @@ -29,7 +29,7 @@ internal static AmqpUnitManager GetInstance() } public AmqpUnit CreateAmqpUnit( - DeviceIdentity deviceIdentity, + IDeviceIdentity deviceIdentity, Func onMethodCallback, Action twinMessageListener, Func onModuleMessageReceivedCallback, @@ -48,9 +48,9 @@ internal static AmqpUnitManager GetInstance() public void RemoveAmqpUnit(AmqpUnit amqpUnit) { - amqpUnit.Dispose(); IAmqpUnitManager amqpConnectionPool = ResolveConnectionPool(amqpUnit.GetDeviceIdentity().IotHubConnectionString.HostName); amqpConnectionPool.RemoveAmqpUnit(amqpUnit); + amqpUnit.Dispose(); } private IAmqpUnitManager ResolveConnectionPool(string host) diff --git a/iothub/device/src/Transport/DeviceIdentity.cs b/iothub/device/src/Transport/Amqp/DeviceIdentity.cs similarity index 77% rename from iothub/device/src/Transport/DeviceIdentity.cs rename to iothub/device/src/Transport/Amqp/DeviceIdentity.cs index dc1505eac6..8689ac3111 100644 --- a/iothub/device/src/Transport/DeviceIdentity.cs +++ b/iothub/device/src/Transport/Amqp/DeviceIdentity.cs @@ -4,7 +4,7 @@ using System.Net; using Microsoft.Azure.Devices.Client.Extensions; -namespace Microsoft.Azure.Devices.Client.Transport +namespace Microsoft.Azure.Devices.Client.Transport.Amqp { /// /// Device configurations @@ -12,16 +12,20 @@ namespace Microsoft.Azure.Devices.Client.Transport /// - connection string /// - transport settings /// - internal class DeviceIdentity + internal class DeviceIdentity : IDeviceIdentity { - internal IotHubConnectionString IotHubConnectionString { get; } - internal AmqpTransportSettings AmqpTransportSettings { get; } - internal ProductInfo ProductInfo { get; } - internal AuthenticationModel AuthenticationModel { get; } - internal string Audience { get; } - internal ClientOptions Options { get; } + public IotHubConnectionString IotHubConnectionString { get; } + public AmqpTransportSettings AmqpTransportSettings { get; } + public ProductInfo ProductInfo { get; } + public AuthenticationModel AuthenticationModel { get; } + public string Audience { get; } + public ClientOptions Options { get; } - internal DeviceIdentity(IotHubConnectionString iotHubConnectionString, AmqpTransportSettings amqpTransportSettings, ProductInfo productInfo, ClientOptions options) + internal DeviceIdentity( + IotHubConnectionString iotHubConnectionString, + AmqpTransportSettings amqpTransportSettings, + ProductInfo productInfo, + ClientOptions options) { IotHubConnectionString = iotHubConnectionString; AmqpTransportSettings = amqpTransportSettings; @@ -31,14 +35,9 @@ internal DeviceIdentity(IotHubConnectionString iotHubConnectionString, AmqpTrans if (amqpTransportSettings.ClientCertificate == null) { Audience = CreateAudience(IotHubConnectionString); - if (iotHubConnectionString.SharedAccessKeyName == null) - { - AuthenticationModel = AuthenticationModel.SasIndividual; - } - else - { - AuthenticationModel = AuthenticationModel.SasGrouped; - } + AuthenticationModel = iotHubConnectionString.SharedAccessKeyName == null + ? AuthenticationModel.SasIndividual + : AuthenticationModel.SasGrouped; } else { @@ -61,7 +60,7 @@ private static string CreateAudience(IotHubConnectionString connectionString) } } - internal bool IsPooling() + public bool IsPooling() { return (AuthenticationModel != AuthenticationModel.X509) && (AmqpTransportSettings?.AmqpConnectionPoolSettings?.Pooling ?? false); } diff --git a/iothub/device/src/Transport/Amqp/IAmqpConnectionHolder.cs b/iothub/device/src/Transport/Amqp/IAmqpConnectionHolder.cs index 1fd4fa5fa4..5def753284 100644 --- a/iothub/device/src/Transport/Amqp/IAmqpConnectionHolder.cs +++ b/iothub/device/src/Transport/Amqp/IAmqpConnectionHolder.cs @@ -10,11 +10,11 @@ namespace Microsoft.Azure.Devices.Client.Transport.Amqp { internal interface IAmqpConnectionHolder : IDisposable { - Task OpenSessionAsync(DeviceIdentity deviceIdentity, CancellationToken cancellationToken); + Task OpenSessionAsync(IDeviceIdentity deviceIdentity, CancellationToken cancellationToken); Task EnsureConnectionAsync(CancellationToken cancellationToken); - Task CreateRefresherAsync(DeviceIdentity deviceIdentity, CancellationToken cancellationToken); + Task CreateRefresherAsync(IDeviceIdentity deviceIdentity, CancellationToken cancellationToken); void Shutdown(); } diff --git a/iothub/device/src/Transport/Amqp/IAmqpUnitManager.cs b/iothub/device/src/Transport/Amqp/IAmqpUnitManager.cs index 09ffe1abc3..9f8705e290 100644 --- a/iothub/device/src/Transport/Amqp/IAmqpUnitManager.cs +++ b/iothub/device/src/Transport/Amqp/IAmqpUnitManager.cs @@ -13,7 +13,7 @@ namespace Microsoft.Azure.Devices.Client.Transport.Amqp internal interface IAmqpUnitManager { AmqpUnit CreateAmqpUnit( - DeviceIdentity deviceIdentity, + IDeviceIdentity deviceIdentity, Func onMethodCallback, Action twinMessageListener, Func onModuleMessageReceivedCallback, diff --git a/iothub/device/src/Transport/Amqp/IDeviceIdentity.cs b/iothub/device/src/Transport/Amqp/IDeviceIdentity.cs new file mode 100644 index 0000000000..e459aed554 --- /dev/null +++ b/iothub/device/src/Transport/Amqp/IDeviceIdentity.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.Azure.Devices.Client.Transport.Amqp +{ + /// + /// Interface for device configurations and common attributes. + /// + internal interface IDeviceIdentity + { + /// + /// Authentication model for the device. + /// + AuthenticationModel AuthenticationModel { get; } + + /// + /// AMQP transport layer settings of the device. + /// + AmqpTransportSettings AmqpTransportSettings { get; } + + /// + /// Device connection string details. + /// + IotHubConnectionString IotHubConnectionString { get; } + + /// + /// SDK,.NET version, Operating system and environment information. + /// + ProductInfo ProductInfo { get; } + + /// + /// Device configuration options at the time of initialization. + /// + ClientOptions Options { get; } + + /// + /// Device authentication audience. + /// + string Audience { get; } + + /// + /// Whether or not the device is part of a connection pooling. + /// + bool IsPooling(); + } +} diff --git a/iothub/device/src/Transport/AmqpIot/AmqpIotConnection.cs b/iothub/device/src/Transport/AmqpIot/AmqpIotConnection.cs index 36004aeb1d..b97c0fc8ce 100644 --- a/iothub/device/src/Transport/AmqpIot/AmqpIotConnection.cs +++ b/iothub/device/src/Transport/AmqpIot/AmqpIotConnection.cs @@ -84,7 +84,7 @@ internal async Task OpenSessionAsync(CancellationToken cancellat } } - internal async Task CreateRefresherAsync(DeviceIdentity deviceIdentity, CancellationToken cancellationToken) + internal async Task CreateRefresherAsync(IDeviceIdentity deviceIdentity, CancellationToken cancellationToken) { if (_amqpConnection.IsClosing()) { diff --git a/iothub/device/src/Transport/AmqpIot/AmqpIotSendingLink.cs b/iothub/device/src/Transport/AmqpIot/AmqpIotSendingLink.cs index 3f0518cb98..0761d48ab6 100644 --- a/iothub/device/src/Transport/AmqpIot/AmqpIotSendingLink.cs +++ b/iothub/device/src/Transport/AmqpIot/AmqpIotSendingLink.cs @@ -117,7 +117,7 @@ internal async Task SendMessagesAsync(IEnumerable m } Outcome outcome; - using (AmqpMessage amqpMessage = AmqpMessage.Create(messageList)) + using (var amqpMessage = AmqpMessage.Create(messageList)) { amqpMessage.MessageFormat = AmqpConstants.AmqpBatchedMessageFormat; outcome = await SendAmqpMessageAsync(amqpMessage, cancellationToken).ConfigureAwait(false); @@ -228,10 +228,7 @@ internal async Task SendTwinGetMessageAsync(string correlationId return new AmqpIotOutcome(outcome); } - internal async Task SendTwinPatchMessageAsync( - string correlationId, - Stream reportedProperties, - CancellationToken cancellationToken) + internal async Task SendTwinPatchMessageAsync(string correlationId, Stream reportedProperties, CancellationToken cancellationToken) { if (Logging.IsEnabled) { diff --git a/iothub/device/src/Transport/AmqpIot/AmqpIotSession.cs b/iothub/device/src/Transport/AmqpIot/AmqpIotSession.cs index cd6c6f26c1..b9c8eeb805 100644 --- a/iothub/device/src/Transport/AmqpIot/AmqpIotSession.cs +++ b/iothub/device/src/Transport/AmqpIot/AmqpIotSession.cs @@ -10,6 +10,7 @@ using Microsoft.Azure.Amqp.Framing; using Microsoft.Azure.Devices.Client.Exceptions; using Microsoft.Azure.Devices.Client.Extensions; +using Microsoft.Azure.Devices.Client.Transport.Amqp; using Microsoft.Azure.Devices.Shared; namespace Microsoft.Azure.Devices.Client.Transport.AmqpIot @@ -58,7 +59,7 @@ internal bool IsClosing() #region Telemetry links internal async Task OpenTelemetrySenderLinkAsync( - DeviceIdentity deviceIdentity, + IDeviceIdentity deviceIdentity, CancellationToken cancellationToken) { return await OpenSendingAmqpLinkAsync( @@ -75,7 +76,7 @@ internal bool IsClosing() } internal async Task OpenMessageReceiverLinkAsync( - DeviceIdentity deviceIdentity, + IDeviceIdentity deviceIdentity, CancellationToken cancellationToken) { return await OpenReceivingAmqpLinkAsync( @@ -96,7 +97,7 @@ internal bool IsClosing() #region EventLink internal async Task OpenEventsReceiverLinkAsync( - DeviceIdentity deviceIdentity, + IDeviceIdentity deviceIdentity, CancellationToken cancellationToken) { return await OpenReceivingAmqpLinkAsync( @@ -117,7 +118,7 @@ internal bool IsClosing() #region MethodLink internal async Task OpenMethodsSenderLinkAsync( - DeviceIdentity deviceIdentity, + IDeviceIdentity deviceIdentity, string correlationIdSuffix, CancellationToken cancellationToken) { @@ -135,7 +136,7 @@ internal bool IsClosing() } internal async Task OpenMethodsReceiverLinkAsync( - DeviceIdentity deviceIdentity, + IDeviceIdentity deviceIdentity, string correlationIdSuffix, CancellationToken cancellationToken) { @@ -157,7 +158,7 @@ internal bool IsClosing() #region TwinLink internal async Task OpenTwinReceiverLinkAsync( - DeviceIdentity deviceIdentity, + IDeviceIdentity deviceIdentity, string correlationIdSuffix, CancellationToken cancellationToken) { @@ -175,7 +176,7 @@ internal bool IsClosing() } internal async Task OpenTwinSenderLinkAsync( - DeviceIdentity deviceIdentity, + IDeviceIdentity deviceIdentity, string correlationIdSuffix, CancellationToken cancellationToken) { @@ -197,7 +198,7 @@ internal bool IsClosing() #region Common link handling private static async Task OpenSendingAmqpLinkAsync( - DeviceIdentity deviceIdentity, + IDeviceIdentity deviceIdentity, AmqpSession amqpSession, byte? senderSettleMode, byte? receiverSettleMode, @@ -278,7 +279,7 @@ internal bool IsClosing() } private static async Task OpenReceivingAmqpLinkAsync( - DeviceIdentity deviceIdentity, + IDeviceIdentity deviceIdentity, AmqpSession amqpSession, byte? senderSettleMode, byte? receiverSettleMode, @@ -353,7 +354,7 @@ internal bool IsClosing() } } - private static string BuildLinkAddress(DeviceIdentity deviceIdentity, string deviceTemplate, string moduleTemplate) + private static string BuildLinkAddress(IDeviceIdentity deviceIdentity, string deviceTemplate, string moduleTemplate) { string path = string.IsNullOrEmpty(deviceIdentity.IotHubConnectionString.ModuleId) ? string.Format( diff --git a/iothub/device/src/Transport/AmqpIot/AmqpIotTransport.cs b/iothub/device/src/Transport/AmqpIot/AmqpIotTransport.cs index 4af039ddb6..3406e2a13d 100644 --- a/iothub/device/src/Transport/AmqpIot/AmqpIotTransport.cs +++ b/iothub/device/src/Transport/AmqpIot/AmqpIotTransport.cs @@ -117,7 +117,7 @@ private async Task CreateClientWebSocketTransportAsync(Cancellati || (Environment.OSVersion.Version.Major == 6 && Environment.OSVersion.Version.Minor <= 1)) { - var websocket = await CreateLegacyClientWebSocketAsync( + IotHubClientWebSocket websocket = await CreateLegacyClientWebSocketAsync( websocketUri, this._amqpTransportSettings.ClientCertificate, cancellationToken) diff --git a/iothub/device/src/Transport/DefaultDelegatingHandler.cs b/iothub/device/src/Transport/DefaultDelegatingHandler.cs index 5876904905..2facb74b96 100644 --- a/iothub/device/src/Transport/DefaultDelegatingHandler.cs +++ b/iothub/device/src/Transport/DefaultDelegatingHandler.cs @@ -31,10 +31,7 @@ protected DefaultDelegatingHandler(IPipelineContext context, IDelegatingHandler public IDelegatingHandler InnerHandler { - get - { - return _innerHandler; - } + get => _innerHandler; protected set { _innerHandler = value; @@ -203,18 +200,6 @@ public virtual Task SendClientTwinPropertyPatchA return InnerHandler?.SendClientTwinPropertyPatchAsync(reportedProperties, cancellationToken) ?? Task.FromResult(null); } - public virtual Task GetPropertiesAsync(PayloadConvention payloadConvention, CancellationToken cancellationToken) - { - ThrowIfDisposed(); - return InnerHandler?.GetPropertiesAsync(payloadConvention, cancellationToken) ?? Task.FromResult(null); - } - - public virtual Task SendPropertyPatchAsync(ClientPropertyCollection reportedProperties, CancellationToken cancellationToken) - { - ThrowIfDisposed(); - return InnerHandler?.SendPropertyPatchAsync(reportedProperties, cancellationToken) ?? Task.FromResult(null); - } - public virtual bool IsUsable => InnerHandler?.IsUsable ?? true; protected void ThrowIfDisposed() diff --git a/iothub/device/src/Transport/ErrorDelegatingHandler.cs b/iothub/device/src/Transport/ErrorDelegatingHandler.cs index d0f881fcd4..83942ad5d1 100644 --- a/iothub/device/src/Transport/ErrorDelegatingHandler.cs +++ b/iothub/device/src/Transport/ErrorDelegatingHandler.cs @@ -158,9 +158,12 @@ private static bool IsSecurityExceptionChain(Exception exceptionChain) private static bool IsTlsSecurity(Exception singleException) { if (// WinHttpException (0x80072F8F): A security error occurred. - (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && (singleException.HResult == unchecked((int)0x80072F8F))) || + (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) + && singleException.HResult == unchecked((int)0x80072F8F)) + || // CURLE_SSL_CACERT (60): Peer certificate cannot be authenticated with known CA certificates. - (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && (singleException.HResult == 60)) || + (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && singleException.HResult == 60) + || singleException is AuthenticationException) { return true; diff --git a/iothub/device/src/Transport/HttpTransportHandler.cs b/iothub/device/src/Transport/HttpTransportHandler.cs index 772788c9b9..494bc964da 100644 --- a/iothub/device/src/Transport/HttpTransportHandler.cs +++ b/iothub/device/src/Transport/HttpTransportHandler.cs @@ -400,12 +400,12 @@ public override Task RejectAsync(string lockToken, CancellationToken cancellatio public override Task GetClientTwinPropertiesAsync(CancellationToken cancellationToken) { - throw new NotImplementedException("This operation is not supported over HTTP. Please use MQTT or AMQP protocol instead."); + throw new NotImplementedException("This operation is not supported over HTTP. Please use MQTT protocol instead."); } public override Task SendClientTwinPropertyPatchAsync(Stream reportedProperties, CancellationToken cancellationToken) { - throw new NotImplementedException("This operation is not supported over HTTP. Please use MQTT or AMQP protocol instead."); + throw new NotImplementedException("This operation is not supported over HTTP. Please use MQTT protocol instead."); } // This is for invoking methods from an edge module to another edge device or edge module. diff --git a/iothub/device/src/Transport/IDeviceClientEndpointIdentityFactory.cs b/iothub/device/src/Transport/IDeviceClientEndpointIdentityFactory.cs deleted file mode 100644 index b0d61a6686..0000000000 --- a/iothub/device/src/Transport/IDeviceClientEndpointIdentityFactory.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. - -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -namespace Microsoft.Azure.Devices.Client.Transport -{ - /// - /// Factory interface to create DeviceClientEndpointIdentity objects for Amqp transport layer - /// - internal interface IDeviceClientEndpointIdentityFactory - { - DeviceIdentity Create(IotHubConnectionString iotHubConnectionString, AmqpTransportSettings amqpTransportSettings, ProductInfo productInfo); - } -} diff --git a/iothub/device/src/Transport/Mqtt/MqttIotHubAdapter.cs b/iothub/device/src/Transport/Mqtt/MqttIotHubAdapter.cs index e93aaf7f7a..54b51c4951 100644 --- a/iothub/device/src/Transport/Mqtt/MqttIotHubAdapter.cs +++ b/iothub/device/src/Transport/Mqtt/MqttIotHubAdapter.cs @@ -5,6 +5,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Contracts; using System.Globalization; using System.IO; @@ -235,7 +236,7 @@ public override void ChannelRead(IChannelHandlerContext context, object message) // If a message is received when the state is Connected or a CONNACK is received when the state is Connecting, then the message should be processed. if (IsInState(StateFlags.Connected) || IsInState(StateFlags.Connecting) && packet.PacketType == PacketType.CONNACK) { - ProcessMessage(context, packet); + ProcessMessageAsync(context, packet); } else { @@ -697,10 +698,10 @@ private void ProcessUnsubAck(IChannelHandlerContext context, UnsubAckPacket pack #region Receiving - private async void ProcessMessage(IChannelHandlerContext context, Packet packet) + private async void ProcessMessageAsync(IChannelHandlerContext context, Packet packet) { if (Logging.IsEnabled) - Logging.Enter(this, context.Name, packet.PacketType, nameof(ProcessMessage)); + Logging.Enter(this, context.Name, packet.PacketType, nameof(ProcessMessageAsync)); try { @@ -732,7 +733,7 @@ private async void ProcessMessage(IChannelHandlerContext context, Packet packet) default: if (Logging.IsEnabled) - Logging.Error(context, $"Received an unexpected packet type {packet.PacketType}, will shut down.", nameof(ProcessMessage)); + Logging.Error(context, $"Received an unexpected packet type {packet.PacketType}, will shut down.", nameof(ProcessMessageAsync)); ShutdownOnErrorAsync(context, new InvalidOperationException($"Unexpected packet type {packet.PacketType}")); break; @@ -741,18 +742,18 @@ private async void ProcessMessage(IChannelHandlerContext context, Packet packet) catch (Exception ex) when (!ex.IsFatal()) { if (Logging.IsEnabled) - Logging.Error(context, $"Received a non-fatal exception while processing a received packet of type {packet.PacketType}, will shut down: {ex}", nameof(ProcessMessage)); + Logging.Error(context, $"Received a non-fatal exception while processing a received packet of type {packet.PacketType}, will shut down: {ex}", nameof(ProcessMessageAsync)); ShutdownOnErrorAsync(context, ex); } finally { if (Logging.IsEnabled) - Logging.Exit(this, context.Name, packet.PacketType, nameof(ProcessMessage)); + Logging.Exit(this, context.Name, packet.PacketType, nameof(ProcessMessageAsync)); } } - [System.Diagnostics.CodeAnalysis.SuppressMessage( + [SuppressMessage( "Reliability", "CA2000:Dispose objects before losing scope", Justification = "The created message is handed to the user and the user application is in charge of disposing the message.")] diff --git a/iothub/device/src/Transport/Mqtt/MqttIotHubAdapterFactory.cs b/iothub/device/src/Transport/Mqtt/MqttIotHubAdapterFactory.cs index 536a0bfcbe..d15395125a 100644 --- a/iothub/device/src/Transport/Mqtt/MqttIotHubAdapterFactory.cs +++ b/iothub/device/src/Transport/Mqtt/MqttIotHubAdapterFactory.cs @@ -1,17 +1,15 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; - namespace Microsoft.Azure.Devices.Client.Transport.Mqtt { internal class MqttIotHubAdapterFactory { - private readonly MqttTransportSettings settings; + private readonly MqttTransportSettings _settings; public MqttIotHubAdapterFactory(MqttTransportSettings settings) { - this.settings = settings; + _settings = settings; } public MqttIotHubAdapter Create( @@ -21,7 +19,7 @@ public MqttIotHubAdapterFactory(MqttTransportSettings settings) ProductInfo productInfo, ClientOptions options) { - IWillMessage willMessage = mqttTransportSettings.HasWill ? this.settings.WillMessage : null; + IWillMessage willMessage = mqttTransportSettings.HasWill ? _settings.WillMessage : null; return new MqttIotHubAdapter( iotHubConnectionString.DeviceId, diff --git a/iothub/device/src/Transport/Mqtt/MqttTransportHandler.cs b/iothub/device/src/Transport/Mqtt/MqttTransportHandler.cs index 151aa798f2..d547c487ef 100644 --- a/iothub/device/src/Transport/Mqtt/MqttTransportHandler.cs +++ b/iothub/device/src/Transport/Mqtt/MqttTransportHandler.cs @@ -972,56 +972,6 @@ public override async Task SendClientTwinPropert return response; } - public override async Task GetPropertiesAsync(PayloadConvention payloadConvention, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - EnsureValidState(); - - using var request = new Message(); - string rid = Guid.NewGuid().ToString(); - request.MqttTopicName = TwinGetTopic.FormatInvariant(rid); - - using Message response = await SendTwinRequestAsync(request, rid, cancellationToken).ConfigureAwait(false); - - using var reader = new StreamReader(response.GetBodyStream(), payloadConvention.PayloadEncoder.ContentEncoding); - string body = reader.ReadToEnd(); - - try - { - ClientTwinProperties twinProperties = JsonConvert.DeserializeObject(body); - var properties = twinProperties.ToClientProperties(payloadConvention); - return properties; - } - catch (JsonReaderException ex) - { - if (Logging.IsEnabled) - Logging.Error(this, $"Failed to parse Twin JSON: {ex}. Message body: '{body}'"); - - throw; - } - } - - public override async Task SendPropertyPatchAsync(ClientPropertyCollection reportedProperties, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - EnsureValidState(); - - byte[] body = reportedProperties.GetPayloadObjectBytes(); - using var bodyStream = new MemoryStream(body); - - using var request = new Message(bodyStream); - - string rid = Guid.NewGuid().ToString(); - request.MqttTopicName = TwinPatchTopic.FormatInvariant(rid); - - using Message message = await SendTwinRequestAsync(request, rid, cancellationToken).ConfigureAwait(false); - return new ClientPropertiesUpdateResponse - { - RequestId = message.Properties[RequestIdKey], - Version = long.Parse(message.Properties[VersionKey], CultureInfo.InvariantCulture) - }; - } - private async Task OpenInternalAsync(CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -1259,7 +1209,6 @@ private async Task SendTwinRequestAsync(Message request, string rid, Ca .Handler(new ActionChannelInitializer(ch => { var tlsHandler = new TlsHandler(streamFactory, clientTlsSettings); - ch.Pipeline.AddLast( tlsHandler, MqttEncoder.Instance, @@ -1351,6 +1300,7 @@ private async Task SendTwinRequestAsync(Message request, string rid, Ca await websocket.ConnectAsync(websocketUri, cts.Token).ConfigureAwait(false); var clientWebSocketChannel = new ClientWebSocketChannel(null, websocket); + clientWebSocketChannel .Option(ChannelOption.Allocator, UnpooledByteBufferAllocator.Default) .Option(ChannelOption.AutoRead, false) @@ -1432,7 +1382,7 @@ private static IEventLoopGroup GetEventLoopGroup() if (!string.IsNullOrWhiteSpace(envValue)) { string processorEventCountValue = Environment.ExpandEnvironmentVariables(envValue); - if (int.TryParse(processorEventCountValue, out var processorThreadCount)) + if (int.TryParse(processorEventCountValue, out int processorThreadCount)) { if (Logging.IsEnabled) Logging.Info(null, $"EventLoopGroup threads count {processorThreadCount}."); diff --git a/iothub/device/src/Transport/Mqtt/MqttTransportSettings.cs b/iothub/device/src/Transport/Mqtt/MqttTransportSettings.cs index e899c55994..ed9c960a79 100644 --- a/iothub/device/src/Transport/Mqtt/MqttTransportSettings.cs +++ b/iothub/device/src/Transport/Mqtt/MqttTransportSettings.cs @@ -232,9 +232,7 @@ public bool CertificateRevocationCheck /// public X509Certificate ClientCertificate { get; set; } - /// - /// The proxy settings to be used when communicating with IoT Hub. - /// + /// public IWebProxy Proxy { get; set; } /// diff --git a/iothub/device/src/Transport/Mqtt/OrderedTwoPhaseWorkQueue.cs b/iothub/device/src/Transport/Mqtt/OrderedTwoPhaseWorkQueue.cs index 1db0c0490a..a137946a92 100644 --- a/iothub/device/src/Transport/Mqtt/OrderedTwoPhaseWorkQueue.cs +++ b/iothub/device/src/Transport/Mqtt/OrderedTwoPhaseWorkQueue.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Collections.Generic; +using System.Collections.Concurrent; using System.Linq; using System.Threading.Tasks; using DotNetty.Transport.Channels; @@ -27,7 +27,7 @@ public IncompleteWorkItem(TWorkId id, TWork workItem) private readonly Func _getWorkId; private readonly Func _completeWorkAsync; - private readonly Queue _incompleteQueue = new Queue(); + private readonly ConcurrentQueue _incompleteQueue = new ConcurrentQueue(); public OrderedTwoPhaseWorkQueue( Func workerAsync, @@ -46,16 +46,22 @@ public Task CompleteWorkAsync(IChannelHandlerContext context, TWorkId workId) throw new IotHubException("Nothing to complete.", isTransient: false); } - IncompleteWorkItem incompleteWorkItem = _incompleteQueue.Peek(); - if (incompleteWorkItem.Id.Equals(workId)) + if (_incompleteQueue.TryDequeue(out IncompleteWorkItem incompleteWorkItem)) { - _incompleteQueue.Dequeue(); - return _completeWorkAsync(context, incompleteWorkItem.WorkItem); - } + if (incompleteWorkItem.Id.Equals(workId)) + { + return _completeWorkAsync(context, incompleteWorkItem.WorkItem); + } - throw new IotHubException( - $"Work must be complete in the same order as it was started. Expected work id: '{incompleteWorkItem.Id}', actual work id: '{workId}'", - isTransient: false); + throw new IotHubException( + $"Work must be complete in the same order as it was started. Expected work id: '{incompleteWorkItem.Id}', actual work id: '{workId}'", + isTransient: false); + } +#if NET451 + return TaskHelpers.CompletedTask; +#else + return Task.CompletedTask; +#endif } protected override async Task DoWorkAsync(IChannelHandlerContext context, TWork work) @@ -77,17 +83,17 @@ public override void Abort(Exception exception) if (stateBefore != State && State == States.Aborted) { - while (_incompleteQueue.Any()) + while (_incompleteQueue.TryDequeue(out IncompleteWorkItem workItem)) { - var workItem = _incompleteQueue.Dequeue().WorkItem as ICancellable; + var cancellableWorkItem = workItem.WorkItem as ICancellable; if (exception == null) { - workItem?.Cancel(); + cancellableWorkItem?.Cancel(); } else { - workItem?.Abort(exception); + cancellableWorkItem?.Abort(exception); } } } diff --git a/iothub/device/src/Transport/Mqtt/SimpleWorkQueue.cs b/iothub/device/src/Transport/Mqtt/SimpleWorkQueue.cs index 85d9713a2d..1395f31c1f 100644 --- a/iothub/device/src/Transport/Mqtt/SimpleWorkQueue.cs +++ b/iothub/device/src/Transport/Mqtt/SimpleWorkQueue.cs @@ -2,7 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Collections.Generic; +using System.Collections.Concurrent; using System.Linq; using System.Threading.Tasks; using DotNetty.Common.Utilities; @@ -25,14 +25,14 @@ namespace Microsoft.Azure.Devices.Client.Transport.Mqtt internal class SimpleWorkQueue { private readonly Func _workerAsync; - private readonly Queue _backlogQueue; + private readonly ConcurrentQueue _backlogQueue; private readonly TaskCompletionSource _completionSource; public SimpleWorkQueue(Func workerAsync) { _workerAsync = workerAsync; _completionSource = new TaskCompletionSource(); - _backlogQueue = new Queue(); + _backlogQueue = new ConcurrentQueue(); } protected States State { get; set; } @@ -111,9 +111,8 @@ public virtual void Abort(Exception exception) case States.FinalProcessing: State = States.Aborted; - while (_backlogQueue.Any()) + while (_backlogQueue.TryDequeue(out TWork workItem)) { - TWork workItem = _backlogQueue.Dequeue(); ReferenceCountUtil.Release(workItem); var cancellableWorkItem = workItem as ICancellable; @@ -146,9 +145,9 @@ private async void StartWorkQueueProcessingAsync(IChannelHandlerContext context) try { while (_backlogQueue.Any() - && State != States.Aborted) + && State != States.Aborted + && _backlogQueue.TryDequeue(out TWork workItem)) { - TWork workItem = _backlogQueue.Dequeue(); await DoWorkAsync(context, workItem).ConfigureAwait(false); } diff --git a/iothub/device/src/Transport/RetryDelegatingHandler.cs b/iothub/device/src/Transport/RetryDelegatingHandler.cs index 0e86bd2326..2b2ed73893 100644 --- a/iothub/device/src/Transport/RetryDelegatingHandler.cs +++ b/iothub/device/src/Transport/RetryDelegatingHandler.cs @@ -54,9 +54,7 @@ private class TransientErrorStrategy : ITransientErrorDetectionStrategy { public bool IsTransient(Exception ex) { - return ex is IotHubException - ? ((IotHubException)ex).IsTransient - : false; + return ex is IotHubException exception && exception.IsTransient; } } @@ -610,50 +608,6 @@ public override async Task SendClientTwinPropert } } - public override async Task GetPropertiesAsync(PayloadConvention payloadConvention, CancellationToken cancellationToken) - { - try - { - Logging.Enter(this, payloadConvention, cancellationToken, nameof(SendPropertyPatchAsync)); - - return await _internalRetryPolicy - .ExecuteAsync( - async () => - { - await EnsureOpenedAsync(false, cancellationToken).ConfigureAwait(false); - return await base.GetPropertiesAsync(payloadConvention, cancellationToken).ConfigureAwait(false); - }, - cancellationToken) - .ConfigureAwait(false); - } - finally - { - Logging.Exit(this, payloadConvention, cancellationToken, nameof(SendPropertyPatchAsync)); - } - } - - public override async Task SendPropertyPatchAsync(ClientPropertyCollection reportedProperties, CancellationToken cancellationToken) - { - try - { - Logging.Enter(this, reportedProperties, cancellationToken, nameof(SendPropertyPatchAsync)); - - return await _internalRetryPolicy - .ExecuteAsync( - async () => - { - await EnsureOpenedAsync(false, cancellationToken).ConfigureAwait(false); - return await base.SendPropertyPatchAsync(reportedProperties, cancellationToken).ConfigureAwait(false); - }, - cancellationToken) - .ConfigureAwait(false); - } - finally - { - Logging.Exit(this, reportedProperties, cancellationToken, nameof(SendPropertyPatchAsync)); - } - } - public override Task OpenAsync(CancellationToken cancellationToken) { return EnsureOpenedAsync(true, cancellationToken); @@ -882,8 +836,8 @@ await _internalRetryPolicy { Logging.Enter(this, timeoutHelper, nameof(OpenAsync)); - // Will throw on error. - await base.OpenAsync(timeoutHelper).ConfigureAwait(false); + // Will throw on error. + await base.OpenAsync(timeoutHelper).ConfigureAwait(false); _onConnectionStatusChanged(ConnectionStatus.Connected, ConnectionStatusChangeReason.Connection_Ok); } catch (Exception ex) when (!ex.IsFatal()) @@ -897,7 +851,7 @@ await _internalRetryPolicy } } - + } // Triggered from connection loss event diff --git a/iothub/device/tests/Amqp/AmqpConnectionPoolTests.cs b/iothub/device/tests/Amqp/AmqpConnectionPoolTests.cs new file mode 100644 index 0000000000..506aa2ff20 --- /dev/null +++ b/iothub/device/tests/Amqp/AmqpConnectionPoolTests.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using FluentAssertions; +using Microsoft.Azure.Devices.Client.Transport; +using Microsoft.Azure.Devices.Client.Transport.Amqp; +using Microsoft.Azure.Devices.Client.Transport.AmqpIot; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace Microsoft.Azure.Devices.Client.Tests.Amqp +{ + [TestClass] + public class AmqpConnectionPoolTests + { + internal class AmqpConnectionPoolTest : AmqpConnectionPool + { + private readonly IDictionary _dictionaryToUse; + + public AmqpConnectionPoolTest(IDictionary dictionaryToUse) + { + _dictionaryToUse = dictionaryToUse; + } + + protected override IDictionary GetAmqpSasGroupedPoolDictionary() + { + return _dictionaryToUse; + } + } + + [TestMethod] + public void AmqpConnectionPool_Add_Remove_ConnectionHolderIsRemoved() + { + string sharedAccessKeyName = "HubOwner"; + uint poolSize = 10; + IDeviceIdentity testDevice = CreatePooledSasGroupedDeviceIdentity(sharedAccessKeyName, poolSize); + IDictionary injectedDictionary = new Dictionary(); + + AmqpConnectionPoolTest pool = new AmqpConnectionPoolTest(injectedDictionary); + + AmqpUnit addedUnit = pool.CreateAmqpUnit(testDevice, null, null, null, null, null); + + injectedDictionary[sharedAccessKeyName].Count().Should().Be((int)poolSize); + + pool.RemoveAmqpUnit(addedUnit); + + foreach (object item in injectedDictionary[sharedAccessKeyName]) + { + item.Should().BeNull(); + } + } + + private IDeviceIdentity CreatePooledSasGroupedDeviceIdentity(string sharedAccessKeyName, uint poolSize) + { + Mock deviceIdentity = new Mock(); + + deviceIdentity.Setup(m => m.IsPooling()).Returns(true); + deviceIdentity.Setup(m => m.AuthenticationModel).Returns(AuthenticationModel.SasGrouped); + deviceIdentity.Setup(m => m.IotHubConnectionString).Returns(new IotHubConnectionString(sharedAccessKeyName: sharedAccessKeyName)); + deviceIdentity.Setup(m => m.AmqpTransportSettings).Returns(new AmqpTransportSettings(TransportType.Amqp_Tcp_Only) + { + AmqpConnectionPoolSettings = new AmqpConnectionPoolSettings() + { + Pooling = true, + MaxPoolSize = poolSize, + } + }); + + return deviceIdentity.Object; + } + } +} diff --git a/iothub/device/tests/Amqp/MoqableAmqpUnit.cs b/iothub/device/tests/Amqp/MoqableAmqpUnit.cs index f3e9004791..1e907a5bf2 100644 --- a/iothub/device/tests/Amqp/MoqableAmqpUnit.cs +++ b/iothub/device/tests/Amqp/MoqableAmqpUnit.cs @@ -1,7 +1,5 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information using System; using System.Runtime.CompilerServices; diff --git a/iothub/device/tests/DeviceClientTests.cs b/iothub/device/tests/DeviceClientTests.cs index 715b0ead00..9d3bba959e 100644 --- a/iothub/device/tests/DeviceClientTests.cs +++ b/iothub/device/tests/DeviceClientTests.cs @@ -8,6 +8,8 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; +using Microsoft.Azure.Devices.Client.Exceptions; +using Microsoft.Azure.Devices.Client.Transport; using Microsoft.Azure.Devices.Client.Transport.Mqtt; using Microsoft.Azure.Devices.Shared; using Microsoft.VisualStudio.TestTools.UnitTesting; @@ -27,13 +29,13 @@ public class DeviceClientTests private static readonly IotHubConnectionString s_cs = new IotHubConnectionString(s_csBuilder); [TestMethod] - [ExpectedException(typeof(ArgumentException))] public void DeviceAuthenticationWithX509Certificate_NullCertificate_Throws() { string hostName = "acme.azure-devices.net"; var authMethod = new DeviceAuthenticationWithX509Certificate("device1", null); - using var deviceClient = DeviceClient.Create(hostName, authMethod, TransportType.Amqp_WebSocket_Only); + Action act = () => DeviceClient.Create(hostName, authMethod, TransportType.Amqp_WebSocket_Only); + act.Should().Throw(); } [TestMethod] @@ -146,10 +148,10 @@ public void DeviceClient_ParamsGatewayAuthMethodTransportArray_Works() } [TestMethod] - [ExpectedException(typeof(ArgumentException))] public void DeviceClient_CreateFromConnectionString_WithModuleIdThrows() { - DeviceClient.CreateFromConnectionString(FakeConnectionStringWithModuleId); + Action act = () => DeviceClient.CreateFromConnectionString(FakeConnectionStringWithModuleId); + act.Should().Throw(); } /* Tests_SRS_DEVICECLIENT_28_002: [This property shall be defaulted to 240000 (4 minutes).] */ @@ -310,7 +312,6 @@ await innerHandler } [TestMethod] - // Tests_SRS_DEVICECLIENT_10_012: [** If the given methodRequestInternal argument is null, fail silently **]** public async Task DeviceClient_OnMethodCalled_NullMethodRequest() { using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); @@ -351,7 +352,6 @@ public async Task DeviceClient_OnMethodCalled_MethodRequestHasEmptyBody() } [TestMethod] - // Tests_SRS_DEVICECLIENT_28_020: [** If the given methodRequestInternal data is not valid json, respond with status code 400 (BAD REQUEST) **]** public async Task DeviceClient_OnMethodCalled_MethodRequestHasInvalidJson() { using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); @@ -372,7 +372,6 @@ public async Task DeviceClient_OnMethodCalled_MethodRequestHasInvalidJson() } [TestMethod] - // Tests_SRS_DEVICECLIENT_10_011: [ The OnMethodCalled shall invoke the specified delegate. ] public async Task DeviceClient_OnMethodCalled_MethodRequestHasValidJson() { using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); @@ -393,7 +392,6 @@ public async Task DeviceClient_OnMethodCalled_MethodRequestHasValidJson() } [TestMethod] - // Tests_SRS_DEVICECLIENT_28_021: [** If the MethodResponse from the MethodHandler is not valid json, respond with status code 500 (USER CODE EXCEPTION) **]** public async Task DeviceClient_OnMethodCalled_MethodResponseHasInvalidJson() { using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); @@ -414,7 +412,6 @@ public async Task DeviceClient_OnMethodCalled_MethodResponseHasInvalidJson() } [TestMethod] - // Tests_SRS_DEVICECLIENT_10_012: [** If the given methodRequestInternal argument is null, fail silently **]** public async Task DeviceClient_OnMethodCalled_NullMethodRequest_With_SetMethodHandler() { using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); @@ -459,8 +456,6 @@ public async Task DeviceClient_OnMethodCalled_MethodRequestHasEmptyBody_With_Set } [TestMethod] - // Tests_SRS_DEVICECLIENT_10_011: [ The OnMethodCalled shall invoke the specified delegate. ] - // Tests_SRS_DEVICECLIENT_03_013: [Otherwise, the MethodResponseInternal constructor shall be invoked with the result supplied.] public async Task DeviceClient_OnMethodCalled_MethodRequestHasValidJson_With_SetMethodHandler() { using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); @@ -483,8 +478,6 @@ public async Task DeviceClient_OnMethodCalled_MethodRequestHasValidJson_With_Set } [TestMethod] - // Tests_SRS_DEVICECLIENT_24_002: [ The OnMethodCalled shall invoke the default delegate if there is no specified delegate for that method. ] - // Tests_SRS_DEVICECLIENT_03_013: [Otherwise, the MethodResponseInternal constructor shall be invoked with the result supplied.] public async Task DeviceClient_OnMethodCalled_MethodRequestHasValidJson_With_SetMethodDefaultHandler() { using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); @@ -505,8 +498,6 @@ public async Task DeviceClient_OnMethodCalled_MethodRequestHasValidJson_With_Set } [TestMethod] - // Tests_SRS_DEVICECLIENT_24_002: [ The OnMethodCalled shall invoke the default delegate if there is no specified delegate for that method. ] - // Tests_SRS_DEVICECLIENT_03_013: [Otherwise, the MethodResponseInternal constructor shall be invoked with the result supplied.] public async Task DeviceClient_OnMethodCalled_MethodRequestHasValidJson_With_SetMethodHandlerNotMatchedAndDefaultHandler() { using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); @@ -534,8 +525,6 @@ public async Task DeviceClient_OnMethodCalled_MethodRequestHasValidJson_With_Set } [TestMethod] - // Tests_SRS_DEVICECLIENT_10_011: [ The OnMethodCalled shall invoke the specified delegate. ] - // Tests_SRS_DEVICECLIENT_03_013: [Otherwise, the MethodResponseInternal constructor shall be invoked with the result supplied.] public async Task DeviceClient_OnMethodCalled_MethodRequestHasValidJson_With_SetMethodHandlerAndDefaultHandler() { using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); @@ -563,8 +552,6 @@ public async Task DeviceClient_OnMethodCalled_MethodRequestHasValidJson_With_Set } [TestMethod] - // Tests_SRS_DEVICECLIENT_10_011: [ The OnMethodCalled shall invoke the specified delegate. ] - // Tests_SRS_DEVICECLIENT_03_012: [If the MethodResponse does not contain result, the MethodResponseInternal constructor shall be invoked with no results.] public async Task DeviceClient_OnMethodCalled_MethodRequestHasValidJson_With_SetMethodHandler_With_No_Result() { using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); @@ -587,7 +574,6 @@ public async Task DeviceClient_OnMethodCalled_MethodRequestHasValidJson_With_Set } [TestMethod] - // Tests_SRS_DEVICECLIENT_28_021: [** If the MethodResponse from the MethodHandler is not valid json, respond with status code 500 (USER CODE EXCEPTION) **]** public async Task DeviceClientOnMethodCalledMethodResponseHasInvalidJsonWithSetMethodHandler() { using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); @@ -610,7 +596,6 @@ public async Task DeviceClientOnMethodCalledMethodResponseHasInvalidJsonWithSetM } [TestMethod] - // Tests_SRS_DEVICECLIENT_10_013: [** If the given method does not have an associated delegate and no default delegate was registered, respond with status code 501 (METHOD NOT IMPLEMENTED) **]** public async Task DeviceClientOnMethodCalledNoMethodHandler() { using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); @@ -625,8 +610,6 @@ public async Task DeviceClientOnMethodCalledNoMethodHandler() } [TestMethod] - // Tests_SRS_DEVICECLIENT_10_001: [ It shall lazy-initialize the deviceMethods property. ] - // Tests_SRS_DEVICECLIENT_10_003: [ The given delegate will only be added if it is not null. ] public async Task DeviceClientSetMethodHandlerSetFirstMethodHandler() { string connectionString = "HostName=acme.azure-devices.net;SharedAccessKeyName=AllAccessKey;DeviceId=dumpy;SharedAccessKey=dGVzdFN0cmluZzE="; @@ -673,8 +656,6 @@ public async Task DeviceClientSetMethodHandlerSetFirstMethodHandler() } [TestMethod] - // Tests_SRS_DEVICECLIENT_10_001: [ It shall lazy-initialize the deviceMethods property. ] - // Tests_SRS_DEVICECLIENT_10_003: [ The given delegate will only be added if it is not null. ] public async Task DeviceClientSetMethodHandlerSetFirstMethodDefaultHandler() { string connectionString = "HostName=acme.azure-devices.net;SharedAccessKeyName=AllAccessKey;DeviceId=dumpy;SharedAccessKey=dGVzdFN0cmluZzE="; @@ -721,7 +702,6 @@ public async Task DeviceClientSetMethodHandlerSetFirstMethodDefaultHandler() } [TestMethod] - // Tests_SRS_DEVICECLIENT_10_002: [ If the given methodName already has an associated delegate, the existing delegate shall be removed. ] public async Task DeviceClientSetMethodHandlerOverwriteExistingDelegate() { string connectionString = "HostName=acme.azure-devices.net;SharedAccessKeyName=AllAccessKey;DeviceId=dumpy;SharedAccessKey=dGVzdFN0cmluZzE="; @@ -781,7 +761,6 @@ public async Task DeviceClientSetMethodHandlerOverwriteExistingDelegate() } [TestMethod] - // Tests_SRS_DEVICECLIENT_24_001: [ If the default callback has already been set, it is replaced with the new callback. ] public async Task DeviceClientSetMethodHandlerOverwriteExistingDefaultDelegate() { string connectionString = "HostName=acme.azure-devices.net;SharedAccessKeyName=AllAccessKey;DeviceId=dumpy;SharedAccessKey=dGVzdFN0cmluZzE="; @@ -841,8 +820,6 @@ public async Task DeviceClientSetMethodHandlerOverwriteExistingDefaultDelegate() } [TestMethod] - // Tests_SRS_DEVICECLIENT_10_004: [ The deviceMethods property shall be deleted if the last delegate has been removed. ] - // Tests_SRS_DEVICECLIENT_10_006: [ It shall DisableMethodsAsync when the last delegate has been removed. ] public async Task DeviceClientSetMethodHandlerUnsetLastMethodHandler() { string connectionString = "HostName=acme.azure-devices.net;SharedAccessKeyName=AllAccessKey;DeviceId=dumpy;SharedAccessKey=dGVzdFN0cmluZzE="; @@ -885,8 +862,6 @@ public async Task DeviceClientSetMethodHandlerUnsetLastMethodHandler() } [TestMethod] - // Tests_SRS_DEVICECLIENT_10_004: [ The deviceMethods property shall be deleted if the last delegate has been removed. ] - // Tests_SRS_DEVICECLIENT_10_006: [ It shall DisableMethodsAsync when the last delegate has been removed. ] public async Task DeviceClientSetMethodHandlerUnsetLastMethodHandlerWithDefaultHandlerSet() { string connectionString = "HostName=acme.azure-devices.net;SharedAccessKeyName=AllAccessKey;DeviceId=dumpy;SharedAccessKey=dGVzdFN0cmluZzE="; @@ -946,8 +921,6 @@ public async Task DeviceClientSetMethodHandlerUnsetLastMethodHandlerWithDefaultH } [TestMethod] - // Tests_SRS_DEVICECLIENT_10_004: [ The deviceMethods property shall be deleted if the last delegate has been removed. ] - // Tests_SRS_DEVICECLIENT_10_006: [ It shall DisableMethodsAsync when the last delegate has been removed. ] public async Task DeviceClientSetMethodHandlerUnsetDefaultHandlerSet() { string connectionString = "HostName=acme.azure-devices.net;SharedAccessKeyName=AllAccessKey;DeviceId=dumpy;SharedAccessKey=dGVzdFN0cmluZzE="; @@ -1020,8 +993,6 @@ public async Task DeviceClientSetMethodHandlerUnsetWhenNoMethodHandler() } [TestMethod] - // Tests_SRS_DEVICECLIENT_10_001: [ It shall lazy-initialize the deviceMethods property. ] - // Tests_SRS_DEVICECLIENT_10_003: [ The given delegate will only be added if it is not null. ] public async Task DeviceClientSetMethodHandlerSetFirstMethodHandlerWithSetMethodHandler() { string connectionString = "HostName=acme.azure-devices.net;SharedAccessKeyName=AllAccessKey;DeviceId=dumpy;SharedAccessKey=dGVzdFN0cmluZzE="; @@ -1059,7 +1030,6 @@ public async Task DeviceClientSetMethodHandlerSetFirstMethodHandlerWithSetMethod } [TestMethod] - // Tests_SRS_DEVICECLIENT_10_002: [ If the given methodName already has an associated delegate, the existing delegate shall be removed. ] public async Task DeviceClientSetMethodHandlerOverwriteExistingDelegateWithSetMethodHandler() { string connectionString = "HostName=acme.azure-devices.net;SharedAccessKeyName=AllAccessKey;DeviceId=dumpy;SharedAccessKey=dGVzdFN0cmluZzE="; @@ -1121,8 +1091,6 @@ public async Task DeviceClientSetMethodHandlerOverwriteExistingDelegateWithSetMe } [TestMethod] - // Tests_SRS_DEVICECLIENT_10_004: [ The deviceMethods property shall be deleted if the last delegate has been removed. ] - // Tests_SRS_DEVICECLIENT_10_006: [ It shall DisableMethodsAsync when the last delegate has been removed. ] public async Task DeviceClientSetMethodHandlerUnsetLastMethodHandlerWithSetMethodHandler() { string connectionString = "HostName=acme.azure-devices.net;SharedAccessKeyName=AllAccessKey;DeviceId=dumpy;SharedAccessKey=dGVzdFN0cmluZzE="; @@ -1182,9 +1150,6 @@ public async Task DeviceClientSetMethodHandlerUnsetWhenNoMethodHandlerWithSetMet } [TestMethod] - // Tests_SRS_DEVICECLIENT_28_024: [** `OnConnectionOpened` shall invoke the connectionStatusChangesHandler if ConnectionStatus is changed **]** - // Tests_SRS_DEVICECLIENT_28_025: [** `SetConnectionStatusChangesHandler` shall set connectionStatusChangesHandler **]** - // Tests_SRS_DEVICECLIENT_28_026: [** `SetConnectionStatusChangesHandler` shall unset connectionStatusChangesHandler if `statusChangesHandler` is null **]** public void DeviceClientOnConnectionOpenedInvokeHandlerForStatusChange() { using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); @@ -1208,7 +1173,6 @@ public void DeviceClientOnConnectionOpenedInvokeHandlerForStatusChange() } [TestMethod] - // Tests_SRS_DEVICECLIENT_28_026: [** `SetConnectionStatusChangesHandler` shall unset connectionStatusChangesHandler if `statusChangesHandler` is null **]** public void DeviceClientOnConnectionOpenedWithNullHandler() { using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); @@ -1231,7 +1195,6 @@ public void DeviceClientOnConnectionOpenedWithNullHandler() } [TestMethod] - // Tests_SRS_DEVICECLIENT_28_024: [** `OnConnectionOpened` shall invoke the connectionStatusChangesHandler if ConnectionStatus is changed **]** public void DeviceClientOnConnectionOpenedNotInvokeHandlerWithoutStatusChange() { using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); @@ -1261,8 +1224,6 @@ public void DeviceClientOnConnectionOpenedNotInvokeHandlerWithoutStatusChange() } [TestMethod] - // Tests_SRS_DEVICECLIENT_28_022: [** `OnConnectionClosed` shall invoke the RecoverConnections process. **]** - // Tests_SRS_DEVICECLIENT_28_023: [** `OnConnectionClosed` shall invoke the connectionStatusChangesHandler if ConnectionStatus is changed. **]** public void DeviceClientOnConnectionClosedInvokeHandlerAndRecoveryForStatusChange() { using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); @@ -1306,99 +1267,113 @@ public void ProductInfoStoresProductInfoOk() } [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public async Task CompleteAsyncThrowsForNullMessage() + public void CompleteAsyncThrowsForNullMessage() { DeviceClient client = DeviceClient.CreateFromConnectionString(FakeConnectionString); - await client.CompleteAsync((Message)null); + + Func act = async () => await client.CompleteAsync((Message)null); + + act.Should().Throw(); } [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public async Task CompleteAsyncWithCancellationTokenThrowsForNullMessage() + public void CompleteAsyncWithCancellationTokenThrowsForNullMessage() { DeviceClient client = DeviceClient.CreateFromConnectionString(FakeConnectionString); - await client.CompleteAsync((Message)null, CancellationToken.None); + + Func act = async () => await client.CompleteAsync((Message)null, CancellationToken.None); + + act.Should().Throw(); } [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public async Task CompleteAsyncThrowsForNullLockToken() + public void CompleteAsyncThrowsForNullLockToken() { DeviceClient client = DeviceClient.CreateFromConnectionString(FakeConnectionString); - await client.CompleteAsync((string)null); + + Func act = async () => await client.CompleteAsync((string)null); + act.Should().Throw(); } [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public async Task CompleteAsyncWithCancellationTokenThrowsForNullLockToken() + public void CompleteAsyncWithCancellationTokenThrowsForNullLockToken() { DeviceClient client = DeviceClient.CreateFromConnectionString(FakeConnectionString); - await client.CompleteAsync((string)null, CancellationToken.None); + + Func act = async () => await client.CompleteAsync((string)null, CancellationToken.None); + act.Should().Throw(); } [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public async Task RejectAsyncThrowsForNullMessage() + public void RejectAsyncThrowsForNullMessage() { DeviceClient client = DeviceClient.CreateFromConnectionString(FakeConnectionString); - await client.RejectAsync((Message)null); + + Func act = async () => await client.RejectAsync((Message)null); + act.Should().Throw(); } [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public async Task RejectAsyncWithCancellationTokenThrowsForNullMessage() + public void RejectAsyncWithCancellationTokenThrowsForNullMessage() { DeviceClient client = DeviceClient.CreateFromConnectionString(FakeConnectionString); - await client.RejectAsync((Message)null, CancellationToken.None); + + Func act = async () => await client.RejectAsync((Message)null, CancellationToken.None); + act.Should().Throw(); } [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public async Task RejectAsyncThrowsForNullLockToken() + public void RejectAsyncThrowsForNullLockToken() { DeviceClient client = DeviceClient.CreateFromConnectionString(FakeConnectionString); - await client.RejectAsync((string)null); + + Func act = async () => await client.RejectAsync((string)null); + act.Should().Throw(); } [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public async Task RejectAsyncWithCancellationTokenThrowsForNullLockToken() + public void RejectAsyncWithCancellationTokenThrowsForNullLockToken() { DeviceClient client = DeviceClient.CreateFromConnectionString(FakeConnectionString); - await client.RejectAsync((string)null, CancellationToken.None); + + Func act = async () => await client.RejectAsync((string)null, CancellationToken.None); + act.Should().Throw(); } [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public async Task AbandonAsyncThrowsForNullMessage() + public void AbandonAsyncThrowsForNullMessage() { DeviceClient client = DeviceClient.CreateFromConnectionString(FakeConnectionString); - await client.AbandonAsync((Message)null); + + Func act = async () => await client.AbandonAsync((Message)null); + act.Should().Throw(); } [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public async Task AbandonAsyncWithCancellationTokenThrowsForNullMessage() + public void AbandonAsyncWithCancellationTokenThrowsForNullMessage() { DeviceClient client = DeviceClient.CreateFromConnectionString(FakeConnectionString); - await client.AbandonAsync((Message)null, CancellationToken.None); + + Func act = async () => await client.AbandonAsync((Message)null, CancellationToken.None); + act.Should().Throw(); } [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public async Task AbandonAsyncThrowsForNullLockToken() + public void AbandonAsyncThrowsForNullLockToken() { DeviceClient client = DeviceClient.CreateFromConnectionString(FakeConnectionString); - await client.AbandonAsync((string)null); + + Func act = async () => await client.AbandonAsync((string)null); + act.Should().Throw(); } [TestMethod] - [ExpectedException(typeof(ArgumentNullException))] - public async Task AbandonAsyncWithCancellationTokenThrowsForNullLockToken() + public void AbandonAsyncWithCancellationTokenThrowsForNullLockToken() { DeviceClient client = DeviceClient.CreateFromConnectionString(FakeConnectionString); - await client.AbandonAsync((string)null, CancellationToken.None); + + Func act = async () => await client.AbandonAsync((string)null, CancellationToken.None); + act.Should().Throw(); } [TestMethod] @@ -1624,8 +1599,8 @@ public void DeviceClient_CreateFromConnectionString_SasTokenTimeToLiveRenewalCon // Since the initialization is internal to the ClientFactory logic and is not observable, we will allow a buffer period to our assertions. var buffer = TimeSpan.FromSeconds(2); - // The initial expiration time calculated is (current utc time - sas ttl supplied). - // The actual expiration time associated with a sas token is recalculated during token generation, but relies on the same sas ttl supplied. + // The initial expiration time calculated is (current UTC time - sas TTL supplied). + // The actual expiration time associated with a sas token is recalculated during token generation, but relies on the same sas TTL supplied. var expectedExpirationTime = startTime.Add(-sasTokenTimeToLive); authMethod.ExpiresOn.Should().BeCloseTo(expectedExpirationTime, (int)buffer.TotalMilliseconds); @@ -1647,7 +1622,7 @@ public void DeviceClient_CreateFromAuthenticationMethod_SasTokenTimeToLiveRenewa }; var pipelineBuilderSubstitute = Substitute.For(); - // This authentication method relies on the default sas token time to live and renewal buffer set by the sdk. + // This authentication method relies on the default sas token time to live and renewal buffer set by the SDK. // These values are 1 hour for sas token expiration and renewed when 15% or less of its lifespan is left. var authMethod1 = new TestDeviceAuthenticationWithTokenRefresh(); int sasExpirationTimeInSecondsSdkDefault = DeviceAuthenticationWithTokenRefresh.DefaultTimeToLiveSeconds; @@ -1666,8 +1641,8 @@ public void DeviceClient_CreateFromAuthenticationMethod_SasTokenTimeToLiveRenewa // Since the initialization is internal to the ClientFactory logic and is not observable, we will allow a buffer period to our assertions. var buffer = TimeSpan.FromSeconds(2); - // The initial expiration time calculated is (current utc time - sas ttl supplied). - // The actual expiration time associated with a sas token is recalculated during token generation, but relies on the same sas ttl supplied. + // The initial expiration time calculated is (current UTC time - sas TTL supplied). + // The actual expiration time associated with a sas token is recalculated during token generation, but relies on the same sas TTL supplied. var sasExpirationTimeFromClientOptions = startTime.Add(-sasTokenTimeToLive); authMethod.ExpiresOn.Should().NotBeCloseTo(sasExpirationTimeFromClientOptions, (int)buffer.TotalMilliseconds); @@ -1773,9 +1748,346 @@ public void DeviceClient_InitWithHttpTransportButNoModelId_DoesNotThrow() .NotThrow(); } + [TestMethod] + public void DeviceClient_ReceiveAsync_Cancelled_MaintainLegacyExecptionBehavior() + { + //arrange + using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); + + var mainProtocolHandler = Substitute.For(); + + // We will setup the main handler which can be either MQTT or AMQP or HTTP handler to throw + // a cancellation token expiry exception (OperationCancelledException) To ensure that we mimic when a token expires. + mainProtocolHandler + .When(x => x.ReceiveAsync(Arg.Any())) + .Do(x => { throw new OperationCanceledException(); }); + + ErrorDelegatingHandler errorHandler = new ErrorDelegatingHandler(null, mainProtocolHandler); + + deviceClient.InnerHandler = errorHandler; + + // We will pass in an expired token to make sure the ErrorDelegationHandler will not throw a different type of exception. + // This can happen if the ErrorDelegationHandler checks the token for expiry before calling into the protocol specific delegate. + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + // act + + Func act = async () => await deviceClient.ReceiveAsync(cts.Token); + + // assert + + act.Should().Throw(); + } + + [TestMethod] + public void DeviceClient_CompleteAsync_Cancelled_MaintainLegacyExecptionBehavior() + { + // arrange + + using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); + + var mainProtocolHandler = Substitute.For(); + + // We will setup the main handler which can be either MQTT or AMQP or HTTP handler to throw + // a cancellation token expiry exception (OperationCancelledException) To ensure that we mimic when a token expires. + mainProtocolHandler + .When(x => x.CompleteAsync(Arg.Any(), Arg.Any())) + .Do(x => { throw new OperationCanceledException(); }); + + ErrorDelegatingHandler errorHandler = new ErrorDelegatingHandler(null, mainProtocolHandler); + + deviceClient.InnerHandler = errorHandler; + + // We will pass in an expired token to make sure the ErrorDelegationHandler or the InternalClient will not throw a different type of exception. + // This can happen if the ErrorDelegationHandler or the InternalClient checks the token for expiry before calling into the protocol specific delegate. + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + // act + + Func act = async () => await deviceClient.CompleteAsync("SomeToken", cts.Token); + + // assert + + act.Should().Throw(); + } + + [TestMethod] + public void DeviceClient_RejectAsync_Cancelled_MaintainLegacyExecptionBehavior() + { + // arrange + + using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); + + var mainProtocolHandler = Substitute.For(); + + // We will setup the main handler which can be either MQTT or AMQP or HTTP handler to throw + // a cancellation token expiry exception (OperationCancelledException) To ensure that we mimic when a token expires. + mainProtocolHandler + .When(x => x.RejectAsync(Arg.Any(), Arg.Any())) + .Do(x => { throw new OperationCanceledException(); }); + + ErrorDelegatingHandler errorHandler = new ErrorDelegatingHandler(null, mainProtocolHandler); + + deviceClient.InnerHandler = errorHandler; + + // act + + // We will pass in an expired token to make sure the ErrorDelegationHandler or the InternalClient will not throw a different type of exception. + // This can happen if the ErrorDelegationHandler or the InternalClient checks the token for expiry before calling into the protocol specific delegate. + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + Func act = async () => await deviceClient.RejectAsync("SomeToken", cts.Token); + + // assert + + act.Should().Throw(); + } + + [TestMethod] + public void DeviceClient_SendEventAsync_Cancelled_MaintainLegacyExecptionBehavior() + { + //arrange + + using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); + + var mainProtocolHandler = Substitute.For(); + + // We will setup the main handler which can be either MQTT or AMQP or HTTP handler to throw + // a cancellation token expiry exception (OperationCancelledException) To ensure that we mimic when a token expires. + mainProtocolHandler + .When(x => x.SendEventAsync(Arg.Any(), Arg.Any())) + .Do(x => { throw new OperationCanceledException(); }); + + ErrorDelegatingHandler errorHandler = new ErrorDelegatingHandler(null, mainProtocolHandler); + + deviceClient.InnerHandler = errorHandler; + + // act + + // We will pass in an expired token to make sure the ErrorDelegationHandler or the InternalClient will not throw a different type of exception. + // This can happen if the ErrorDelegationHandler or the InternalClient checks the token for expiry before calling into the protocol specific delegate. + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + using var message = new Message(); + Func act = async () => await deviceClient.SendEventAsync(message, cts.Token); + + // assert + + act.Should().Throw(); + } + + [TestMethod] + public void DeviceClient_OpenAsync_Cancelled_MaintainLegacyExecptionBehavior() + { + // arrange + + using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); + + var mainProtocolHandler = Substitute.For(); + + // We will setup the main handler which can be either MQTT or AMQP or HTTP handler to throw + // a cancellation token expiry exception (OperationCancelledException) To ensure that we mimic when a token expires. + mainProtocolHandler + .When(x => x.OpenAsync(Arg.Any())) + .Do(x => { throw new OperationCanceledException(); }); + + ErrorDelegatingHandler errorHandler = new ErrorDelegatingHandler(null, mainProtocolHandler); + + deviceClient.InnerHandler = errorHandler; + + // act + + // We will pass in an expired token to make sure the ErrorDelegationHandler or the InternalClient will not throw a different type of exception. + // This can happen if the ErrorDelegationHandler or the InternalClient checks the token for expiry before calling into the protocol specific delegate. + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + Func act = async () => await deviceClient.OpenAsync(cts.Token); + + // assert + + act.Should().Throw(); + } + + [TestMethod] + public void DeviceClient_AbandonAsync_Cancelled_MaintainLegacyExecptionBehavior() + { + // arrange + + using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); + + var mainProtocolHandler = Substitute.For(); + + // We will setup the main handler which can be either MQTT or AMQP or HTTP handler to throw + // a cancellation token expiry exception (OperationCancelledException) To ensure that we mimic when a token expires. + mainProtocolHandler + .When(x => x.AbandonAsync(Arg.Any(), Arg.Any())) + .Do(x => { throw new OperationCanceledException(); }); + + ErrorDelegatingHandler errorHandler = new ErrorDelegatingHandler(null, mainProtocolHandler); + + deviceClient.InnerHandler = errorHandler; + + // act + + // We will pass in an expired token to make sure the ErrorDelegationHandler or the InternalClient will not throw a different type of exception. + // This can happen if the ErrorDelegationHandler or the InternalClient checks the token for expiry before calling into the protocol specific delegate. + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + Func act = async () => await deviceClient.AbandonAsync("SomeLockToken", cts.Token); + + // assert + + act.Should().Throw(); + } + + [TestMethod] + public void DeviceClient_UpdateReportedPropertiesAsync_Cancelled_MaintainLegacyExecptionBehavior() + { + //arrange + + using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); + + var mainProtocolHandler = Substitute.For(); + + // We will setup the main handler which can be either MQTT or AMQP or HTTP handler to throw + // a cancellation token expiry exception (OperationCancelledException) To ensure that we mimic when a token expires. + mainProtocolHandler + .When(x => x.SendClientTwinPropertyPatchAsync(Arg.Any(), Arg.Any())) + .Do(x => { throw new OperationCanceledException(); }); + + ErrorDelegatingHandler errorHandler = new ErrorDelegatingHandler(null, mainProtocolHandler); + + deviceClient.InnerHandler = errorHandler; + + // act + + // We will pass in an expired token to make sure the ErrorDelegationHandler or the InternalClient will not throw a different type of exception. + // This can happen if the ErrorDelegationHandler or the InternalClient checks the token for expiry before calling into the protocol specific delegate. + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + Func act = async () => await deviceClient.UpdateReportedPropertiesAsync(new TwinCollection(), cts.Token); + + // assert + + act.Should().Throw(); + } + + [TestMethod] + public void DeviceClient_GetTwinAsync_Cancelled_MaintainLegacyExecptionBehavior() + { + // arrange + + using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); + + var mainProtocolHandler = Substitute.For(); + + // We will setup the main handler which can be either MQTT or AMQP or HTTP handler to throw + // a cancellation token expiry exception (OperationCancelledException) To ensure that we mimic when a token expires. + mainProtocolHandler + .When(x => x.GetClientTwinPropertiesAsync(Arg.Any())) + .Do(x => { throw new OperationCanceledException(); }); + + ErrorDelegatingHandler errorHandler = new ErrorDelegatingHandler(null, mainProtocolHandler); + + deviceClient.InnerHandler = errorHandler; + + // act + + // We will pass in an expired token to make sure the ErrorDelegationHandler or the InternalClient will not throw a different type of exception. + // This can happen if the ErrorDelegationHandler or the InternalClient checks the token for expiry before calling into the protocol specific delegate. + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + Func act = async () => await deviceClient.GetTwinAsync(cts.Token); + + // assert + + act.Should().Throw(); + } + + [TestMethod] + public void DeviceClient_CloseAsync_Cancelled_MaintainLegacyExecptionBehavior() + { + // arrange + + using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); + + var mainProtocolHandler = Substitute.For(); + + // We will setup the main handler which can be either MQTT or AMQP or HTTP handler to throw + // a cancellation token expiry exception (OperationCancelledException) To ensure that we mimic when a token expires. + mainProtocolHandler + .When(x => x.CloseAsync(Arg.Any())) + .Do(x => { throw new OperationCanceledException(); }); + + ErrorDelegatingHandler errorHandler = new ErrorDelegatingHandler(null, mainProtocolHandler); + + deviceClient.InnerHandler = errorHandler; + + // act + + // We will pass in an expired token to make sure the ErrorDelegationHandler or the InternalClient will not throw a different type of exception. + // This can happen if the ErrorDelegationHandler or the InternalClient checks the token for expiry before calling into the protocol specific delegate. + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + Func act = async () => await deviceClient.CloseAsync(cts.Token); + + // assert + + act.Should().Throw(); + } + + [TestMethod] + public void DeviceClient_SetDesiredPropertyCallbackAsync_Cancelled_MaintainLegacyExecptionBehavior() + { + // arrange + + using var deviceClient = DeviceClient.CreateFromConnectionString(FakeConnectionString); + + var mainProtocolHandler = Substitute.For(); + + // We will setup the main handler which can be either MQTT or AMQP or HTTP handler to throw + // a cancellation token expiry exception (OperationCancelledException) To ensure that we mimic when a token expires. + mainProtocolHandler + .When(x => x.EnableTwinPatchAsync(Arg.Any())) + .Do(x => { throw new OperationCanceledException(); }); + + mainProtocolHandler + .When(x => x.DisableTwinPatchAsync(Arg.Any())) + .Do(x => { throw new OperationCanceledException(); }); + + ErrorDelegatingHandler errorHandler = new ErrorDelegatingHandler(null, mainProtocolHandler); + + deviceClient.InnerHandler = errorHandler; + + // act + + // We will pass in an expired token to make sure the ErrorDelegationHandler or the InternalClient will not throw a different type of exception. + // This can happen if the ErrorDelegationHandler or the InternalClient checks the token for expiry before calling into the protocol specific delegate. + using var cts = new CancellationTokenSource(); + cts.Cancel(); + + Func act = async () => await deviceClient.SetDesiredPropertyUpdateCallbackAsync( + (patch, context) => Task.FromResult(true), + deviceClient, + cts.Token); + + // assert + + act.Should().Throw(); + } + private class TestDeviceAuthenticationWithTokenRefresh : DeviceAuthenticationWithTokenRefresh { - // This authentication method relies on the default sas token time to live and renewal buffer set by the sdk. + // This authentication method relies on the default sas token time to live and renewal buffer set by the SDK. public TestDeviceAuthenticationWithTokenRefresh() : base("someTestDevice") { } diff --git a/iothub/device/tests/ErrorDelegatingHandlerTests.cs b/iothub/device/tests/ErrorDelegatingHandlerTests.cs index d287a25c1d..f55e80fdfc 100644 --- a/iothub/device/tests/ErrorDelegatingHandlerTests.cs +++ b/iothub/device/tests/ErrorDelegatingHandlerTests.cs @@ -208,7 +208,7 @@ private static async Task TestExceptionThrown(Type thrownExceptionType, Type exp var innerHandler = Substitute.For(); var sut = new ErrorDelegatingHandler(contextMock, innerHandler); - //initial OpenAsync to emulate Gatekeeper behaviour + //initial OpenAsync to emulate Gatekeeper behavior var cancellationToken = new CancellationToken(); innerHandler.OpenAsync(Arg.Any()).Returns(TaskHelpers.CompletedTask); await sut.OpenAsync(cancellationToken).ConfigureAwait(false); @@ -229,7 +229,7 @@ private static async Task TestExceptionThrown(Type thrownExceptionType, Type exp await ((Func)(() => act(sut))).ExpectedAsync(expectedExceptionType).ConfigureAwait(false); //override outcome - setup[0] = true;//otherwise previosly setup call will happen and throw; + setup[0] = true;//otherwise previously setup call will happen and throw; mockSetup(innerHandler).Returns(new Message()); //act @@ -252,7 +252,7 @@ private static async Task TestExceptionThrown(Type thrownExceptionType, Type exp innerHandler.OpenAsync(Arg.Any()).Returns(TaskHelpers.CompletedTask); var sut = new ErrorDelegatingHandler(contextMock, innerHandler); - //initial OpenAsync to emulate Gatekeeper behaviour + //initial OpenAsync to emulate Gatekeeper behavior var cancellationToken = new CancellationToken(); await sut.OpenAsync(cancellationToken).ConfigureAwait(false); @@ -272,7 +272,7 @@ private static async Task TestExceptionThrown(Type thrownExceptionType, Type exp await ((Func)(() => act(sut))).ExpectedAsync(expectedExceptionType).ConfigureAwait(false); //override outcome - setup[0] = true;//otherwise previosly setup call will happen and throw; + setup[0] = true;//otherwise previously setup call will happen and throw; mockSetup(innerHandler).Returns(TaskHelpers.CompletedTask); //act @@ -310,7 +310,7 @@ private static async Task TestExceptionThrown(Type thrownExceptionType, Type exp await ((Func)(() => act(sut))).ExpectedAsync(expectedExceptionType).ConfigureAwait(false); //override outcome - setup[0] = true;//otherwise previosly setup call will happen and throw; + setup[0] = true;//otherwise previously setup call will happen and throw; mockSetup(innerHandler).Returns(TaskHelpers.CompletedTask); //act diff --git a/iothub/device/tests/Microsoft.Azure.Devices.Client.Tests.csproj b/iothub/device/tests/Microsoft.Azure.Devices.Client.Tests.csproj index fe56d9a840..bd290e6dfe 100644 --- a/iothub/device/tests/Microsoft.Azure.Devices.Client.Tests.csproj +++ b/iothub/device/tests/Microsoft.Azure.Devices.Client.Tests.csproj @@ -26,6 +26,8 @@ + + diff --git a/iothub/service/src/Common/Amqp/LegacyClientWebSocketTransport.cs b/iothub/service/src/Common/Amqp/LegacyClientWebSocketTransport.cs index 0ed936706e..91987b998e 100644 --- a/iothub/service/src/Common/Amqp/LegacyClientWebSocketTransport.cs +++ b/iothub/service/src/Common/Amqp/LegacyClientWebSocketTransport.cs @@ -197,7 +197,7 @@ protected override bool OpenInternal() protected override bool CloseInternal() { - var webSocketState = this.webSocket.State; + IotHubClientWebSocket.WebSocketState webSocketState = this.webSocket.State; if (webSocketState != IotHubClientWebSocket.WebSocketState.Closed && webSocketState != IotHubClientWebSocket.WebSocketState.Aborted) { this.CloseInternalAsync().Fork(); @@ -237,7 +237,7 @@ void OnReadComplete(IAsyncResult result) return; } - Task taskResult = (Task)result; + var taskResult = (Task)result; var args = (TransportAsyncCallbackArgs)taskResult.AsyncState; this.ReadTaskDone(taskResult, args); @@ -294,7 +294,7 @@ static void OnWriteComplete(IAsyncResult result) return; } - Task taskResult = (Task)result; + var taskResult = (Task)result; var args = (TransportAsyncCallbackArgs)taskResult.AsyncState; WriteTaskDone(taskResult, args); args.CompletedCallback(args); @@ -325,7 +325,7 @@ static bool WriteTaskDone(Task taskResult, TransportAsyncCallbackArgs args) void ThrowIfNotOpen() { - var webSocketState = this.webSocket.State; + IotHubClientWebSocket.WebSocketState webSocketState = this.webSocket.State; if (webSocketState == IotHubClientWebSocket.WebSocketState.Open) { return; diff --git a/iothub/service/src/Common/IOThreadScheduler.cs b/iothub/service/src/Common/IOThreadScheduler.cs index b08a2ccfef..69fc4951f4 100644 --- a/iothub/service/src/Common/IOThreadScheduler.cs +++ b/iothub/service/src/Common/IOThreadScheduler.cs @@ -176,7 +176,7 @@ private bool ScheduleCallbackHelper(Action callback, object state) if (wrapped) { // Wrapped around the circular buffer. Create a new, bigger IOThreadScheduler. - IOThreadScheduler next = + var next = new IOThreadScheduler(Math.Min(this.slots.Length * 2, MaximumCapacity), this.slotsLowPri.Length); Interlocked.CompareExchange(ref IOThreadScheduler.current, next, this); } @@ -234,7 +234,7 @@ private bool ScheduleCallbackLowPriHelper(Action callback, object state) if (wrapped) { - IOThreadScheduler next = + var next = new IOThreadScheduler(this.slots.Length, Math.Min(this.slotsLowPri.Length * 2, MaximumCapacity)); Interlocked.CompareExchange(ref IOThreadScheduler.current, next, this); } diff --git a/iothub/service/src/Common/InternalBufferManager.cs b/iothub/service/src/Common/InternalBufferManager.cs index 3006c792b0..831edd0ec6 100644 --- a/iothub/service/src/Common/InternalBufferManager.cs +++ b/iothub/service/src/Common/InternalBufferManager.cs @@ -181,7 +181,7 @@ public PooledBufferManager(long maxMemoryToPool, int maxBufferSize) { this.tuningLock = new object(); this.remainingMemory = maxMemoryToPool; - List bufferPoolList = new List(); + var bufferPoolList = new List(); for (int bufferSize = minBufferSize; ;) { @@ -236,7 +236,7 @@ private void ChangeQuota(ref BufferPool bufferPool, int delta) { BufferPool oldBufferPool = bufferPool; int newLimit = oldBufferPool.Limit + delta; - BufferPool newBufferPool = BufferPool.CreatePool(oldBufferPool.BufferSize, newLimit); + var newBufferPool = BufferPool.CreatePool(oldBufferPool.BufferSize, newLimit); for (int i = 0; i < newLimit; i++) { byte[] buffer = oldBufferPool.Take(); diff --git a/iothub/service/src/Common/PartialTrustHelpers.cs b/iothub/service/src/Common/PartialTrustHelpers.cs index 96b7de624d..f6a69e5280 100644 --- a/iothub/service/src/Common/PartialTrustHelpers.cs +++ b/iothub/service/src/Common/PartialTrustHelpers.cs @@ -173,7 +173,7 @@ internal static bool HasEtwPermissions() throw new NotImplementedException(); #else //Currently unrestricted permissions are required to create Etw provider. - PermissionSet permissions = new PermissionSet(PermissionState.Unrestricted); + var permissions = new PermissionSet(PermissionState.Unrestricted); return CheckAppDomainPermissions(permissions); #endif } diff --git a/iothub/service/src/Common/ReadOnlyDictionary45.cs b/iothub/service/src/Common/ReadOnlyDictionary45.cs index 30e18eae75..afc25cc36c 100644 --- a/iothub/service/src/Common/ReadOnlyDictionary45.cs +++ b/iothub/service/src/Common/ReadOnlyDictionary45.cs @@ -348,7 +348,7 @@ void ICollection.CopyTo(Array array, int index) throw Fx.Exception.Argument(nameof(array), Resources.InvalidBufferSize); } - KeyValuePair[] pairs = array as KeyValuePair[]; + var pairs = array as KeyValuePair[]; if (pairs != null) { Dictionary.CopyTo(pairs, index); @@ -372,7 +372,7 @@ void ICollection.CopyTo(Array array, int index) try { - foreach (var item in Dictionary) + foreach (KeyValuePair item in Dictionary) { objects[index++] = new KeyValuePair(item.Key, item.Value); } diff --git a/iothub/service/src/Common/SingletonDictionary.cs b/iothub/service/src/Common/SingletonDictionary.cs index 50d2f01446..4561fb8e1a 100644 --- a/iothub/service/src/Common/SingletonDictionary.cs +++ b/iothub/service/src/Common/SingletonDictionary.cs @@ -23,7 +23,7 @@ public void Dispose() if (!this.disposed) { this.disposed = true; - foreach (var kvp in this.dictionary) + foreach (System.Collections.Generic.KeyValuePair> kvp in this.dictionary) { if (kvp.Value.Task.Status == TaskStatus.RanToCompletion) { diff --git a/iothub/service/src/Configurations/Configuration.cs b/iothub/service/src/Configurations/Configuration.cs index 50b0c597fe..6eb6987e33 100644 --- a/iothub/service/src/Configurations/Configuration.cs +++ b/iothub/service/src/Configurations/Configuration.cs @@ -9,22 +9,22 @@ namespace Microsoft.Azure.Devices { /// - /// Device configurations provide the ability to perform IoT device configuration at scale. - /// You can define configurations and summarize compliance as the configuration is applied. + /// The configuration for IoT hub device and module twins. /// /// + /// Device configurations provide the ability to perform IoT device configuration at scale. + /// You can define configurations and summarize compliance as the configuration is applied. /// See for more details. /// - [System.Diagnostics.CodeAnalysis.SuppressMessage( - "Naming", - "CA1724:Type names should not match namespaces", - Justification = "Cannot change type names as it is considered a breaking change.")] public class Configuration : IETagHolder { /// /// Initializes a new instance of the class. /// - /// The configuration Id. Lowercase and the following special characters are allowed: [-+%_*!']. + /// + /// The configuration Id. + /// Lowercase and the following special characters are allowed: [-+%_*!']. + /// public Configuration(string configurationId) : this() { @@ -41,19 +41,19 @@ internal Configuration() } /// - /// Gets the identifier for the configuration. + /// The unique identifier of the configuration. /// [JsonProperty(PropertyName = "id", Required = Required.Always)] public string Id { get; internal set; } /// - /// Gets Schema version for the configuration + /// The schema version of the configuration. /// [JsonProperty(PropertyName = "schemaVersion", NullValueHandling = NullValueHandling.Ignore)] public string SchemaVersion { get; } /// - /// Gets or sets labels for the configuration + /// The key-value pairs used to describe the configuration. /// [JsonProperty(PropertyName = "labels", NullValueHandling = NullValueHandling.Ignore)] #pragma warning disable CA2227 // Collection properties should be read only @@ -62,7 +62,7 @@ internal Configuration() #pragma warning restore CA2227 // Collection properties should be read only /// - /// Gets or sets content for the configuration + /// The content of the configuration. /// [JsonProperty(PropertyName = "content", NullValueHandling = NullValueHandling.Ignore)] public ConfigurationContent Content { get; set; } = new ConfigurationContent(); @@ -74,45 +74,48 @@ internal Configuration() public string ContentType { get; } /// - /// Gets or sets target condition for the configuration + /// The query used to define the targeted devices or modules. /// + /// + /// The query is based on twin tags and/or reported properties. + /// [JsonProperty(PropertyName = "targetCondition")] public string TargetCondition { get; set; } /// - /// Gets creation time for the configuration + /// The creation date and time of the configuration. /// [JsonProperty(PropertyName = "createdTimeUtc")] public DateTime CreatedTimeUtc { get; internal set; } /// - /// Gets last update time for the configuration + /// The update date and time of the configuration. /// [JsonProperty(PropertyName = "lastUpdatedTimeUtc")] public DateTime LastUpdatedTimeUtc { get; internal set; } /// - /// Gets or sets priority for the configuration + /// The priority number assigned to the configuration. /// [JsonProperty(PropertyName = "priority")] public int Priority { get; set; } /// - /// System configuration metrics + /// The system metrics computed by the IoT Hub that cannot be customized. /// [JsonProperty(PropertyName = "systemMetrics", NullValueHandling = NullValueHandling.Ignore)] public ConfigurationMetrics SystemMetrics { get; internal set; } = new ConfigurationMetrics(); /// - /// Custom configuration metrics + /// The custom metrics specified by the developer as queries against twin reported properties. /// [JsonProperty(PropertyName = "metrics", NullValueHandling = NullValueHandling.Ignore)] public ConfigurationMetrics Metrics { get; set; } = new ConfigurationMetrics(); /// - /// Gets or sets configuration's ETag + /// The ETag of the configuration. /// - [JsonProperty(PropertyName = "etag")] + [JsonProperty(PropertyName = "etag", NullValueHandling = NullValueHandling.Ignore)] public string ETag { get; set; } } } diff --git a/iothub/service/src/Configurations/ConfigurationContent.cs b/iothub/service/src/Configurations/ConfigurationContent.cs index a203797d55..a8077d0a45 100644 --- a/iothub/service/src/Configurations/ConfigurationContent.cs +++ b/iothub/service/src/Configurations/ConfigurationContent.cs @@ -12,7 +12,7 @@ namespace Microsoft.Azure.Devices public class ConfigurationContent { /// - /// Gets or sets the configurations to be applied. + /// The modules configuration content. /// /// /// See @@ -25,7 +25,7 @@ public class ConfigurationContent #pragma warning restore CA2227 // Collection properties should be read only /// - /// Gets or sets the configurations to be applied on device modules + /// The device module configuration content. /// [JsonProperty(PropertyName = "moduleContent")] #pragma warning disable CA2227 // Collection properties should be read only @@ -34,7 +34,7 @@ public class ConfigurationContent #pragma warning restore CA2227 // Collection properties should be read only /// - /// Gets or sets the configurations to be applied on devices. + /// The device configuration content. /// [JsonProperty(PropertyName = "deviceContent")] #pragma warning disable CA2227 // Collection properties should be read only diff --git a/iothub/service/src/Configurations/ConfigurationImportMode.cs b/iothub/service/src/Configurations/ConfigurationImportMode.cs new file mode 100644 index 0000000000..137951cc67 --- /dev/null +++ b/iothub/service/src/Configurations/ConfigurationImportMode.cs @@ -0,0 +1,21 @@ +using System.Runtime.Serialization; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Microsoft.Azure.Devices +{ + /// + /// Identifies the behavior when merging a configuration to the registry during import actions. + /// + [JsonConverter(typeof(StringEnumConverter))] + public enum ConfigurationImportMode + { + /// + /// If a configuration does not exist with the specified Id, it is newly registered. + /// If the configuration already exists, existing information is overwritten with the provided input data only if there is an ETag match. + /// If there is an ETag mismatch, an error is written to the log file. + /// + [EnumMember(Value = "createOrUpdateIfMatchETag")] + CreateOrUpdateIfMatchETag = 4, + } +} diff --git a/iothub/service/src/Configurations/ImportConfiguration.cs b/iothub/service/src/Configurations/ImportConfiguration.cs new file mode 100644 index 0000000000..0a29895799 --- /dev/null +++ b/iothub/service/src/Configurations/ImportConfiguration.cs @@ -0,0 +1,23 @@ +using Newtonsoft.Json; + +namespace Microsoft.Azure.Devices +{ + /// + /// A class for creating and serializing a for a bulk import + /// job using . + /// + public class ImportConfiguration : Configuration + { + /// + public ImportConfiguration(string configurationId) + : base(configurationId) + { + } + + /// + /// The type of registry operation and E Tag preferences. + /// + [JsonProperty(PropertyName = "importMode", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] + public ConfigurationImportMode ImportMode { get; set; } + } +} diff --git a/iothub/service/src/DigitalTwin/Authentication/DigitalTwinConnectionStringCredential.cs b/iothub/service/src/DigitalTwin/Authentication/DigitalTwinConnectionStringCredential.cs index 03838d7f40..a33d30c151 100644 --- a/iothub/service/src/DigitalTwin/Authentication/DigitalTwinConnectionStringCredential.cs +++ b/iothub/service/src/DigitalTwin/Authentication/DigitalTwinConnectionStringCredential.cs @@ -9,7 +9,7 @@ namespace Microsoft.Azure.Devices.Authentication /// /// Allows authentication to the API using a Shared Access Key generated from the connection string provided. /// The PnP client is auto generated from swagger and needs to implement a specific class to pass to the protocol layer - /// unlike the rest of the clients which are hand-written. So, this implementation for authentication is specific to digital twin (Pnp). + /// unlike the rest of the clients which are hand-written. So, this implementation for authentication is specific to digital twin (PnP). /// internal class DigitalTwinConnectionStringCredential : DigitalTwinServiceClientCredentials { diff --git a/iothub/service/src/DigitalTwin/Authentication/DigitalTwinSasCredential.cs b/iothub/service/src/DigitalTwin/Authentication/DigitalTwinSasCredential.cs index eb93a2e9ec..02d0a7bd2a 100644 --- a/iothub/service/src/DigitalTwin/Authentication/DigitalTwinSasCredential.cs +++ b/iothub/service/src/DigitalTwin/Authentication/DigitalTwinSasCredential.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; -using System.Collections.Generic; -using System.Text; + using Azure; using Microsoft.Azure.Devices.Authentication; @@ -11,11 +9,11 @@ namespace Microsoft.Azure.Devices.DigitalTwin.Authentication /// /// Allows authentication to the API using a Shared Access Key provided by custom implementation. /// The PnP client is auto generated from swagger and needs to implement a specific class to pass to the protocol layer - /// unlike the rest of the clients which are hand-written. So, this implementation for authentication is specific to digital twin (Pnp). + /// unlike the rest of the clients which are hand-written. So, this implementation for authentication is specific to digital twin (PnP). /// internal class DigitalTwinSasCredential : DigitalTwinServiceClientCredentials { - private AzureSasCredential _credential; + private readonly AzureSasCredential _credential; public DigitalTwinSasCredential(AzureSasCredential credential) { diff --git a/iothub/service/src/DigitalTwin/Authentication/DigitalTwinTokenCredential.cs b/iothub/service/src/DigitalTwin/Authentication/DigitalTwinTokenCredential.cs index 3b0bc31fe7..51a816e4b4 100644 --- a/iothub/service/src/DigitalTwin/Authentication/DigitalTwinTokenCredential.cs +++ b/iothub/service/src/DigitalTwin/Authentication/DigitalTwinTokenCredential.cs @@ -1,8 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -using System; -using System.Collections.Generic; -using System.Text; + using System.Threading; using Azure.Core; using Microsoft.Azure.Devices.Authentication; @@ -19,7 +17,7 @@ internal class DigitalTwinTokenCredential : DigitalTwinServiceClientCredentials { private readonly object _tokenLock = new object(); private AccessToken? _cachedAccessToken; - private TokenCredential _credential; + private readonly TokenCredential _credential; public DigitalTwinTokenCredential(TokenCredential credential) { diff --git a/iothub/service/src/DigitalTwin/DigitalTwinClient.cs b/iothub/service/src/DigitalTwin/DigitalTwinClient.cs index ab2d7548f4..6a237f62f2 100644 --- a/iothub/service/src/DigitalTwin/DigitalTwinClient.cs +++ b/iothub/service/src/DigitalTwin/DigitalTwinClient.cs @@ -19,8 +19,10 @@ namespace Microsoft.Azure.Devices { /// /// The Digital Twins Service Client contains methods to retrieve and update digital twin information, and invoke commands on a digital twin device. - /// For more information, see /// + /// + /// For more information, see + /// public class DigitalTwinClient : IDisposable { private const string HttpsEndpointPrefix = "https"; @@ -29,16 +31,20 @@ public class DigitalTwinClient : IDisposable /// /// Creates an instance of , provided for unit testing purposes only. - /// Use the CreateFromConnectionString or Create method to create an instance to use the client. /// public DigitalTwinClient() { } /// - /// Initializes a new instance of the class. + /// Creates DigitalTwinClient from an IoT hub connection string. + /// /// The IoT hub's connection string. - /// The delegating handlers to add to the http client pipeline. You can add handlers for tracing, implementing a retry strategy, routing requests through a proxy, etc. + /// + /// The delegating handlers to add to the http client pipeline. + /// You can add handlers for tracing, implementing a retry strategy, routing requests through a proxy, etc. + /// + /// A DigitalTwinsClient instance. public static DigitalTwinClient CreateFromConnectionString(string connectionString, params DelegatingHandler[] handlers) { connectionString.ThrowIfNullOrWhiteSpace(nameof(connectionString)); @@ -49,15 +55,21 @@ public static DigitalTwinClient CreateFromConnectionString(string connectionStri } /// - /// Creates an instance of . + /// Creates DigitalTwinClient, authenticating using an identity in Azure Active Directory (AAD). /// - /// IoT hub host name. - /// Azure Active Directory credentials to authenticate with IoT hub. See - /// The delegating handlers to add to the http client pipeline. You can add handlers for tracing, implementing a retry strategy, routing requests through a proxy, etc. - /// An instance of . /// - /// For more information on configuring IoT hub with Azure Active Directory, see + /// For more about information on the options of authenticating using a derived instance of , see + /// . + /// For more information on configuring IoT hub with Azure Active Directory, see + /// /// + /// IoT hub host name. + /// Azure Active Directory (AAD) credentials to authenticate with IoT hub. See + /// + /// The delegating handlers to add to the http client pipeline. You can add handlers for tracing, + /// implementing a retry strategy, routing requests through a proxy, etc. + /// + /// A DigitalTwinsClient instance. public static DigitalTwinClient Create( string hostName, TokenCredential credential, @@ -78,12 +90,17 @@ public static DigitalTwinClient CreateFromConnectionString(string connectionStri } /// - /// Creates an instance of . + /// Creates DigitalTwinClient using a shared access signature provided and refreshed as necessary by the caller. /// + /// + /// Users may wish to build their own shared access signature (SAS) tokens rather than give the shared key to the SDK and let it manage signing and renewal. + /// The object gives the SDK access to the SAS token, while the caller can update it as necessary using the + /// method. + /// /// IoT hub host name. /// Credential that generates a SAS token to authenticate with IoT hub. See . /// The delegating handlers to add to the http client pipeline. You can add handlers for tracing, implementing a retry strategy, routing requests through a proxy, etc. - /// An instance of . + /// A DigitalTwinsClient instance. public static DigitalTwinClient Create( string hostName, AzureSasCredential credential, @@ -124,8 +141,10 @@ public static DigitalTwinClient CreateFromConnectionString(string connectionStri /// /// Updates a digital twin. - /// For further information on how to create the json-patch, see /// + /// + /// For further information on how to create the json-patch, see . + /// /// The Id of the digital twin. /// The application/json-patch+json operations to be performed on the specified digital twin. /// The optional settings for this request. @@ -183,7 +202,7 @@ public static DigitalTwinClient CreateFromConnectionString(string connectionStri /// The command payload. /// The optional settings for this request. /// The cancellationToken. - /// The application/json command invocation response and the http response. + /// The application/json command invocation response and the http response. public virtual async Task> InvokeComponentCommandAsync( string digitalTwinId, string componentName, @@ -229,8 +248,8 @@ protected virtual void Dispose(bool disposing) private DigitalTwinClient(string hostName, DigitalTwinServiceClientCredentials credentials, params DelegatingHandler[] handlers) { - var httpsEndpoint = new UriBuilder(HttpsEndpointPrefix, hostName).Uri; - var httpMessageHandler = HttpClientHelper.CreateDefaultHttpMessageHandler(null, httpsEndpoint, ServicePointHelpers.DefaultConnectionLeaseTimeout); + Uri httpsEndpoint = new UriBuilder(HttpsEndpointPrefix, hostName).Uri; + HttpMessageHandler httpMessageHandler = HttpClientHelper.CreateDefaultHttpMessageHandler(null, httpsEndpoint, ServicePointHelpers.DefaultConnectionLeaseTimeout); #pragma warning disable CA2000 // Dispose objects before losing scope (httpMessageHandlerWithDelegatingHandlers is disposed when the http client owning it is disposed) HttpMessageHandler httpMessageHandlerWithDelegatingHandlers = CreateHttpHandlerPipeline(httpMessageHandler, handlers); #pragma warning restore CA2000 // Dispose objects before losing scope diff --git a/iothub/service/src/ExportImportDevice.cs b/iothub/service/src/ExportImportDevice.cs index f6ea5dd3c6..3ef0570bbc 100644 --- a/iothub/service/src/ExportImportDevice.cs +++ b/iothub/service/src/ExportImportDevice.cs @@ -1,12 +1,7 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. -// --------------------------------------------------------------- -// Copyright (c) Microsoft Corporation. All rights reserved. -// --------------------------------------------------------------- using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; using Microsoft.Azure.Devices.Shared; using Newtonsoft.Json; @@ -21,19 +16,34 @@ public sealed class ExportImportDevice private string _twinETag; /// - /// Type definition for the property. + /// The desired and reported properties of the twin. /// + /// + /// Type definition for the property. + /// + /// The maximum depth of the object is 10. + /// public sealed class PropertyContainer { /// - /// Desired properties are requested updates by a service client. + /// The collection of desired property key-value pairs. /// + /// + /// The keys are UTF-8 encoded, case-sensitive and up-to 1KB in length. Allowed characters + /// exclude UNICODE control characters (segments C0 and C1), '.', '$' and space. The + /// desired porperty values are JSON objects, up-to 4KB in length. + /// [JsonProperty(PropertyName = "desired", NullValueHandling = NullValueHandling.Ignore)] public TwinCollection DesiredProperties { get; set; } /// - /// Reported properties are the latest value reported by the device. + /// The collection of reported property key-value pairs. /// + /// + /// The keys are UTF-8 encoded, case-sensitive and up-to 1KB in length. Allowed characters + /// exclude UNICODE control characters (segments C0 and C1), '.', '$' and space. The + /// reported property values are JSON objects, up-to 4KB in length. + /// [JsonProperty(PropertyName = "reported", NullValueHandling = NullValueHandling.Ignore)] public TwinCollection ReportedProperties { get; set; } } @@ -65,17 +75,16 @@ public ExportImportDevice(Device device, ImportMode importmode) Authentication = device.Authentication; Capabilities = device.Capabilities; DeviceScope = device.Scope; - ParentScopes = device.ParentScopes; } /// - /// Id of the device. + /// The unique identifier of the device. /// [JsonProperty(PropertyName = "id", Required = Required.Always)] public string Id { get; set; } /// - /// Module Id for the object. + /// The unique identifier of the module, if applicable. /// [JsonProperty(PropertyName = "moduleId", NullValueHandling = NullValueHandling.Ignore)] public string ModuleId { get; set; } @@ -83,6 +92,10 @@ public ExportImportDevice(Device device, ImportMode importmode) /// /// A string representing an ETag for the entity as per RFC7232. /// + /// + /// The value is only used if import mode is updateIfMatchETag, in that case the import operation is performed + /// only if this ETag matches the value maintained by the server. + /// [JsonProperty(PropertyName = "eTag", NullValueHandling = NullValueHandling.Ignore)] public string ETag { @@ -91,32 +104,46 @@ public string ETag } /// - /// Import mode of the device. + /// The type of registry operation and ETag preferences. /// [JsonProperty(PropertyName = "importMode", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] public ImportMode ImportMode { get; set; } /// - /// Status of the device. + /// The status of the device or module. /// + /// + /// If disabled, it cannot connect to the service. + /// [JsonProperty(PropertyName = "status", Required = Required.Always)] public DeviceStatus Status { get; set; } /// - /// Status reason of the device. + /// The 128 character-long string that stores the reason for the device identity status. /// + /// + /// All UTF-8 characters are allowed. + /// [JsonProperty(PropertyName = "statusReason", NullValueHandling = NullValueHandling.Ignore)] public string StatusReason { get; set; } /// - /// Authentication mechanism of the device. + /// The authentication mechanism used by the module. /// + /// + /// This parameter is optional and defaults to SAS if not provided. In that case, primary/secondary + /// access keys are auto-generated. + /// [JsonProperty(PropertyName = "authentication")] public AuthenticationMechanism Authentication { get; set; } /// /// String representing a Twin ETag for the entity, as per RFC7232. /// + /// + /// The value is only used if import mode is updateIfMatchETag, in that case the import operation is + /// performed only if this ETag matches the value maintained by the server. + /// [JsonProperty(PropertyName = "twinETag", NullValueHandling = NullValueHandling.Ignore)] public string TwinETag { @@ -125,50 +152,37 @@ public string TwinETag } /// - /// Tags representing a collection of properties. + /// The JSON document read and written by the solution back end. The tags are not visible to device apps. /// [JsonProperty(PropertyName = "tags", NullValueHandling = NullValueHandling.Ignore)] public TwinCollection Tags { get; set; } /// - /// Desired and reported property bags + /// The desired and reported properties for the device or module. /// [JsonProperty(PropertyName = "properties", NullValueHandling = NullValueHandling.Ignore)] public PropertyContainer Properties { get; set; } /// - /// Status of capabilities enabled on the device + /// Status of capabilities enabled on the device or module. /// [JsonProperty(PropertyName = "capabilities", NullValueHandling = NullValueHandling.Ignore)] public DeviceCapabilities Capabilities { get; set; } /// - /// The scope of the device. For edge devices, this is auto-generated and immutable. For leaf devices, set this to create child/parent - /// relationship. + /// The scope of the device. For edge devices, this is auto-generated and immutable. For leaf + /// devices, set this to create child/parent relationship. /// /// - /// For leaf devices, the value to set a parent edge device can be retrieved from the parent edge device's device scope property. + /// For leaf devices, the value to set a parent edge device can be retrieved from the parent + /// edge device's device scope property. /// - /// For more information, see . + /// For more information, see + /// . /// [JsonProperty(PropertyName = "deviceScope", NullValueHandling = NullValueHandling.Include)] public string DeviceScope { get; set; } - /// - /// The scopes of the upper level edge devices if applicable. - /// - /// - /// For edge devices, the value to set a parent edge device can be retrieved from the parent edge device's property. - /// - /// For leaf devices, this could be set to the same value as or left for the service to copy over. - /// - /// For now, this list can only have 1 element in the collection. - /// - /// For more information, see . - /// - [JsonProperty(PropertyName = "parentScopes", NullValueHandling = NullValueHandling.Ignore)] - public IList ParentScopes { get; internal set; } = new List(); - private static string SanitizeETag(string eTag) { if (!string.IsNullOrWhiteSpace(eTag)) diff --git a/iothub/service/src/GlobalSuppressions.cs b/iothub/service/src/GlobalSuppressions.cs index 7fc7c806ad..576697ce43 100644 --- a/iothub/service/src/GlobalSuppressions.cs +++ b/iothub/service/src/GlobalSuppressions.cs @@ -5,5 +5,20 @@ // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Not localizing", Scope = "module")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "SDK hides non-actionable errors from user", Scope = "module")] +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage( + "Globalization", + "CA1303:Do not pass literals as localized parameters", + Justification = "Not localizing", + Scope = "module")] +[assembly: SuppressMessage( + "Design", + "CA1031:Do not catch general exception types", + Justification = "SDK hides non-actionable errors from user", + Scope = "module")] +[assembly: SuppressMessage( + "Usage", + "CA2208:Instantiate argument exceptions correctly", + Justification = "SDK throws a more descriptive argument exception message.", + Scope = "module")] diff --git a/iothub/service/src/HttpTransportSettings.cs b/iothub/service/src/HttpTransportSettings.cs index 867bee88f6..e829726d11 100644 --- a/iothub/service/src/HttpTransportSettings.cs +++ b/iothub/service/src/HttpTransportSettings.cs @@ -20,12 +20,39 @@ public HttpTransportSettings() } /// - /// Proxy information. + /// The web proxy that will be used to connect to IoT hub when using the HTTP protocol. /// + /// + /// An instance of a class that implements . + /// /// - /// This is used when a device is on a network that doesn't have direct internet access and needs to access it via a proxy, - /// especially when MQTT and AMQP ports are disallowed to the internet. + /// The class is only used for the or the ; so the proxy set here will only be valid for those clients. /// + /// + /// To set a proxy you must instantiate an instance of the class--or any class that derives from . The snippet below shows a method that returns a device using a proxy that connects to localhost on port 8888. + /// + /// static JobClient GetJobClient() + /// { + /// try + /// { + /// var proxyHost = "localhost"; + /// var proxyPort = 8888; + /// var transportSettings = new HttpTransportSettings + /// { + /// Proxy = new WebProxy(proxyHost, proxyPort) + /// }; + /// // Specify the WebProxy to be used for the HTTP connection + /// var jobClient = JobClient.CreateFromConnectionString("a connection string", transportSettings); + /// return jobClient; + /// } + /// catch (Exception) + /// { + /// Console.WriteLine("Error creating client."); + /// throw; + /// } + /// } + /// + /// public IWebProxy Proxy { get; set; } /// diff --git a/iothub/service/src/IAuthenticationMethod.cs b/iothub/service/src/IAuthenticationMethod.cs index 67abe5b9b8..5e7930a505 100644 --- a/iothub/service/src/IAuthenticationMethod.cs +++ b/iothub/service/src/IAuthenticationMethod.cs @@ -4,7 +4,7 @@ namespace Microsoft.Azure.Devices { /// - /// Authentication interface to use for IoTHub communications. + /// Authentication interface to use for IoT hub communications. /// public interface IAuthenticationMethod { diff --git a/iothub/service/src/IotHubConnectionProperties.cs b/iothub/service/src/IotHubConnectionProperties.cs index 89162d50c4..f3816297c8 100644 --- a/iothub/service/src/IotHubConnectionProperties.cs +++ b/iothub/service/src/IotHubConnectionProperties.cs @@ -1,8 +1,8 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using Microsoft.Azure.Amqp; using Microsoft.Azure.Devices.Common; diff --git a/iothub/service/src/IotHubSasCredentialProperties.cs b/iothub/service/src/IotHubSasCredentialProperties.cs index ac7fe649aa..933f400cd1 100644 --- a/iothub/service/src/IotHubSasCredentialProperties.cs +++ b/iothub/service/src/IotHubSasCredentialProperties.cs @@ -1,17 +1,16 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. + using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using Microsoft.Azure.Amqp; -using System.Globalization; -using System.Linq; -using Microsoft.Azure.Devices.Common.Data; #if !NET451 +using System.Collections.Generic; +using System.Linq; using Azure; +using Microsoft.Azure.Devices.Common.Data; #endif @@ -55,7 +54,7 @@ public override Task GetTokenAsync(Uri namespaceAddress, string applie // Parse the SAS token to find the expiration date and time. // SharedAccessSignature sr=ENCODED(dh://myiothub.azure-devices.net/a/b/c?myvalue1=a)&sig=&se=[&skn=] var tokenParts = _credential.Signature.Split('&').ToList(); - var expiresAtTokenPart = tokenParts.Where(tokenPart => tokenPart.StartsWith("se=", StringComparison.OrdinalIgnoreCase)); + IEnumerable expiresAtTokenPart = tokenParts.Where(tokenPart => tokenPart.StartsWith("se=", StringComparison.OrdinalIgnoreCase)); if (!expiresAtTokenPart.Any()) { @@ -70,8 +69,8 @@ public override Task GetTokenAsync(Uri namespaceAddress, string applie throw new InvalidOperationException($"Invalid seconds from epoch time on {nameof(AzureSasCredential)} signature."); } - DateTime epochTime = new DateTime(1970, 1, 1); - TimeSpan timeToLiveFromEpochTime = TimeSpan.FromSeconds(secondsFromEpochTime); + var epochTime = new DateTime(1970, 1, 1); + var timeToLiveFromEpochTime = TimeSpan.FromSeconds(secondsFromEpochTime); DateTime expiresAt = epochTime.Add(timeToLiveFromEpochTime); var token = new CbsToken( diff --git a/iothub/service/src/IotHubTokenCredentialProperties.cs b/iothub/service/src/IotHubTokenCredentialProperties.cs index 3591a66038..1efa16ecf2 100644 --- a/iothub/service/src/IotHubTokenCredentialProperties.cs +++ b/iothub/service/src/IotHubTokenCredentialProperties.cs @@ -1,16 +1,15 @@ // Copyright (c) Microsoft. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. + using System; -using System.Collections.Generic; -using System.Text; using System.Threading.Tasks; using Microsoft.Azure.Amqp; -using System.Threading; -using Microsoft.Azure.Devices.Common; #if !NET451 +using System.Threading; using Azure.Core; +using Microsoft.Azure.Devices.Common; #endif @@ -23,7 +22,7 @@ internal class IotHubTokenCrendentialProperties : IotHubConnectionProperties { #if !NET451 - private const string _tokenType = "Bearer"; + private const string TokenType = "Bearer"; private readonly TokenCredential _credential; private readonly object _tokenLock = new object(); private AccessToken? _cachedAccessToken; @@ -63,8 +62,7 @@ public override string GetAuthorizationHeader() } } - return $"{_tokenType} {_cachedAccessToken.Value.Token}"; - + return $"{TokenType} {_cachedAccessToken.Value.Token}"; #endif } @@ -82,8 +80,8 @@ public async override Task GetTokenAsync(Uri namespaceAddress, string new TokenRequestContext(CommonConstants.IotHubAadTokenScopes), new CancellationToken()).ConfigureAwait(false); return new CbsToken( - $"{_tokenType} {token.Token}", - _tokenType, + $"{TokenType} {token.Token}", + TokenType, token.ExpiresOn.UtcDateTime); #endif } diff --git a/iothub/service/src/JobClient/JobClient.cs b/iothub/service/src/JobClient/JobClient.cs index 18b97cea4c..e273d5fb77 100644 --- a/iothub/service/src/JobClient/JobClient.cs +++ b/iothub/service/src/JobClient/JobClient.cs @@ -23,22 +23,24 @@ namespace Microsoft.Azure.Devices /// /// Job management /// + /// + /// For more information, see . + /// public class JobClient : IDisposable { - private const string _jobsUriFormat = "/jobs/v2/{0}?{1}"; - private const string _jobsQueryFormat = "/jobs/v2/query?{0}"; - private const string _CancelJobUriFormat = "/jobs/v2/{0}/cancel?{1}"; + private const string JobsUriFormat = "/jobs/v2/{0}?{1}"; + private const string JobsQueryFormat = "/jobs/v2/query?{0}"; + private const string CancelJobUriFormat = "/jobs/v2/{0}/cancel?{1}"; - private const string _continuationTokenHeader = "x-ms-continuation"; - private const string _pageSizeHeader = "x-ms-max-item-count"; + private const string ContinuationTokenHeader = "x-ms-continuation"; + private const string PageSizeHeader = "x-ms-max-item-count"; private static readonly TimeSpan s_defaultOperationTimeout = TimeSpan.FromSeconds(100); private IHttpClientHelper _httpClientHelper; /// - /// Creates an instance of , provided for unit testing purposes only. - /// Use the CreateFromConnectionString method to create an instance to use the client. + /// Creates JobClient, provided for unit testing purposes only. /// public JobClient() { @@ -62,22 +64,21 @@ internal JobClient(IotHubConnectionProperties connectionProperties, HttpTranspor } /// - /// Creates a JobClient from the IoT Hub connection string. - /// For more information, see + /// Creates JobClient from the IoT hub connection string. /// - /// The IoT Hub connection string. - /// A JobClient instance. + /// The IoT hub connection string. + /// A JobClient instance. public static JobClient CreateFromConnectionString(string connectionString) { return CreateFromConnectionString(connectionString, new HttpTransportSettings()); } /// - /// Creates a JobClient from the IoT Hub connection string and HTTP transport settings + /// Creates JobClient from the IoT hub connection string and HTTP transport settings. /// - /// The IoT Hub connection string. - /// The HTTP transport settings. - /// A JobClient instance. + /// The IoT hub connection string. + /// The HTTP transport settings. + /// A JobClient instance. public static JobClient CreateFromConnectionString(string connectionString, HttpTransportSettings transportSettings) { if (transportSettings == null) @@ -93,15 +94,18 @@ public static JobClient CreateFromConnectionString(string connectionString, Http #if !NET451 /// - /// Creates an instance of . + /// Creates JobClient, authenticating using an identity in Azure Active Directory (AAD). /// - /// IoT hub host name. - /// Azure Active Directory credentials to authenticate with IoT hub. See - /// The HTTP transport settings. - /// An instance of . /// - /// For more information on configuring IoT hub with Azure Active Directory, see + /// For more about information on the options of authenticating using a derived instance of , see + /// . + /// For more information on configuring IoT hub with Azure Active Directory, see + /// /// + /// IoT hub host name. + /// Azure Active Directory (AAD) credentials to authenticate with IoT hub. See + /// The HTTP transport settings. + /// A JobClient instance. public static JobClient Create( string hostName, TokenCredential credential, @@ -122,12 +126,17 @@ public static JobClient CreateFromConnectionString(string connectionString, Http } /// - /// Creates an instance of . + /// Creates JobClient using a shared access signature provided and refreshed as necessary by the caller. /// + /// + /// Users may wish to build their own shared access signature (SAS) tokens rather than give the shared key to the SDK and let it manage signing and renewal. + /// The object gives the SDK access to the SAS token, while the caller can update it as necessary using the + /// method. + /// /// IoT hub host name. /// Credential that generates a SAS token to authenticate with IoT hub. See . /// The HTTP transport settings. - /// An instance of . + /// A JobClient instance. public static JobClient Create( string hostName, AzureSasCredential credential, @@ -298,7 +307,7 @@ public virtual Task CancelJobAsync(string jobId, CancellationToken EnsureInstanceNotClosed(); return _httpClientHelper.PostAsync( - new Uri(_CancelJobUriFormat.FormatInvariant(jobId, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative), + new Uri(CancelJobUriFormat.FormatInvariant(jobId, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative), null, null, null, @@ -402,7 +411,7 @@ private void EnsureInstanceNotClosed() private static Uri GetJobUri(string jobId) { - return new Uri(_jobsUriFormat.FormatInvariant(jobId ?? string.Empty, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); + return new Uri(JobsUriFormat.FormatInvariant(jobId ?? string.Empty, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); } private Task CreateJobAsync(JobRequest jobRequest, CancellationToken cancellationToken) @@ -442,7 +451,7 @@ await ExceptionHandlingHelper.GetExceptionMessageAsync(responseMessage).Configur private static Uri BuildQueryJobUri(JobType? jobType, JobStatus? jobStatus) { - var stringBuilder = new StringBuilder(_jobsQueryFormat.FormatInvariant(ClientApiVersionHelper.ApiVersionQueryString)); + var stringBuilder = new StringBuilder(JobsQueryFormat.FormatInvariant(ClientApiVersionHelper.ApiVersionQueryString)); if (jobType != null) { @@ -468,12 +477,12 @@ private async Task GetJobsAsync(JobType? jobType, JobStatus? jobSta var customHeaders = new Dictionary(); if (!string.IsNullOrWhiteSpace(continuationToken)) { - customHeaders.Add(_continuationTokenHeader, continuationToken); + customHeaders.Add(ContinuationTokenHeader, continuationToken); } if (pageSize != null) { - customHeaders.Add(_pageSizeHeader, pageSize.ToString()); + customHeaders.Add(PageSizeHeader, pageSize.ToString()); } HttpResponseMessage response = await _httpClientHelper.GetAsync( diff --git a/iothub/service/src/JobProperties.cs b/iothub/service/src/JobProperties.cs index ca5bb057ee..fa2c705128 100644 --- a/iothub/service/src/JobProperties.cs +++ b/iothub/service/src/JobProperties.cs @@ -124,13 +124,31 @@ public JobProperties() [JsonProperty(PropertyName = "identity", NullValueHandling = NullValueHandling.Ignore)] public ManagedIdentity Identity { get; set; } + /// + /// Whether or not to include configurations in the import or export job. + /// + /// + /// The service assumes this is false, if not specified. If true, then configurations are included in the data export/import. + /// + [JsonProperty(PropertyName = "includeConfigurations", NullValueHandling = NullValueHandling.Ignore)] + private bool? IncludeConfigurations { get; set; } // waiting for bug fix before publicizing + + /// + /// Specifies the name of the blob to use when exporting/importing configurations. + /// + /// + /// The service assumes this is configurations.txt, if not specified. + /// + [JsonProperty(PropertyName = "configurationsBlobName", NullValueHandling = NullValueHandling.Ignore)] + private string ConfigurationsBlobName { get; set; } // waiting for bug fix before publicizing + #pragma warning disable CA1054 // Uri parameters should not be strings /// - /// Creates an instance of JobProperties with parameters ready to start an Import job + /// Creates an instance of JobProperties with parameters ready to start an import job. /// /// URI to a blob container that contains registry data to sync. Including a SAS token is dependent on the parameter. - /// URI to a blob container. This is used to output the status of the job and the results. Including a SAS token is dependent on the parameter. + /// URI to a blob container. This is used to output the status of the job and the results. Including a SAS token is dependent on the parameter. /// The blob name to be used when importing from the provided input blob container /// Specifies authentication type being used for connecting to storage account /// User assigned managed identity used to access storage account for import and export jobs. @@ -149,12 +167,12 @@ public JobProperties() OutputBlobContainerUri = outputBlobContainerUri, InputBlobName = inputBlobName, StorageAuthenticationType = storageAuthenticationType, - Identity = identity + Identity = identity, }; } /// - /// Creates an instance of JobProperties with parameters ready to start an Import job + /// Creates an instance of JobProperties with parameters ready to start an export job. /// /// URI to a blob container. This is used to output the status of the job and the results. Including a SAS token is dependent on the parameter. /// Indicates if authorization keys are included in export output @@ -176,7 +194,7 @@ public JobProperties() ExcludeKeysInExport = excludeKeysInExport, OutputBlobName = outputBlobName, StorageAuthenticationType = storageAuthenticationType, - Identity = identity + Identity = identity, }; } diff --git a/iothub/service/src/ManagedIdentity.cs b/iothub/service/src/ManagedIdentity.cs index 8ef757b1a5..d911fc668e 100644 --- a/iothub/service/src/ManagedIdentity.cs +++ b/iothub/service/src/ManagedIdentity.cs @@ -2,8 +2,7 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; -using System.Collections.Generic; -using System.Text; +using System.ComponentModel; using Newtonsoft.Json; namespace Microsoft.Azure.Devices @@ -15,10 +14,22 @@ namespace Microsoft.Azure.Devices /// public class ManagedIdentity { + /// + /// The user identity resource Id used to access the storage account for import and export jobs. + /// + [Obsolete("Use UserAssignedIdentity instead")] + [EditorBrowsable(EditorBrowsableState.Never)] + [JsonIgnore] + public string userAssignedIdentity + { + get => UserAssignedIdentity; + set => UserAssignedIdentity = value; + } + /// /// The user identity resource Id used to access the storage account for import and export jobs. /// [JsonProperty(PropertyName = "userAssignedIdentity", NullValueHandling = NullValueHandling.Ignore)] - public string userAssignedIdentity { get; set; } + public string UserAssignedIdentity { get; set; } } } diff --git a/iothub/service/src/Message.cs b/iothub/service/src/Message.cs index 942c17878d..f55023e9bf 100644 --- a/iothub/service/src/Message.cs +++ b/iothub/service/src/Message.cs @@ -368,7 +368,7 @@ public byte[] GetBytes() if (_bodyStream is BufferListStream listStream) { // We can trust Amqp bufferListStream.Length; - var bytes = new byte[listStream.Length]; + byte[] bytes = new byte[listStream.Length]; listStream.Read(bytes, 0, bytes.Length); return bytes; } diff --git a/iothub/service/src/RegistryManager.cs b/iothub/service/src/RegistryManager.cs index 9770408567..6963f3f474 100644 --- a/iothub/service/src/RegistryManager.cs +++ b/iothub/service/src/RegistryManager.cs @@ -14,6 +14,7 @@ using Microsoft.Azure.Devices.Common.Exceptions; using Microsoft.Azure.Devices.Shared; using Newtonsoft.Json; +using System.Diagnostics.CodeAnalysis; #if !NET451 @@ -26,35 +27,37 @@ namespace Microsoft.Azure.Devices { /// /// Contains methods that services can use to perform create, remove, update and delete operations on devices. - /// For more information, see /// - [System.Diagnostics.CodeAnalysis.SuppressMessage( + /// + /// For more information, see + /// + [SuppressMessage( "Naming", "CA1716:Identifiers should not match keywords", Justification = "Cannot change parameter names as it is considered a breaking change.")] public class RegistryManager : IDisposable { - private const string _adminUriFormat = "/$admin/{0}?{1}"; - private const string _requestUriFormat = "/devices/{0}?{1}"; - private const string _jobsUriFormat = "/jobs{0}?{1}"; - private const string _statisticsUriFormat = "/statistics/devices?" + ClientApiVersionHelper.ApiVersionQueryString; - private const string _devicesRequestUriFormat = "/devices/?top={0}&{1}"; - private const string _devicesQueryUriFormat = "/devices/query?" + ClientApiVersionHelper.ApiVersionQueryString; - private const string _wildcardEtag = "*"; + private const string AdminUriFormat = "/$admin/{0}?{1}"; + private const string RequestUriFormat = "/devices/{0}?{1}"; + private const string JobsUriFormat = "/jobs{0}?{1}"; + private const string StatisticsUriFormat = "/statistics/devices?" + ClientApiVersionHelper.ApiVersionQueryString; + private const string DevicesRequestUriFormat = "/devices/?top={0}&{1}"; + private const string DevicesQueryUriFormat = "/devices/query?" + ClientApiVersionHelper.ApiVersionQueryString; + private const string WildcardEtag = "*"; - private const string _continuationTokenHeader = "x-ms-continuation"; - private const string _pageSizeHeader = "x-ms-max-item-count"; + private const string ContinuationTokenHeader = "x-ms-continuation"; + private const string PageSizeHeader = "x-ms-max-item-count"; - private const string _twinUriFormat = "/twins/{0}?{1}"; + private const string TwinUriFormat = "/twins/{0}?{1}"; - private const string _modulesRequestUriFormat = "/devices/{0}/modules/{1}?{2}"; - private const string _modulesOnDeviceRequestUriFormat = "/devices/{0}/modules?{1}"; - private const string _moduleTwinUriFormat = "/twins/{0}/modules/{1}?{2}"; + private const string ModulesRequestUriFormat = "/devices/{0}/modules/{1}?{2}"; + private const string ModulesOnDeviceRequestUriFormat = "/devices/{0}/modules?{1}"; + private const string ModuleTwinUriFormat = "/twins/{0}/modules/{1}?{2}"; - private const string _configurationRequestUriFormat = "/configurations/{0}?{1}"; - private const string _configurationsRequestUriFormat = "/configurations/?top={0}&{1}"; + private const string ConfigurationRequestUriFormat = "/configurations/{0}?{1}"; + private const string ConfigurationsRequestUriFormat = "/configurations/?top={0}&{1}"; - private const string _applyConfigurationOnDeviceUriFormat = "/devices/{0}/applyConfigurationContent?" + ClientApiVersionHelper.ApiVersionQueryString; + private const string ApplyConfigurationOnDeviceUriFormat = "/devices/{0}/applyConfigurationContent?" + ClientApiVersionHelper.ApiVersionQueryString; private static readonly TimeSpan s_regexTimeoutMilliseconds = TimeSpan.FromMilliseconds(500); @@ -70,8 +73,7 @@ public class RegistryManager : IDisposable private IHttpClientHelper _httpClientHelper; /// - /// Creates an instance of , provided for unit testing purposes only. - /// Use the CreateFromConnectionString method to create an instance to use the client. + /// Creates an instance of RegistryManager, provided for unit testing purposes only. /// public RegistryManager() { @@ -97,21 +99,22 @@ internal RegistryManager(string iotHubName, IHttpClientHelper httpClientHelper) } /// - /// Creates a RegistryManager from the IoT Hub connection string. + /// Creates RegistryManager from an IoT hub connection string. /// - /// The IoT Hub connection string. - /// An RegistryManager instance. + /// The IoT hub connection string. + /// A RegistryManager instance. public static RegistryManager CreateFromConnectionString(string connectionString) { return CreateFromConnectionString(connectionString, new HttpTransportSettings()); } /// - /// Creates a RegistryManager from the IoT Hub connection string and transport settings + /// Creates an instance of RegistryManager, authenticating using an IoT hub connection string, and specifying + /// HTTP transport settings. /// - /// The IoT Hub connection string. + /// The IoT hub connection string. /// The HTTP transport settings. - /// An RegistryManager instance. + /// A RegistryManager instance. public static RegistryManager CreateFromConnectionString(string connectionString, HttpTransportSettings transportSettings) { if (transportSettings == null) @@ -127,15 +130,18 @@ public static RegistryManager CreateFromConnectionString(string connectionString #if !NET451 /// - /// Creates an instance of RegistryManager. + /// Creates RegistryManager, authenticating using an identity in Azure Active Directory (AAD). /// - /// IoT hub host name. - /// Azure Active Directory credentials to authenticate with IoT hub. See - /// The HTTP transport settings. - /// An instance of . /// - /// For more information on configuring IoT hub with Azure Active Directory, see + /// For more about information on the options of authenticating using a derived instance of , see + /// . + /// For more information on configuring IoT hub with Azure Active Directory, see + /// /// + /// IoT hub host name. + /// Azure Active Directory (AAD) credentials to authenticate with IoT hub. + /// The HTTP transport settings. + /// A RegistryManager instance. public static RegistryManager Create( string hostName, TokenCredential credential, @@ -156,12 +162,17 @@ public static RegistryManager CreateFromConnectionString(string connectionString } /// - /// Creates an instance of . + /// Creates RegistryManager using a shared access signature provided and refreshed as necessary by the caller. /// + /// + /// Users may wish to build their own shared access signature (SAS) tokens rather than give the shared key to the SDK and let it manage signing and renewal. + /// The object gives the SDK access to the SAS token, while the caller can update it as necessary using the + /// method. + /// /// IoT hub host name. /// Credential that generates a SAS token to authenticate with IoT hub. See . /// The HTTP transport settings. - /// An instance of . + /// A RegistryManager instance. public static RegistryManager Create( string hostName, AzureSasCredential credential, @@ -1410,6 +1421,7 @@ public virtual Task ExportDevicesAsync(string exportBlobContainer /// /// Parameters for the job. /// Task cancellation token. + /// Conditionally includes configurations, if specified. /// JobProperties of the newly created job. public virtual Task ExportDevicesAsync(JobProperties jobParameters, CancellationToken cancellationToken = default) { @@ -1507,6 +1519,7 @@ public virtual Task ImportDevicesAsync(string importBlobContainer /// /// Parameters for the job. /// Task cancellation token. + /// Conditionally includes configurations, if specified. /// JobProperties of the newly created job. public virtual Task ImportDevicesAsync(JobProperties jobParameters, CancellationToken cancellationToken = default) { @@ -2470,14 +2483,14 @@ private Task UpdateTwinInternalAsync(string deviceId, Twin twin, string et GetTwinUri(deviceId), twin, etag, - etag == _wildcardEtag ? PutOperationType.ForceUpdateEntity : PutOperationType.UpdateEntity, + etag == WildcardEtag ? PutOperationType.ForceUpdateEntity : PutOperationType.UpdateEntity, errorMappingOverrides, cancellationToken) : _httpClientHelper.PatchAsync( GetTwinUri(deviceId), twin, etag, - etag == _wildcardEtag ? PutOperationType.ForceUpdateEntity : PutOperationType.UpdateEntity, + etag == WildcardEtag ? PutOperationType.ForceUpdateEntity : PutOperationType.UpdateEntity, errorMappingOverrides, cancellationToken); } @@ -2522,14 +2535,14 @@ await ExceptionHandlingHelper.GetExceptionMessageAsync(responseMessage).Configur GetModuleTwinRequestUri(deviceId, moduleId), twin, etag, - etag == _wildcardEtag ? PutOperationType.ForceUpdateEntity : PutOperationType.UpdateEntity, + etag == WildcardEtag ? PutOperationType.ForceUpdateEntity : PutOperationType.UpdateEntity, errorMappingOverrides, cancellationToken) : _httpClientHelper.PatchAsync( GetModuleTwinRequestUri(deviceId, moduleId), twin, etag, - etag == _wildcardEtag ? PutOperationType.ForceUpdateEntity : PutOperationType.UpdateEntity, + etag == WildcardEtag ? PutOperationType.ForceUpdateEntity : PutOperationType.UpdateEntity, errorMappingOverrides, cancellationToken); } @@ -2556,12 +2569,12 @@ private async Task ExecuteQueryAsync(string sqlQueryString, int? pa var customHeaders = new Dictionary(); if (!string.IsNullOrWhiteSpace(continuationToken)) { - customHeaders.Add(_continuationTokenHeader, continuationToken); + customHeaders.Add(ContinuationTokenHeader, continuationToken); } if (pageSize != null) { - customHeaders.Add(_pageSizeHeader, pageSize.ToString()); + customHeaders.Add(PageSizeHeader, pageSize.ToString()); } HttpResponseMessage response = await _httpClientHelper @@ -2600,79 +2613,79 @@ private Task CreateJobAsync(JobProperties jobProperties, Cancella private static Uri GetRequestUri(string deviceId) { deviceId = WebUtility.UrlEncode(deviceId); - return new Uri(_requestUriFormat.FormatInvariant(deviceId, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); + return new Uri(RequestUriFormat.FormatInvariant(deviceId, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); } private static Uri GetModulesRequestUri(string deviceId, string moduleId) { deviceId = WebUtility.UrlEncode(deviceId); moduleId = WebUtility.UrlEncode(moduleId); - return new Uri(_modulesRequestUriFormat.FormatInvariant(deviceId, moduleId, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); + return new Uri(ModulesRequestUriFormat.FormatInvariant(deviceId, moduleId, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); } private static Uri GetModulesOnDeviceRequestUri(string deviceId) { deviceId = WebUtility.UrlEncode(deviceId); - return new Uri(_modulesOnDeviceRequestUriFormat.FormatInvariant(deviceId, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); + return new Uri(ModulesOnDeviceRequestUriFormat.FormatInvariant(deviceId, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); } private static Uri GetModuleTwinRequestUri(string deviceId, string moduleId) { deviceId = WebUtility.UrlEncode(deviceId); moduleId = WebUtility.UrlEncode(moduleId); - return new Uri(_moduleTwinUriFormat.FormatInvariant(deviceId, moduleId, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); + return new Uri(ModuleTwinUriFormat.FormatInvariant(deviceId, moduleId, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); } private static Uri GetConfigurationRequestUri(string configurationId) { configurationId = WebUtility.UrlEncode(configurationId); - return new Uri(_configurationRequestUriFormat.FormatInvariant(configurationId, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); + return new Uri(ConfigurationRequestUriFormat.FormatInvariant(configurationId, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); } private static Uri GetConfigurationsRequestUri(int maxCount) { - return new Uri(_configurationsRequestUriFormat.FormatInvariant(maxCount, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); + return new Uri(ConfigurationsRequestUriFormat.FormatInvariant(maxCount, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); } private static Uri GetApplyConfigurationOnDeviceRequestUri(string deviceId) { - return new Uri(_applyConfigurationOnDeviceUriFormat.FormatInvariant(deviceId), UriKind.Relative); + return new Uri(ApplyConfigurationOnDeviceUriFormat.FormatInvariant(deviceId), UriKind.Relative); } private static Uri GetBulkRequestUri(string apiVersionQueryString) { - return new Uri(_requestUriFormat.FormatInvariant(string.Empty, apiVersionQueryString), UriKind.Relative); + return new Uri(RequestUriFormat.FormatInvariant(string.Empty, apiVersionQueryString), UriKind.Relative); } private static Uri GetJobUri(string jobId, string apiVersion = ClientApiVersionHelper.ApiVersionQueryString) { - return new Uri(_jobsUriFormat.FormatInvariant(jobId, apiVersion), UriKind.Relative); + return new Uri(JobsUriFormat.FormatInvariant(jobId, apiVersion), UriKind.Relative); } private static Uri GetDevicesRequestUri(int maxCount) { - return new Uri(_devicesRequestUriFormat.FormatInvariant(maxCount, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); + return new Uri(DevicesRequestUriFormat.FormatInvariant(maxCount, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); } private static Uri QueryDevicesRequestUri() { - return new Uri(_devicesQueryUriFormat, UriKind.Relative); + return new Uri(DevicesQueryUriFormat, UriKind.Relative); } private static Uri GetAdminUri(string operation) { - return new Uri(_adminUriFormat.FormatInvariant(operation, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); + return new Uri(AdminUriFormat.FormatInvariant(operation, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); } private static Uri GetStatisticsUri() { - return new Uri(_statisticsUriFormat, UriKind.Relative); + return new Uri(StatisticsUriFormat, UriKind.Relative); } private static Uri GetTwinUri(string deviceId) { deviceId = WebUtility.UrlEncode(deviceId); - return new Uri(_twinUriFormat.FormatInvariant(deviceId, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); + return new Uri(TwinUriFormat.FormatInvariant(deviceId, ClientApiVersionHelper.ApiVersionQueryString), UriKind.Relative); } private static void ValidateDeviceId(Device device) diff --git a/iothub/service/src/ServiceAuthenticationWithDeviceSharedAccessPolicyKey.cs b/iothub/service/src/ServiceAuthenticationWithDeviceSharedAccessPolicyKey.cs index 21bf338279..24d778c146 100644 --- a/iothub/service/src/ServiceAuthenticationWithDeviceSharedAccessPolicyKey.cs +++ b/iothub/service/src/ServiceAuthenticationWithDeviceSharedAccessPolicyKey.cs @@ -21,6 +21,16 @@ public ServiceAuthenticationWithDeviceSharedAccessPolicyKey(string deviceId, str Key = sharedAccessKey; } + /// + /// Shared access key of the device + /// + public string Key { get; set; } + + /// + /// Name of device + /// + public string DeviceId { get; set; } + /// /// Populates the builder with values needed to authenticate with device's shared access key. /// @@ -33,21 +43,11 @@ public IotHubConnectionStringBuilder Populate(IotHubConnectionStringBuilder iotH throw new ArgumentNullException(nameof(iotHubConnectionStringBuilder)); } - iotHubConnectionStringBuilder.SharedAccessKey = this.Key; - iotHubConnectionStringBuilder.DeviceId = this.DeviceId; + iotHubConnectionStringBuilder.SharedAccessKey = Key; + iotHubConnectionStringBuilder.DeviceId = DeviceId; iotHubConnectionStringBuilder.SharedAccessSignature = null; return iotHubConnectionStringBuilder; } - - /// - /// Shared access key of the device - /// - public string Key { get; set; } - - /// - /// Name of device - /// - public string DeviceId { get; set; } } } diff --git a/iothub/service/src/ServiceAuthenticationWithDeviceSharedAccessPolicyToken.cs b/iothub/service/src/ServiceAuthenticationWithDeviceSharedAccessPolicyToken.cs index 0d207fc9c6..d49772bb77 100644 --- a/iothub/service/src/ServiceAuthenticationWithDeviceSharedAccessPolicyToken.cs +++ b/iothub/service/src/ServiceAuthenticationWithDeviceSharedAccessPolicyToken.cs @@ -21,6 +21,16 @@ public ServiceAuthenticationWithDeviceSharedAccessPolicyToken(string deviceId, s Token = sharedAccessSignature; } + /// + /// Name of device + /// + public string DeviceId { get; set; } + + /// + /// Shared access signature generated using device's shared access key + /// + public string Token { get; set; } + /// /// Populates the builder with values needed to authenticate with device's shared access signature. /// @@ -34,20 +44,10 @@ public IotHubConnectionStringBuilder Populate(IotHubConnectionStringBuilder iotH } iotHubConnectionStringBuilder.SharedAccessKey = null; - iotHubConnectionStringBuilder.DeviceId = this.DeviceId; - iotHubConnectionStringBuilder.SharedAccessSignature = this.Token; + iotHubConnectionStringBuilder.DeviceId = DeviceId; + iotHubConnectionStringBuilder.SharedAccessSignature = Token; return iotHubConnectionStringBuilder; } - - /// - /// Name of device - /// - public string DeviceId { get; set; } - - /// - /// Shared access signature generated using device's shared access key - /// - public string Token { get; set; } } } diff --git a/iothub/service/src/ServiceAuthenticationWithSharedAccessPolicyKey.cs b/iothub/service/src/ServiceAuthenticationWithSharedAccessPolicyKey.cs index c30cfda7f8..b6bd3c6887 100644 --- a/iothub/service/src/ServiceAuthenticationWithSharedAccessPolicyKey.cs +++ b/iothub/service/src/ServiceAuthenticationWithSharedAccessPolicyKey.cs @@ -31,7 +31,7 @@ public ServiceAuthenticationWithSharedAccessPolicyKey(string policyName, string public string PolicyName { get => _policyName; - set { SetPolicyName(value); } + set => SetPolicyName(value); } /// diff --git a/iothub/service/src/ServiceClient.cs b/iothub/service/src/ServiceClient.cs index e8639fa4e5..b46ea8f9ce 100644 --- a/iothub/service/src/ServiceClient.cs +++ b/iothub/service/src/ServiceClient.cs @@ -45,14 +45,16 @@ public enum TransportType /// /// Contains methods that services can use to send messages to devices - /// For more information, see /// + /// + /// For more information, see + /// public class ServiceClient : IDisposable { - private const string _statisticsUriFormat = "/statistics/service?" + ClientApiVersionHelper.ApiVersionQueryString; - private const string _purgeMessageQueueFormat = "/devices/{0}/commands?" + ClientApiVersionHelper.ApiVersionQueryString; - private const string _deviceMethodUriFormat = "/twins/{0}/methods?" + ClientApiVersionHelper.ApiVersionQueryString; - private const string _moduleMethodUriFormat = "/twins/{0}/modules/{1}/methods?" + ClientApiVersionHelper.ApiVersionQueryString; + private const string StatisticsUriFormat = "/statistics/service?" + ClientApiVersionHelper.ApiVersionQueryString; + private const string PurgeMessageQueueFormat = "/devices/{0}/commands?" + ClientApiVersionHelper.ApiVersionQueryString; + private const string DeviceMethodUriFormat = "/twins/{0}/methods?" + ClientApiVersionHelper.ApiVersionQueryString; + private const string ModuleMethodUriFormat = "/twins/{0}/modules/{1}/methods?" + ClientApiVersionHelper.ApiVersionQueryString; private static readonly TimeSpan s_defaultOperationTimeout = TimeSpan.FromSeconds(100); @@ -68,8 +70,6 @@ public class ServiceClient : IDisposable private int _sendingDeliveryTag; - internal readonly IotHubConnection Connection; - /// /// Creates an instance of , provided for unit testing purposes only. /// Use the CreateFromConnectionString method to create an instance to use the client. @@ -116,11 +116,11 @@ internal ServiceClient(IotHubConnection connection, IHttpClientHelper httpClient } /// - /// Create an instance of ServiceClient from the specified IoT Hub connection string. + /// Creates ServiceClient from an IoT hub connection string. /// - /// Connection string for the IoT Hub. + /// Connection string for the IoT hub. /// The that allow configuration of the service client instance during initialization. - /// An instance of ServiceClient. + /// A ServiceClient instance. public static ServiceClient CreateFromConnectionString(string connectionString, ServiceClientOptions options = default) { return CreateFromConnectionString(connectionString, TransportType.Amqp, options); @@ -129,17 +129,20 @@ public static ServiceClient CreateFromConnectionString(string connectionString, #if !NET451 /// - /// Creates a using Azure Active Directory credentials and the specified transport type. + /// Creates ServiceClient, authenticating using an identity in Azure Active Directory (AAD). /// + /// + /// For more about information on the options of authenticating using a derived instance of , see + /// . + /// For more information on configuring IoT hub with Azure Active Directory, see + /// + /// /// IoT hub host name. /// Azure Active Directory credentials to authenticate with IoT hub. See /// Specifies whether Amqp or Amqp_WebSocket_Only transport is used. /// Specifies the AMQP_WS and HTTP proxy settings for service client. /// The options that allow configuration of the service client instance during initialization. - /// An instance of . - /// - /// For more information on configuring IoT hub with Azure Active Directory, see - /// + /// A ServiceClient instance. public static ServiceClient Create( string hostName, TokenCredential credential, @@ -149,7 +152,7 @@ public static ServiceClient CreateFromConnectionString(string connectionString, { if (string.IsNullOrEmpty(hostName)) { - throw new ArgumentNullException($"{nameof(hostName)}, Parameter cannot be null or empty"); + throw new ArgumentNullException($"{nameof(hostName)}, Parameter cannot be null or empty"); } if (credential == null) @@ -168,14 +171,19 @@ public static ServiceClient CreateFromConnectionString(string connectionString, } /// - /// Creates a using SAS token and the specified transport type. + /// Creates ServiceClient using a shared access signature provided and refreshed as necessary by the caller. /// + /// + /// Users may wish to build their own shared access signature (SAS) tokens rather than give the shared key to the SDK and let it manage signing and renewal. + /// The object gives the SDK access to the SAS token, while the caller can update it as necessary using the + /// method. + /// /// IoT hub host name. /// Credential that generates a SAS token to authenticate with IoT hub. See . /// Specifies whether Amqp or Amqp_WebSocket_Only transport is used. /// Specifies the AMQP_WS and HTTP proxy settings for service client. /// The options that allow configuration of the service client instance during initialization. - /// An instance of . + /// A ServiceClient instance. public static ServiceClient Create( string hostName, AzureSasCredential credential, @@ -205,6 +213,8 @@ public static ServiceClient CreateFromConnectionString(string connectionString, #endif + internal IotHubConnection Connection { get; } + /// public void Dispose() { @@ -607,25 +617,25 @@ private static TimeSpan GetInvokeDeviceMethodOperationTimeout(CloudToDeviceMetho private static Uri GetStatisticsUri() { - return new Uri(_statisticsUriFormat, UriKind.Relative); + return new Uri(StatisticsUriFormat, UriKind.Relative); } private static Uri GetPurgeMessageQueueAsyncUri(string deviceId) { - return new Uri(_purgeMessageQueueFormat.FormatInvariant(deviceId), UriKind.Relative); + return new Uri(PurgeMessageQueueFormat.FormatInvariant(deviceId), UriKind.Relative); } private static Uri GetDeviceMethodUri(string deviceId) { deviceId = WebUtility.UrlEncode(deviceId); - return new Uri(_deviceMethodUriFormat.FormatInvariant(deviceId), UriKind.Relative); + return new Uri(DeviceMethodUriFormat.FormatInvariant(deviceId), UriKind.Relative); } private static Uri GetModuleMethodUri(string deviceId, string moduleId) { deviceId = WebUtility.UrlEncode(deviceId); moduleId = WebUtility.UrlEncode(moduleId); - return new Uri(_moduleMethodUriFormat.FormatInvariant(deviceId, moduleId), UriKind.Relative); + return new Uri(ModuleMethodUriFormat.FormatInvariant(deviceId, moduleId), UriKind.Relative); } } } diff --git a/iothub/service/src/ServiceClientTransportSettings.cs b/iothub/service/src/ServiceClientTransportSettings.cs index 0e00bac607..cb559084ed 100644 --- a/iothub/service/src/ServiceClientTransportSettings.cs +++ b/iothub/service/src/ServiceClientTransportSettings.cs @@ -21,13 +21,78 @@ public ServiceClientTransportSettings() } /// - /// The proxy settings to be used on the AMQP client. + /// The web proxy that will be used to connect to IoT hub when using the AMQP over web sockets. /// + /// + /// An instance of a class that implements . + /// + /// + /// This setting will be used when the client attempts to connect over web sockets. For example, if the client attempts to connect to IoT hub using the client will first try over TCP. If that fails, the client will fall back to using web sockets and will use the proxy setting. This setting is to be used in conjunction with the property. + /// + /// + /// To set a proxy you must instantiate an instance of the class--or any class that derives from . The snippet below shows a method that returns a device using a proxy that connects to localhost on port 8888. + /// + /// static ServiceClient GetServiceClient() + /// { + /// try + /// { + /// var proxyHost = "localhost"; + /// var proxyPort = 8888; + /// var proxy = new WebProxy(proxyHost, proxyPort); + /// var transportSettings = new ServiceClientTransportSettings + /// { + /// AmqpProxy = proxy, + /// HttpProxy = proxy + /// }; + /// var serviceClient = ServiceClient.CreateFromConnectionString("a connection string", Microsoft.Azure.Devices.TransportType.Amqp_WebSocket_Only, transportSettings ); + /// return serviceClient; + /// } + /// catch (Exception) + /// { + /// Console.WriteLine("Error creating client."); + /// throw; + /// } + /// } + /// + /// public IWebProxy AmqpProxy { get; set; } /// - /// The proxy settings to be used on the HTTP client. + /// The web proxy that will be used to connect to IoT hub when operations must execute over HTTP. /// + /// + /// An instance of a class that implements . + /// + /// + /// Methods such as are executed over HTTP and not AMQP. This setting will ensure those methods are executed over the specified proxy. This setting is to be used in conjunction with the property. + /// + /// + /// To set a proxy you must instantiate an instance of the class--or any class that derives from . The snippet below shows a method that returns a device using a proxy that connects to localhost on port 8888. + /// + /// static ServiceClient GetServiceClient() + /// { + /// try + /// { + /// var proxyHost = "localhost"; + /// var proxyPort = 8888; + /// var proxy = new WebProxy(proxyHost, proxyPort); + /// var transportSettings = new ServiceClientTransportSettings + /// { + /// AmqpProxy = proxy, + /// HttpProxy = proxy + /// }; + /// // Specify the WebProxy to be used for the web socket connection + /// var serviceClient = ServiceClient.CreateFromConnectionString("a connection string", Microsoft.Azure.Devices.TransportType.Amqp_WebSocket_Only, transportSettings ); + /// return serviceClient; + /// } + /// catch (Exception) + /// { + /// Console.WriteLine("Error creating client."); + /// throw; + /// } + /// } + /// + /// public IWebProxy HttpProxy { get; set; } /// diff --git a/iothub/service/src/ServicePointHelpers.cs b/iothub/service/src/ServicePointHelpers.cs index 12c357013a..1e40f8f3b0 100644 --- a/iothub/service/src/ServicePointHelpers.cs +++ b/iothub/service/src/ServicePointHelpers.cs @@ -32,7 +32,7 @@ public static void SetLimits(HttpMessageHandler messageHandler, Uri baseUri, int #if !NET451 httpClientHandler.MaxConnectionsPerServer = DefaultMaxConnectionsPerServer; #endif - var servicePoint = ServicePointManager.FindServicePoint(baseUri); + ServicePoint servicePoint = ServicePointManager.FindServicePoint(baseUri); servicePoint.ConnectionLeaseTimeout = connectionLeaseTimeoutMilliseconds; break; #if NETCOREAPP && !NETCOREAPP2_0 && !NETCOREAPP1_0 && !NETCOREAPP1_1 diff --git a/iothub/service/src/net451/Common/ActionItem.cs b/iothub/service/src/net451/Common/ActionItem.cs index c80e806c9b..39ec1ee79f 100644 --- a/iothub/service/src/net451/Common/ActionItem.cs +++ b/iothub/service/src/net451/Common/ActionItem.cs @@ -215,14 +215,14 @@ static void InvokeWithContext(object state) [Fx.Tag.SecurityNote(Critical = "Called by the scheduler without any user context on the stack")] static void InvokeWithoutContext(object state) { - ActionItem tempState = (ActionItem)state; + var tempState = (ActionItem)state; tempState.Invoke(); tempState.isScheduled = false; } [Fx.Tag.SecurityNote(Critical = "Called after applying the user context on the stack")] static void OnContextApplied(object o) { - ActionItem tempState = (ActionItem)o; + var tempState = (ActionItem)o; tempState.Invoke(); tempState.isScheduled = false; } diff --git a/iothub/service/src/net451/Common/SynchronizedPool.cs b/iothub/service/src/net451/Common/SynchronizedPool.cs index 2d7a3e8f60..ea6ce9bdcf 100644 --- a/iothub/service/src/net451/Common/SynchronizedPool.cs +++ b/iothub/service/src/net451/Common/SynchronizedPool.cs @@ -203,7 +203,7 @@ void RecordTakeFromGlobalPool(int thisThreadID) } else { - PendingEntry[] newPending = new PendingEntry[localPending.Length * 2]; + var newPending = new PendingEntry[localPending.Length * 2]; Array.Copy(localPending, newPending, localPending.Length); this.pending = newPending; } diff --git a/provisioning/device/src/GlobalSuppressions.cs b/provisioning/device/src/GlobalSuppressions.cs index 7fc7c806ad..15f281aa6f 100644 --- a/provisioning/device/src/GlobalSuppressions.cs +++ b/provisioning/device/src/GlobalSuppressions.cs @@ -5,5 +5,15 @@ // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Not localizing", Scope = "module")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "SDK hides non-actionable errors from user", Scope = "module")] +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage( + "Globalization", + "CA1303:Do not pass literals as localized parameters", + Justification = "Not localizing", + Scope = "module")] +[assembly: SuppressMessage( + "Design", + "CA1031:Do not catch general exception types", + Justification = "SDK hides non-actionable errors from user", + Scope = "module")] diff --git a/provisioning/device/src/ProvisioningTransportException.cs b/provisioning/device/src/ProvisioningTransportException.cs index 209173fd59..14a1b92248 100644 --- a/provisioning/device/src/ProvisioningTransportException.cs +++ b/provisioning/device/src/ProvisioningTransportException.cs @@ -98,8 +98,8 @@ public ProvisioningTransportException(string message, Exception innerException, public ProvisioningTransportException(string message, Exception innerException, bool isTransient, ProvisioningErrorDetails errorDetails) : base(message, innerException) { - this.IsTransient = isTransient; - this.ErrorDetails = errorDetails; + IsTransient = isTransient; + ErrorDetails = errorDetails; } /// @@ -125,7 +125,7 @@ protected ProvisioningTransportException(SerializationInfo info, StreamingContex public override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); - info.AddValue(IsTransientValueSerializationStoreName, this.IsTransient); + info.AddValue(IsTransientValueSerializationStoreName, IsTransient); } } } diff --git a/provisioning/service/src/Auth/ServiceConnectionStringBuilder.cs b/provisioning/service/src/Auth/ServiceConnectionStringBuilder.cs index 811c3bce35..5b53cc8d17 100644 --- a/provisioning/service/src/Auth/ServiceConnectionStringBuilder.cs +++ b/provisioning/service/src/Auth/ServiceConnectionStringBuilder.cs @@ -98,7 +98,7 @@ public override string ToString() { Validate(); - StringBuilder stringBuilder = new StringBuilder(); + var stringBuilder = new StringBuilder(); stringBuilder.AppendKeyValuePairIfNotEmpty(HostNamePropertyName, HostName); stringBuilder.AppendKeyValuePairIfNotEmpty(SharedAccessKeyNamePropertyName, SharedAccessKeyName); stringBuilder.AppendKeyValuePairIfNotEmpty(SharedAccessKeyPropertyName, SharedAccessKey); diff --git a/provisioning/service/src/Auth/SharedAccessSignature.cs b/provisioning/service/src/Auth/SharedAccessSignature.cs index 4f50a1c6af..20afe689d4 100644 --- a/provisioning/service/src/Auth/SharedAccessSignature.cs +++ b/provisioning/service/src/Auth/SharedAccessSignature.cs @@ -163,7 +163,7 @@ public void Authorize(Uri targetAddress) public string ComputeSignature(byte[] key) { - List fields = new List(); + var fields = new List(); fields.Add(_encodedAudience); fields.Add(_expiry); diff --git a/provisioning/service/src/Auth/SharedAccessSignatureBuilder.cs b/provisioning/service/src/Auth/SharedAccessSignatureBuilder.cs index 4f0ef1d1ed..41e5ba5249 100644 --- a/provisioning/service/src/Auth/SharedAccessSignatureBuilder.cs +++ b/provisioning/service/src/Auth/SharedAccessSignatureBuilder.cs @@ -48,7 +48,7 @@ private static string BuildSignature(string keyName, string key, string target, { string expiresOn = BuildExpiresOn(timeToLive); string audience = WebUtility.UrlEncode(target); - List fields = new List(); + var fields = new List(); fields.Add(audience); fields.Add(expiresOn); diff --git a/provisioning/service/src/Config/BulkEnrollmentOperation.cs b/provisioning/service/src/Config/BulkEnrollmentOperation.cs index cc7a920b1e..37247453f8 100644 --- a/provisioning/service/src/Config/BulkEnrollmentOperation.cs +++ b/provisioning/service/src/Config/BulkEnrollmentOperation.cs @@ -93,7 +93,7 @@ public static string ToJson(BulkOperationMode mode, IEnumerable /// It is the result of any query for the provisioning service. This class will parse the result and /// return it in a best format possible. For the known formats in , you can - /// just cast the items. In case of unknown type, the items will contain a list of string + /// just cast the items. In case of unknown type, the items will contain a list of string /// and you shall parse it by your own. /// /// The provisioning service query result is composed by 2 system properties and a body. This class exposes @@ -22,14 +22,18 @@ namespace Microsoft.Azure.Devices.Provisioning.Service /// /// The system properties are: /// - /// type: + /// + /// type: /// Identify the type of the content in the body. You can use it to cast the objects /// in the items list. See for the possible types and classes - /// to cast. - /// continuationToken: + /// to cast. + /// + /// + /// continuationToken: /// Contains the token the uniquely identify the next page of information. The /// service will return the next page of this query when you send a new query with - /// this token. + /// this token. + /// /// /// /// And the body is a JSON list of the specific type. For instance, if the system diff --git a/provisioning/service/src/Config/X509Attestation.cs b/provisioning/service/src/Config/X509Attestation.cs index c76b8525f1..18a3855e95 100644 --- a/provisioning/service/src/Config/X509Attestation.cs +++ b/provisioning/service/src/Config/X509Attestation.cs @@ -19,14 +19,20 @@ namespace Microsoft.Azure.Devices.Provisioning.Service /// An X509 attestation can contains one of the 3 types of certificate: /// /// - /// Client or Alias certificate: - /// Called on this class as clientCertificates, this certificate can authenticate a single device. - /// Signing or Root certificate: + /// + /// Client or Alias certificate: + /// Called on this class as clientCertificates, this certificate can authenticate a single device. + /// + /// + /// Signing or Root certificate: /// Called on this class as rootCertificates, this certificate can create multiple Client certificates - /// to authenticate multiple devices. - /// CA Reference: + /// to authenticate multiple devices. + /// + /// + /// CA Reference: /// Called on this class as X509CAReferences, this is a CA reference for a rootCertificate that can - /// creates multiple Client certificates to authenticate multiple devices. + /// creates multiple Client certificates to authenticate multiple devices. + /// /// /// /// The provisioning service allows user to create and . @@ -38,7 +44,7 @@ namespace Microsoft.Azure.Devices.Provisioning.Service /// primary is mandatory, the secondary is optional. /// /// The provisioning service will process the provided certificates, but will never return it back. Instead of - /// it, and + /// it, and /// will return the certificate information for the certificates. /// public sealed class X509Attestation : Attestation diff --git a/provisioning/service/src/Config/X509CertificateWithInfo.cs b/provisioning/service/src/Config/X509CertificateWithInfo.cs index 406644e1a2..61b7312c1d 100644 --- a/provisioning/service/src/Config/X509CertificateWithInfo.cs +++ b/provisioning/service/src/Config/X509CertificateWithInfo.cs @@ -140,7 +140,7 @@ private static void ValidateCertificate(X509Certificate2 certificate) private static void ValidateCertificate(string certificate) { byte[] certBytes = System.Text.Encoding.ASCII.GetBytes(certificate ?? throw new ArgumentException("Certificate cannot be null.")); - X509Certificate2 cert = new X509Certificate2(certBytes); + var cert = new X509Certificate2(certBytes); ValidateCertificate(cert); cert.Dispose(); } diff --git a/provisioning/service/src/GlobalSuppressions.cs b/provisioning/service/src/GlobalSuppressions.cs index 7fc7c806ad..15f281aa6f 100644 --- a/provisioning/service/src/GlobalSuppressions.cs +++ b/provisioning/service/src/GlobalSuppressions.cs @@ -5,5 +5,15 @@ // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Not localizing", Scope = "module")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "SDK hides non-actionable errors from user", Scope = "module")] +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage( + "Globalization", + "CA1303:Do not pass literals as localized parameters", + Justification = "Not localizing", + Scope = "module")] +[assembly: SuppressMessage( + "Design", + "CA1031:Do not catch general exception types", + Justification = "SDK hides non-actionable errors from user", + Scope = "module")] diff --git a/provisioning/service/src/Query.cs b/provisioning/service/src/Query.cs index b5a6a58dc1..47e6d03b60 100644 --- a/provisioning/service/src/Query.cs +++ b/provisioning/service/src/Query.cs @@ -3,14 +3,13 @@ using System; using System.Collections.Generic; -using System.Net; +using System.Globalization; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.Azure.Devices.Common; -using Newtonsoft.Json; using Microsoft.Azure.Devices.Common.Service.Auth; -using System.Globalization; +using Newtonsoft.Json; namespace Microsoft.Azure.Devices.Provisioning.Service { @@ -20,38 +19,44 @@ namespace Microsoft.Azure.Devices.Provisioning.Service /// /// The iterator is the result of the query factory for /// - /// IndividualEnrollment: - /// + /// + /// + /// IndividualEnrollment + /// + /// + /// + /// + /// EnrollmentGroup + /// /// - /// EnrollmentGroup: - /// - /// - /// RegistrationStatus: - /// + /// + /// + /// RegistrationStatus + /// /// /// /// On all cases, the contains a SQL query that must follow the /// Query Language for the Device Provisioning Service. /// - /// Optionally, an Integer with the page size, can determine the maximum number of the items in the + /// Optionally, an Integer with the page size, can determine the maximum number of the items in the /// returned by the . It must be any positive integer, and if it /// contains 0, the Device Provisioning Service will ignore it and use a standard page size. /// - /// You can use this Object as a standard iterator, just using the HasNext and NextAsync in a - /// while loop, up to the point where the HasNext contains false. But, keep - /// in mind that the can contain a empty list, even if the HasNext contained - /// true. For example, image that you have 10 IndividualEnrollment in the Device Provisioning Service - /// and you created new query with the PageSize equals 5. In the first iteration, HasNext - /// will contains true, and the first NextAsync will return a QueryResult with - /// 5 items. After, your code will check the HasNext, which will contains true again. Now, + /// You can use this Object as a standard iterator, just using the HasNext and NextAsync in a + /// while loop, up to the point where the HasNext contains false. But, keep + /// in mind that the can contain a empty list, even if the HasNext contained + /// true. For example, image that you have 10 IndividualEnrollment in the Device Provisioning Service + /// and you created new query with the PageSize equals 5. In the first iteration, HasNext + /// will contains true, and the first NextAsync will return a QueryResult with + /// 5 items. After, your code will check the HasNext, which will contains true again. Now, /// before you get the next page, somebody deletes all the IndividualEnrollment. What happened, when you call the - /// NextAsync, it will return a valid QueryResult, but the + /// NextAsync, it will return a valid QueryResult, but the /// will contain an empty list. /// - /// Besides the Items, the QueryResult contains the . + /// Besides the Items, the QueryResult contains the . /// You can also store a query context (QuerySpecification + ContinuationToken) and restart it in the future, from /// the point where you stopped. Just recreating the query with the same and calling - /// the passing the stored ContinuationToken. + /// the passing the stored ContinuationToken. /// public class Query : IDisposable { @@ -61,10 +66,10 @@ public class Query : IDisposable private const string QueryUriFormat = "{0}/query?{1}"; - private string _querySpecificationJson; + private readonly string _querySpecificationJson; private IContractApiHttp _contractApiHttp; - private Uri _queryPath; - private CancellationToken _cancellationToken; + private readonly Uri _queryPath; + private readonly CancellationToken _cancellationToken; private bool _hasNext; internal Query( @@ -126,10 +131,10 @@ public class Query : IDisposable /// Getter for has next /// /// - /// Contains true if the query is not finished in the Device Provisioning Service, and another + /// Contains true if the query is not finished in the Device Provisioning Service, and another /// iteration with may return more items. Call after - /// a true HasNext will result in a that can or - /// cannot contains elements. But call after a false HasNext + /// a true HasNext will result in a that can or + /// cannot contains elements. But call after a false HasNext /// will result in a exception. /// public bool HasNext() @@ -150,7 +155,7 @@ public bool HasNext() /// /// Return the next page of result for the query using a new continuationToken. /// - /// the String with the previous continuationToken. It cannot be null or empty. + /// the String with the previous continuationToken. It cannot be null or empty. /// The with the next page of items for the query. /// if the query does no have more pages to return. public async Task NextAsync(string continuationToken) @@ -211,7 +216,7 @@ public async Task NextAsync() /* SRS_QUERY_21_017: [The next shall set hasNext as true if the continuationToken is not null, or false if it is null.] */ _hasNext = (ContinuationToken != null); - QueryResult result = new QueryResult(type, httpResponse.Body, ContinuationToken); + var result = new QueryResult(type, httpResponse.Body, ContinuationToken); return result; } diff --git a/provisioning/service/tests/Config/AttestationMechanismTests.cs b/provisioning/service/tests/Config/AttestationMechanismTests.cs index dd22756ece..f7d4ba5b5d 100644 --- a/provisioning/service/tests/Config/AttestationMechanismTests.cs +++ b/provisioning/service/tests/Config/AttestationMechanismTests.cs @@ -85,7 +85,7 @@ public void AttestationMechanismConstructorThrowsOnAttestationNull() public void AttestationMechanismConstructorSucceedOnTPMAttestation() { // arrange - act - AttestationMechanism attestationMechanism = new AttestationMechanism(SampleTpmAttestation); + var attestationMechanism = new AttestationMechanism(SampleTpmAttestation); // assert Assert.IsNotNull(attestationMechanism); @@ -98,7 +98,7 @@ public void AttestationMechanismConstructorSucceedOnTPMAttestation() public void AttestationMechanismConstructorThrowsOnUnknownAttestation() { // arrange - UnknownAttestation unknownAttestation = new UnknownAttestation(); + var unknownAttestation = new UnknownAttestation(); // act - assert TestAssert.Throws(() => new AttestationMechanism(unknownAttestation)); @@ -112,7 +112,7 @@ public void AttestationMechanismConstructorThrowsOnUnknownAttestation() public void AttestationMechanismConstructorSucceedOnX509Attestation() { // arrange - act - AttestationMechanism attestationMechanism = new AttestationMechanism(SampleX509RootAttestation); + var attestationMechanism = new AttestationMechanism(SampleX509RootAttestation); // assert Assert.IsNotNull(attestationMechanism); @@ -237,7 +237,7 @@ public void AttestationMechanismConstructorJSONSucceedOnSymmetricKeyType() Assert.IsNotNull(attestationMechanism); Assert.IsTrue(attestationMechanism.Type == AttestationMechanismType.SymmetricKey); Assert.IsTrue(attestationMechanism.GetAttestation() is SymmetricKeyAttestation); - SymmetricKeyAttestation symmetricKeyAttestation = (SymmetricKeyAttestation) attestationMechanism.GetAttestation(); + var symmetricKeyAttestation = (SymmetricKeyAttestation) attestationMechanism.GetAttestation(); Assert.AreEqual(samplePrimaryKey, symmetricKeyAttestation.PrimaryKey); Assert.AreEqual(sampleSecondaryKey, symmetricKeyAttestation.SecondaryKey); diff --git a/provisioning/service/tests/Config/EnrollmentGroupTests.cs b/provisioning/service/tests/Config/EnrollmentGroupTests.cs index 242e2ff80d..072bdf4e24 100644 --- a/provisioning/service/tests/Config/EnrollmentGroupTests.cs +++ b/provisioning/service/tests/Config/EnrollmentGroupTests.cs @@ -79,7 +79,7 @@ public class EnrollmentGroupTests public void EnrollmentGroupConstructorSucceed() { // arrange - act - EnrollmentGroup individualEnrollment = new EnrollmentGroup(SampleEnrollmentGroupId, SampleX509RootAttestation); + var individualEnrollment = new EnrollmentGroup(SampleEnrollmentGroupId, SampleX509RootAttestation); // assert Assert.AreEqual(SampleEnrollmentGroupId, individualEnrollment.EnrollmentGroupId); diff --git a/provisioning/service/tests/Config/IndividualEnrollmentTests.cs b/provisioning/service/tests/Config/IndividualEnrollmentTests.cs index bd2371d5c5..f4cf62b3dc 100644 --- a/provisioning/service/tests/Config/IndividualEnrollmentTests.cs +++ b/provisioning/service/tests/Config/IndividualEnrollmentTests.cs @@ -105,7 +105,7 @@ public class IndividualEnrollmentTests public void IndividualEnrollmentConstructorSucceedOnTPM() { // arrange - act - IndividualEnrollment individualEnrollment = new IndividualEnrollment(SampleRegistrationId, SampleTpmAttestation); + var individualEnrollment = new IndividualEnrollment(SampleRegistrationId, SampleTpmAttestation); // assert Assert.AreEqual(SampleRegistrationId, individualEnrollment.RegistrationId); @@ -116,7 +116,7 @@ public void IndividualEnrollmentConstructorSucceedOnTPM() public void IndividualEnrollmentConstructorSucceedOnX509Client() { // arrange - act - IndividualEnrollment individualEnrollment = new IndividualEnrollment(SampleRegistrationId, SampleX509ClientAttestation); + var individualEnrollment = new IndividualEnrollment(SampleRegistrationId, SampleX509ClientAttestation); // assert Assert.AreEqual(SampleRegistrationId, individualEnrollment.RegistrationId); @@ -127,7 +127,7 @@ public void IndividualEnrollmentConstructorSucceedOnX509Client() public void IndividualEnrollmentConstructorSucceedOnX509CAReference() { // arrange - act - IndividualEnrollment individualEnrollment = new IndividualEnrollment(SampleRegistrationId, SampleX509CAReferenceAttestation); + var individualEnrollment = new IndividualEnrollment(SampleRegistrationId, SampleX509CAReferenceAttestation); // assert Assert.AreEqual(SampleRegistrationId, individualEnrollment.RegistrationId); diff --git a/provisioning/service/tests/Config/QueryResultTests.cs b/provisioning/service/tests/Config/QueryResultTests.cs index 80fb9c13f8..f11c960430 100644 --- a/provisioning/service/tests/Config/QueryResultTests.cs +++ b/provisioning/service/tests/Config/QueryResultTests.cs @@ -174,7 +174,7 @@ public void QueryResultConstructorThrowsOnInvalidParameters() public void QueryResultConstructorSucceedOnIndividualEnrollment() { // arrange - act - QueryResult queryResult = new QueryResult(SerializedNameEnrollment, SampleEnrollmentsJSON, SampleContinuationToken); + var queryResult = new QueryResult(SerializedNameEnrollment, SampleEnrollmentsJSON, SampleContinuationToken); // assert Assert.IsNotNull(queryResult); @@ -190,7 +190,7 @@ public void QueryResultConstructorSucceedOnIndividualEnrollment() public void QueryResultConstructorSucceedOnEnrollmentGroup() { // arrange - act - QueryResult queryResult = new QueryResult(SerializedNameEnrollmentGroup, SampleEnrollmentGroupJSON, SampleContinuationToken); + var queryResult = new QueryResult(SerializedNameEnrollmentGroup, SampleEnrollmentGroupJSON, SampleContinuationToken); // assert Assert.IsNotNull(queryResult); @@ -206,7 +206,7 @@ public void QueryResultConstructorSucceedOnEnrollmentGroup() public void QueryResultConstructorSucceedOnDeviceRegistration() { // arrange - act - QueryResult queryResult = new QueryResult(SerializedNameDeviceRegistration, SampleRegistrationStatusJSON, SampleContinuationToken); + var queryResult = new QueryResult(SerializedNameDeviceRegistration, SampleRegistrationStatusJSON, SampleContinuationToken); // assert Assert.IsNotNull(queryResult); @@ -222,7 +222,7 @@ public void QueryResultConstructorSucceedOnDeviceRegistration() public void QueryResultConstructorSucceedOnUnknownWithNullBody() { // arrange - act - QueryResult queryResult = new QueryResult(SerializedNameUnknown, null, SampleContinuationToken); + var queryResult = new QueryResult(SerializedNameUnknown, null, SampleContinuationToken); // assert Assert.IsNotNull(queryResult); @@ -236,7 +236,7 @@ public void QueryResultConstructorSucceedOnUnknownWithNullBody() public void QueryResultConstructorSucceedOnUnknownWithObjectListBody() { // arrange - act - QueryResult queryResult = new QueryResult(SerializedNameUnknown, SampleListJObjectJSON, SampleContinuationToken); + var queryResult = new QueryResult(SerializedNameUnknown, SampleListJObjectJSON, SampleContinuationToken); // assert Assert.IsNotNull(queryResult); @@ -252,7 +252,7 @@ public void QueryResultConstructorSucceedOnUnknownWithObjectListBody() public void QueryResultConstructorSucceedOnUnknownWithIntegerListBody() { // arrange - act - QueryResult queryResult = new QueryResult(SerializedNameUnknown, SampleListIntJSON, SampleContinuationToken); + var queryResult = new QueryResult(SerializedNameUnknown, SampleListIntJSON, SampleContinuationToken); // assert Assert.IsNotNull(queryResult); @@ -271,7 +271,7 @@ public void QueryResultConstructorSucceedOnUnknownWithStringBody() string body = "This is a non deserializable body"; // act - QueryResult queryResult = new QueryResult(SerializedNameUnknown, body, SampleContinuationToken); + var queryResult = new QueryResult(SerializedNameUnknown, body, SampleContinuationToken); // assert Assert.IsNotNull(queryResult); @@ -290,7 +290,7 @@ public void QueryResultConstructorSucceedOnNullContinuationToken() string body = "This is a non deserializable body"; // act - QueryResult queryResult = new QueryResult(SerializedNameUnknown, body, null); + var queryResult = new QueryResult(SerializedNameUnknown, body, null); // assert Assert.IsNotNull(queryResult); @@ -304,7 +304,7 @@ public void QueryResultConstructorSucceedOnEmptyContinuationToken() string body = "This is a non deserializable body"; // act - QueryResult queryResult = new QueryResult(SerializedNameUnknown, body, ""); + var queryResult = new QueryResult(SerializedNameUnknown, body, ""); // assert Assert.IsNotNull(queryResult); diff --git a/provisioning/service/tests/Config/SymmetricKeyAttestationTest.cs b/provisioning/service/tests/Config/SymmetricKeyAttestationTest.cs index 20f5037fa4..46b8e3beff 100644 --- a/provisioning/service/tests/Config/SymmetricKeyAttestationTest.cs +++ b/provisioning/service/tests/Config/SymmetricKeyAttestationTest.cs @@ -17,7 +17,7 @@ public class SymmetricKeyAttestationTest [TestMethod] public void constructorAllowsBase64EncodedKeys() { - SymmetricKeyAttestation symmetricKeyAttestation = new SymmetricKeyAttestation(validKeyValue, validKeyValue2); + var symmetricKeyAttestation = new SymmetricKeyAttestation(validKeyValue, validKeyValue2); Assert.AreEqual(validKeyValue, symmetricKeyAttestation.PrimaryKey); Assert.AreEqual(validKeyValue2, symmetricKeyAttestation.SecondaryKey); @@ -32,7 +32,7 @@ public void jsonSerializingWorks() "\"secondaryKey\":\"" + validKeyValue2 + "\"" + "}"; - SymmetricKeyAttestation symmetricKeyAttestation = new SymmetricKeyAttestation(validKeyValue, validKeyValue2); + var symmetricKeyAttestation = new SymmetricKeyAttestation(validKeyValue, validKeyValue2); string json = Newtonsoft.Json.JsonConvert.SerializeObject(symmetricKeyAttestation); diff --git a/provisioning/service/tests/Config/TpmAttestationTests.cs b/provisioning/service/tests/Config/TpmAttestationTests.cs index ceca77869d..ebdb045427 100644 --- a/provisioning/service/tests/Config/TpmAttestationTests.cs +++ b/provisioning/service/tests/Config/TpmAttestationTests.cs @@ -48,7 +48,7 @@ public void TpmAttestationSucceedOnNullStorageRootKey() string storageRootKey = null; // act - assert - TpmAttestation tpmAttestation = new TpmAttestation(endorsementKey, storageRootKey); + var tpmAttestation = new TpmAttestation(endorsementKey, storageRootKey); // assert Assert.AreEqual(endorsementKey, tpmAttestation.EndorsementKey); @@ -63,7 +63,7 @@ public void TpmAttestationSucceedOnEmptyStorageRootKey() string storageRootKey = ""; // act - assert - TpmAttestation tpmAttestation = new TpmAttestation(endorsementKey, storageRootKey); + var tpmAttestation = new TpmAttestation(endorsementKey, storageRootKey); // assert Assert.AreEqual(endorsementKey, tpmAttestation.EndorsementKey); @@ -78,7 +78,7 @@ public void TpmAttestationSucceedOnValidEndorsementKey() string endorsementKey = Key; // act - TpmAttestation tpmAttestation = new TpmAttestation(endorsementKey); + var tpmAttestation = new TpmAttestation(endorsementKey); // assert Assert.AreEqual(endorsementKey, tpmAttestation.EndorsementKey); @@ -93,7 +93,7 @@ public void TpmAttestationSucceedOnValidEndorsementKeyAndStorageRootKey() string storageRootKey = Key; // act - TpmAttestation tpmAttestation = new TpmAttestation(endorsementKey, storageRootKey); + var tpmAttestation = new TpmAttestation(endorsementKey, storageRootKey); // assert Assert.AreEqual(endorsementKey, tpmAttestation.EndorsementKey); @@ -112,7 +112,7 @@ public void TpmAttestationSucceedOnSerialization() " \"endorsementKey\":\""+endorsementKey+"\"," + " \"storageRootKey\":\"" + storageRootKey + "\"" + "}"; - TpmAttestation tpmAttestation = new TpmAttestation(endorsementKey, storageRootKey); + var tpmAttestation = new TpmAttestation(endorsementKey, storageRootKey); // act string json = Newtonsoft.Json.JsonConvert.SerializeObject(tpmAttestation); @@ -130,7 +130,7 @@ public void TpmAttestationSucceedOnSerializationWithoutStorageRootKey() "{" + " \"endorsementKey\":\"" + endorsementKey + "\"" + "}"; - TpmAttestation tpmAttestation = new TpmAttestation(endorsementKey); + var tpmAttestation = new TpmAttestation(endorsementKey); // act string json = Newtonsoft.Json.JsonConvert.SerializeObject(tpmAttestation); @@ -150,7 +150,7 @@ public void TpmAttestationSucceedOnSerializationWithEmptyStorageRootKey() " \"endorsementKey\":\"" + endorsementKey + "\"," + " \"storageRootKey\":\"" + storageRootKey + "\"" + "}"; - TpmAttestation tpmAttestation = new TpmAttestation(endorsementKey, storageRootKey); + var tpmAttestation = new TpmAttestation(endorsementKey, storageRootKey); // act string json = Newtonsoft.Json.JsonConvert.SerializeObject(tpmAttestation); diff --git a/provisioning/service/tests/Config/TwinStateTests.cs b/provisioning/service/tests/Config/TwinStateTests.cs index 6653237ba6..cc7b24dd60 100644 --- a/provisioning/service/tests/Config/TwinStateTests.cs +++ b/provisioning/service/tests/Config/TwinStateTests.cs @@ -73,7 +73,7 @@ public void TwinStateSucceedOnNull() TwinCollection desiredProperties = null; // act - TwinState initialTwin = new TwinState(tags, desiredProperties); + var initialTwin = new TwinState(tags, desiredProperties); // assert Assert.IsNull(initialTwin.Tags); @@ -88,7 +88,7 @@ public void TwinStateSucceedOnTagsWitoutDesiredProperties() TwinCollection desiredProperties = null; // act - TwinState initialTwin = new TwinState(tags, desiredProperties); + var initialTwin = new TwinState(tags, desiredProperties); // assert Assert.AreEqual(tags, initialTwin.Tags); @@ -106,7 +106,7 @@ public void TwinStateSucceedOnDesiredPropertiesWitoutTags() TwinCollection desiredProperties = SampleDesiredProperties; // act - TwinState initialTwin = new TwinState(tags, desiredProperties); + var initialTwin = new TwinState(tags, desiredProperties); // assert Assert.IsNull(initialTwin.Tags); @@ -121,7 +121,7 @@ public void TwinStateSucceedOnDesiredPropertiesAndTags() TwinCollection desiredProperties = SampleDesiredProperties; // act - TwinState initialTwin = new TwinState(tags, desiredProperties); + var initialTwin = new TwinState(tags, desiredProperties); // assert Assert.AreEqual(tags, initialTwin.Tags); @@ -134,7 +134,7 @@ public void TwinStateSucceedOnTagsToJson() // arrange TwinCollection tags = SampleTags; TwinCollection desiredProperties = null; - TwinState initialTwin = new TwinState(tags, desiredProperties); + var initialTwin = new TwinState(tags, desiredProperties); // act string jsonResult = JsonConvert.SerializeObject(initialTwin); @@ -149,7 +149,7 @@ public void TwinStateSucceedOnDesiredPropertiesToJson() // arrange TwinCollection tags = null; TwinCollection desiredProperties = SampleDesiredProperties; - TwinState initialTwin = new TwinState(tags, desiredProperties); + var initialTwin = new TwinState(tags, desiredProperties); // act string jsonResult = JsonConvert.SerializeObject(initialTwin); @@ -164,7 +164,7 @@ public void TwinStateSucceedOnToJson() // arrange TwinCollection tags = SampleTags; TwinCollection desiredProperties = SampleDesiredProperties; - TwinState initialTwin = new TwinState(tags, desiredProperties); + var initialTwin = new TwinState(tags, desiredProperties); // act string jsonResult = JsonConvert.SerializeObject(initialTwin); diff --git a/provisioning/service/tests/Config/X509AttestationTests.cs b/provisioning/service/tests/Config/X509AttestationTests.cs index 71377b4e1e..4373bc83cb 100644 --- a/provisioning/service/tests/Config/X509AttestationTests.cs +++ b/provisioning/service/tests/Config/X509AttestationTests.cs @@ -118,10 +118,10 @@ public void X509AttestationCreateFromRootCertificatesThrowsOnNullPrimaryCertific public void X509AttestationCreateFromClientCertificatesSucceedOnPrimaryCertificate() { // arrange - X509Certificate2 primary = new X509Certificate2(Encoding.ASCII.GetBytes(PUBLIC_KEY_CERTIFICATE_STRING)); + var primary = new X509Certificate2(Encoding.ASCII.GetBytes(PUBLIC_KEY_CERTIFICATE_STRING)); // act - X509Attestation attestation = X509Attestation.CreateFromClientCertificates(primary); + var attestation = X509Attestation.CreateFromClientCertificates(primary); primary.Dispose(); // assert @@ -135,11 +135,11 @@ public void X509AttestationCreateFromClientCertificatesSucceedOnPrimaryCertifica public void X509AttestationCreateFromClientCertificatesSucceedOnPrimaryAndSecondaryCertificates() { // arrange - X509Certificate2 primary = new X509Certificate2(Encoding.ASCII.GetBytes(PUBLIC_KEY_CERTIFICATE_STRING)); - X509Certificate2 secondary = new X509Certificate2(Encoding.ASCII.GetBytes(PUBLIC_KEY_CERTIFICATE_STRING)); + var primary = new X509Certificate2(Encoding.ASCII.GetBytes(PUBLIC_KEY_CERTIFICATE_STRING)); + var secondary = new X509Certificate2(Encoding.ASCII.GetBytes(PUBLIC_KEY_CERTIFICATE_STRING)); // act - X509Attestation attestation = X509Attestation.CreateFromClientCertificates(primary, secondary); + var attestation = X509Attestation.CreateFromClientCertificates(primary, secondary); primary.Dispose(); secondary.Dispose(); @@ -154,11 +154,11 @@ public void X509AttestationCreateFromClientCertificatesSucceedOnPrimaryAndSecond public void X509AttestationCreateFromClientCertificatesSucceedOnPrimaryAndSecondaryNullCertificates() { // arrange - X509Certificate2 primary = new X509Certificate2(Encoding.ASCII.GetBytes(PUBLIC_KEY_CERTIFICATE_STRING)); + var primary = new X509Certificate2(Encoding.ASCII.GetBytes(PUBLIC_KEY_CERTIFICATE_STRING)); X509Certificate2 secondary = null; // act - X509Attestation attestation = X509Attestation.CreateFromClientCertificates(primary, secondary); + var attestation = X509Attestation.CreateFromClientCertificates(primary, secondary); primary.Dispose(); // assert @@ -175,7 +175,7 @@ public void X509AttestationCreateFromClientCertificatesSucceedOnPrimaryString() string primary = PUBLIC_KEY_CERTIFICATE_STRING; // act - X509Attestation attestation = X509Attestation.CreateFromClientCertificates(primary); + var attestation = X509Attestation.CreateFromClientCertificates(primary); // assert Assert.IsNotNull(attestation.ClientCertificates.Primary); @@ -192,7 +192,7 @@ public void X509AttestationCreateFromClientCertificatesSucceedOnPrimaryAndSecond string secondary = PUBLIC_KEY_CERTIFICATE_STRING; // act - X509Attestation attestation = X509Attestation.CreateFromClientCertificates(primary, secondary); + var attestation = X509Attestation.CreateFromClientCertificates(primary, secondary); // assert Assert.IsNotNull(attestation.ClientCertificates.Primary); @@ -209,7 +209,7 @@ public void X509AttestationCreateFromClientCertificatesSucceedOnPrimaryAndSecond string secondary = null; // act - X509Attestation attestation = X509Attestation.CreateFromClientCertificates(primary, secondary); + var attestation = X509Attestation.CreateFromClientCertificates(primary, secondary); // assert Assert.IsNotNull(attestation.ClientCertificates.Primary); @@ -223,10 +223,10 @@ public void X509AttestationCreateFromClientCertificatesSucceedOnPrimaryAndSecond public void X509AttestationCreateFromRootCertificatesSucceedOnPrimaryCertificate() { // arrange - X509Certificate2 primary = new X509Certificate2(Encoding.ASCII.GetBytes(PUBLIC_KEY_CERTIFICATE_STRING)); + var primary = new X509Certificate2(Encoding.ASCII.GetBytes(PUBLIC_KEY_CERTIFICATE_STRING)); // act - X509Attestation attestation = X509Attestation.CreateFromRootCertificates(primary); + var attestation = X509Attestation.CreateFromRootCertificates(primary); primary.Dispose(); // assert @@ -240,11 +240,11 @@ public void X509AttestationCreateFromRootCertificatesSucceedOnPrimaryCertificate public void X509AttestationCreateFromRootCertificatesSucceedOnPrimaryAndSecondaryCertificates() { // arrange - X509Certificate2 primary = new X509Certificate2(Encoding.ASCII.GetBytes(PUBLIC_KEY_CERTIFICATE_STRING)); - X509Certificate2 secondary = new X509Certificate2(Encoding.ASCII.GetBytes(PUBLIC_KEY_CERTIFICATE_STRING)); + var primary = new X509Certificate2(Encoding.ASCII.GetBytes(PUBLIC_KEY_CERTIFICATE_STRING)); + var secondary = new X509Certificate2(Encoding.ASCII.GetBytes(PUBLIC_KEY_CERTIFICATE_STRING)); // act - X509Attestation attestation = X509Attestation.CreateFromRootCertificates(primary, secondary); + var attestation = X509Attestation.CreateFromRootCertificates(primary, secondary); primary.Dispose(); secondary.Dispose(); @@ -259,11 +259,11 @@ public void X509AttestationCreateFromRootCertificatesSucceedOnPrimaryAndSecondar public void X509AttestationCreateFromRootCertificatesSucceedOnPrimaryAndSecondaryNullCertificates() { // arrange - X509Certificate2 primary = new X509Certificate2(Encoding.ASCII.GetBytes(PUBLIC_KEY_CERTIFICATE_STRING)); + var primary = new X509Certificate2(Encoding.ASCII.GetBytes(PUBLIC_KEY_CERTIFICATE_STRING)); X509Certificate2 secondary = null; // act - X509Attestation attestation = X509Attestation.CreateFromRootCertificates(primary, secondary); + var attestation = X509Attestation.CreateFromRootCertificates(primary, secondary); primary.Dispose(); // assert @@ -280,7 +280,7 @@ public void X509AttestationCreateFromRootCertificatesSucceedOnPrimaryString() string primary = PUBLIC_KEY_CERTIFICATE_STRING; // act - X509Attestation attestation = X509Attestation.CreateFromRootCertificates(primary); + var attestation = X509Attestation.CreateFromRootCertificates(primary); // assert Assert.IsNotNull(attestation.RootCertificates.Primary); @@ -297,7 +297,7 @@ public void X509AttestationCreateFromRootCertificatesSucceedOnPrimaryAndSecondar string secondary = PUBLIC_KEY_CERTIFICATE_STRING; // act - X509Attestation attestation = X509Attestation.CreateFromRootCertificates(primary, secondary); + var attestation = X509Attestation.CreateFromRootCertificates(primary, secondary); // assert Assert.IsNotNull(attestation.RootCertificates.Primary); @@ -314,7 +314,7 @@ public void X509AttestationCreateFromRootCertificatesSucceedOnPrimaryAndSecondar string secondary = null; // act - X509Attestation attestation = X509Attestation.CreateFromRootCertificates(primary, secondary); + var attestation = X509Attestation.CreateFromRootCertificates(primary, secondary); // assert Assert.IsNotNull(attestation.RootCertificates.Primary); @@ -345,7 +345,7 @@ public void X509AttestationCreateFromCAReferencesSucceedOnPrimaryString() string primary = CA_REFERENCE_STRING; // act - X509Attestation attestation = X509Attestation.CreateFromCAReferences(primary); + var attestation = X509Attestation.CreateFromCAReferences(primary); // assert Assert.IsNotNull(attestation.CAReferences.Primary); @@ -362,7 +362,7 @@ public void X509AttestationCreateFromCAReferencesSucceedOnPrimaryAndSecondaryStr string secondary = CA_REFERENCE_STRING; // act - X509Attestation attestation = X509Attestation.CreateFromCAReferences(primary, secondary); + var attestation = X509Attestation.CreateFromCAReferences(primary, secondary); // assert Assert.IsNotNull(attestation.CAReferences.Primary); @@ -379,7 +379,7 @@ public void X509AttestationCreateFromCAReferencesSucceedOnPrimaryAndSecondaryNul string secondary = null; // act - X509Attestation attestation = X509Attestation.CreateFromCAReferences(primary, secondary); + var attestation = X509Attestation.CreateFromCAReferences(primary, secondary); // assert Assert.IsNotNull(attestation.CAReferences.Primary); diff --git a/provisioning/service/tests/Config/X509CertificateWithInfoTests.cs b/provisioning/service/tests/Config/X509CertificateWithInfoTests.cs index 3878712051..3bfac11ee2 100644 --- a/provisioning/service/tests/Config/X509CertificateWithInfoTests.cs +++ b/provisioning/service/tests/Config/X509CertificateWithInfoTests.cs @@ -143,7 +143,7 @@ public void X509CertificateWithInfoSucceedOnJsonWithInfo() string json = makeJson(SUBJECT_NAME, SHA1THUMBPRINT, SHA256THUMBPRINT, ISSUER_NAME, NOT_BEFORE_UTC_STRING, NOT_AFTER_UTC_STRING, SERIAL_NUMBER, VERSION); // act - var x509CertificateWithInfo = Newtonsoft.Json.JsonConvert.DeserializeObject(json); + X509CertificateWithInfo x509CertificateWithInfo = Newtonsoft.Json.JsonConvert.DeserializeObject(json); // assert Assert.IsNotNull(x509CertificateWithInfo.Info); diff --git a/provisioning/service/tests/Config/X509CertificatesTests.cs b/provisioning/service/tests/Config/X509CertificatesTests.cs index 3dc2f23a16..107c4c54a0 100644 --- a/provisioning/service/tests/Config/X509CertificatesTests.cs +++ b/provisioning/service/tests/Config/X509CertificatesTests.cs @@ -127,7 +127,7 @@ public void X509CertificatesSucceedOnJsonWithPrimaryCertificate() "}"; // act - var x509Certificates = JsonConvert.DeserializeObject(json); + X509Certificates x509Certificates = JsonConvert.DeserializeObject(json); // assert Assert.IsNotNull(x509Certificates.Primary); @@ -148,7 +148,7 @@ public void X509CertificatesSucceedOnJsonWithPrimaryAndSecondaryCertificate() "}"; // act - var x509Certificates = JsonConvert.DeserializeObject(json); + X509Certificates x509Certificates = JsonConvert.DeserializeObject(json); // assert Assert.IsNotNull(x509Certificates.Primary); diff --git a/provisioning/service/tests/TestAssert.cs b/provisioning/service/tests/TestAssert.cs index 1c570bc602..db2c9d8d1a 100644 --- a/provisioning/service/tests/TestAssert.cs +++ b/provisioning/service/tests/TestAssert.cs @@ -98,8 +98,8 @@ public static void AreEqual(JArray expectedJObject, JArray actualJObject) for (int index = 0; index < expectedJObject.Count; index++) { - var expectedItem = expectedJObject[index]; - var actualItem = actualJObject[index]; + JToken expectedItem = expectedJObject[index]; + JToken actualItem = actualJObject[index]; if (expectedItem.Type == JTokenType.Object) { AreEqual((JObject)expectedItem, (JObject)actualItem); diff --git a/provisioning/transport/amqp/src/AmqpAuthStrategySymmetricKey.cs b/provisioning/transport/amqp/src/AmqpAuthStrategySymmetricKey.cs index b0ff294f52..5bcc802b09 100644 --- a/provisioning/transport/amqp/src/AmqpAuthStrategySymmetricKey.cs +++ b/provisioning/transport/amqp/src/AmqpAuthStrategySymmetricKey.cs @@ -34,7 +34,7 @@ public override AmqpSettings CreateAmqpSettings(string idScope) saslProvider.Versions.Add(AmqpConstants.DefaultProtocolVersion); settings.TransportProviders.Add(saslProvider); - SaslPlainHandler saslHandler = new SaslPlainHandler(); + var saslHandler = new SaslPlainHandler(); saslHandler.AuthenticationIdentity = $"{idScope}/registrations/{_security.GetRegistrationID()}"; string key = _security.GetPrimaryKey(); saslHandler.Password = ProvisioningSasBuilder.BuildSasSignature("registration", key, saslHandler.AuthenticationIdentity, TimeSpan.FromDays(1)); diff --git a/provisioning/transport/amqp/src/AmqpAuthStrategyTpm.cs b/provisioning/transport/amqp/src/AmqpAuthStrategyTpm.cs index 46e6484d51..c434da921a 100644 --- a/provisioning/transport/amqp/src/AmqpAuthStrategyTpm.cs +++ b/provisioning/transport/amqp/src/AmqpAuthStrategyTpm.cs @@ -32,7 +32,7 @@ public override AmqpSettings CreateAmqpSettings(string idScope) byte[] ekBuffer = _security.GetEndorsementKey(); byte[] srkBuffer = _security.GetStorageRootKey(); - SaslTpmHandler tpmHandler = new SaslTpmHandler(ekBuffer, srkBuffer, idScope, _security); + var tpmHandler = new SaslTpmHandler(ekBuffer, srkBuffer, idScope, _security); saslProvider.AddHandler(tpmHandler); return settings; diff --git a/provisioning/transport/amqp/src/GlobalSuppressions.cs b/provisioning/transport/amqp/src/GlobalSuppressions.cs index 7fc7c806ad..15f281aa6f 100644 --- a/provisioning/transport/amqp/src/GlobalSuppressions.cs +++ b/provisioning/transport/amqp/src/GlobalSuppressions.cs @@ -5,5 +5,15 @@ // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Not localizing", Scope = "module")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "SDK hides non-actionable errors from user", Scope = "module")] +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage( + "Globalization", + "CA1303:Do not pass literals as localized parameters", + Justification = "Not localizing", + Scope = "module")] +[assembly: SuppressMessage( + "Design", + "CA1031:Do not catch general exception types", + Justification = "SDK hides non-actionable errors from user", + Scope = "module")] diff --git a/provisioning/transport/amqp/src/ProvisioningErrorDetailsAmqp.cs b/provisioning/transport/amqp/src/ProvisioningErrorDetailsAmqp.cs index 703ce20d40..bce278a221 100644 --- a/provisioning/transport/amqp/src/ProvisioningErrorDetailsAmqp.cs +++ b/provisioning/transport/amqp/src/ProvisioningErrorDetailsAmqp.cs @@ -8,7 +8,7 @@ namespace Microsoft.Azure.Devices.Provisioning.Client.Transport { - [SuppressMessage("Microsoft.Performance", "CA1812", Justification = "Is instantiated by json convertor")] + [SuppressMessage("Microsoft.Performance", "CA1812", Justification = "Is instantiated by json converter")] internal class ProvisioningErrorDetailsAmqp : ProvisioningErrorDetails { /// @@ -20,13 +20,11 @@ internal class ProvisioningErrorDetailsAmqp : ProvisioningErrorDetails public static TimeSpan? GetRetryAfterFromApplicationProperties(AmqpMessage amqpResponse, TimeSpan defaultInterval) { - object retryAfter; - if (amqpResponse.ApplicationProperties != null && amqpResponse.ApplicationProperties.Map.TryGetValue(RetryAfterKey, out retryAfter)) + if (amqpResponse.ApplicationProperties != null && amqpResponse.ApplicationProperties.Map.TryGetValue(RetryAfterKey, out object retryAfter)) { - int secondsToWait; - if (int.TryParse(retryAfter.ToString(), out secondsToWait)) + if (int.TryParse(retryAfter.ToString(), out int secondsToWait)) { - TimeSpan serviceRecommendedDelay = TimeSpan.FromSeconds(secondsToWait); + var serviceRecommendedDelay = TimeSpan.FromSeconds(secondsToWait); if (serviceRecommendedDelay.TotalSeconds < defaultInterval.TotalSeconds) { @@ -46,23 +44,16 @@ internal class ProvisioningErrorDetailsAmqp : ProvisioningErrorDetails { if (rejected.Error != null && rejected.Error.Info != null) { - object retryAfter; - if (rejected.Error.Info.TryGetValue(RetryAfterKey, out retryAfter)) + if (rejected.Error.Info.TryGetValue(RetryAfterKey, out object retryAfter)) { - int secondsToWait = 0; - if (int.TryParse(retryAfter.ToString(), out secondsToWait)) + if (int.TryParse(retryAfter.ToString(), out int secondsToWait)) { - if (secondsToWait < defaultInterval.Seconds) - { - return defaultInterval; - } - else - { - return TimeSpan.FromSeconds(secondsToWait); - } + return secondsToWait < defaultInterval.Seconds + ? defaultInterval + : TimeSpan.FromSeconds(secondsToWait); } } - + } return null; diff --git a/provisioning/transport/amqp/src/SaslTpmHandler.cs b/provisioning/transport/amqp/src/SaslTpmHandler.cs index 3b435d9528..da93acffdd 100644 --- a/provisioning/transport/amqp/src/SaslTpmHandler.cs +++ b/provisioning/transport/amqp/src/SaslTpmHandler.cs @@ -75,7 +75,7 @@ public override int GetHashCode() { unchecked { - var hashCode = _endorsementKey != null ? _endorsementKey.GetHashCode() : 0; + int hashCode = _endorsementKey != null ? _endorsementKey.GetHashCode() : 0; hashCode = (hashCode * 397) ^ (_storageRootKey != null ? _storageRootKey.GetHashCode() : 0); #if NETSTANDARD2_1 hashCode = (hashCode * 397) ^ (_idScope != null ? _idScope.GetHashCode(StringComparison.Ordinal) : 0); @@ -105,7 +105,7 @@ public override SaslHandler Clone() public override void OnChallenge(SaslChallenge challenge) { - var challengeAction = GetChallengeAction(challenge); + SaslChallengeAction challengeAction = GetChallengeAction(challenge); switch (challengeAction) { @@ -138,7 +138,7 @@ private void SendLastResponse() _hostName, _nonceBuffer); - var responseBuffer = new byte[sas.Length + 1]; + byte[] responseBuffer = new byte[sas.Length + 1]; responseBuffer[0] = 0x0; Buffer.BlockCopy(Encoding.UTF8.GetBytes(sas), 0, responseBuffer, 1, sas.Length); @@ -148,7 +148,7 @@ private void SendLastResponse() private void SaveEncodedNonceSegment(SaslChallenge saslChallenge) { - var sequenceNumber = GetSequenceNumber(saslChallenge); + byte sequenceNumber = GetSequenceNumber(saslChallenge); if (_nextSequenceNumber != sequenceNumber) { throw new AmqpException(AmqpErrorCode.InvalidField, @@ -182,7 +182,7 @@ private void SendStorageRootKey() private byte[] CreateStorageRootKeyMessage() { - var responseBuffer = new byte[_storageRootKey.Length + 1]; + byte[] responseBuffer = new byte[_storageRootKey.Length + 1]; responseBuffer[0] = 0x0; Buffer.BlockCopy(_storageRootKey, 0, responseBuffer, 1, _storageRootKey.Length); return responseBuffer; @@ -190,7 +190,7 @@ private byte[] CreateStorageRootKeyMessage() private static SaslChallengeAction GetChallengeAction(SaslChallenge saslChallenge) { - var challengeBytes = saslChallenge.Challenge.Array; + byte[] challengeBytes = saslChallenge.Challenge.Array; if (challengeBytes == null || challengeBytes.Length == 0) { @@ -237,7 +237,7 @@ private void SendSaslInitMessage(SaslInit init) private byte[] CreateSaslInitMessage(SaslInit init) { init.HostName = _hostName; - StringBuilder initContent = new StringBuilder(); + var initContent = new StringBuilder(); initContent.Append(_idScope); initContent.Append('\0'); initContent.Append(_security.GetRegistrationID()); @@ -245,7 +245,7 @@ private byte[] CreateSaslInitMessage(SaslInit init) byte[] initContentInBytes = Encoding.UTF8.GetBytes(initContent.ToString()); - var responseBuffer = new byte[initContentInBytes.Length + _endorsementKey.Length + 1]; + byte[] responseBuffer = new byte[initContentInBytes.Length + _endorsementKey.Length + 1]; responseBuffer[0] = 0x0; Buffer.BlockCopy(initContentInBytes, 0, responseBuffer, 1, initContentInBytes.Length); Buffer.BlockCopy(_endorsementKey, 0, responseBuffer, initContentInBytes.Length + 1, _endorsementKey.Length); diff --git a/provisioning/transport/amqp/src/TaskHelpers.cs b/provisioning/transport/amqp/src/TaskHelpers.cs index 1e18b1b634..c15b2d6ec5 100644 --- a/provisioning/transport/amqp/src/TaskHelpers.cs +++ b/provisioning/transport/amqp/src/TaskHelpers.cs @@ -56,7 +56,7 @@ public static IAsyncResult ToAsyncResult(this Task task, AsyncCallback cal public static void EndAsyncResult(IAsyncResult asyncResult) { - Task task = asyncResult as Task; + var task = asyncResult as Task; if (task == null) { throw new ArgumentException($"Given {nameof(asyncResult)} is not subclass of Task."); diff --git a/provisioning/transport/amqp/tests/ProvisioningErrorDetailsAmqpTests.cs b/provisioning/transport/amqp/tests/ProvisioningErrorDetailsAmqpTests.cs index ba0a6e39fc..355029977a 100644 --- a/provisioning/transport/amqp/tests/ProvisioningErrorDetailsAmqpTests.cs +++ b/provisioning/transport/amqp/tests/ProvisioningErrorDetailsAmqpTests.cs @@ -114,7 +114,7 @@ public void GetRetryFromRejectedReturnsNullIfNoError() public void GetRetryAfterFromApplicationPropertiesSuccess() { int expectedRetryAfter = 42; - using AmqpMessage amqpResponse = AmqpMessage.Create(); + using var amqpResponse = AmqpMessage.Create(); amqpResponse.ApplicationProperties = new ApplicationProperties(); amqpResponse.ApplicationProperties.Map.Add(new MapKey("Retry-After"), expectedRetryAfter); TimeSpan? actual = ProvisioningErrorDetailsAmqp.GetRetryAfterFromApplicationProperties(amqpResponse, s_defaultInterval); @@ -126,7 +126,7 @@ public void GetRetryAfterFromApplicationPropertiesSuccess() public void GetRetryAfterFromApplicationPropertiesReturnsDefaultIfRetryAfterValueIsNegative() { int expectedRetryAfter = -1; - using AmqpMessage amqpResponse = AmqpMessage.Create(); + using var amqpResponse = AmqpMessage.Create(); amqpResponse.ApplicationProperties = new ApplicationProperties(); amqpResponse.ApplicationProperties.Map.Add(new MapKey("Retry-After"), expectedRetryAfter); TimeSpan? actual = ProvisioningErrorDetailsAmqp.GetRetryAfterFromApplicationProperties(amqpResponse, s_defaultInterval); @@ -138,7 +138,7 @@ public void GetRetryAfterFromApplicationPropertiesReturnsDefaultIfRetryAfterValu public void GetRetryAfterFromApplicationPropertiesReturnsDefaultIfRetryAfterValueIsZero() { int expectedRetryAfter = 0; - using AmqpMessage amqpResponse = AmqpMessage.Create(); + using var amqpResponse = AmqpMessage.Create(); amqpResponse.ApplicationProperties = new ApplicationProperties(); amqpResponse.ApplicationProperties.Map.Add(new MapKey("Retry-After"), expectedRetryAfter); TimeSpan? actual = ProvisioningErrorDetailsAmqp.GetRetryAfterFromApplicationProperties(amqpResponse, s_defaultInterval); @@ -149,7 +149,7 @@ public void GetRetryAfterFromApplicationPropertiesReturnsDefaultIfRetryAfterValu [TestMethod] public void GetRetryAfterFromApplicationPropertiesReturnsNullIfNoRetryAfterApplicationProperty() { - using AmqpMessage amqpResponse = AmqpMessage.Create(); + using var amqpResponse = AmqpMessage.Create(); amqpResponse.ApplicationProperties = new ApplicationProperties(); TimeSpan? actual = ProvisioningErrorDetailsAmqp.GetRetryAfterFromApplicationProperties(amqpResponse, s_defaultInterval); Assert.IsNull(actual); @@ -158,7 +158,7 @@ public void GetRetryAfterFromApplicationPropertiesReturnsNullIfNoRetryAfterAppli [TestMethod] public void GetRetryAfterFromApplicationPropertiesReturnsNullIfNoApplicationProperties() { - using AmqpMessage amqpResponse = AmqpMessage.Create(); + using var amqpResponse = AmqpMessage.Create(); TimeSpan? actual = ProvisioningErrorDetailsAmqp.GetRetryAfterFromApplicationProperties(amqpResponse, s_defaultInterval); Assert.IsNull(actual); } diff --git a/provisioning/transport/amqp/tests/RetryJitterTests.cs b/provisioning/transport/amqp/tests/RetryJitterTests.cs index 87580c7486..e7253b5c7a 100644 --- a/provisioning/transport/amqp/tests/RetryJitterTests.cs +++ b/provisioning/transport/amqp/tests/RetryJitterTests.cs @@ -15,7 +15,7 @@ public class RetryJitterTests public void RetryJitterGeneratedDelayLargerOrEqualToDefaultDelay() { int expectedMinimumDelay = 2; - TimeSpan DefaultDelay = TimeSpan.FromSeconds(expectedMinimumDelay); + var DefaultDelay = TimeSpan.FromSeconds(expectedMinimumDelay); TimeSpan GeneratedDelay = RetryJitter.GenerateDelayWithJitterForRetry(DefaultDelay); Assert.IsNotNull(GeneratedDelay); Assert.IsTrue(GeneratedDelay.Seconds >= DefaultDelay.Seconds); @@ -26,7 +26,7 @@ public void RetryJitterGeneratedDelayNoLargerThanFiveSeconds() { //current maximum jitter delay is 5 seconds, may change in the future int expectedMinimumDelay = 0; - TimeSpan DefaultDelay = TimeSpan.FromSeconds(expectedMinimumDelay); + var DefaultDelay = TimeSpan.FromSeconds(expectedMinimumDelay); TimeSpan GeneratedDelay = RetryJitter.GenerateDelayWithJitterForRetry(DefaultDelay); Assert.IsNotNull(GeneratedDelay); Assert.IsTrue(GeneratedDelay.Seconds <= 5); diff --git a/provisioning/transport/http/src/ApiVersionDelegatingHandler.cs b/provisioning/transport/http/src/ApiVersionDelegatingHandler.cs index be725b4b38..09823cb382 100644 --- a/provisioning/transport/http/src/ApiVersionDelegatingHandler.cs +++ b/provisioning/transport/http/src/ApiVersionDelegatingHandler.cs @@ -19,7 +19,7 @@ protected override Task SendAsync(HttpRequestMessage reques Logging.Enter(this, $"{request.RequestUri}", nameof(SendAsync)); } - var valueCollection = HttpUtility.ParseQueryString(request.RequestUri.Query); + System.Collections.Specialized.NameValueCollection valueCollection = HttpUtility.ParseQueryString(request.RequestUri.Query); valueCollection[ClientApiVersionHelper.ApiVersionName] = ClientApiVersionHelper.ApiVersion; var builder = new UriBuilder(request.RequestUri) diff --git a/provisioning/transport/http/src/CertificateChainCredentials.cs b/provisioning/transport/http/src/CertificateChainCredentials.cs index de193c865d..cbfffad02f 100644 --- a/provisioning/transport/http/src/CertificateChainCredentials.cs +++ b/provisioning/transport/http/src/CertificateChainCredentials.cs @@ -24,7 +24,7 @@ public override void InitializeServiceClient(ServiceClient client) { base.InitializeServiceClient(client); - HttpClientHandler httpClientHandler = client.HttpMessageHandlers.FirstOrDefault((handler) => handler is HttpClientHandler) as HttpClientHandler; + var httpClientHandler = client.HttpMessageHandlers.FirstOrDefault((handler) => handler is HttpClientHandler) as HttpClientHandler; Debug.Assert(httpClientHandler != null); httpClientHandler.ClientCertificates.AddRange(_certificateChain.ToArray()); diff --git a/provisioning/transport/http/src/Generated/RuntimeRegistration.cs b/provisioning/transport/http/src/Generated/RuntimeRegistration.cs index 954f915718..93e7cdd305 100644 --- a/provisioning/transport/http/src/Generated/RuntimeRegistration.cs +++ b/provisioning/transport/http/src/Generated/RuntimeRegistration.cs @@ -100,7 +100,7 @@ public RuntimeRegistration(DeviceProvisioningServiceRuntimeClient client) if (_shouldTrace) { _invocationId = ServiceClientTracing.NextInvocationId.ToString(CultureInfo.InvariantCulture); - Dictionary tracingParameters = new Dictionary + var tracingParameters = new Dictionary { { "registrationId", registrationId }, { "operationId", operationId }, @@ -110,8 +110,8 @@ public RuntimeRegistration(DeviceProvisioningServiceRuntimeClient client) ServiceClientTracing.Enter(_invocationId, this, "OperationStatusLookup", tracingParameters); } // Construct URL - var _baseUrl = Client.BaseUri.AbsoluteUri; - var _url = new Uri( + string _baseUrl = Client.BaseUri.AbsoluteUri; + string _url = new Uri( new Uri(_baseUrl + (_baseUrl.EndsWith("/", StringComparison.Ordinal) ? "" : "/")), "{idScope}/registrations/{registrationId}/operations/{operationId}").ToString(); @@ -134,7 +134,7 @@ public RuntimeRegistration(DeviceProvisioningServiceRuntimeClient client) if (customHeaders != null) { - foreach (var _header in customHeaders) + foreach (KeyValuePair> _header in customHeaders) { if (_httpRequest.Headers.Contains(_header.Key)) { @@ -292,7 +292,7 @@ public RuntimeRegistration(DeviceProvisioningServiceRuntimeClient client) if (_shouldTrace) { _invocationId = ServiceClientTracing.NextInvocationId.ToString(CultureInfo.InvariantCulture); - Dictionary tracingParameters = new Dictionary + var tracingParameters = new Dictionary { { "registrationId", registrationId }, { "deviceRegistration", deviceRegistration }, @@ -302,8 +302,8 @@ public RuntimeRegistration(DeviceProvisioningServiceRuntimeClient client) ServiceClientTracing.Enter(_invocationId, this, "DeviceRegistrationStatusLookup", tracingParameters); } // Construct URL - var _baseUrl = Client.BaseUri.AbsoluteUri; - var _url = new Uri(new Uri(_baseUrl + (_baseUrl.EndsWith("/", StringComparison.Ordinal) ? "" : "/")), "{idScope}/registrations/{registrationId}").ToString(); + string _baseUrl = Client.BaseUri.AbsoluteUri; + string _url = new Uri(new Uri(_baseUrl + (_baseUrl.EndsWith("/", StringComparison.Ordinal) ? "" : "/")), "{idScope}/registrations/{registrationId}").ToString(); #if NETSTANDARD2_1 _url = _url.Replace("{registrationId}", Uri.EscapeDataString(registrationId), StringComparison.Ordinal); @@ -322,7 +322,7 @@ public RuntimeRegistration(DeviceProvisioningServiceRuntimeClient client) if (customHeaders != null) { - foreach (var _header in customHeaders) + foreach (KeyValuePair> _header in customHeaders) { if (_httpRequest.Headers.Contains(_header.Key)) { @@ -473,7 +473,7 @@ public RuntimeRegistration(DeviceProvisioningServiceRuntimeClient client) if (_shouldTrace) { _invocationId = ServiceClientTracing.NextInvocationId.ToString(CultureInfo.InvariantCulture); - Dictionary tracingParameters = new Dictionary + var tracingParameters = new Dictionary { { "registrationId", registrationId }, { "deviceRegistration", deviceRegistration }, @@ -484,8 +484,8 @@ public RuntimeRegistration(DeviceProvisioningServiceRuntimeClient client) ServiceClientTracing.Enter(_invocationId, this, "RegisterDevice", tracingParameters); } // Construct URL - var _baseUrl = Client.BaseUri.AbsoluteUri; - var _url = new Uri(new Uri(_baseUrl + (_baseUrl.EndsWith("/", StringComparison.Ordinal) ? "" : "/")), "{idScope}/registrations/{registrationId}/register").ToString(); + string _baseUrl = Client.BaseUri.AbsoluteUri; + string _url = new Uri(new Uri(_baseUrl + (_baseUrl.EndsWith("/", StringComparison.Ordinal) ? "" : "/")), "{idScope}/registrations/{registrationId}/register").ToString(); #if NETSTANDARD2_1 _url = _url.Replace("{registrationId}", Uri.EscapeDataString(registrationId), StringComparison.Ordinal); @@ -495,7 +495,7 @@ public RuntimeRegistration(DeviceProvisioningServiceRuntimeClient client) _url = _url.Replace("{idScope}", Uri.EscapeDataString(idScope)); #endif - List _queryParameters = new List(); + var _queryParameters = new List(); if (forceRegistration != null) { _queryParameters.Add(string.Format( @@ -516,7 +516,7 @@ public RuntimeRegistration(DeviceProvisioningServiceRuntimeClient client) if (customHeaders != null) { - foreach (var _header in customHeaders) + foreach (KeyValuePair> _header in customHeaders) { if (_httpRequest.Headers.Contains(_header.Key)) { diff --git a/provisioning/transport/http/src/Generated/RuntimeRegistrationExtensions.cs b/provisioning/transport/http/src/Generated/RuntimeRegistrationExtensions.cs index 5730b86369..51b034e257 100644 --- a/provisioning/transport/http/src/Generated/RuntimeRegistrationExtensions.cs +++ b/provisioning/transport/http/src/Generated/RuntimeRegistrationExtensions.cs @@ -61,7 +61,7 @@ internal static partial class RuntimeRegistrationExtensions string idScope, CancellationToken cancellationToken = default(CancellationToken)) { - using (var _result = await operations.OperationStatusLookupWithHttpMessagesAsync( + using (Rest.HttpOperationResponse _result = await operations.OperationStatusLookupWithHttpMessagesAsync( registrationId, operationId, idScope, @@ -119,7 +119,7 @@ internal static partial class RuntimeRegistrationExtensions DeviceRegistration deviceRegistration = default(DeviceRegistration), CancellationToken cancellationToken = default(CancellationToken)) { - using (var _result = await operations.DeviceRegistrationStatusLookupWithHttpMessagesAsync( + using (Rest.HttpOperationResponse _result = await operations.DeviceRegistrationStatusLookupWithHttpMessagesAsync( registrationId, idScope, deviceRegistration, @@ -187,7 +187,7 @@ internal static partial class RuntimeRegistrationExtensions bool? forceRegistration = default(bool?), CancellationToken cancellationToken = default(CancellationToken)) { - using (var _result = await operations.RegisterDeviceWithHttpMessagesAsync(registrationId, idScope, deviceRegistration, forceRegistration, null, cancellationToken).ConfigureAwait(false)) + using (Rest.HttpOperationResponse _result = await operations.RegisterDeviceWithHttpMessagesAsync(registrationId, idScope, deviceRegistration, forceRegistration, null, cancellationToken).ConfigureAwait(false)) { _result.Body.RetryAfter = _result.Response.Headers.RetryAfter?.Delta; return _result.Body; diff --git a/provisioning/transport/http/src/GlobalSuppressions.cs b/provisioning/transport/http/src/GlobalSuppressions.cs index 7fc7c806ad..15f281aa6f 100644 --- a/provisioning/transport/http/src/GlobalSuppressions.cs +++ b/provisioning/transport/http/src/GlobalSuppressions.cs @@ -5,5 +5,15 @@ // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Not localizing", Scope = "module")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "SDK hides non-actionable errors from user", Scope = "module")] +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage( + "Globalization", + "CA1303:Do not pass literals as localized parameters", + Justification = "Not localizing", + Scope = "module")] +[assembly: SuppressMessage( + "Design", + "CA1031:Do not catch general exception types", + Justification = "SDK hides non-actionable errors from user", + Scope = "module")] diff --git a/provisioning/transport/http/src/ProvisioningTransportHandlerHttp.cs b/provisioning/transport/http/src/ProvisioningTransportHandlerHttp.cs index 6014d906a5..c70a7341a1 100644 --- a/provisioning/transport/http/src/ProvisioningTransportHandlerHttp.cs +++ b/provisioning/transport/http/src/ProvisioningTransportHandlerHttp.cs @@ -187,7 +187,7 @@ await Task try { - var errorDetails = JsonConvert.DeserializeObject(ex.Response.Content); + ProvisioningErrorDetailsHttp errorDetails = JsonConvert.DeserializeObject(ex.Response.Content); if (isTransient) { @@ -248,7 +248,7 @@ await Task try { - var errorDetails = JsonConvert.DeserializeObject(ex.Response.Content); + ProvisioningErrorDetailsHttp errorDetails = JsonConvert.DeserializeObject(ex.Response.Content); throw new ProvisioningTransportException(ex.Response.Content, ex, isTransient, errorDetails); } catch (JsonException jex) diff --git a/provisioning/transport/http/src/SymmetricKeyCredentials.cs b/provisioning/transport/http/src/SymmetricKeyCredentials.cs index 88b253eca9..ed652f092e 100644 --- a/provisioning/transport/http/src/SymmetricKeyCredentials.cs +++ b/provisioning/transport/http/src/SymmetricKeyCredentials.cs @@ -30,7 +30,7 @@ public SymmetricKeyCredentials(string symmetricKey) : base() public override Task ProcessHttpRequestAsync(HttpRequestMessage request, CancellationToken cancellationToken) { string audience = request.RequestUri.AbsolutePath.Trim('/'); - var segments = audience.Split('/'); + string[] segments = audience.Split('/'); _sasToken = ProvisioningSasBuilder.BuildSasSignature(Registration, this.SymmetricKey, string.Concat(segments[0], '/', segments[1], '/', segments[2]), TimeSpan.FromDays(1)); SetAuthorizationHeader(request, _sasToken); diff --git a/provisioning/transport/mqtt/src/GlobalSuppressions.cs b/provisioning/transport/mqtt/src/GlobalSuppressions.cs index 7fc7c806ad..15f281aa6f 100644 --- a/provisioning/transport/mqtt/src/GlobalSuppressions.cs +++ b/provisioning/transport/mqtt/src/GlobalSuppressions.cs @@ -5,5 +5,15 @@ // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Not localizing", Scope = "module")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "SDK hides non-actionable errors from user", Scope = "module")] +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage( + "Globalization", + "CA1303:Do not pass literals as localized parameters", + Justification = "Not localizing", + Scope = "module")] +[assembly: SuppressMessage( + "Design", + "CA1031:Do not catch general exception types", + Justification = "SDK hides non-actionable errors from user", + Scope = "module")] diff --git a/provisioning/transport/mqtt/src/Microsoft.Azure.Devices.Provisioning.Transport.Mqtt.csproj b/provisioning/transport/mqtt/src/Microsoft.Azure.Devices.Provisioning.Transport.Mqtt.csproj index 7bdbb72933..b547ec41c1 100644 --- a/provisioning/transport/mqtt/src/Microsoft.Azure.Devices.Provisioning.Transport.Mqtt.csproj +++ b/provisioning/transport/mqtt/src/Microsoft.Azure.Devices.Provisioning.Transport.Mqtt.csproj @@ -100,6 +100,8 @@ + + all diff --git a/provisioning/transport/mqtt/src/ProvisioningErrorDetailsMqtt.cs b/provisioning/transport/mqtt/src/ProvisioningErrorDetailsMqtt.cs index 6c3288875e..8fd143bc5f 100644 --- a/provisioning/transport/mqtt/src/ProvisioningErrorDetailsMqtt.cs +++ b/provisioning/transport/mqtt/src/ProvisioningErrorDetailsMqtt.cs @@ -32,7 +32,7 @@ internal class ProvisioningErrorDetailsMqtt : ProvisioningErrorDetails int secondsToWait; if (int.TryParse(queryKeyAndValue[1], out secondsToWait)) { - TimeSpan serviceRecommendedDelay = TimeSpan.FromSeconds(secondsToWait); + var serviceRecommendedDelay = TimeSpan.FromSeconds(secondsToWait); if (serviceRecommendedDelay.TotalSeconds < defaultPoolingInterval.TotalSeconds) { diff --git a/shared/src/ConventionBasedConstants.cs b/shared/src/ConventionBasedConstants.cs index 2313acf92e..14f8579644 100644 --- a/shared/src/ConventionBasedConstants.cs +++ b/shared/src/ConventionBasedConstants.cs @@ -4,7 +4,7 @@ namespace Microsoft.Azure.Devices.Shared { /// - /// Container for common convention based constants. + /// Common convention based constants. /// public static class ConventionBasedConstants { @@ -29,17 +29,17 @@ public static class ConventionBasedConstants public const string ValuePropertyName = "value"; /// - /// Represents the JSON document property name for the Ack Code of a writable property response. + /// Represents the JSON document property name for the acknowledgement code of a writable property response. /// public const string AckCodePropertyName = "ac"; /// - /// Represents the JSON document property name for the Ack Version of a writable property response. + /// Represents the JSON document property name for the acknowledgement version of a writable property response. /// public const string AckVersionPropertyName = "av"; /// - /// Represents the JSON document property name for the Ack Description of a writable property response. + /// Represents the JSON document property name for the acknowledgement description of a writable property response. /// public const string AckDescriptionPropertyName = "ad"; } diff --git a/shared/src/DefaultPayloadConvention.cs b/shared/src/DefaultPayloadConvention.cs index 749fb31a13..bdb5cef08e 100644 --- a/shared/src/DefaultPayloadConvention.cs +++ b/shared/src/DefaultPayloadConvention.cs @@ -14,7 +14,7 @@ public sealed class DefaultPayloadConvention : PayloadConvention /// /// A static instance of this class. /// - public static readonly DefaultPayloadConvention Instance = new DefaultPayloadConvention(); + public static DefaultPayloadConvention Instance { get; } = new DefaultPayloadConvention(); /// public override PayloadSerializer PayloadSerializer { get; } = NewtonsoftJsonPayloadSerializer.Instance; diff --git a/shared/src/GlobalSuppressions.cs b/shared/src/GlobalSuppressions.cs index 7fc7c806ad..15f281aa6f 100644 --- a/shared/src/GlobalSuppressions.cs +++ b/shared/src/GlobalSuppressions.cs @@ -5,5 +5,15 @@ // Project-level suppressions either have no target or are given // a specific target and scoped to a namespace, type, member, etc. -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "Not localizing", Scope = "module")] -[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1031:Do not catch general exception types", Justification = "SDK hides non-actionable errors from user", Scope = "module")] +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage( + "Globalization", + "CA1303:Do not pass literals as localized parameters", + Justification = "Not localizing", + Scope = "module")] +[assembly: SuppressMessage( + "Design", + "CA1031:Do not catch general exception types", + Justification = "SDK hides non-actionable errors from user", + Scope = "module")] diff --git a/shared/src/Microsoft.Azure.Devices.Shared.csproj b/shared/src/Microsoft.Azure.Devices.Shared.csproj index 0237e86288..c53589679c 100644 --- a/shared/src/Microsoft.Azure.Devices.Shared.csproj +++ b/shared/src/Microsoft.Azure.Devices.Shared.csproj @@ -51,7 +51,6 @@ - all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/shared/src/NewtonsoftJsonPayloadSerializer.cs b/shared/src/NewtonsoftJsonPayloadSerializer.cs index 349c76676b..1468d83f07 100644 --- a/shared/src/NewtonsoftJsonPayloadSerializer.cs +++ b/shared/src/NewtonsoftJsonPayloadSerializer.cs @@ -7,7 +7,7 @@ namespace Microsoft.Azure.Devices.Shared { /// - /// A implementation. + /// A PayloadSerializer implementation. /// public class NewtonsoftJsonPayloadSerializer : PayloadSerializer { @@ -19,7 +19,7 @@ public class NewtonsoftJsonPayloadSerializer : PayloadSerializer /// /// The default instance of this class. /// - public static readonly NewtonsoftJsonPayloadSerializer Instance = new NewtonsoftJsonPayloadSerializer(); + public static NewtonsoftJsonPayloadSerializer Instance { get; } = new NewtonsoftJsonPayloadSerializer(); /// public override string ContentType => ApplicationJson; @@ -50,7 +50,8 @@ public override T ConvertFromObject(object objectToConvert) public override bool TryGetNestedObjectValue(object nestedObject, string propertyName, out T outValue) { outValue = default; - if (nestedObject == null || string.IsNullOrEmpty(propertyName)) + if (nestedObject == null + || string.IsNullOrEmpty(propertyName)) { return false; } @@ -62,7 +63,8 @@ public override bool TryGetNestedObjectValue(object nestedObject, string prop ? DeserializeToType((string)nestedObject) : nestedObject as JObject; - if (nestedObjectAsJObject != null && nestedObjectAsJObject.TryGetValue(propertyName, out JToken element)) + if (nestedObjectAsJObject != null + && nestedObjectAsJObject.TryGetValue(propertyName, out JToken element)) { outValue = element.ToObject(); return true; diff --git a/shared/src/PayloadConvention.cs b/shared/src/PayloadConvention.cs index 468083e9c1..ade7fc2345 100644 --- a/shared/src/PayloadConvention.cs +++ b/shared/src/PayloadConvention.cs @@ -6,9 +6,11 @@ namespace Microsoft.Azure.Devices.Shared /// /// The payload convention class. /// - /// The payload convention is used to define a specific serializer as well as a specific content encoding. - /// For example, IoT has a convention that is designed - /// to make it easier to get started with products that use specific conventions by default. + /// + /// The payload convention is used to define a specific serializer as well as a specific content encoding. + /// For example, Azure IoT has a Plug and Play convention + /// that is designed to make it easier to get started with products that use specific conventions by default. + /// public abstract class PayloadConvention { /// @@ -26,8 +28,8 @@ public abstract class PayloadConvention /// /// Returns the byte array for the convention-based message. /// - /// This base class will use the and to create this byte array. - /// The convention-based message that is to be sent. + /// This will use the and to create the byte array. + /// The convention-based message to be sent. /// The correctly encoded object for this convention. public virtual byte[] GetObjectBytes(object objectToSendWithConvention) { diff --git a/shared/src/PayloadEncoder.cs b/shared/src/PayloadEncoder.cs index 03b5c8bb72..b6f3cc622f 100644 --- a/shared/src/PayloadEncoder.cs +++ b/shared/src/PayloadEncoder.cs @@ -6,12 +6,12 @@ namespace Microsoft.Azure.Devices.Shared { /// - /// This class specifies the byte encoding for the payload. + /// Specifies the byte encoding for the payload. /// /// - /// The encoder is responsible for encoding all of your objects into the correct bytes for the that uses it. + /// The encoder is responsible for encoding all objects into the correct bytes for the that uses it. /// - /// By default we have implemented the class that uses + /// By default, there are implementations of the class that uses /// to handle the encoding for the class. /// /// diff --git a/shared/src/PayloadSerializer.cs b/shared/src/PayloadSerializer.cs index 15e7ee435f..d258e061ed 100644 --- a/shared/src/PayloadSerializer.cs +++ b/shared/src/PayloadSerializer.cs @@ -7,18 +7,21 @@ namespace Microsoft.Azure.Devices.Shared /// Provides the serialization for a specified convention. /// /// - /// The serializer is responsible for converting all of your objects into the correct format for the that uses it. + /// The serializer is responsible for converting all objects into the correct format for the that uses it. /// - /// By default we have implemented the class that uses + /// By default there are implementions the class that uses /// to handle the serialization for the class. /// /// public abstract class PayloadSerializer { /// - /// Used to specify what type of content to expect. + /// Used to specify what type of content will be in the payload. /// - /// This can be free-form but should adhere to standard MIME types. For example, "application/json" is what we implement by default. + /// + /// This can be free-form but should adhere to standard MIME types, + /// for example: "application/json". + /// /// A string representing the content type to use when sending a payload. public abstract string ContentType { get; } @@ -32,17 +35,19 @@ public abstract class PayloadSerializer /// /// Convert the serialized string to an object. /// - /// The type you want to return. - /// String to deserialize. + /// The type to return. + /// The string to deserialize. /// A fully deserialized type. public abstract T DeserializeToType(string stringToDeserialize); /// /// Converts the object using the serializer. /// - /// This class is used by the PayloadCollection-based classes to attempt to convert from the native serializer type + /// + /// This class is used by the PayloadCollection-based classes to attempt to convert from the native serializer type /// (for example, JObject or JsonElement) to the desired type. - /// When you implement this you need to be aware of what type your serializer will use for anonymous types. + /// When implementing this, be aware of what type the serializer will use for anonymous types. + /// /// The type to convert to. /// The object to convert. /// A converted object @@ -52,15 +57,15 @@ public abstract class PayloadSerializer /// Gets a nested property from the serialized data. /// /// - /// This is used internally by our PayloadCollection-based classes to attempt to get a property of the underlying object. + /// This is used by the PayloadCollection-based classes to attempt to get a property of the underlying object. /// An example of this would be a property under the component. /// /// The type to convert the retrieved property to. /// The object that might contain the nested property. /// This needs to be in the json object equivalent format as required by the serializer or the string representation of it. /// The name of the property to be retrieved. - /// True if the nested object contains an element with the specified key; otherwise, it returns false. - /// + /// The retrieved value. + /// True if the nested object contains an element with the specified key, otherwise false. public abstract bool TryGetNestedObjectValue(object nestedObject, string propertyName, out T outValue); /// @@ -69,7 +74,7 @@ public abstract class PayloadSerializer /// The value of the property. /// The status code of the write operation. /// The version the property is responding to. - /// An optional description of the writable property response. + /// An optional response description to the writable property request. /// The writable property response to be used with this serializer. public abstract IWritablePropertyResponse CreateWritablePropertyResponse(object value, int statusCode, long version, string description = default); } diff --git a/shared/src/TwinCollectionJsonConverter.cs b/shared/src/TwinCollectionJsonConverter.cs index 794feb4ddb..7860decdfd 100644 --- a/shared/src/TwinCollectionJsonConverter.cs +++ b/shared/src/TwinCollectionJsonConverter.cs @@ -21,7 +21,7 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s return; } - TwinCollection properties = value as TwinCollection; + var properties = value as TwinCollection; if (properties == null) { throw new InvalidOperationException("Object passed is not of type TwinCollection."); diff --git a/shared/src/TwinJsonConverter.cs b/shared/src/TwinJsonConverter.cs index cfe404a3a6..718a3fe703 100644 --- a/shared/src/TwinJsonConverter.cs +++ b/shared/src/TwinJsonConverter.cs @@ -248,7 +248,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist break; case CapabilitiesJsonTag: - var capabilitiesDictionary = serializer.Deserialize>(reader); + Dictionary capabilitiesDictionary = serializer.Deserialize>(reader); twin.Capabilities = new DeviceCapabilities { IotEdge = capabilitiesDictionary.ContainsKey(IotEdgeName) && (bool)capabilitiesDictionary[IotEdgeName] diff --git a/shared/src/Utf8PayloadEncoder.cs b/shared/src/Utf8PayloadEncoder.cs index 0330c3fcf2..b7efbb1153 100644 --- a/shared/src/Utf8PayloadEncoder.cs +++ b/shared/src/Utf8PayloadEncoder.cs @@ -6,14 +6,14 @@ namespace Microsoft.Azure.Devices.Shared { /// - /// A UTF-8 implementation. + /// A UTF-8 PayloadEncoder implementation. /// public class Utf8PayloadEncoder : PayloadEncoder { /// /// The default instance of this class. /// - public static readonly Utf8PayloadEncoder Instance = new Utf8PayloadEncoder(); + public static Utf8PayloadEncoder Instance { get; } = new Utf8PayloadEncoder(); /// public override Encoding ContentEncoding => Encoding.UTF8; diff --git a/vsts/build-release-artifacts.yml b/vsts/build-release-artifacts.yml index 49f59f34b5..ece700c110 100644 --- a/vsts/build-release-artifacts.yml +++ b/vsts/build-release-artifacts.yml @@ -1,9 +1,10 @@ # update .csproj file versions and create a release branch/commit name: BumpVersion_$(BuildID)_$(Date:yyyyMMdd)$(Rev:.r) +pool: + vmImage: windows-latest -phases: - -- phase: Phase_1 +jobs: +- job: Build steps: - task: EsrpClientTool@1 - task: DownloadBuildArtifacts@0 @@ -18,7 +19,7 @@ phases: - checkout: self # self represents the repo where the initial Pipelines YAML file was found persistCredentials: 'true' # set to 'true' to leave the OAuth token in the Git config after the initial fetch - clean: 'resources' + clean: true - task: DownloadSecureFile@1 displayName: 'Download secure file - iothubsdksign-auth-base64.pfx' @@ -91,6 +92,11 @@ phases: ESRP_LOG_DIR: $(Build.StagingDirectory)/build-tools/csharp/new/logs AZURE_IOT_LOCALPACKAGES: $(Build.StagingDirectory)/build-tools/csharp/nuget_local + - task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0 + displayName: 'SBOM Generation Task' + inputs: + BuildDropPath: '$(Build.SourcesDirectory)/bin/pkg' + - task: PublishBuildArtifacts@1 displayName: 'Publish Signed Nuget Packages' inputs: diff --git a/vsts/vsts.yaml b/vsts/vsts.yaml index a7f8c7de81..e6fc47108c 100644 --- a/vsts/vsts.yaml +++ b/vsts/vsts.yaml @@ -17,9 +17,9 @@ resources: clean: true jobs: - ### Linux build ### - - job: LINUX - displayName: Linux + ### Linux build 1 ### + - job: LINUX1 + displayName: Linux pri 1 .NET targets timeoutInMinutes: 75 strategy: # Change maxParallel to 1 make builds run in serial rather than in parallel @@ -29,10 +29,112 @@ jobs: FRAMEWORK: net5.0 .Net Core 3.1: FRAMEWORK: netcoreapp3.1 + + condition: succeeded() + pool: + # If this is changed, don't forget to update supported_platforms.md in the root directory. That document outlines what OS we test on and should stay up to date. + vmImage: ubuntu-20.04 + steps: + - task: Docker@1 + displayName: "Start TPM Simulator" + inputs: + containerregistrytype: "Container Registry" + command: "Run an image" + imageName: aziotbld/testtpm + containerName: "testtpm-instance" + + ports: | + 127.0.0.1:2321:2321 + 127.0.0.1:2322:2322 + + restartPolicy: unlessStopped + + - task: Docker@1 + displayName: "Start Test Proxy" + inputs: + containerregistrytype: "Container Registry" + command: "Run an image" + imageName: aziotbld/testproxy + containerName: "testproxy-instance" + ports: "127.0.0.1:8888:8888" + restartPolicy: unlessStopped + + - powershell: ./vsts/gatedBuild.ps1 + displayName: build + env: + IOTHUB_CONNECTION_STRING: $(IOTHUB-CONNECTION-STRING) + IOTHUB_PFX_X509_THUMBPRINT: $(IOTHUB-PFX-X509-THUMBPRINT) + IOTHUB_X509_PFX_CERTIFICATE: $(IOTHUB-X509-PFX-CERTIFICATE) + DPS_IDSCOPE: $(DPS-IDSCOPE) + DPS_GLOBALDEVICEENDPOINT: $(DPS-GLOBALDEVICEENDPOINT) + DPS_INDIVIDUALX509_PFX_CERTIFICATE: $(DPS-INDIVIDUALX509-PFX-CERTIFICATE) + DPS_GROUPX509_PFX_CERTIFICATE: $(DPS-GROUPX509-PFX-CERTIFICATE) + DPS_X509_PFX_CERTIFICATE_PASSWORD: $(DPS-X509-PFX-CERTIFICATE-PASSWORD) + DPS_GROUPX509_CERTIFICATE_CHAIN: $(DPS-GROUPX509-CERTIFICATE-CHAIN) + DPS_TPM_REGISTRATIONID: $(DPS-TPM-REGISTRATIONID) + DPS_TPM_DEVICEID: $(DPS-TPM-DEVICEID) + PROVISIONING_CONNECTION_STRING: $(PROVISIONING-CONNECTION-STRING) + STORAGE_ACCOUNT_CONNECTION_STRING: $(STORAGE-ACCOUNT-CONNECTION-STRING) + IOTHUB_DEVICE_CONN_STRING_INVALIDCERT: $(IOTHUB-DEVICE-CONN-STRING-INVALIDCERT) + IOTHUB_CONN_STRING_INVALIDCERT: $(IOTHUB-CONN-STRING-INVALIDCERT) + DPS_GLOBALDEVICEENDPOINT_INVALIDCERT: $(DPS-GLOBALDEVICEENDPOINT-INVALIDCERT) + PROVISIONING_CONNECTION_STRING_INVALIDCERT: $(PROVISIONING-CONNECTION-STRING-INVALIDCERT) + FAR_AWAY_IOTHUB_HOSTNAME: $(FAR-AWAY-IOTHUB-HOSTNAME) + CUSTOM_ALLOCATION_POLICY_WEBHOOK: $(CUSTOM-ALLOCATION-POLICY-WEBHOOK) + IOTHUB_PROXY_SERVER_ADDRESS: 127.0.0.1:8888 + MSFT_TENANT_ID: $(MSFT-TENANT-ID) + LA_AAD_APP_ID: $(LA-AAD-APP-ID) + LA_AAD_APP_CERT_BASE64: $(LA-AAD-APP-CERT-BASE64) + LA_WORKSPACE_ID: $(LA-WORKSPACE-ID) + IOTHUB_X509_CHAIN_DEVICE_NAME: $(IOTHUB-X509-CHAIN-DEVICE-NAME) + HUB_CHAIN_DEVICE_PFX_CERTIFICATE: $(HUB-CHAIN-DEVICE-PFX-CERTIFICATE) + HUB_CHAIN_ROOT_CA_CERTIFICATE: $(HUB-CHAIN-ROOT-CA-CERTIFICATE) + HUB_CHAIN_INTERMEDIATE1_CERTIFICATE: $(HUB-CHAIN-INTERMEDIATE1-CERTIFICATE) + HUB_CHAIN_INTERMEDIATE2_CERTIFICATE: $(HUB-CHAIN-INTERMEDIATE2-CERTIFICATE) + E2E_IKEY: $(E2E-IKEY) + TARGET_BRANCH: $(System.PullRequest.TargetBranch) + FRAMEWORK: $(FRAMEWORK) + IOTHUB_CLIENT_ID: $(IOTHUB-CLIENT-ID) + IOTHUB_CLIENT_SECRET: $(IOTHUB-CLIENT-SECRET) + IOTHUB_USER_ASSIGNED_MSI_RESOURCE_ID: $(IOTHUB-USER-ASSIGNED-MSI-RESOURCE-ID) + + - task: CopyFiles@2 + displayName: "Copy files to the artifacts folder" + inputs: + SourceFolder: "$(Build.SourcesDirectory)" + Contents: "**/*.trx" + TargetFolder: "$(Build.ArtifactStagingDirectory)" + + condition: always() + + - task: PublishBuildArtifacts@1 + displayName: "Publish Artifact: testresults_linux_$(FRAMEWORK)" + inputs: + ArtifactName: testresults_linux_$(FRAMEWORK) + + condition: always() + + - task: PublishTestResults@2 + displayName: "Publish Test Results **/*.trx" + inputs: + testRunner: VSTest + testRunTitle: "Linux Tests ($(FRAMEWORK)) (Attempt $(System.JobAttempt))" + testResultsFiles: "**/*.trx" + + condition: always() + + ### Linux build 2 ### + - job: LINUX2 + displayName: Linux pri 2 .NET targets + timeoutInMinutes: 75 + strategy: + # Change maxParallel to 1 make builds run in serial rather than in parallel + maxParallel: 100 + matrix: .Net Core 2.1.18: FRAMEWORK: netcoreapp2.1.18 - condition: succeeded() + condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) pool: # If this is changed, don't forget to update supported_platforms.md in the root directory. That document outlines what OS we test on and should stay up to date. vmImage: ubuntu-20.04 @@ -125,9 +227,9 @@ jobs: condition: always() - ### Windows build ### - - job: WINDOWS - displayName: Windows + ### Windows build 1 ### + - job: WINDOWS1 + displayName: Windows pri 1 .NET targets timeoutInMinutes: 75 strategy: # Change maxParallel to 1 make builds run in serial rather than in parallel @@ -137,14 +239,117 @@ jobs: FRAMEWORK: net5.0 .Net Core 3.1: FRAMEWORK: netcoreapp3.1 + .Net Framework 4.5.1: + FRAMEWORK: net451 + + condition: succeeded() + pool: + # If this is changed, don't forget to update supported_platforms.md in the root directory. That document outlines what OS we test on and should stay up to date. + vmImage: windows-2019 + steps: + - script: | + call "C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Enterprise\\Common7\\Tools\\VsDevCmd.bat" + sn -Vr *,31bf3856ad364e35 + + displayName: "Disable strong name validation" + + - script: | + choco install -y squid + + displayName: "Install Squid" + + - powershell: ./vsts/start_tpm_windows.ps1 + displayName: "Start TPM Simulator" + + - powershell: ./vsts/gatedBuild.ps1 + displayName: build + env: + IOTHUB_CONNECTION_STRING: $(IOTHUB-CONNECTION-STRING) + IOTHUB_PFX_X509_THUMBPRINT: $(IOTHUB-PFX-X509-THUMBPRINT) + IOTHUB_X509_PFX_CERTIFICATE: $(IOTHUB-X509-PFX-CERTIFICATE) + DPS_IDSCOPE: $(DPS-IDSCOPE) + DPS_GLOBALDEVICEENDPOINT: $(DPS-GLOBALDEVICEENDPOINT) + DPS_INDIVIDUALX509_PFX_CERTIFICATE: $(DPS-INDIVIDUALX509-PFX-CERTIFICATE) + DPS_GROUPX509_PFX_CERTIFICATE: $(DPS-GROUPX509-PFX-CERTIFICATE) + DPS_X509_PFX_CERTIFICATE_PASSWORD: $(DPS-X509-PFX-CERTIFICATE-PASSWORD) + DPS_GROUPX509_CERTIFICATE_CHAIN: $(DPS-GROUPX509-CERTIFICATE-CHAIN) + DPS_TPM_REGISTRATIONID: $(DPS-TPM-REGISTRATIONID) + DPS_TPM_DEVICEID: $(DPS-TPM-DEVICEID) + PROVISIONING_CONNECTION_STRING: $(PROVISIONING-CONNECTION-STRING) + STORAGE_ACCOUNT_CONNECTION_STRING: $(STORAGE-ACCOUNT-CONNECTION-STRING) + IOTHUB_DEVICE_CONN_STRING_INVALIDCERT: $(IOTHUB-DEVICE-CONN-STRING-INVALIDCERT) + IOTHUB_CONN_STRING_INVALIDCERT: $(IOTHUB-CONN-STRING-INVALIDCERT) + DPS_GLOBALDEVICEENDPOINT_INVALIDCERT: $(DPS-GLOBALDEVICEENDPOINT-INVALIDCERT) + PROVISIONING_CONNECTION_STRING_INVALIDCERT: $(PROVISIONING-CONNECTION-STRING-INVALIDCERT) + FAR_AWAY_IOTHUB_HOSTNAME: $(FAR-AWAY-IOTHUB-HOSTNAME) + CUSTOM_ALLOCATION_POLICY_WEBHOOK: $(CUSTOM-ALLOCATION-POLICY-WEBHOOK) + IOTHUB_PROXY_SERVER_ADDRESS: 127.0.0.1:3128 + MSFT_TENANT_ID: $(MSFT-TENANT-ID) + LA_AAD_APP_ID: $(LA-AAD-APP-ID) + LA_AAD_APP_CERT_BASE64: $(LA-AAD-APP-CERT-BASE64) + LA_WORKSPACE_ID: $(LA-WORKSPACE-ID) + IOTHUB_X509_CHAIN_DEVICE_NAME: $(IOTHUB-X509-CHAIN-DEVICE-NAME) + HUB_CHAIN_DEVICE_PFX_CERTIFICATE: $(HUB-CHAIN-DEVICE-PFX-CERTIFICATE) + HUB_CHAIN_ROOT_CA_CERTIFICATE: $(HUB-CHAIN-ROOT-CA-CERTIFICATE) + HUB_CHAIN_INTERMEDIATE1_CERTIFICATE: $(HUB-CHAIN-INTERMEDIATE1-CERTIFICATE) + HUB_CHAIN_INTERMEDIATE2_CERTIFICATE: $(HUB-CHAIN-INTERMEDIATE2-CERTIFICATE) + E2E_IKEY: $(E2E-IKEY) + TARGET_BRANCH: $(System.PullRequest.TargetBranch) + FRAMEWORK: $(FRAMEWORK) + IOTHUB_CLIENT_ID: $(IOTHUB-CLIENT-ID) + IOTHUB_CLIENT_SECRET: $(IOTHUB-CLIENT-SECRET) + IOTHUB_USER_ASSIGNED_MSI_RESOURCE_ID: $(IOTHUB-USER-ASSIGNED-MSI-RESOURCE-ID) + + - task: CopyFiles@2 + displayName: "Copy TRX files to the artifacts folder" + inputs: + SourceFolder: "$(Build.SourcesDirectory)" + Contents: "**/*.trx" + TargetFolder: "$(Build.ArtifactStagingDirectory)" + + condition: always() + + - task: CopyFiles@2 + displayName: "Copy ETL files to the artifacts folder" + inputs: + SourceFolder: "$(Build.SourcesDirectory)" + Contents: "**/*.etl" + TargetFolder: "$(Build.ArtifactStagingDirectory)" + + condition: always() + + - task: PublishBuildArtifacts@1 + displayName: "Publish Artifact: testresults" + inputs: + ArtifactName: testresults_windows_$(FRAMEWORK) + + condition: always() + + - task: PublishTestResults@2 + displayName: "Publish Test Results **/*.trx" + inputs: + testRunner: VSTest + testResultsFiles: "**/*.trx" + testRunTitle: "Windows Tests ($(FRAMEWORK)) (Attempt $(System.JobAttempt))" + platform: Windows + configuration: "Debug UT + Release E2E ($(FRAMEWORK))" + + condition: always() + + ### Windows build 2 ### + - job: WINDOWS2 + displayName: Windows pri 2 .NET targets + timeoutInMinutes: 75 + strategy: + # Change maxParallel to 1 make builds run in serial rather than in parallel + maxParallel: 100 + matrix: .Net Core 2.1.18: FRAMEWORK: netcoreapp2.1.18 .Net Framework 4.7.2: FRAMEWORK: net472 - .Net Framework 4.5.1: - FRAMEWORK: net451 - condition: succeeded() + condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest')) pool: # If this is changed, don't forget to update supported_platforms.md in the root directory. That document outlines what OS we test on and should stay up to date. vmImage: windows-2019 @@ -333,7 +538,7 @@ jobs: instanceUrlForTsaV2: 'MSAZURE' projectNameMSAZURE: 'One' areaPath: 'One\IoT\Platform and Devices\IoT Devices\SDKs\Managed' - iterationPath: 'One\IoT\Backlog' + iterationPath: 'One\Custom\IoT\Backlog' - task: securedevelopmentteam.vss-secure-development-tools.build-task-postanalysis.PostAnalysis@1 displayName: "Post Analysis"