diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 32f03d8e6a..981ce363a2 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -31,6 +31,8 @@ Please follow the instructions and template below to save us time requesting add Below is a generic bug report format. We recommend you use it as a template and replace the information below each header with your own. +Note that bugs that only affect unsupported platforms will likely be treated as feature requests, and may be closed as "won't fix" if we have no plans to support that platform. See [this document](../../supported_platforms.md) for details on which platforms are officially supported. + ------------------------------- delete above ------------------------------- diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index ba999c8c9c..cfc496eb27 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -26,6 +26,8 @@ Thank you for raising a feature request! To get started, here's a few things to As an open source project, we welcome PRs for new features, and we don't want to reject any hard work you've done to contribute. **So propose your solution through an issue first** so we can discuss the changes, and if things look good we will ask you submit a PR to implement the changes. +Note that feature requests that only affect unsupported platforms may be closed as "won't fix" if we have no plans to support that platform. See [this document](../../supported_platforms.md) for details on which platforms are officially supported. + ------------------------------- delete above ------------------------------- **Is your feature request related to a problem? Please describe.** diff --git a/azureiot.sln b/azureiot.sln index 5b4d4d37ae..dd21735d2b 100644 --- a/azureiot.sln +++ b/azureiot.sln @@ -77,14 +77,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Azure.Devices.Shared.Tests", "shared\tests\Microsoft.Azure.Devices.Shared.Tests.csproj", "{CEEE435F-32FC-4DE5-8735-90F6AC950A01}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{2368415A-9C09-4F47-9636-FDCA4B85C88C}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "convention-based-samples", "convention-based-samples", "{22318FE4-1453-41BF-A38D-9401C4F16023}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Thermostat", "iothub\device\samples\convention-based-samples\Thermostat\Thermostat.csproj", "{5658A5DF-EDEF-4561-9F0B-A37EEABC8135}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TemperatureController", "iothub\device\samples\convention-based-samples\TemperatureController\TemperatureController.csproj", "{B557FCFE-015C-4A65-81B6-B4987E07BFB7}" -EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -158,14 +150,6 @@ Global {CEEE435F-32FC-4DE5-8735-90F6AC950A01}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {CEEE435F-32FC-4DE5-8735-90F6AC950A01}.Debug|Any CPU.Build.0 = Debug|Any CPU {CEEE435F-32FC-4DE5-8735-90F6AC950A01}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5658A5DF-EDEF-4561-9F0B-A37EEABC8135}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5658A5DF-EDEF-4561-9F0B-A37EEABC8135}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5658A5DF-EDEF-4561-9F0B-A37EEABC8135}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5658A5DF-EDEF-4561-9F0B-A37EEABC8135}.Release|Any CPU.Build.0 = Release|Any CPU - {B557FCFE-015C-4A65-81B6-B4987E07BFB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B557FCFE-015C-4A65-81B6-B4987E07BFB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B557FCFE-015C-4A65-81B6-B4987E07BFB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B557FCFE-015C-4A65-81B6-B4987E07BFB7}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -200,10 +184,6 @@ Global {275DEE86-1EEA-47C4-A9C5-797DF20EC8A7} = {3AA089A9-A035-439E-BAF6-C3975A334379} {8E25CDE3-992D-4942-8C38-51A0D8E8EB70} = {9C260BF0-1CCA-45A2-AAB8-6419291B8B88} {CEEE435F-32FC-4DE5-8735-90F6AC950A01} = {3AA089A9-A035-439E-BAF6-C3975A334379} - {2368415A-9C09-4F47-9636-FDCA4B85C88C} = {A48437BA-3C5B-431E-9B2F-96C850E9E1A5} - {22318FE4-1453-41BF-A38D-9401C4F16023} = {2368415A-9C09-4F47-9636-FDCA4B85C88C} - {5658A5DF-EDEF-4561-9F0B-A37EEABC8135} = {22318FE4-1453-41BF-A38D-9401C4F16023} - {B557FCFE-015C-4A65-81B6-B4987E07BFB7} = {22318FE4-1453-41BF-A38D-9401C4F16023} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AF61665D-340A-494B-9705-571456BDC752} diff --git a/e2e/test/Helpers/StorageContainer.cs b/e2e/test/Helpers/StorageContainer.cs index e92fdfebc0..d763128d2f 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(Configuration.Storage.ConnectionString); + CloudStorageAccount 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 78d7a6ee5c..659e5fd770 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(Configuration.IoTHub.ConnectionString); + using RegistryManager rm = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); s_logger.Trace($"{nameof(GetTestDeviceAsync)}: Creating device {deviceName} with type {type}."); Client.IAuthenticationMethod auth = null; @@ -89,12 +89,12 @@ private static async Task CreateDeviceAsync(TestDeviceType type, str { X509Thumbprint = new X509Thumbprint { - PrimaryThumbprint = Configuration.IoTHub.GetCertificateWithPrivateKey().Thumbprint + PrimaryThumbprint = TestConfiguration.IoTHub.GetCertificateWithPrivateKey().Thumbprint } }; #pragma warning disable CA2000 // Dispose objects before losing scope - X509Certificate and DeviceAuthenticationWithX509Certificate are disposed when TestDevice is disposed. - authCertificate = Configuration.IoTHub.GetCertificateWithPrivateKey(); + authCertificate = TestConfiguration.IoTHub.GetCertificateWithPrivateKey(); auth = new DeviceAuthenticationWithX509Certificate(deviceName, authCertificate); #pragma warning restore CA2000 // Dispose objects before losing scope - X509Certificate and DeviceAuthenticationWithX509Certificate are disposed when TestDevice is disposed. } @@ -129,7 +129,7 @@ public string ConnectionString { get { - string iotHubHostName = GetHostName(Configuration.IoTHub.ConnectionString); + string iotHubHostName = GetHostName(TestConfiguration.IoTHub.ConnectionString); return $"HostName={iotHubHostName};DeviceId={Device.Id};SharedAccessKey={Device.Authentication.SymmetricKey.PrimaryKey}"; } } @@ -137,7 +137,7 @@ public string ConnectionString /// /// Used in conjunction with DeviceClient.Create() /// - public string IoTHubHostName => GetHostName(Configuration.IoTHub.ConnectionString); + public string IoTHubHostName => GetHostName(TestConfiguration.IoTHub.ConnectionString); /// /// Device Id @@ -182,7 +182,7 @@ public DeviceClient CreateDeviceClient(ITransportSettings[] transportSettings, C } else { - deviceClient = DeviceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString, Device.Id, transportSettings, options); + deviceClient = DeviceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString, Device.Id, transportSettings, options); s_logger.Trace($"{nameof(CreateDeviceClient)}: Created {nameof(DeviceClient)} {Device.Id} from IoTHub connection string: ID={TestLogger.IdOf(deviceClient)}"); } } @@ -197,7 +197,7 @@ public DeviceClient CreateDeviceClient(ITransportSettings[] transportSettings, C public async Task RemoveDeviceAsync() { - using var rm = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var rm = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); await rm.RemoveDeviceAsync(Id).ConfigureAwait(false); } diff --git a/e2e/test/Helpers/TestModule.cs b/e2e/test/Helpers/TestModule.cs index d5747dbcc1..4c1e105a22 100644 --- a/e2e/test/Helpers/TestModule.cs +++ b/e2e/test/Helpers/TestModule.cs @@ -28,7 +28,7 @@ public static async Task GetTestModuleAsync(string deviceNamePrefix, string deviceName = testDevice.Id; string moduleName = "E2E_" + moduleNamePrefix + Guid.NewGuid(); - using var rm = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var rm = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); logger.Trace($"{nameof(GetTestModuleAsync)}: Creating module for device {deviceName}."); var requestModule = new Module(deviceName, moduleName); @@ -49,7 +49,7 @@ public string ConnectionString { get { - string iotHubHostName = GetHostName(Configuration.IoTHub.ConnectionString); + string iotHubHostName = GetHostName(TestConfiguration.IoTHub.ConnectionString); return $"HostName={iotHubHostName};DeviceId={_module.DeviceId};ModuleId={_module.Id};SharedAccessKey={_module.Authentication.SymmetricKey.PrimaryKey}"; } } diff --git a/e2e/test/config/Configuration.AzureSecurityCenterForIoTLogAnalytics.cs b/e2e/test/config/TestConfiguration.AzureSecurityCenterForIoTLogAnalytics.cs similarity index 94% rename from e2e/test/config/Configuration.AzureSecurityCenterForIoTLogAnalytics.cs rename to e2e/test/config/TestConfiguration.AzureSecurityCenterForIoTLogAnalytics.cs index 43b6bb67ae..6f4b93b100 100644 --- a/e2e/test/config/Configuration.AzureSecurityCenterForIoTLogAnalytics.cs +++ b/e2e/test/config/TestConfiguration.AzureSecurityCenterForIoTLogAnalytics.cs @@ -3,7 +3,7 @@ namespace Microsoft.Azure.Devices.E2ETests { - public static partial class Configuration + public static partial class TestConfiguration { public static class AzureSecurityCenterForIoTLogAnalytics { diff --git a/e2e/test/config/Configuration.IoTHub.cs b/e2e/test/config/TestConfiguration.IoTHub.cs similarity index 99% rename from e2e/test/config/Configuration.IoTHub.cs rename to e2e/test/config/TestConfiguration.IoTHub.cs index 0d5e4ffbec..2e2a9264ec 100644 --- a/e2e/test/config/Configuration.IoTHub.cs +++ b/e2e/test/config/TestConfiguration.IoTHub.cs @@ -19,7 +19,7 @@ namespace Microsoft.Azure.Devices.E2ETests { - public static partial class Configuration + public static partial class TestConfiguration { public static partial class IoTHub { diff --git a/e2e/test/config/Configuration.Provisioning.cs b/e2e/test/config/TestConfiguration.Provisioning.cs similarity index 97% rename from e2e/test/config/Configuration.Provisioning.cs rename to e2e/test/config/TestConfiguration.Provisioning.cs index fc0f036d46..a65f27e557 100644 --- a/e2e/test/config/Configuration.Provisioning.cs +++ b/e2e/test/config/TestConfiguration.Provisioning.cs @@ -6,7 +6,7 @@ namespace Microsoft.Azure.Devices.E2ETests { - public static partial class Configuration + public static partial class TestConfiguration { public static partial class Provisioning { diff --git a/e2e/test/config/Configuration.Storage.cs b/e2e/test/config/TestConfiguration.Storage.cs similarity index 85% rename from e2e/test/config/Configuration.Storage.cs rename to e2e/test/config/TestConfiguration.Storage.cs index b633840691..7d9b4a9f17 100644 --- a/e2e/test/config/Configuration.Storage.cs +++ b/e2e/test/config/TestConfiguration.Storage.cs @@ -4,7 +4,7 @@ namespace Microsoft.Azure.Devices.E2ETests { - public static partial class Configuration + public static partial class TestConfiguration { public static class Storage { @@ -17,7 +17,7 @@ public static class Storage static Storage() { - ConnectionString = Configuration.GetValue("STORAGE_ACCOUNT_CONNECTION_STRING"); + ConnectionString = TestConfiguration.GetValue("STORAGE_ACCOUNT_CONNECTION_STRING"); Name = s_saName.Match(ConnectionString).Value; Key = s_saKey.Match(ConnectionString).Value; } diff --git a/e2e/test/config/Configuration.cs b/e2e/test/config/TestConfiguration.cs similarity index 98% rename from e2e/test/config/Configuration.cs rename to e2e/test/config/TestConfiguration.cs index cf8ed2db34..3f7cb89e21 100644 --- a/e2e/test/config/Configuration.cs +++ b/e2e/test/config/TestConfiguration.cs @@ -6,7 +6,7 @@ namespace Microsoft.Azure.Devices.E2ETests { - public static partial class Configuration + public static partial class TestConfiguration { private static string GetValue(string envName, string defaultValue = null) { diff --git a/e2e/test/iothub/CombinedClientOperationsPoolAmqpTests.cs b/e2e/test/iothub/CombinedClientOperationsPoolAmqpTests.cs index 8fea36bb4b..2e20e6038f 100644 --- a/e2e/test/iothub/CombinedClientOperationsPoolAmqpTests.cs +++ b/e2e/test/iothub/CombinedClientOperationsPoolAmqpTests.cs @@ -122,7 +122,7 @@ private async Task DeviceCombinedClientOperationsAsync( ConnectionStringAuthScope authScope = ConnectionStringAuthScope.Device) { // Initialize service client for service-side operations - using var serviceClient = ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); // Message payload and properties for C2D operation var messagesSent = new Dictionary>(); diff --git a/e2e/test/iothub/ConnectionStatusChangeHandlerTests.cs b/e2e/test/iothub/ConnectionStatusChangeHandlerTests.cs index f5cb3795ee..92e1cca9ae 100644 --- a/e2e/test/iothub/ConnectionStatusChangeHandlerTests.cs +++ b/e2e/test/iothub/ConnectionStatusChangeHandlerTests.cs @@ -109,7 +109,7 @@ private async Task DeviceClient_Gives_ConnectionStatus_DeviceDisabled_Base( using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix + $"_{Guid.NewGuid()}").ConfigureAwait(false); string deviceConnectionString = testDevice.ConnectionString; - var config = new Configuration.IoTHub.ConnectionStringParser(deviceConnectionString); + var config = new TestConfiguration.IoTHub.ConnectionStringParser(deviceConnectionString); string deviceId = config.DeviceID; ConnectionStatus? status = null; @@ -139,7 +139,7 @@ private async Task DeviceClient_Gives_ConnectionStatus_DeviceDisabled_Base( Assert.IsNotNull(twin); // Delete/disable the device in IoT Hub. This should trigger the ConnectionStatusChangesHandler. - using (RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString)) + using (RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString)) { await registryManagerOperation(registryManager, deviceId).ConfigureAwait(false); } @@ -198,7 +198,7 @@ private async Task ModuleClient_Gives_ConnectionStatus_DeviceDisabled_Base( Assert.IsNotNull(twin); // Delete/disable the device in IoT Hub. - using (RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString)) + using (RegistryManager 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 007ff9ede0..9f367eded3 100644 --- a/e2e/test/iothub/DeviceClientX509AuthenticationE2ETests.cs +++ b/e2e/test/iothub/DeviceClientX509AuthenticationE2ETests.cs @@ -24,13 +24,13 @@ namespace Microsoft.Azure.Devices.E2ETests public class DeviceClientX509AuthenticationE2ETests : E2EMsTestBase { private static readonly string s_devicePrefix = $"{nameof(DeviceClientX509AuthenticationE2ETests)}_"; - private static X509Certificate2 s_selfSignedCertificateWithPrivateKey = Configuration.IoTHub.GetCertificateWithPrivateKey(); - private static X509Certificate2 s_chainCertificateWithPrivateKey = Configuration.IoTHub.GetChainDeviceCertificateWithPrivateKey(); + private static X509Certificate2 s_selfSignedCertificateWithPrivateKey = TestConfiguration.IoTHub.GetCertificateWithPrivateKey(); + private static X509Certificate2 s_chainCertificateWithPrivateKey = TestConfiguration.IoTHub.GetChainDeviceCertificateWithPrivateKey(); private readonly string _hostName; public DeviceClientX509AuthenticationE2ETests() { - _hostName = GetHostName(Configuration.IoTHub.ConnectionString); + _hostName = GetHostName(TestConfiguration.IoTHub.ConnectionString); } [LoggedTestMethod] @@ -151,12 +151,12 @@ public async Task X509_Cert_Chain_Install_Test_MQTT_TCP() // arrange var chainCerts = new X509Certificate2Collection { - Configuration.IoTHub.GetRootCACertificate(), - Configuration.IoTHub.GetIntermediate1Certificate(), - Configuration.IoTHub.GetIntermediate2Certificate() + TestConfiguration.IoTHub.GetRootCACertificate(), + TestConfiguration.IoTHub.GetIntermediate1Certificate(), + TestConfiguration.IoTHub.GetIntermediate2Certificate() }; using var auth = new DeviceAuthenticationWithX509Certificate( - Configuration.IoTHub.X509ChainDeviceName, + TestConfiguration.IoTHub.X509ChainDeviceName, s_chainCertificateWithPrivateKey, chainCerts); using DeviceClient deviceClient = DeviceClient.Create( @@ -178,12 +178,12 @@ public async Task X509_Cert_Chain_Install_Test_AMQP_TCP() // arrange var chainCerts = new X509Certificate2Collection { - Configuration.IoTHub.GetRootCACertificate(), - Configuration.IoTHub.GetIntermediate1Certificate(), - Configuration.IoTHub.GetIntermediate2Certificate() + TestConfiguration.IoTHub.GetRootCACertificate(), + TestConfiguration.IoTHub.GetIntermediate1Certificate(), + TestConfiguration.IoTHub.GetIntermediate2Certificate() }; using var auth = new DeviceAuthenticationWithX509Certificate( - Configuration.IoTHub.X509ChainDeviceName, + TestConfiguration.IoTHub.X509ChainDeviceName, s_chainCertificateWithPrivateKey, chainCerts); using DeviceClient deviceClient = DeviceClient.Create( diff --git a/e2e/test/iothub/DeviceTokenRefreshE2ETests.cs b/e2e/test/iothub/DeviceTokenRefreshE2ETests.cs index f56846dcb7..96f308eacc 100644 --- a/e2e/test/iothub/DeviceTokenRefreshE2ETests.cs +++ b/e2e/test/iothub/DeviceTokenRefreshE2ETests.cs @@ -30,7 +30,7 @@ public async Task DeviceClient_Not_Exist_AMQP() { using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix).ConfigureAwait(false); - var config = new Configuration.IoTHub.ConnectionStringParser(testDevice.ConnectionString); + 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)) { await deviceClient.OpenAsync().ConfigureAwait(false); @@ -43,7 +43,7 @@ public async Task DeviceClient_Bad_Credentials_AMQP() { using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix).ConfigureAwait(false); - var config = new Configuration.IoTHub.ConnectionStringParser(testDevice.ConnectionString); + 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)) { @@ -82,7 +82,7 @@ public async Task DeviceClient_TokenConnectionDoubleRelease_Ok() string deviceConnectionString = testDevice.ConnectionString; - var config = new Configuration.IoTHub.ConnectionStringParser(deviceConnectionString); + var config = new TestConfiguration.IoTHub.ConnectionStringParser(deviceConnectionString); string iotHub = config.IotHubHostName; string deviceId = config.DeviceID; string key = config.SharedAccessKey; diff --git a/e2e/test/iothub/FaultInjectionPoolAmqpTests.cs b/e2e/test/iothub/FaultInjectionPoolAmqpTests.cs index e02914dda0..2f0d65dff5 100644 --- a/e2e/test/iothub/FaultInjectionPoolAmqpTests.cs +++ b/e2e/test/iothub/FaultInjectionPoolAmqpTests.cs @@ -13,6 +13,6 @@ namespace Microsoft.Azure.Devices.E2ETests [TestCategory("LongRunning")] public partial class FaultInjectionPoolAmqpTests : E2EMsTestBase { - private static readonly string s_proxyServerAddress = Configuration.IoTHub.ProxyServerAddress; + private static readonly string s_proxyServerAddress = TestConfiguration.IoTHub.ProxyServerAddress; } } diff --git a/e2e/test/iothub/FileUploadE2ETests.cs b/e2e/test/iothub/FileUploadE2ETests.cs index c19ba70962..d2ed780c50 100644 --- a/e2e/test/iothub/FileUploadE2ETests.cs +++ b/e2e/test/iothub/FileUploadE2ETests.cs @@ -23,7 +23,7 @@ public class FileUploadE2ETests : E2EMsTestBase private const int FileSizeSmall = 10 * 1024; private const int FileSizeBig = 5120 * 1024; private readonly string _devicePrefix = $"{nameof(FileUploadE2ETests)}_"; - private static readonly X509Certificate2 s_selfSignedCertificate = Configuration.IoTHub.GetCertificateWithPrivateKey(); + private static readonly X509Certificate2 s_selfSignedCertificate = TestConfiguration.IoTHub.GetCertificateWithPrivateKey(); [LoggedTestMethod] [TestCategory("LongRunning")] @@ -104,7 +104,7 @@ public async Task FileUpload_SmallFile_Http_GranularSteps_Proxy() using var fileStreamSource = new FileStream(filename, FileMode.Open, FileAccess.Read); var fileUploadTransportSettings = new Http1TransportSettings() { - Proxy = new WebProxy(Configuration.IoTHub.ProxyServerAddress) + Proxy = new WebProxy(TestConfiguration.IoTHub.ProxyServerAddress) }; await UploadFileGranularAsync(fileStreamSource, filename, fileUploadTransportSettings).ConfigureAwait(false); diff --git a/e2e/test/iothub/SasCredentialAuthenticationTests.cs b/e2e/test/iothub/SasCredentialAuthenticationTests.cs index 09881b57bb..827f05ec2b 100644 --- a/e2e/test/iothub/SasCredentialAuthenticationTests.cs +++ b/e2e/test/iothub/SasCredentialAuthenticationTests.cs @@ -39,9 +39,9 @@ public class SasCredentialAuthenticationTests : E2EMsTestBase public async Task RegistryManager_Http_SasCredentialAuth_Success() { // arrange - string signature = Configuration.IoTHub.GetIotHubSharedAccessSignature(TimeSpan.FromHours(1)); + string signature = TestConfiguration.IoTHub.GetIotHubSharedAccessSignature(TimeSpan.FromHours(1)); using var registryManager = RegistryManager.Create( - Configuration.IoTHub.GetIotHubHostName(), + TestConfiguration.IoTHub.GetIotHubHostName(), new AzureSasCredential(signature)); var device = new Device(Guid.NewGuid().ToString()); @@ -60,10 +60,10 @@ public async Task RegistryManager_Http_SasCredentialAuth_Success() public async Task RegistryManager_Http_SasCredentialAuth_Renewed_Success() { // arrange - string signature = Configuration.IoTHub.GetIotHubSharedAccessSignature(TimeSpan.FromHours(-1)); + string signature = TestConfiguration.IoTHub.GetIotHubSharedAccessSignature(TimeSpan.FromHours(-1)); var sasCredential = new AzureSasCredential(signature); using var registryManager = RegistryManager.Create( - Configuration.IoTHub.GetIotHubHostName(), + TestConfiguration.IoTHub.GetIotHubHostName(), sasCredential); var device = new Device(Guid.NewGuid().ToString()); @@ -78,7 +78,7 @@ public async Task RegistryManager_Http_SasCredentialAuth_Renewed_Success() { // Expected to be unauthorized exception. } - signature = Configuration.IoTHub.GetIotHubSharedAccessSignature(TimeSpan.FromHours(1)); + signature = TestConfiguration.IoTHub.GetIotHubSharedAccessSignature(TimeSpan.FromHours(1)); sasCredential.Update(signature); Device createdDevice = await registryManager.AddDeviceAsync(device).ConfigureAwait(false); @@ -93,9 +93,9 @@ public async Task RegistryManager_Http_SasCredentialAuth_Renewed_Success() public async Task JobClient_Http_SasCredentialAuth_Success() { // arrange - string signature = Configuration.IoTHub.GetIotHubSharedAccessSignature(TimeSpan.FromHours(1)); + string signature = TestConfiguration.IoTHub.GetIotHubSharedAccessSignature(TimeSpan.FromHours(1)); using var jobClient = JobClient.Create( - Configuration.IoTHub.GetIotHubHostName(), + TestConfiguration.IoTHub.GetIotHubHostName(), new AzureSasCredential(signature)); string jobId = "JOBSAMPLE" + Guid.NewGuid().ToString(); @@ -138,9 +138,9 @@ public async Task DigitalTwinClient_Http_SasCredentialAuth_Success() // Call openAsync() to open the device's connection, so that the ModelId is sent over Mqtt CONNECT packet. await deviceClient.OpenAsync().ConfigureAwait(false); - string signature = Configuration.IoTHub.GetIotHubSharedAccessSignature(TimeSpan.FromHours(1)); + string signature = TestConfiguration.IoTHub.GetIotHubSharedAccessSignature(TimeSpan.FromHours(1)); using var digitalTwinClient = DigitalTwinClient.Create( - Configuration.IoTHub.GetIotHubHostName(), + TestConfiguration.IoTHub.GetIotHubHostName(), new AzureSasCredential(signature)); // act @@ -164,9 +164,9 @@ public async Task Service_Amqp_SasCredentialAuth_Success() using DeviceClient deviceClient = testDevice.CreateDeviceClient(Client.TransportType.Mqtt); await deviceClient.OpenAsync().ConfigureAwait(false); - string signature = Configuration.IoTHub.GetIotHubSharedAccessSignature(TimeSpan.FromHours(1)); + string signature = TestConfiguration.IoTHub.GetIotHubSharedAccessSignature(TimeSpan.FromHours(1)); using var serviceClient = ServiceClient.Create( - Configuration.IoTHub.GetIotHubHostName(), + TestConfiguration.IoTHub.GetIotHubHostName(), new AzureSasCredential(signature), TransportType.Amqp); @@ -188,10 +188,10 @@ public async Task Service_Amqp_SasCredentialAuth_Renewed_Success() using DeviceClient deviceClient = testDevice.CreateDeviceClient(Client.TransportType.Mqtt); await deviceClient.OpenAsync().ConfigureAwait(false); - string signature = Configuration.IoTHub.GetIotHubSharedAccessSignature(TimeSpan.FromHours(-1)); + string signature = TestConfiguration.IoTHub.GetIotHubSharedAccessSignature(TimeSpan.FromHours(-1)); var sasCredential = new AzureSasCredential(signature); using var serviceClient = ServiceClient.Create( - Configuration.IoTHub.GetIotHubHostName(), + TestConfiguration.IoTHub.GetIotHubHostName(), sasCredential, TransportType.Amqp); @@ -206,7 +206,7 @@ public async Task Service_Amqp_SasCredentialAuth_Renewed_Success() // Expected to get an unauthorized exception. } - signature = Configuration.IoTHub.GetIotHubSharedAccessSignature(TimeSpan.FromHours(1)); + signature = TestConfiguration.IoTHub.GetIotHubSharedAccessSignature(TimeSpan.FromHours(1)); sasCredential.Update(signature); await serviceClient.OpenAsync().ConfigureAwait(false); using var message = new Message(Encoding.ASCII.GetBytes("Hello, Cloud!")); diff --git a/e2e/test/iothub/TokenCredentialAuthenticationTests.cs b/e2e/test/iothub/TokenCredentialAuthenticationTests.cs index 22d4448106..cbbcc0633c 100644 --- a/e2e/test/iothub/TokenCredentialAuthenticationTests.cs +++ b/e2e/test/iothub/TokenCredentialAuthenticationTests.cs @@ -40,8 +40,8 @@ public async Task RegistryManager_Http_TokenCredentialAuth_Success() { // arrange using var registryManager = RegistryManager.Create( - Configuration.IoTHub.GetIotHubHostName(), - Configuration.IoTHub.GetClientSecretCredential()); + TestConfiguration.IoTHub.GetIotHubHostName(), + TestConfiguration.IoTHub.GetClientSecretCredential()); var device = new Device(Guid.NewGuid().ToString()); @@ -60,8 +60,8 @@ public async Task JobClient_Http_TokenCredentialAuth_Success() { // arrange using var jobClient = JobClient.Create( - Configuration.IoTHub.GetIotHubHostName(), - Configuration.IoTHub.GetClientSecretCredential()); + TestConfiguration.IoTHub.GetIotHubHostName(), + TestConfiguration.IoTHub.GetClientSecretCredential()); string jobId = "JOBSAMPLE" + Guid.NewGuid().ToString(); string jobDeviceId = "JobsSample_Device"; @@ -104,8 +104,8 @@ public async Task DigitalTwinClient_Http_TokenCredentialAuth_Success() await deviceClient.OpenAsync().ConfigureAwait(false); using var digitalTwinClient = DigitalTwinClient.Create( - Configuration.IoTHub.GetIotHubHostName(), - Configuration.IoTHub.GetClientSecretCredential()); + TestConfiguration.IoTHub.GetIotHubHostName(), + TestConfiguration.IoTHub.GetClientSecretCredential()); // act HttpOperationResponse response = await digitalTwinClient @@ -129,8 +129,8 @@ public async Task Service_Amqp_TokenCredentialAuth_Success() await deviceClient.OpenAsync().ConfigureAwait(false); using var serviceClient = ServiceClient.Create( - Configuration.IoTHub.GetIotHubHostName(), - Configuration.IoTHub.GetClientSecretCredential(), + TestConfiguration.IoTHub.GetIotHubHostName(), + TestConfiguration.IoTHub.GetClientSecretCredential(), TransportType.Amqp); // act diff --git a/e2e/test/iothub/command/CommandE2ETests.cs b/e2e/test/iothub/command/CommandE2ETests.cs index 02bea0171d..e1f00fcc26 100644 --- a/e2e/test/iothub/command/CommandE2ETests.cs +++ b/e2e/test/iothub/command/CommandE2ETests.cs @@ -70,7 +70,7 @@ public static async Task DigitalTwinsSendCommandAndVerifyResponseAsync(string de int statusCode = 0; #if NET451 - ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); logger.Trace($"{nameof(DigitalTwinsSendCommandAndVerifyResponseAsync)}: Invoke command {commandName}."); @@ -95,7 +95,7 @@ await serviceClient.InvokeDeviceMethodAsync( serviceClient.Dispose(); #else - DigitalTwinClient digitalTwinClient = DigitalTwinClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + DigitalTwinClient digitalTwinClient = DigitalTwinClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); logger.Trace($"{nameof(DigitalTwinsSendCommandAndVerifyResponseAsync)}: Invoke command {commandName}."); diff --git a/e2e/test/iothub/messaging/AzureSecurityCenterForIoTLogAnalyticsClient.cs b/e2e/test/iothub/messaging/AzureSecurityCenterForIoTLogAnalyticsClient.cs index b3e50baac8..c2e1581d4c 100644 --- a/e2e/test/iothub/messaging/AzureSecurityCenterForIoTLogAnalyticsClient.cs +++ b/e2e/test/iothub/messaging/AzureSecurityCenterForIoTLogAnalyticsClient.cs @@ -38,10 +38,10 @@ public class AzureSecurityCenterForIoTLogAnalyticsClient : IDisposable | where DeviceId == ""{0}"" | where IoTRawEventId == ""{1}"""; - private readonly string _workspaceId = Configuration.AzureSecurityCenterForIoTLogAnalytics.WorkspacedId; - private readonly string _aadTenant = Configuration.AzureSecurityCenterForIoTLogAnalytics.AadTenant; - private readonly string _appId = Configuration.AzureSecurityCenterForIoTLogAnalytics.AadAppId; - private readonly string _appCertificate = Configuration.AzureSecurityCenterForIoTLogAnalytics.AadAppCertificate; + private readonly string _workspaceId = TestConfiguration.AzureSecurityCenterForIoTLogAnalytics.WorkspacedId; + private readonly string _aadTenant = TestConfiguration.AzureSecurityCenterForIoTLogAnalytics.AadTenant; + private readonly string _appId = TestConfiguration.AzureSecurityCenterForIoTLogAnalytics.AadAppId; + private readonly string _appCertificate = TestConfiguration.AzureSecurityCenterForIoTLogAnalytics.AadAppCertificate; private readonly TimeSpan _polingInterval = TimeSpan.FromSeconds(20); private readonly TimeSpan _timeout = TimeSpan.FromMinutes(5); diff --git a/e2e/test/iothub/messaging/FaultInjectionPoolAmqpTests.MessageReceiveFaultInjectionPoolAmqpTests.cs b/e2e/test/iothub/messaging/FaultInjectionPoolAmqpTests.MessageReceiveFaultInjectionPoolAmqpTests.cs index ae029a5dab..e23297a804 100644 --- a/e2e/test/iothub/messaging/FaultInjectionPoolAmqpTests.MessageReceiveFaultInjectionPoolAmqpTests.cs +++ b/e2e/test/iothub/messaging/FaultInjectionPoolAmqpTests.MessageReceiveFaultInjectionPoolAmqpTests.cs @@ -895,7 +895,7 @@ private async Task ReceiveMessageRecoveryPoolOverAmqpAsync( string proxyAddress = null) { // Initialize the service client - using var serviceClient = ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); async Task TestOperationAsync(DeviceClient deviceClient, TestDevice testDevice, TestDeviceCallbackHandler _) { @@ -953,7 +953,7 @@ private async Task ReceiveMessageUsingCallbackRecoveryPoolOverAmqpAsync( string proxyAddress = null) { // Initialize the service client - ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + ServiceClient 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 1ef202f21d..8f0ab42de0 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(Configuration.IoTHub.ConnectionString)) + using (ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString)) { await deviceClient.OpenAsync().ConfigureAwait(false); diff --git a/e2e/test/iothub/messaging/MessageReceiveE2EPoolAmqpTests.cs b/e2e/test/iothub/messaging/MessageReceiveE2EPoolAmqpTests.cs index 357c0dbbc8..3cdc8a4ece 100644 --- a/e2e/test/iothub/messaging/MessageReceiveE2EPoolAmqpTests.cs +++ b/e2e/test/iothub/messaging/MessageReceiveE2EPoolAmqpTests.cs @@ -205,7 +205,7 @@ private async Task ReceiveMessagePoolOverAmqpAsync( var messagesSent = new Dictionary>(); // Initialize the service client - var serviceClient = ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); async Task InitOperationAsync(DeviceClient deviceClient, TestDevice testDevice, TestDeviceCallbackHandler _) { @@ -253,7 +253,7 @@ private async Task ReceiveMessageUsingCallbackPoolOverAmqpAsync( ConnectionStringAuthScope authScope = ConnectionStringAuthScope.Device) { // Initialize the service client - var serviceClient = ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); async Task InitOperationAsync(DeviceClient deviceClient, TestDevice testDevice, TestDeviceCallbackHandler testDeviceCallbackHandler) { @@ -301,7 +301,7 @@ private async Task ReceiveMessageUsingCallbackAndUnsubscribePoolOverAmqpAsync( ConnectionStringAuthScope authScope = ConnectionStringAuthScope.Device) { // Initialize the service client - var serviceClient = ServiceClient.CreateFromConnectionString(Configuration.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/MessageReceiveE2ETests.cs b/e2e/test/iothub/messaging/MessageReceiveE2ETests.cs index dd82f2349d..e3e58f40f1 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(Configuration.IoTHub.ConnectionString); + using ServiceClient 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(Configuration.IoTHub.ConnectionString); + using ServiceClient 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(Configuration.IoTHub.ConnectionString); + using ServiceClient 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(Configuration.IoTHub.ConnectionString); + using ServiceClient 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(Configuration.IoTHub.ConnectionString); + using ServiceClient 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(Configuration.IoTHub.ConnectionString); + using ServiceClient 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(Configuration.IoTHub.ConnectionString); + using ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); (Message msg, string payload, string p1Value) = ComposeC2dTestMessage(Logger); diff --git a/e2e/test/iothub/messaging/MessageReceiveFaultInjectionTests.cs b/e2e/test/iothub/messaging/MessageReceiveFaultInjectionTests.cs index 9b3a72bac1..cf30a99335 100644 --- a/e2e/test/iothub/messaging/MessageReceiveFaultInjectionTests.cs +++ b/e2e/test/iothub/messaging/MessageReceiveFaultInjectionTests.cs @@ -363,7 +363,7 @@ private async Task ReceiveMessageRecovery( TimeSpan delayInSec, string proxyAddress = null) { - using var serviceClient = ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); async Task InitOperationAsync(DeviceClient deviceClient, TestDevice testDevice) { await serviceClient.OpenAsync().ConfigureAwait(false); @@ -418,7 +418,7 @@ private async Task ReceiveMessageWithCallbackRecoveryAsync( string proxyAddress = null) { TestDeviceCallbackHandler testDeviceCallbackHandler = null; - using var serviceClient = ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); async Task InitOperationAsync(DeviceClient deviceClient, TestDevice testDevice) { diff --git a/e2e/test/iothub/messaging/MessageSendE2ETests.cs b/e2e/test/iothub/messaging/MessageSendE2ETests.cs index cf9fec5e0a..5872b277e7 100644 --- a/e2e/test/iothub/messaging/MessageSendE2ETests.cs +++ b/e2e/test/iothub/messaging/MessageSendE2ETests.cs @@ -20,10 +20,20 @@ namespace Microsoft.Azure.Devices.E2ETests.Messaging public partial class MessageSendE2ETests : E2EMsTestBase { private const int MessageBatchCount = 5; - private const int LargeMessageSizeInBytes = 255 * 1024; // The maximum message size for device to cloud messages is 256 KB. We are allowing 1 KB of buffer for message header information etc. - private readonly string DevicePrefix = $"{nameof(MessageSendE2ETests)}_"; - private readonly string ModulePrefix = $"{nameof(MessageSendE2ETests)}_"; - private static string ProxyServerAddress = Configuration.IoTHub.ProxyServerAddress; + + // The maximum message size for device to cloud messages is 256 KB. We are allowing 1 KB of buffer for message header information etc. + private const int LargeMessageSizeInBytes = 255 * 1024; + + // The size of a device to cloud message. This exceeds the the maximum message size set by the hub; 256 KB. + private const int ExceedAllowedMessageSizeInBytes = 300 * 1024; + + // The size of a device to cloud message. This overly exceeds the maximum message size set by the hub, which is 256 KB. The reason why we are testing for this case is because + // we noticed a different behavior between this case, and the case where the message size is less than 1 MB. + private const int OverlyExceedAllowedMessageSizeInBytes = 3000 * 1024; + + private readonly string _devicePrefix = $"{nameof(MessageSendE2ETests)}_"; + private readonly string _modulePrefix = $"{nameof(MessageSendE2ETests)}_"; + private static string s_proxyServerAddress = TestConfiguration.IoTHub.ProxyServerAddress; [LoggedTestMethod] public async Task Message_DeviceSendSingleMessage_Amqp() @@ -80,7 +90,7 @@ public async Task Message_DeviceSendSingleMessage_AmqpWs_WithHeartbeats() public async Task Message_DeviceSendSingleMessage_Http_WithProxy() { Client.Http1TransportSettings httpTransportSettings = new Client.Http1TransportSettings(); - httpTransportSettings.Proxy = new WebProxy(ProxyServerAddress); + httpTransportSettings.Proxy = new WebProxy(s_proxyServerAddress); ITransportSettings[] transportSettings = new ITransportSettings[] { httpTransportSettings }; await SendSingleMessage(TestDeviceType.Sasl, transportSettings).ConfigureAwait(false); @@ -105,7 +115,7 @@ public async Task Message_DeviceSendSingleMessage_Http_WithCustomProxy() public async Task Message_DeviceSendSingleMessage_AmqpWs_WithProxy() { Client.AmqpTransportSettings amqpTransportSettings = new Client.AmqpTransportSettings(Client.TransportType.Amqp_WebSocket_Only); - amqpTransportSettings.Proxy = new WebProxy(ProxyServerAddress); + amqpTransportSettings.Proxy = new WebProxy(s_proxyServerAddress); ITransportSettings[] transportSettings = new ITransportSettings[] { amqpTransportSettings }; await SendSingleMessage(TestDeviceType.Sasl, transportSettings).ConfigureAwait(false); @@ -117,7 +127,7 @@ public async Task Message_DeviceSendSingleMessage_MqttWs_WithProxy() { Client.Transport.Mqtt.MqttTransportSettings mqttTransportSettings = new Client.Transport.Mqtt.MqttTransportSettings(Client.TransportType.Mqtt_WebSocket_Only); - mqttTransportSettings.Proxy = new WebProxy(ProxyServerAddress); + mqttTransportSettings.Proxy = new WebProxy(s_proxyServerAddress); ITransportSettings[] transportSettings = new ITransportSettings[] { mqttTransportSettings }; await SendSingleMessage(TestDeviceType.Sasl, transportSettings).ConfigureAwait(false); @@ -128,7 +138,7 @@ public async Task Message_DeviceSendSingleMessage_MqttWs_WithProxy() public async Task Message_ModuleSendSingleMessage_AmqpWs_WithProxy() { Client.AmqpTransportSettings amqpTransportSettings = new Client.AmqpTransportSettings(Client.TransportType.Amqp_WebSocket_Only); - amqpTransportSettings.Proxy = new WebProxy(ProxyServerAddress); + amqpTransportSettings.Proxy = new WebProxy(s_proxyServerAddress); ITransportSettings[] transportSettings = new ITransportSettings[] { amqpTransportSettings }; await SendSingleMessageModule(transportSettings).ConfigureAwait(false); @@ -140,7 +150,7 @@ public async Task Message_ModuleSendSingleMessage_MqttWs_WithProxy() { Client.Transport.Mqtt.MqttTransportSettings mqttTransportSettings = new Client.Transport.Mqtt.MqttTransportSettings(Client.TransportType.Mqtt_WebSocket_Only); - mqttTransportSettings.Proxy = new WebProxy(ProxyServerAddress); + mqttTransportSettings.Proxy = new WebProxy(s_proxyServerAddress); ITransportSettings[] transportSettings = new ITransportSettings[] { mqttTransportSettings }; await SendSingleMessageModule(transportSettings).ConfigureAwait(false); @@ -212,7 +222,7 @@ public async Task X509_DeviceSendBatchMessages_Http() [ExpectedException(typeof(MessageTooLargeException))] public async Task Message_ClientThrowsForMqttTopicNameTooLong() { - using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix).ConfigureAwait(false); + using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); using DeviceClient deviceClient = testDevice.CreateDeviceClient(Client.TransportType.Mqtt); await deviceClient.OpenAsync().ConfigureAwait(false); @@ -245,9 +255,95 @@ public async Task Message_DeviceSendSingleLargeMessageAsync(TestDeviceType testD await SendSingleMessage(testDeviceType, transportType, messageSize).ConfigureAwait(false); } + [LoggedTestMethod] + [ExpectedException(typeof(MessageTooLargeException))] + public async Task Message_DeviceSendMessageOverAllowedSize_Amqp() + { + await SendSingleMessage(TestDeviceType.Sasl, Client.TransportType.Amqp_Tcp_Only, ExceedAllowedMessageSizeInBytes).ConfigureAwait(false); + } + + [LoggedTestMethod] + [ExpectedException(typeof(MessageTooLargeException))] + public async Task Message_DeviceSendMessageOverAllowedSize_AmqpWs() + { + await SendSingleMessage(TestDeviceType.Sasl, Client.TransportType.Amqp_WebSocket_Only, ExceedAllowedMessageSizeInBytes).ConfigureAwait(false); + } + + // MQTT protocol will throw an InvalidOperationException if the PUBLISH packet is greater than + // Hub limits: https://github.com/Azure/azure-iot-sdk-csharp/blob/d46e0f07fe8d80e21e07b41c2e75b0bd1fcb8f80/iothub/device/src/Transport/Mqtt/MqttIotHubAdapter.cs#L1175 + // This flow is a bit different from other protocols where we do not inspect the packet being sent but rather rely on service validating it + // and throwing a MessageTooLargeException, if relevant. + [LoggedTestMethod] + [ExpectedException(typeof(InvalidOperationException))] + public async Task Message_DeviceSendMessageOverAllowedSize_Mqtt() + { + await SendSingleMessage(TestDeviceType.Sasl, Client.TransportType.Mqtt_Tcp_Only, ExceedAllowedMessageSizeInBytes).ConfigureAwait(false); + } + + // MQTT protocol will throw an InvalidOperationException if the PUBLISH packet is greater than + // Hub limits: https://github.com/Azure/azure-iot-sdk-csharp/blob/d46e0f07fe8d80e21e07b41c2e75b0bd1fcb8f80/iothub/device/src/Transport/Mqtt/MqttIotHubAdapter.cs#L1175 + // This flow is a bit different from other protocols where we do not inspect the packet being sent but rather rely on service validating it + // and throwing a MessageTooLargeException, if relevant. + [LoggedTestMethod] + [ExpectedException(typeof(InvalidOperationException))] + public async Task Message_DeviceSendMessageOverAllowedSize_MqttWs() + { + await SendSingleMessage(TestDeviceType.Sasl, Client.TransportType.Mqtt_WebSocket_Only, ExceedAllowedMessageSizeInBytes).ConfigureAwait(false); + } + + [LoggedTestMethod] + [ExpectedException(typeof(MessageTooLargeException))] + public async Task Message_DeviceSendMessageOverAllowedSize_Http() + { + await SendSingleMessage(TestDeviceType.Sasl, Client.TransportType.Http1, ExceedAllowedMessageSizeInBytes).ConfigureAwait(false); + } + + [LoggedTestMethod] + [ExpectedException(typeof(MessageTooLargeException))] + public async Task Message_DeviceSendMessageWayOverAllowedSize_Amqp() + { + await SendSingleMessage(TestDeviceType.Sasl, Client.TransportType.Amqp_Tcp_Only, OverlyExceedAllowedMessageSizeInBytes).ConfigureAwait(false); + } + + [LoggedTestMethod] + [ExpectedException(typeof(MessageTooLargeException))] + public async Task Message_DeviceSendMessageWayOverAllowedSize_AmqpWs() + { + await SendSingleMessage(TestDeviceType.Sasl, Client.TransportType.Amqp_WebSocket_Only, OverlyExceedAllowedMessageSizeInBytes).ConfigureAwait(false); + } + + // MQTT protocol will throw an InvalidOperationException if the PUBLISH packet is greater than + // Hub limits: https://github.com/Azure/azure-iot-sdk-csharp/blob/d46e0f07fe8d80e21e07b41c2e75b0bd1fcb8f80/iothub/device/src/Transport/Mqtt/MqttIotHubAdapter.cs#L1175 + // This flow is a bit different from other protocols where we do not inspect the packet being sent but rather rely on service validating it + // and throwing a MessageTooLargeException, if relevant. + [LoggedTestMethod] + [ExpectedException(typeof(InvalidOperationException))] + public async Task Message_DeviceSendMessageWayOverAllowedSize_Mqtt() + { + await SendSingleMessage(TestDeviceType.Sasl, Client.TransportType.Mqtt_Tcp_Only, OverlyExceedAllowedMessageSizeInBytes).ConfigureAwait(false); + } + + // MQTT protocol will throw an InvalidOperationException if the PUBLISH packet is greater than + // Hub limits: https://github.com/Azure/azure-iot-sdk-csharp/blob/d46e0f07fe8d80e21e07b41c2e75b0bd1fcb8f80/iothub/device/src/Transport/Mqtt/MqttIotHubAdapter.cs#L1175 + // This flow is a bit different from other protocols where we do not inspect the packet being sent but rather rely on service validating it + // and throwing a MessageTooLargeException, if relevant. + [LoggedTestMethod] + [ExpectedException(typeof(InvalidOperationException))] + public async Task Message_DeviceSendMessageWayOverAllowedSize_MqttWs() + { + await SendSingleMessage(TestDeviceType.Sasl, Client.TransportType.Mqtt_WebSocket_Only, OverlyExceedAllowedMessageSizeInBytes).ConfigureAwait(false); + } + + [LoggedTestMethod] + [ExpectedException(typeof(MessageTooLargeException))] + public async Task Message_DeviceSendMessageWayOverAllowedSize_Http() + { + await SendSingleMessage(TestDeviceType.Sasl, Client.TransportType.Http1, OverlyExceedAllowedMessageSizeInBytes).ConfigureAwait(false); + } + private async Task SendSingleMessage(TestDeviceType type, Client.TransportType transport, int messageSize = 0) { - using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix, type).ConfigureAwait(false); + using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix, type).ConfigureAwait(false); using DeviceClient deviceClient = testDevice.CreateDeviceClient(transport); await deviceClient.OpenAsync().ConfigureAwait(false); @@ -257,7 +353,7 @@ private async Task SendSingleMessage(TestDeviceType type, Client.TransportType t private async Task SendBatchMessages(TestDeviceType type, Client.TransportType transport) { - using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix, type).ConfigureAwait(false); + using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix, type).ConfigureAwait(false); using DeviceClient deviceClient = testDevice.CreateDeviceClient(transport); await deviceClient.OpenAsync().ConfigureAwait(false); @@ -267,7 +363,7 @@ private async Task SendBatchMessages(TestDeviceType type, Client.TransportType t private async Task SendSingleMessage(TestDeviceType type, ITransportSettings[] transportSettings, int messageSize = 0) { - using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix, type).ConfigureAwait(false); + using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix, type).ConfigureAwait(false); using DeviceClient deviceClient = testDevice.CreateDeviceClient(transportSettings); await deviceClient.OpenAsync().ConfigureAwait(false); @@ -277,7 +373,7 @@ private async Task SendSingleMessage(TestDeviceType type, ITransportSettings[] t private async Task SendSingleMessageModule(ITransportSettings[] transportSettings) { - TestModule testModule = await TestModule.GetTestModuleAsync(DevicePrefix, ModulePrefix, Logger).ConfigureAwait(false); + TestModule testModule = await TestModule.GetTestModuleAsync(_devicePrefix, _modulePrefix, Logger).ConfigureAwait(false); using var moduleClient = ModuleClient.CreateFromConnectionString(testModule.ConnectionString, transportSettings); await moduleClient.OpenAsync().ConfigureAwait(false); diff --git a/e2e/test/iothub/messaging/MessageSendFaultInjectionTests.cs b/e2e/test/iothub/messaging/MessageSendFaultInjectionTests.cs index 7546178e68..c5a89c2236 100644 --- a/e2e/test/iothub/messaging/MessageSendFaultInjectionTests.cs +++ b/e2e/test/iothub/messaging/MessageSendFaultInjectionTests.cs @@ -18,7 +18,7 @@ namespace Microsoft.Azure.Devices.E2ETests.Messaging public partial class MessageSendFaultInjectionTests : E2EMsTestBase { private readonly string _devicePrefix = $"E2E_{nameof(MessageSendFaultInjectionTests)}_"; - private static readonly string s_proxyServerAddress = Configuration.IoTHub.ProxyServerAddress; + private static readonly string s_proxyServerAddress = TestConfiguration.IoTHub.ProxyServerAddress; [LoggedTestMethod] public async Task Message_TcpConnectionLossSendRecovery_Amqp() diff --git a/e2e/test/iothub/method/MethodE2ETests.cs b/e2e/test/iothub/method/MethodE2ETests.cs index 395bb79946..973b2dcfbb 100644 --- a/e2e/test/iothub/method/MethodE2ETests.cs +++ b/e2e/test/iothub/method/MethodE2ETests.cs @@ -129,7 +129,7 @@ public async Task Method_ServiceSendsMethodThroughProxyWithDefaultTimeout() { var serviceClientTransportSettings = new ServiceClientTransportSettings { - HttpProxy = new WebProxy(Configuration.IoTHub.ProxyServerAddress) + HttpProxy = new WebProxy(TestConfiguration.IoTHub.ProxyServerAddress) }; await SendMethodAndRespondAsync( @@ -144,7 +144,7 @@ public async Task Method_ServiceSendsMethodThroughProxyWithCustomTimeout() { var serviceClientTransportSettings = new ServiceClientTransportSettings { - HttpProxy = new WebProxy(Configuration.IoTHub.ProxyServerAddress) + HttpProxy = new WebProxy(TestConfiguration.IoTHub.ProxyServerAddress) }; await SendMethodAndRespondAsync( @@ -159,7 +159,7 @@ await SendMethodAndRespondAsync( public async Task Method_ServiceInvokeDeviceMethodWithUnknownDeviceThrows() { // setup - using ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using ServiceClient 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(Configuration.IoTHub.ConnectionString); + using ServiceClient serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); var methodInvocation = new CloudToDeviceMethod("SetTelemetryInterval"); methodInvocation.SetPayloadJson("10"); @@ -282,7 +282,7 @@ await deviceClient null) .ConfigureAwait(false); - using var serviceClient = ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var serviceClient = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); var c2dMethod = new CloudToDeviceMethod(commandName, TimeSpan.FromMinutes(1), TimeSpan.FromMinutes(1)).SetPayloadJson(null); // act @@ -313,8 +313,8 @@ public static async Task ServiceSendMethodAndVerifyNotReceivedAsync( ServiceClientTransportSettings serviceClientTransportSettings = default) { ServiceClient serviceClient = serviceClientTransportSettings == default - ? ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString) - : ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString, TransportType.Amqp, serviceClientTransportSettings); + ? ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString) + : ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString, TransportType.Amqp, serviceClientTransportSettings); TimeSpan methodTimeout = responseTimeout == default ? s_defaultMethodTimeoutMinutes : responseTimeout; logger.Trace($"{nameof(ServiceSendMethodAndVerifyResponseAsync)}: Invoke method {methodName}."); @@ -346,8 +346,8 @@ public static async Task ServiceSendMethodAndVerifyResponseAsync( ServiceClientTransportSettings serviceClientTransportSettings = default) { ServiceClient serviceClient = serviceClientTransportSettings == default - ? ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString) - : ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString, TransportType.Amqp, serviceClientTransportSettings); + ? ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString) + : ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString, TransportType.Amqp, serviceClientTransportSettings); TimeSpan methodTimeout = responseTimeout == default ? s_defaultMethodTimeoutMinutes : responseTimeout; logger.Trace($"{nameof(ServiceSendMethodAndVerifyResponseAsync)}: Invoke method {methodName}."); CloudToDeviceMethodResult response = @@ -375,8 +375,8 @@ public static async Task ServiceSendMethodAndVerifyResponseAsync( ServiceClientTransportSettings serviceClientTransportSettings = default) { ServiceClient serviceClient = serviceClientTransportSettings == default - ? ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString) - : ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString, TransportType.Amqp, serviceClientTransportSettings); + ? ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString) + : ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString, TransportType.Amqp, serviceClientTransportSettings); TimeSpan methodTimeout = responseTimeout == default ? s_defaultMethodTimeoutMinutes : responseTimeout; diff --git a/e2e/test/iothub/method/MethodFaultInjectionTests.cs b/e2e/test/iothub/method/MethodFaultInjectionTests.cs index 483e6df6e0..4b33787d59 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(Configuration.IoTHub.ConnectionString); + using ServiceClient 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 0a2da2c3c7..7db552aa57 100644 --- a/e2e/test/iothub/properties/PropertiesE2ETests.cs +++ b/e2e/test/iothub/properties/PropertiesE2ETests.cs @@ -22,7 +22,7 @@ public class PropertiesE2ETests : E2EMsTestBase { private readonly string _devicePrefix = $"E2E_{nameof(PropertiesE2ETests)}_"; - private static readonly RegistryManager s_registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + private static readonly RegistryManager s_registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); private static readonly TimeSpan s_maxWaitTimeForCallback = TimeSpan.FromSeconds(30); private static readonly Dictionary s_mapOfPropertyValues = new Dictionary @@ -225,7 +225,7 @@ public static async Task Properties_DeviceSetsPropertyAndGetsItBackAsync(Devi public static async Task RegistryManagerUpdateWritablePropertyAsync(string deviceId, string propName, T propValue) { - using var registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); var twinPatch = new Twin(); twinPatch.Properties.Desired[propName] = propValue; @@ -311,7 +311,7 @@ private async Task Properties_ServiceSetsWritablePropertyAndDeviceReceivesItOnNe string propValue = Guid.NewGuid().ToString(); TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using var registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); var twinPatch = new Twin(); @@ -333,7 +333,7 @@ private async Task Properties_DeviceSetsPropertyAndServiceReceivesItAsync(Client string propValue = Guid.NewGuid().ToString(); TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using var registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); var patch = new ClientPropertyCollection(); @@ -357,7 +357,7 @@ private async Task Properties_DeviceSendsNullValueForPropertyResultsServiceRemov string propEmptyValue = "{}"; TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using var registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); // First send a property patch with valid values for both prop1 and prop2. @@ -416,7 +416,7 @@ private async Task Properties_ClientHandlesRejectionInvalidPropertyNameAsync(Cli string propName2 = Guid.NewGuid().ToString(); TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using var registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); Func func = async () => diff --git a/e2e/test/iothub/properties/PropertiesFaultInjectionTests.cs b/e2e/test/iothub/properties/PropertiesFaultInjectionTests.cs index aa32c93a20..3c71d594f6 100644 --- a/e2e/test/iothub/properties/PropertiesFaultInjectionTests.cs +++ b/e2e/test/iothub/properties/PropertiesFaultInjectionTests.cs @@ -153,7 +153,7 @@ await FaultInjection private async Task RegistryManagerUpdateDesiredPropertyAsync(string deviceId, string propName, string propValue) { - using var registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); var twinPatch = new Twin(); twinPatch.Properties.Desired[propName] = propValue; diff --git a/e2e/test/iothub/properties/PropertiesWithComponentsE2ETests.cs b/e2e/test/iothub/properties/PropertiesWithComponentsE2ETests.cs index 156c67f9fd..96040512d5 100644 --- a/e2e/test/iothub/properties/PropertiesWithComponentsE2ETests.cs +++ b/e2e/test/iothub/properties/PropertiesWithComponentsE2ETests.cs @@ -24,7 +24,7 @@ public class PropertiesWithComponentsE2ETests : E2EMsTestBase private readonly string _devicePrefix = $"E2E_{nameof(PropertiesWithComponentsE2ETests)}_"; - private static readonly RegistryManager s_registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + private static readonly RegistryManager s_registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); private static readonly TimeSpan s_maxWaitTimeForCallback = TimeSpan.FromSeconds(30); private static readonly Dictionary s_mapOfPropertyValues = new Dictionary @@ -227,7 +227,7 @@ public static async Task PropertiesWithComponents_DeviceSetsPropertyAndGetsItBac public static async Task RegistryManagerUpdateWritablePropertyAsync(string deviceId, string componentName, string propName, T propValue) { - using var registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); var twinPatch = new Twin(); var componentProperties = new TwinCollection @@ -318,7 +318,7 @@ private async Task PropertiesWithComponents_ServiceSetsWritablePropertyAndDevice string propValue = Guid.NewGuid().ToString(); TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using var registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); var twinPatch = new Twin(); @@ -345,7 +345,7 @@ private async Task PropertiesWithComponents_DeviceSetsPropertyAndServiceReceives string propValue = Guid.NewGuid().ToString(); TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using var registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); var patch = new ClientPropertyCollection(); @@ -369,7 +369,7 @@ private async Task Properties_DeviceSendsNullValueForPropertyResultsServiceRemov string propEmptyValue = "{}"; TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using var registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); // First send a property patch with valid values for both prop1 and prop2. @@ -444,7 +444,7 @@ private async Task PropertiesWithComponents_ClientHandlesRejectionInvalidPropert string propName2 = Guid.NewGuid().ToString(); TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using var registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); using var deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); Func func = async () => diff --git a/e2e/test/iothub/service/BulkOperationsE2ETests.cs b/e2e/test/iothub/service/BulkOperationsE2ETests.cs index dc3d518229..53ebc5c813 100644 --- a/e2e/test/iothub/service/BulkOperationsE2ETests.cs +++ b/e2e/test/iothub/service/BulkOperationsE2ETests.cs @@ -25,7 +25,7 @@ public async Task BulkOperations_UpdateTwins2Device_Ok() var tagValue = Guid.NewGuid().ToString(); using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix).ConfigureAwait(false); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); Twin twin = await registryManager.GetTwinAsync(testDevice.Id).ConfigureAwait(false); @@ -52,7 +52,7 @@ public async Task BulkOperations_UpdateTwins2DevicePatch_Ok() var tagValue = Guid.NewGuid().ToString(); using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix).ConfigureAwait(false); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); Twin twin = new Twin(); twin.DeviceId = testDevice.Id; @@ -79,7 +79,7 @@ public async Task BulkOperations_UpdateTwins2Module_Ok() var tagValue = Guid.NewGuid().ToString(); TestModule testModule = await TestModule.GetTestModuleAsync(DevicePrefix, ModulePrefix, Logger).ConfigureAwait(false); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); Twin twin = await registryManager.GetTwinAsync(testModule.DeviceId, testModule.Id).ConfigureAwait(false); @@ -108,7 +108,7 @@ public async Task BulkOperations_UpdateTwins2ModulePatch_Ok() TestModule testModule = await TestModule.GetTestModuleAsync(DevicePrefix, ModulePrefix, Logger).ConfigureAwait(false); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); var twin = new Twin(); twin.DeviceId = testModule.DeviceId; twin.ModuleId = testModule.Id; diff --git a/e2e/test/iothub/service/DigitalTwinClientE2ETests.cs b/e2e/test/iothub/service/DigitalTwinClientE2ETests.cs index a76bceac00..3aafb1c217 100644 --- a/e2e/test/iothub/service/DigitalTwinClientE2ETests.cs +++ b/e2e/test/iothub/service/DigitalTwinClientE2ETests.cs @@ -25,7 +25,7 @@ public class DigitalTwinClientE2ETests : E2EMsTestBase private const string TemperatureControllerModelId = "dtmi:com:example:TemperatureController;1"; private readonly string _devicePrefix = $"E2E_{nameof(DigitalTwinClientE2ETests)}_"; - private static readonly string s_connectionString = Configuration.IoTHub.ConnectionString; + private static readonly string s_connectionString = TestConfiguration.IoTHub.ConnectionString; [LoggedTestMethod] public async Task DigitalTwinWithOnlyRootComponentOperationsAsync() diff --git a/e2e/test/iothub/service/IoTHubCertificateValidationE2ETest.cs b/e2e/test/iothub/service/IoTHubCertificateValidationE2ETest.cs index dbe4ad2fa2..151d21bb51 100644 --- a/e2e/test/iothub/service/IoTHubCertificateValidationE2ETest.cs +++ b/e2e/test/iothub/service/IoTHubCertificateValidationE2ETest.cs @@ -18,7 +18,7 @@ public class IoTHubCertificateValidationE2ETest : E2EMsTestBase [LoggedTestMethod] public async Task RegistryManager_QueryDevicesInvalidServiceCertificateHttp_Fails() { - using RegistryManager rm = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionStringInvalidServiceCertificate); + using RegistryManager rm = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionStringInvalidServiceCertificate); IQuery query = rm.CreateQuery("select * from devices"); IotHubCommunicationException exception = await Assert.ThrowsExceptionAsync( () => query.GetNextAsTwinAsync()).ConfigureAwait(false); @@ -51,7 +51,7 @@ public async Task ServiceClient_SendMessageToDeviceInvalidServiceCertificateAmqp private static async Task TestServiceClientInvalidServiceCertificate(TransportType transport) { using ServiceClient service = ServiceClient.CreateFromConnectionString( - Configuration.IoTHub.ConnectionStringInvalidServiceCertificate, + TestConfiguration.IoTHub.ConnectionStringInvalidServiceCertificate, transport); using var testMessage = new Message(); await service.SendAsync("testDevice1", testMessage).ConfigureAwait(false); @@ -60,7 +60,7 @@ private static async Task TestServiceClientInvalidServiceCertificate(TransportTy [LoggedTestMethod] public async Task JobClient_ScheduleTwinUpdateInvalidServiceCertificateHttp_Fails() { - using JobClient jobClient = JobClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionStringInvalidServiceCertificate); + using JobClient jobClient = JobClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionStringInvalidServiceCertificate); var exception = await Assert.ThrowsExceptionAsync( () => jobClient.ScheduleTwinUpdateAsync( "testDevice", @@ -130,7 +130,7 @@ private static async Task TestDeviceClientInvalidServiceCertificate(Client.Trans { using (DeviceClient deviceClient = DeviceClient.CreateFromConnectionString( - Configuration.IoTHub.DeviceConnectionStringInvalidServiceCertificate, + TestConfiguration.IoTHub.DeviceConnectionStringInvalidServiceCertificate, transport)) { using var testMessage = new Client.Message(); diff --git a/e2e/test/iothub/service/IoTHubServiceProxyE2ETests.cs b/e2e/test/iothub/service/IoTHubServiceProxyE2ETests.cs index 867ba646f8..0cf3a69c07 100644 --- a/e2e/test/iothub/service/IoTHubServiceProxyE2ETests.cs +++ b/e2e/test/iothub/service/IoTHubServiceProxyE2ETests.cs @@ -22,8 +22,8 @@ public class IoTHubServiceProxyE2ETests : E2EMsTestBase private readonly string DevicePrefix = $"{nameof(IoTHubServiceProxyE2ETests)}_"; private const string JobDeviceId = "JobsSample_Device"; private const string JobTestTagName = "JobsSample_Tag"; - private static string s_connectionString = Configuration.IoTHub.ConnectionString; - private static string s_proxyServerAddress = Configuration.IoTHub.ProxyServerAddress; + private static string s_connectionString = TestConfiguration.IoTHub.ConnectionString; + private static string s_proxyServerAddress = TestConfiguration.IoTHub.ProxyServerAddress; private const int MaxIterationWait = 30; private static readonly TimeSpan _waitDuration = TimeSpan.FromSeconds(5); diff --git a/e2e/test/iothub/service/PnpServiceTests.cs b/e2e/test/iothub/service/PnpServiceTests.cs index c2026ed853..631309c795 100644 --- a/e2e/test/iothub/service/PnpServiceTests.cs +++ b/e2e/test/iothub/service/PnpServiceTests.cs @@ -44,7 +44,7 @@ public async Task DeviceTwin_Contains_ModelId() // Act // Get device twin. - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); Twin twin = await registryManager.GetTwinAsync(testDevice.Device.Id).ConfigureAwait(false); // Assert @@ -66,8 +66,8 @@ public async Task DeviceTwin_Contains_ModelId_X509() { ModelId = TestModelId, }; - string hostName = HostNameHelper.GetHostName(Configuration.IoTHub.ConnectionString); - X509Certificate2 authCertificate = Configuration.IoTHub.GetCertificateWithPrivateKey(); + 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); await deviceClient.OpenAsync().ConfigureAwait(false); @@ -75,7 +75,7 @@ public async Task DeviceTwin_Contains_ModelId_X509() // Act // Get device twin. - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); Twin twin = await registryManager.GetTwinAsync(testDevice.Device.Id).ConfigureAwait(false); // Assert @@ -110,7 +110,7 @@ public async Task ModuleTwin_Contains_ModelId() // Act // Get module twin. - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager 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 87067dfb7d..0e4c681fcb 100644 --- a/e2e/test/iothub/service/RegistryManagerE2ETests.cs +++ b/e2e/test/iothub/service/RegistryManagerE2ETests.cs @@ -28,10 +28,10 @@ public async Task RegistryManager_BadProxy_ThrowsException() { // arrange using RegistryManager registryManager = RegistryManager.CreateFromConnectionString( - Configuration.IoTHub.ConnectionString, + TestConfiguration.IoTHub.ConnectionString, new HttpTransportSettings { - Proxy = new WebProxy(Configuration.IoTHub.InvalidProxyServerAddress), + Proxy = new WebProxy(TestConfiguration.IoTHub.InvalidProxyServerAddress), }); // act @@ -47,7 +47,7 @@ public async Task RegistryManager_AddAndRemoveDeviceWithScope() string edgeId2 = _devicePrefix + Guid.NewGuid(); string deviceId = _devicePrefix + Guid.NewGuid(); - using var registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); try { @@ -95,7 +95,7 @@ public async Task RegistryManager_AddDeviceWithTwinWithDeviceCapabilities() { string deviceId = _devicePrefix + Guid.NewGuid(); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); var twin = new Twin { Tags = new TwinCollection(@"{ companyId: 1234 }"), @@ -124,7 +124,7 @@ public async Task RegistryManager_BulkLifecycle() devices.Add(new Device(_devicePrefix + Guid.NewGuid())); } - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); // Test that you can create devices in bulk BulkRegistryOperationResult bulkAddResult = await registryManager.AddDevices2Async(devices).ConfigureAwait(false); @@ -174,10 +174,10 @@ public async Task RegistryManager_AddDeviceWithProxy() string deviceId = _devicePrefix + Guid.NewGuid(); var transportSettings = new HttpTransportSettings { - Proxy = new WebProxy(Configuration.IoTHub.ProxyServerAddress) + Proxy = new WebProxy(TestConfiguration.IoTHub.ProxyServerAddress) }; - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString, transportSettings); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString, transportSettings); var device = new Device(deviceId); await registryManager.AddDeviceAsync(device).ConfigureAwait(false); } @@ -186,7 +186,7 @@ public async Task RegistryManager_AddDeviceWithProxy() public async Task RegistryManager_Query_Works() { // arrange - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); string deviceId = $"{_devicePrefix}{Guid.NewGuid()}"; try @@ -238,7 +238,7 @@ public async Task ModulesClient_GetModulesOnDevice() } Device device = null; - using RegistryManager client = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager client = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); try { @@ -281,7 +281,7 @@ public async Task ModulesClient_IdentityLifecycle() string testDeviceId = $"IdentityLifecycleDevice{Guid.NewGuid()}"; string testModuleId = $"IdentityLifecycleModule{Guid.NewGuid()}"; - using RegistryManager client = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager client = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); try { @@ -324,7 +324,7 @@ public async Task ModulesClient_IdentityLifecycle() [LoggedTestMethod] public async Task ModulesClient_DeviceTwinLifecycle() { - using RegistryManager client = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager client = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); TestModule module = await TestModule.GetTestModuleAsync(_devicePrefix, _modulePrefix, Logger).ConfigureAwait(false); try diff --git a/e2e/test/iothub/service/RegistryManagerExportDevicesTests.cs b/e2e/test/iothub/service/RegistryManagerExportDevicesTests.cs index 29a9d3d6c5..33c5c7c20e 100644 --- a/e2e/test/iothub/service/RegistryManagerExportDevicesTests.cs +++ b/e2e/test/iothub/service/RegistryManagerExportDevicesTests.cs @@ -54,8 +54,9 @@ public async Task RegistryManager_ExportDevices(StorageAuthenticationType storag // arrange StorageContainer storageContainer = null; + string edgeId = $"{nameof(RegistryManager_ExportDevices)}-Edge-{StorageContainer.GetRandomSuffix(4)}"; string deviceId = $"{nameof(RegistryManager_ExportDevices)}-{StorageContainer.GetRandomSuffix(4)}"; - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); Logger.Trace($"Using deviceId {deviceId}"); @@ -71,11 +72,21 @@ public async Task RegistryManager_ExportDevices(StorageAuthenticationType storag ? storageContainer.SasUri : storageContainer.Uri; + var edge = await registryManager + .AddDeviceAsync( + new Device(edgeId) + { + Authentication = new AuthenticationMechanism { Type = AuthenticationType.Sas }, + Capabilities = new Shared.DeviceCapabilities { IotEdge = true }, + }) + .ConfigureAwait(false); + await registryManager .AddDeviceAsync( new Device(deviceId) { Authentication = new AuthenticationMechanism { Type = AuthenticationType.Sas }, + Scope = edge.Scope, }) .ConfigureAwait(false); @@ -90,7 +101,7 @@ await registryManager ManagedIdentity identity = null; if (isUserAssignedMsi) { - string userAssignedMsiResourceId = Configuration.IoTHub.UserAssignedMsiResourceId; + string userAssignedMsiResourceId = TestConfiguration.IoTHub.UserAssignedMsiResourceId; identity = new ManagedIdentity { userAssignedIdentity = userAssignedMsiResourceId @@ -151,6 +162,7 @@ await registryManager { Logger.Trace($"Found device in export as [{serializedDeivce}]"); foundDeviceInExport = true; + device.DeviceScope.Should().Be(edge.Scope); break; } } @@ -163,6 +175,7 @@ await registryManager storageContainer?.Dispose(); await registryManager.RemoveDeviceAsync(deviceId).ConfigureAwait(false); + await registryManager.RemoveDeviceAsync(edgeId).ConfigureAwait(false); } catch { } } diff --git a/e2e/test/iothub/service/RegistryManagerImportDevicesTests.cs b/e2e/test/iothub/service/RegistryManagerImportDevicesTests.cs index 6f7a9df07c..56d8fa9ed1 100644 --- a/e2e/test/iothub/service/RegistryManagerImportDevicesTests.cs +++ b/e2e/test/iothub/service/RegistryManagerImportDevicesTests.cs @@ -49,7 +49,7 @@ public async Task RegistryManager_ImportDevices(StorageAuthenticationType storag StorageContainer storageContainer = null; string deviceId = $"{nameof(RegistryManager_ImportDevices)}-{StorageContainer.GetRandomSuffix(4)}"; - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); Logger.Trace($"Using deviceId {deviceId}"); @@ -88,7 +88,7 @@ public async Task RegistryManager_ImportDevices(StorageAuthenticationType storag ManagedIdentity identity = null; if (isUserAssignedMsi) { - string userAssignedMsiResourceId = Configuration.IoTHub.UserAssignedMsiResourceId; + string userAssignedMsiResourceId = TestConfiguration.IoTHub.UserAssignedMsiResourceId; identity = new ManagedIdentity { userAssignedIdentity = userAssignedMsiResourceId diff --git a/e2e/test/iothub/service/ServiceClientE2ETests.cs b/e2e/test/iothub/service/ServiceClientE2ETests.cs index efdc065733..ebad2565b7 100644 --- a/e2e/test/iothub/service/ServiceClientE2ETests.cs +++ b/e2e/test/iothub/service/ServiceClientE2ETests.cs @@ -48,7 +48,7 @@ private async Task DefaultTimeout() private async Task TestTimeout(TimeSpan? timeout) { using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, DevicePrefix).ConfigureAwait(false); - using var sender = ServiceClient.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using var sender = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); Stopwatch sw = new Stopwatch(); sw.Start(); @@ -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(Configuration.IoTHub.ConnectionString, transportType); + using ServiceClient 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(Configuration.IoTHub.ConnectionString); + using ServiceClient 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(Configuration.IoTHub.ConnectionString, options); + using ServiceClient 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(Configuration.IoTHub.ConnectionString, options); + using ServiceClient sender = ServiceClient.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString, options); string messageId = Guid.NewGuid().ToString(); // act diff --git a/e2e/test/iothub/twin/TwinE2ETests.cs b/e2e/test/iothub/twin/TwinE2ETests.cs index 3856362d09..be9d0aecca 100644 --- a/e2e/test/iothub/twin/TwinE2ETests.cs +++ b/e2e/test/iothub/twin/TwinE2ETests.cs @@ -21,7 +21,7 @@ public class TwinE2ETests : E2EMsTestBase { private readonly string _devicePrefix = $"E2E_{nameof(TwinE2ETests)}_"; - private static readonly RegistryManager _registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + private static readonly RegistryManager _registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); private static readonly List s_listOfPropertyValues = new List { @@ -523,7 +523,7 @@ await deviceClient public static async Task RegistryManagerUpdateDesiredPropertyAsync(string deviceId, string propName, object propValue) { - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); var twinPatch = new Twin(); twinPatch.Properties.Desired[propName] = propValue; @@ -602,7 +602,7 @@ private async Task Twin_ServiceSetsDesiredPropertyAndDeviceReceivesItOnNextGetAs var propValue = Guid.NewGuid().ToString(); using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); var twinPatch = new Twin(); @@ -622,7 +622,7 @@ private async Task Twin_DeviceSetsReportedPropertyAndServiceReceivesItAsync(Clie var propValue = Guid.NewGuid().ToString(); using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); var patch = new TwinCollection(); @@ -643,7 +643,7 @@ private async Task Twin_ServiceDoesNotCreateNullPropertyInCollectionAsync(Client var propEmptyValue = "{}"; using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); await deviceClient @@ -694,7 +694,7 @@ private async Task Twin_ClientHandlesRejectionInvalidPropertyNameAsync(Client.Tr var propName2 = Guid.NewGuid().ToString(); using TestDevice testDevice = await TestDevice.GetTestDeviceAsync(Logger, _devicePrefix).ConfigureAwait(false); - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); using DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(testDevice.ConnectionString, transport); var exceptionThrown = false; diff --git a/e2e/test/iothub/twin/TwinFaultInjectionTests.cs b/e2e/test/iothub/twin/TwinFaultInjectionTests.cs index 4cd53e1a79..0d76a46aec 100644 --- a/e2e/test/iothub/twin/TwinFaultInjectionTests.cs +++ b/e2e/test/iothub/twin/TwinFaultInjectionTests.cs @@ -256,7 +256,7 @@ await FaultInjection private async Task RegistryManagerUpdateDesiredPropertyAsync(string deviceId, string propName, string propValue) { - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); var twinPatch = new Twin(); twinPatch.Properties.Desired[propName] = propValue; @@ -273,7 +273,7 @@ private async Task Twin_DeviceDesiredPropertyUpdateRecoveryAsync( string proxyAddress = null) { TestDeviceCallbackHandler testDeviceCallbackHandler = null; - using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(Configuration.IoTHub.ConnectionString); + using RegistryManager registryManager = RegistryManager.CreateFromConnectionString(TestConfiguration.IoTHub.ConnectionString); using var cts = new CancellationTokenSource(FaultInjection.RecoveryTime); var propName = Guid.NewGuid().ToString(); diff --git a/e2e/test/provisioning/ProvisioningCertificateValidationE2ETest.cs b/e2e/test/provisioning/ProvisioningCertificateValidationE2ETest.cs index 1f5086d978..c794e82e76 100644 --- a/e2e/test/provisioning/ProvisioningCertificateValidationE2ETest.cs +++ b/e2e/test/provisioning/ProvisioningCertificateValidationE2ETest.cs @@ -20,7 +20,7 @@ public class ProvisioningCertificateValidationE2ETest : E2EMsTestBase public async Task ProvisioningServiceClient_QueryInvalidServiceCertificateHttp_Fails() { using ProvisioningServiceClient provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString( - Configuration.Provisioning.ConnectionStringInvalidServiceCertificate); + TestConfiguration.Provisioning.ConnectionStringInvalidServiceCertificate); Query q = provisioningServiceClient.CreateEnrollmentGroupQuery( new QuerySpecification("SELECT * FROM enrollmentGroups")); @@ -97,10 +97,10 @@ public async Task ProvisioningDeviceClient_RegisterAsyncInvalidServiceCertificat private static async Task TestInvalidServiceCertificate(ProvisioningTransportHandler transport) { - using X509Certificate2 cert = Configuration.Provisioning.GetIndividualEnrollmentCertificate(); + using X509Certificate2 cert = TestConfiguration.Provisioning.GetIndividualEnrollmentCertificate(); using var security = new SecurityProviderX509Certificate(cert); ProvisioningDeviceClient provisioningDeviceClient = ProvisioningDeviceClient.Create( - Configuration.Provisioning.GlobalDeviceEndpointInvalidServiceCertificate, + TestConfiguration.Provisioning.GlobalDeviceEndpointInvalidServiceCertificate, "0ne00000001", security, transport); diff --git a/e2e/test/provisioning/ProvisioningE2ETests.cs b/e2e/test/provisioning/ProvisioningE2ETests.cs index 1b55ccad36..c0ac148b0b 100644 --- a/e2e/test/provisioning/ProvisioningE2ETests.cs +++ b/e2e/test/provisioning/ProvisioningE2ETests.cs @@ -4,6 +4,7 @@ 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; @@ -32,10 +33,10 @@ public class ProvisioningE2ETests : E2EMsTestBase private const string InvalidIdScope = "0neFFFFFFFF"; private const string PayloadJsonData = "{\"testKey\":\"testValue\"}"; private const string InvalidGlobalAddress = "httpbin.org"; - private static readonly string s_globalDeviceEndpoint = Configuration.Provisioning.GlobalDeviceEndpoint; - private static readonly string s_proxyServerAddress = Configuration.IoTHub.ProxyServerAddress; - private static readonly X509Certificate2 s_individualEnrollmentCertificate = Configuration.Provisioning.GetIndividualEnrollmentCertificate(); - private static readonly X509Certificate2 s_groupEnrollmentCertificate = Configuration.Provisioning.GetGroupEnrollmentCertificate(); + private static readonly string s_globalDeviceEndpoint = TestConfiguration.Provisioning.GlobalDeviceEndpoint; + private static readonly string s_proxyServerAddress = TestConfiguration.IoTHub.ProxyServerAddress; + private static readonly X509Certificate2 s_individualEnrollmentCertificate = TestConfiguration.Provisioning.GetIndividualEnrollmentCertificate(); + private static readonly X509Certificate2 s_groupEnrollmentCertificate = TestConfiguration.Provisioning.GetGroupEnrollmentCertificate(); private readonly string _idPrefix = $"e2e-{nameof(ProvisioningE2ETests).ToLower()}-"; private readonly VerboseTestLogger _verboseLog = VerboseTestLogger.GetInstance(); @@ -375,17 +376,17 @@ private async Task ProvisioningDeviceClientCustomAllocationPolicyAsync( bool setCustomProxy, string customServerProxy = null) { - string closeHostName = IotHubConnectionStringBuilder.Create(Configuration.IoTHub.ConnectionString).HostName; + string closeHostName = IotHubConnectionStringBuilder.Create(TestConfiguration.IoTHub.ConnectionString).HostName; - ICollection iotHubsToProvisionTo = new List() { closeHostName, Configuration.Provisioning.FarAwayIotHubHostName }; + ICollection iotHubsToProvisionTo = new List() { closeHostName, TestConfiguration.Provisioning.FarAwayIotHubHostName }; string expectedDestinationHub = ""; - if (closeHostName.Length > Configuration.Provisioning.FarAwayIotHubHostName.Length) + if (closeHostName.Length > TestConfiguration.Provisioning.FarAwayIotHubHostName.Length) { expectedDestinationHub = closeHostName; } - else if (closeHostName.Length < Configuration.Provisioning.FarAwayIotHubHostName.Length) + else if (closeHostName.Length < TestConfiguration.Provisioning.FarAwayIotHubHostName.Length) { - expectedDestinationHub = Configuration.Provisioning.FarAwayIotHubHostName; + expectedDestinationHub = TestConfiguration.Provisioning.FarAwayIotHubHostName; } else { @@ -435,6 +436,62 @@ public async Task ProvisioningDeviceClient_ValidRegistrationId_MqttWsWithProxy_S await ProvisioningDeviceClient_ValidRegistrationId_Register_Ok(Client.TransportType.Mqtt, AttestationMechanismType.SymmetricKey, EnrollmentType.Individual, true, s_proxyServerAddress).ConfigureAwait(false); } + [LoggedTestMethod] + public async Task ProvisioningDeviceClient_ValidRegistrationId_TimeSpanTimeoutRespected_Mqtt() + { + try + { + await ProvisioningDeviceClient_ValidRegistrationId_Register_Ok(Client.TransportType.Mqtt_Tcp_Only, AttestationMechanismType.SymmetricKey, EnrollmentType.Individual, TimeSpan.Zero).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + return; // expected exception was thrown, so exit the test + } + + throw new AssertFailedException("Expected an OperationCanceledException to be thrown since the timeout was set to TimeSpan.Zero"); + } + + [LoggedTestMethod] + public async Task ProvisioningDeviceClient_ValidRegistrationId_TimeSpanTimeoutRespected_Https() + { + try + { + await ProvisioningDeviceClient_ValidRegistrationId_Register_Ok(Client.TransportType.Http1, AttestationMechanismType.SymmetricKey, EnrollmentType.Individual, TimeSpan.Zero).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + return; // expected exception was thrown, so exit the test + } + + throw new AssertFailedException("Expected an OperationCanceledException to be thrown since the timeout was set to TimeSpan.Zero"); + } + + [LoggedTestMethod] + public async Task ProvisioningDeviceClient_ValidRegistrationId_TimeSpanTimeoutRespected_Amqps() + { + try + { + await ProvisioningDeviceClient_ValidRegistrationId_Register_Ok(Client.TransportType.Amqp_Tcp_Only, AttestationMechanismType.SymmetricKey, EnrollmentType.Individual, TimeSpan.Zero).ConfigureAwait(false); + } + catch (ProvisioningTransportException ex) when (ex.InnerException is SocketException && ((SocketException) ex.InnerException).SocketErrorCode == SocketError.TimedOut) + { + // The expected exception is a bit different in AMQP compared to MQTT/HTTPS + return; // expected exception was thrown, so exit the test + } + + throw new AssertFailedException("Expected an OperationCanceledException to be thrown since the timeout was set to TimeSpan.Zero"); + } + + public async Task ProvisioningDeviceClient_ValidRegistrationId_Register_Ok( + Client.TransportType transportType, + AttestationMechanismType attestationType, + EnrollmentType? enrollmentType, + TimeSpan timeout) + { + //Default reprovisioning settings: Hashed allocation, no reprovision policy, hub names, or custom allocation policy + await ProvisioningDeviceClientValidRegistrationIdRegisterOkAsync(transportType, attestationType, enrollmentType, false, null, AllocationPolicy.Hashed, null, null, null, timeout, s_proxyServerAddress).ConfigureAwait(false); + } + public async Task ProvisioningDeviceClient_ValidRegistrationId_Register_Ok( Client.TransportType transportType, AttestationMechanismType attestationType, @@ -443,7 +500,7 @@ public async Task ProvisioningDeviceClient_ValidRegistrationId_Register_Ok( string proxyServerAddress = null) { //Default reprovisioning settings: Hashed allocation, no reprovision policy, hub names, or custom allocation policy - await ProvisioningDeviceClientValidRegistrationIdRegisterOkAsync(transportType, attestationType, enrollmentType, setCustomProxy, null, AllocationPolicy.Hashed, null, null, null, s_proxyServerAddress).ConfigureAwait(false); + await ProvisioningDeviceClientValidRegistrationIdRegisterOkAsync(transportType, attestationType, enrollmentType, setCustomProxy, null, AllocationPolicy.Hashed, null, null, null, TimeSpan.MaxValue, proxyServerAddress).ConfigureAwait(false); } public async Task ProvisioningDeviceClient_ValidRegistrationId_Register_Ok( @@ -455,7 +512,7 @@ public async Task ProvisioningDeviceClient_ValidRegistrationId_Register_Ok( string proxyServerAddress = null) { //Default reprovisioning settings: Hashed allocation, no reprovision policy, hub names, or custom allocation policy - var iothubs = new List() { IotHubConnectionStringBuilder.Create(Configuration.IoTHub.ConnectionString).HostName }; + var iothubs = new List() { IotHubConnectionStringBuilder.Create(TestConfiguration.IoTHub.ConnectionString).HostName }; await ProvisioningDeviceClientValidRegistrationIdRegisterOkAsync( transportType, attestationType, @@ -466,7 +523,8 @@ await ProvisioningDeviceClientValidRegistrationIdRegisterOkAsync( null, iothubs, capabilities, - s_proxyServerAddress) + TimeSpan.MaxValue, + proxyServerAddress) .ConfigureAwait(false); } @@ -480,6 +538,7 @@ private async Task ProvisioningDeviceClientValidRegistrationIdRegisterOkAsync( CustomAllocationDefinition customAllocationDefinition, ICollection iothubs, DeviceCapabilities deviceCapabilities, + TimeSpan timeout, string proxyServerAddress = null) { string groupId = _idPrefix + AttestationTypeToString(attestationType) + "-" + Guid.NewGuid(); @@ -503,7 +562,7 @@ private async Task ProvisioningDeviceClientValidRegistrationIdRegisterOkAsync( ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create( s_globalDeviceEndpoint, - Configuration.Provisioning.IdScope, + TestConfiguration.Provisioning.IdScope, security, transport); @@ -519,7 +578,14 @@ private async Task ProvisioningDeviceClientValidRegistrationIdRegisterOkAsync( { try { - result = await provClient.RegisterAsync(cts.Token).ConfigureAwait(false); + if (timeout != TimeSpan.MaxValue) + { + result = await provClient.RegisterAsync(timeout).ConfigureAwait(false); + } + else + { + result = await provClient.RegisterAsync(cts.Token).ConfigureAwait(false); + } break; } // Catching all ProvisioningTransportException as the status code is not the same for Mqtt, Amqp and Http. @@ -567,7 +633,7 @@ private async Task ProvisioningDeviceClientProvisioningFlowCustomAllocationAlloc var customAllocationDefinition = new CustomAllocationDefinition { - WebhookUrl = Configuration.Provisioning.CustomAllocationPolicyWebhook, + WebhookUrl = TestConfiguration.Provisioning.CustomAllocationPolicyWebhook, ApiVersion = "2019-03-31", }; @@ -590,7 +656,7 @@ private async Task ProvisioningDeviceClientProvisioningFlowCustomAllocationAlloc ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create( s_globalDeviceEndpoint, - Configuration.Provisioning.IdScope, + TestConfiguration.Provisioning.IdScope, security, transport); using var cts = new CancellationTokenSource(PassingTimeoutMiliseconds); @@ -657,7 +723,7 @@ public async Task ProvisioningDeviceClient_InvalidRegistrationId_TpmRegister_Fai using SecurityProvider security = new SecurityProviderTpmSimulator("invalidregistrationid"); ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create( s_globalDeviceEndpoint, - Configuration.Provisioning.IdScope, + TestConfiguration.Provisioning.IdScope, security, transport); @@ -824,7 +890,7 @@ private async Task ProvisioningDeviceClientInvalidGlobalAddressRegisterFailAsync ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create( InvalidGlobalAddress, - Configuration.Provisioning.IdScope, + TestConfiguration.Provisioning.IdScope, security, transport); @@ -906,7 +972,7 @@ private async Task CreateSecurityProviderFromNameAsync(Attesta { _verboseLog.WriteLine($"{nameof(CreateSecurityProviderFromNameAsync)}({attestationType})"); - using ProvisioningServiceClient provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString(Configuration.Provisioning.ConnectionString); + using ProvisioningServiceClient provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString(TestConfiguration.Provisioning.ConnectionString); switch (attestationType) { @@ -936,7 +1002,7 @@ private async Task CreateSecurityProviderFromNameAsync(Attesta case EnrollmentType.Group: certificate = s_groupEnrollmentCertificate; - collection = Configuration.Provisioning.GetGroupEnrollmentChain(); + collection = TestConfiguration.Provisioning.GetGroupEnrollmentChain(); break; default: diff --git a/e2e/test/provisioning/ProvisioningServiceClientE2ETests.cs b/e2e/test/provisioning/ProvisioningServiceClientE2ETests.cs index 484339064e..72ee57237b 100644 --- a/e2e/test/provisioning/ProvisioningServiceClientE2ETests.cs +++ b/e2e/test/provisioning/ProvisioningServiceClientE2ETests.cs @@ -19,7 +19,7 @@ namespace Microsoft.Azure.Devices.E2ETests.Provisioning [TestCategory("DPS")] public class ProvisioningServiceClientE2ETests : E2EMsTestBase { - private static readonly string s_proxyServerAddress = Configuration.IoTHub.ProxyServerAddress; + private static readonly string s_proxyServerAddress = TestConfiguration.IoTHub.ProxyServerAddress; private static readonly string s_devicePrefix = $"E2E_{nameof(ProvisioningServiceClientE2ETests)}_"; #pragma warning disable CA1823 @@ -114,7 +114,7 @@ public async Task ProvisioningServiceClient_GetEnrollmentGroupAttestation_Symmet public async Task ProvisioningServiceClient_GetIndividualEnrollmentAttestation(AttestationMechanismType attestationType) { - using ProvisioningServiceClient provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString(Configuration.Provisioning.ConnectionString); + using ProvisioningServiceClient 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); @@ -144,7 +144,7 @@ public async Task ProvisioningServiceClient_GetIndividualEnrollmentAttestation(A public async Task ProvisioningServiceClient_GetEnrollmentGroupAttestation(AttestationMechanismType attestationType) { - using ProvisioningServiceClient provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString(Configuration.Provisioning.ConnectionString); + using ProvisioningServiceClient 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); @@ -268,7 +268,7 @@ public static async Task CreateIndividualEnrollment(Provis using (var tpmSim = new SecurityProviderTpmSimulator(registrationId)) { string base64Ek = Convert.ToBase64String(tpmSim.GetEndorsementKey()); - using ProvisioningServiceClient provisioningService = ProvisioningServiceClient.CreateFromConnectionString(Configuration.Provisioning.ConnectionString); + using ProvisioningServiceClient provisioningService = ProvisioningServiceClient.CreateFromConnectionString(TestConfiguration.Provisioning.ConnectionString); individualEnrollment = new IndividualEnrollment(registrationId, new TpmAttestation(base64Ek)) { Capabilities = capabilities, @@ -347,7 +347,7 @@ public static ProvisioningServiceClient CreateProvisioningService(string proxySe transportSettings.Proxy = new WebProxy(proxyServerAddress); } - return ProvisioningServiceClient.CreateFromConnectionString(Configuration.Provisioning.ConnectionString, transportSettings); + return ProvisioningServiceClient.CreateFromConnectionString(TestConfiguration.Provisioning.ConnectionString, transportSettings); } /// diff --git a/e2e/test/provisioning/ReprovisioningE2ETests.cs b/e2e/test/provisioning/ReprovisioningE2ETests.cs index 11802a6bb0..f647a52d02 100644 --- a/e2e/test/provisioning/ReprovisioningE2ETests.cs +++ b/e2e/test/provisioning/ReprovisioningE2ETests.cs @@ -28,10 +28,10 @@ namespace Microsoft.Azure.Devices.E2ETests.Provisioning public class ReprovisioningE2ETests : E2EMsTestBase { private const int PassingTimeoutMiliseconds = 10 * 60 * 1000; - private static readonly string s_globalDeviceEndpoint = Configuration.Provisioning.GlobalDeviceEndpoint; - private static string s_proxyServerAddress = Configuration.IoTHub.ProxyServerAddress; - private static readonly X509Certificate2 s_individualEnrollmentCertificate = Configuration.Provisioning.GetIndividualEnrollmentCertificate(); - private static readonly X509Certificate2 s_groupEnrollmentCertificate = Configuration.Provisioning.GetGroupEnrollmentCertificate(); + private static readonly string s_globalDeviceEndpoint = TestConfiguration.Provisioning.GlobalDeviceEndpoint; + private static string s_proxyServerAddress = TestConfiguration.IoTHub.ProxyServerAddress; + private static readonly X509Certificate2 s_individualEnrollmentCertificate = TestConfiguration.Provisioning.GetIndividualEnrollmentCertificate(); + private static readonly X509Certificate2 s_groupEnrollmentCertificate = TestConfiguration.Provisioning.GetGroupEnrollmentCertificate(); private readonly string _devicePrefix = $"E2E_{nameof(ProvisioningE2ETests)}_"; private readonly VerboseTestLogger _verboseLog = VerboseTestLogger.GetInstance(); @@ -211,8 +211,8 @@ 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(Configuration.IoTHub.ConnectionString); - ICollection iotHubsToStartAt = new List() { Configuration.Provisioning.FarAwayIotHubHostName }; + IotHubConnectionStringBuilder 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,8 +223,8 @@ 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(Configuration.IoTHub.ConnectionString); - ICollection iotHubsToStartAt = new List() { Configuration.Provisioning.FarAwayIotHubHostName }; + IotHubConnectionStringBuilder 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,8 +234,8 @@ 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(Configuration.IoTHub.ConnectionString); - ICollection iotHubsToStartAt = new List() { Configuration.Provisioning.FarAwayIotHubHostName }; + IotHubConnectionStringBuilder 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); } @@ -283,7 +283,7 @@ public async Task ProvisioningDeviceClient_ReprovisioningFlow( ProvisioningDeviceClient provClient = ProvisioningDeviceClient.Create( s_globalDeviceEndpoint, - Configuration.Provisioning.IdScope, + TestConfiguration.Provisioning.IdScope, security, transport); using var cts = new CancellationTokenSource(PassingTimeoutMiliseconds); @@ -359,7 +359,7 @@ private async Task CreateSecurityProviderFromName(AttestationM { _verboseLog.WriteLine($"{nameof(CreateSecurityProviderFromName)}({attestationType})"); - using var provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString(Configuration.Provisioning.ConnectionString); + using var provisioningServiceClient = ProvisioningServiceClient.CreateFromConnectionString(TestConfiguration.Provisioning.ConnectionString); switch (attestationType) { @@ -369,7 +369,7 @@ private async Task CreateSecurityProviderFromName(AttestationM string base64Ek = Convert.ToBase64String(tpmSim.GetEndorsementKey()); - using (ProvisioningServiceClient provisioningService = ProvisioningServiceClient.CreateFromConnectionString(Configuration.Provisioning.ConnectionString)) + using (ProvisioningServiceClient 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 }; @@ -394,7 +394,7 @@ private async Task CreateSecurityProviderFromName(AttestationM case EnrollmentType.Group: certificate = s_groupEnrollmentCertificate; - collection = Configuration.Provisioning.GetGroupEnrollmentChain(); + collection = TestConfiguration.Provisioning.GetGroupEnrollmentChain(); break; default: @@ -513,7 +513,7 @@ private void ConfirmDeviceInExpectedHub(DeviceRegistrationResult result, Reprovi if (allocationPolicy == AllocationPolicy.GeoLatency) { - Assert.AreNotEqual(result.AssignedHub, Configuration.Provisioning.FarAwayIotHubHostName); + Assert.AreNotEqual(result.AssignedHub, TestConfiguration.Provisioning.FarAwayIotHubHostName); } } else diff --git a/iothub/device/samples/convention-based-samples/TemperatureController/Models/DeviceInformation.json b/iothub/device/samples/convention-based-samples/TemperatureController/Models/DeviceInformation.json deleted file mode 100644 index 6d59180dc8..0000000000 --- a/iothub/device/samples/convention-based-samples/TemperatureController/Models/DeviceInformation.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "@id": "dtmi:azure:DeviceManagement:DeviceInformation;1", - "@type": "Interface", - "displayName": "Device Information", - "contents": [ - { - "@type": "Property", - "name": "manufacturer", - "displayName": "Manufacturer", - "schema": "string", - "description": "Company name of the device manufacturer. This could be the same as the name of the original equipment manufacturer (OEM). Ex. Contoso." - }, - { - "@type": "Property", - "name": "model", - "displayName": "Device model", - "schema": "string", - "description": "Device model name or ID. Ex. Surface Book 2." - }, - { - "@type": "Property", - "name": "swVersion", - "displayName": "Software version", - "schema": "string", - "description": "Version of the software on your device. This could be the version of your firmware. Ex. 1.3.45" - }, - { - "@type": "Property", - "name": "osName", - "displayName": "Operating system name", - "schema": "string", - "description": "Name of the operating system on the device. Ex. Windows 10 IoT Core." - }, - { - "@type": "Property", - "name": "processorArchitecture", - "displayName": "Processor architecture", - "schema": "string", - "description": "Architecture of the processor on the device. Ex. x64 or ARM." - }, - { - "@type": "Property", - "name": "processorManufacturer", - "displayName": "Processor manufacturer", - "schema": "string", - "description": "Name of the manufacturer of the processor on the device. Ex. Intel." - }, - { - "@type": "Property", - "name": "totalStorage", - "displayName": "Total storage", - "schema": "double", - "description": "Total available storage on the device in kilobytes. Ex. 2048000 kilobytes." - }, - { - "@type": "Property", - "name": "totalMemory", - "displayName": "Total memory", - "schema": "double", - "description": "Total available memory on the device in kilobytes. Ex. 256000 kilobytes." - } - ], - "@context": "dtmi:dtdl:context;2" -} \ No newline at end of file diff --git a/iothub/device/samples/convention-based-samples/TemperatureController/Models/TemperatureController.json b/iothub/device/samples/convention-based-samples/TemperatureController/Models/TemperatureController.json deleted file mode 100644 index 997b5cd34a..0000000000 --- a/iothub/device/samples/convention-based-samples/TemperatureController/Models/TemperatureController.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "@context": [ - "dtmi:iotcentral:context;2", - "dtmi:dtdl:context;2" - ], - "@id": "dtmi:com:example:TemperatureController;2", - "@type": "Interface", - "contents": [ - { - "@type": [ - "Telemetry", - "DataSize" - ], - "description": { - "en": "Current working set of the device memory in KiB." - }, - "displayName": { - "en": "Working Set" - }, - "name": "workingSet", - "schema": "double", - "unit": "kibibit" - }, - { - "@type": "Property", - "displayName": { - "en": "Serial Number" - }, - "name": "serialNumber", - "schema": "string", - "writable": false - }, - { - "@type": "Command", - "commandType": "synchronous", - "description": { - "en": "Reboots the device after waiting the number of seconds specified." - }, - "displayName": { - "en": "Reboot" - }, - "name": "reboot", - "request": { - "@type": "CommandPayload", - "description": { - "en": "Number of seconds to wait before rebooting the device." - }, - "displayName": { - "en": "Delay" - }, - "name": "delay", - "schema": "integer" - } - }, - { - "@type": "Component", - "displayName": { - "en": "thermostat1" - }, - "name": "thermostat1", - "schema": "dtmi:com:example:Thermostat;1" - }, - { - "@type": "Component", - "displayName": { - "en": "thermostat2" - }, - "name": "thermostat2", - "schema": "dtmi:com:example:Thermostat;2" - }, - { - "@type": "Component", - "displayName": { - "en": "DeviceInfo" - }, - "name": "deviceInformation", - "schema": "dtmi:azure:DeviceManagement:DeviceInformation;1" - } - ], - "displayName": { - "en": "Temperature Controller" - } - } \ No newline at end of file diff --git a/iothub/device/samples/convention-based-samples/TemperatureController/Models/Thermostat.json b/iothub/device/samples/convention-based-samples/TemperatureController/Models/Thermostat.json deleted file mode 100644 index 46b21f85cb..0000000000 --- a/iothub/device/samples/convention-based-samples/TemperatureController/Models/Thermostat.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "@context": "dtmi:dtdl:context;2", - "@id": "dtmi:com:example:Thermostat;1", - "@type": "Interface", - "displayName": "Thermostat", - "description": "Reports current temperature and provides desired temperature control.", - "contents": [ - { - "@type": [ - "Telemetry", - "Temperature" - ], - "name": "temperature", - "displayName" : "Temperature", - "description" : "Temperature in degrees Celsius.", - "schema": "double", - "unit": "degreeCelsius" - }, - { - "@type": [ - "Property", - "Temperature" - ], - "name": "targetTemperature", - "schema": "double", - "displayName": "Target Temperature", - "description": "Allows to remotely specify the desired target temperature.", - "unit" : "degreeCelsius", - "writable": true - }, - { - "@type": [ - "Property", - "Temperature" - ], - "name": "maxTempSinceLastReboot", - "schema": "double", - "unit" : "degreeCelsius", - "displayName": "Max temperature since last reboot.", - "description": "Returns the max temperature since last device reboot." - }, - { - "@type": "Command", - "name": "getMaxMinReport", - "displayName": "Get Max-Min report.", - "description": "This command returns the max, min and average temperature from the specified time to the current time.", - "request": { - "name": "since", - "displayName": "Since", - "description": "Period to return the max-min report.", - "schema": "dateTime" - }, - "response": { - "name" : "tempReport", - "displayName": "Temperature Report", - "schema": { - "@type": "Object", - "fields": [ - { - "name": "maxTemp", - "displayName": "Max temperature", - "schema": "double" - }, - { - "name": "minTemp", - "displayName": "Min temperature", - "schema": "double" - }, - { - "name" : "avgTemp", - "displayName": "Average Temperature", - "schema": "double" - }, - { - "name" : "startTime", - "displayName": "Start Time", - "schema": "dateTime" - }, - { - "name" : "endTime", - "displayName": "End Time", - "schema": "dateTime" - } - ] - } - } - } - ] -} diff --git a/iothub/device/samples/convention-based-samples/TemperatureController/Models/Thermostat2.json b/iothub/device/samples/convention-based-samples/TemperatureController/Models/Thermostat2.json deleted file mode 100644 index 2309b4a5d4..0000000000 --- a/iothub/device/samples/convention-based-samples/TemperatureController/Models/Thermostat2.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "@context": "dtmi:dtdl:context;2", - "@id": "dtmi:com:example:Thermostat;2", - "@type": "Interface", - "displayName": "Thermostat", - "description": "Reports current temperature and provides desired temperature control.", - "contents": [ - { - "@type": [ - "Telemetry", - "Temperature" - ], - "name": "temperature", - "displayName" : "Temperature", - "description" : "Temperature in degrees Celsius.", - "schema": "double", - "unit": "degreeCelsius" - }, - { - "@type": [ - "Property", - "Temperature" - ], - "name": "targetTemperature", - "schema": "double", - "displayName": "Target Temperature", - "description": "Allows to remotely specify the desired target temperature.", - "unit" : "degreeCelsius", - "writable": true - }, - { - "@type": [ - "Property", - "Temperature" - ], - "name": "maxTempSinceLastReboot", - "schema": "double", - "unit" : "degreeCelsius", - "displayName": "Max temperature since last reboot.", - "description": "Returns the max temperature since last device reboot." - }, - { - "@type": "Command", - "name": "getMaxMinReport", - "displayName": "Get Max-Min report.", - "description": "This command returns the max, min and average temperature from the specified time to the current time.", - "request": { - "name": "since", - "displayName": "Since", - "description": "Period to return the max-min report.", - "schema": "dateTime" - }, - "response": { - "name" : "tempReport", - "displayName": "Temperature Report", - "schema": { - "@type": "Object", - "fields": [ - { - "name": "maxTemp", - "displayName": "Max temperature", - "schema": "double" - }, - { - "name": "minTemp", - "displayName": "Min temperature", - "schema": "double" - }, - { - "name" : "avgTemp", - "displayName": "Average Temperature", - "schema": "double" - }, - { - "name" : "startTime", - "displayName": "Start Time", - "schema": "dateTime" - }, - { - "name" : "endTime", - "displayName": "End Time", - "schema": "dateTime" - } - ] - } - } - } - ] -} diff --git a/iothub/device/samples/convention-based-samples/TemperatureController/Parameter.cs b/iothub/device/samples/convention-based-samples/TemperatureController/Parameter.cs deleted file mode 100644 index 9f8f5b99ff..0000000000 --- a/iothub/device/samples/convention-based-samples/TemperatureController/Parameter.cs +++ /dev/null @@ -1,86 +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 CommandLine; -using Microsoft.Extensions.Logging; -using System; - -namespace Microsoft.Azure.Devices.Client.Samples -{ - /// - /// Parameters for the application supplied via command line arguments. - /// If the parameter is not supplied via command line args, it will look for it in environment variables. - /// - internal class Parameters - { - [Option( - "DeviceSecurityType", - HelpText = "(Required) The flow that will be used for connecting the device for the sample. Possible case-insensitive values include: dps, connectionString." + - "\nDefaults to environment variable \"IOTHUB_DEVICE_SECURITY_TYPE\".")] - public string DeviceSecurityType { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_SECURITY_TYPE"); - - [Option( - 'p', - "PrimaryConnectionString", - HelpText = "(Required if DeviceSecurityType is \"connectionString\"). \nThe primary connection string for the device to simulate." + - "\nDefaults to environment variable \"IOTHUB_DEVICE_CONNECTION_STRING\".")] - public string PrimaryConnectionString { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_CONNECTION_STRING"); - - [Option( - 'e', - "DpsEndpoint", - HelpText = "(Required if DeviceSecurityType is \"dps\"). \nThe DPS endpoint to use during device provisioning." + - "\nDefaults to environment variable \"IOTHUB_DEVICE_DPS_ENDPOINT\".")] - public string DpsEndpoint { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_DPS_ENDPOINT"); - - [Option( - 'i', - "DpsIdScope", - HelpText = "(Required if DeviceSecurityType is \"dps\"). \nThe DPS ID Scope to use during device provisioning." + - "\nDefaults to environment variable \"IOTHUB_DEVICE_DPS_ID_SCOPE\".")] - public string DpsIdScope { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_DPS_ID_SCOPE"); - - [Option( - 'd', - "DeviceId", - HelpText = "(Required if DeviceSecurityType is \"dps\"). \nThe device registration Id to use during device provisioning." + - "\nDefaults to environment variable \"IOTHUB_DEVICE_DPS_DEVICE_ID\".")] - public string DeviceId { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_DPS_DEVICE_ID"); - - [Option( - 'k', - "DeviceSymmetricKey", - HelpText = "(Required if DeviceSecurityType is \"dps\"). \nThe device symmetric key to use during device provisioning." + - "\nDefaults to environment variable \"IOTHUB_DEVICE_DPS_DEVICE_KEY\".")] - public string DeviceSymmetricKey { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_DPS_DEVICE_KEY"); - - [Option( - 'r', - "Application running time (in seconds)", - Required = false, - HelpText = "The running time for this console application. Leave it unassigned to run the application until it is explicitly canceled using Control+C.")] - public double? ApplicationRunningTime { get; set; } - - public bool Validate(ILogger logger) - { - if (string.IsNullOrWhiteSpace(DeviceSecurityType)) - { - logger.LogWarning("Device provisioning type not set, please set the environment variable \"IOTHUB_DEVICE_SECURITY_TYPE\"" + - "or pass in \"-s | --DeviceSecurityType\" through command line. \nWill default to using \"dps\" flow."); - - DeviceSecurityType = "dps"; - } - - return (DeviceSecurityType.ToLowerInvariant()) switch - { - "dps" => !string.IsNullOrWhiteSpace(DpsEndpoint) - && !string.IsNullOrWhiteSpace(DpsIdScope) - && !string.IsNullOrWhiteSpace(DeviceId) - && !string.IsNullOrWhiteSpace(DeviceSymmetricKey), - "connectionstring" => !string.IsNullOrWhiteSpace(PrimaryConnectionString), - _ => throw new ArgumentException($"Unrecognized value for device provisioning received: {DeviceSecurityType}." + - $" It should be either \"dps\" or \"connectionString\" (case-insensitive)."), - }; - } - } -} diff --git a/iothub/device/samples/convention-based-samples/TemperatureController/Program.cs b/iothub/device/samples/convention-based-samples/TemperatureController/Program.cs deleted file mode 100644 index e2fcc0ba2e..0000000000 --- a/iothub/device/samples/convention-based-samples/TemperatureController/Program.cs +++ /dev/null @@ -1,165 +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 CommandLine; -using Microsoft.Azure.Devices.Provisioning.Client; -using Microsoft.Azure.Devices.Provisioning.Client.PlugAndPlay; -using Microsoft.Azure.Devices.Provisioning.Client.Transport; -using Microsoft.Azure.Devices.Shared; -using Microsoft.Extensions.Logging; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Azure.Devices.Client.Samples -{ - public class Program - { - // DTDL interface used: https://github.com/Azure/iot-plugandplay-models/blob/main/dtmi/com/example/temperaturecontroller-2.json - // The TemperatureController model contains 2 Thermostat components that implement different versions of Thermostat models. - // Both Thermostat models are identical in definition but this is done to allow IoT Central to handle - // TemperatureController model correctly. - private const string ModelId = "dtmi:com:example:TemperatureController;2"; - - private static ILogger s_logger; - - public static async Task Main(string[] args) - { - // Parse application parameters - Parameters parameters = null; - ParserResult result = Parser.Default.ParseArguments(args) - .WithParsed(parsedParams => - { - parameters = parsedParams; - }) - .WithNotParsed(errors => - { - Environment.Exit(1); - }); - - s_logger = InitializeConsoleDebugLogger(); - if (!parameters.Validate(s_logger)) - { - throw new ArgumentException("Required parameters are not set. Please recheck required variables by using \"--help\""); - } - - var runningTime = parameters.ApplicationRunningTime != null - ? TimeSpan.FromSeconds((double)parameters.ApplicationRunningTime) - : Timeout.InfiniteTimeSpan; - - s_logger.LogInformation("Press Control+C to quit the sample."); - using var cts = new CancellationTokenSource(runningTime); - Console.CancelKeyPress += (sender, eventArgs) => - { - eventArgs.Cancel = true; - cts.Cancel(); - s_logger.LogInformation("Sample execution cancellation requested; will exit."); - }; - - s_logger.LogDebug($"Set up the device client."); - using DeviceClient deviceClient = await SetupDeviceClientAsync(parameters, cts.Token); - var sample = new TemperatureControllerSample(deviceClient, s_logger); - await sample.PerformOperationsAsync(cts.Token); - - // PerformOperationsAsync is designed to run until cancellation has been explicitly requested, either through - // cancellation token expiration or by Console.CancelKeyPress. - // As a result, by the time the control reaches the call to close the device client, the cancellation token source would - // have already had cancellation requested. - // Hence, if you want to pass a cancellation token to any subsequent calls, a new token needs to be generated. - // For device client APIs, you can also call them without a cancellation token, which will set a default - // cancellation timeout of 4 minutes: https://github.com/Azure/azure-iot-sdk-csharp/blob/64f6e9f24371bc40ab3ec7a8b8accbfb537f0fe1/iothub/device/src/InternalClient.cs#L1922 - await deviceClient.CloseAsync(); - } - - private static ILogger InitializeConsoleDebugLogger() - { - ILoggerFactory loggerFactory = LoggerFactory.Create(builder => - { - builder - .AddFilter(level => level >= LogLevel.Debug) - .AddConsole(options => - { - options.TimestampFormat = "[MM/dd/yyyy HH:mm:ss]"; - }); - }); - - return loggerFactory.CreateLogger(); - } - - private static async Task SetupDeviceClientAsync(Parameters parameters, CancellationToken cancellationToken) - { - DeviceClient deviceClient; - switch (parameters.DeviceSecurityType.ToLowerInvariant()) - { - case "dps": - s_logger.LogDebug($"Initializing via DPS"); - DeviceRegistrationResult dpsRegistrationResult = await ProvisionDeviceAsync(parameters, cancellationToken); - var authMethod = new DeviceAuthenticationWithRegistrySymmetricKey(dpsRegistrationResult.DeviceId, parameters.DeviceSymmetricKey); - deviceClient = InitializeDeviceClient(dpsRegistrationResult.AssignedHub, authMethod); - break; - - case "connectionstring": - s_logger.LogDebug($"Initializing via IoT Hub connection string"); - deviceClient = InitializeDeviceClient(parameters.PrimaryConnectionString); - break; - - default: - throw new ArgumentException($"Unrecognized value for device provisioning received: {parameters.DeviceSecurityType}." + - $" It should be either \"dps\" or \"connectionString\" (case-insensitive)."); - } - return deviceClient; - } - - // Provision a device via DPS, by sending the PnP model Id as DPS payload. - private static async Task ProvisionDeviceAsync(Parameters parameters, CancellationToken cancellationToken) - { - SecurityProvider symmetricKeyProvider = new SecurityProviderSymmetricKey(parameters.DeviceId, parameters.DeviceSymmetricKey, null); - ProvisioningTransportHandler mqttTransportHandler = new ProvisioningTransportHandlerMqtt(); - ProvisioningDeviceClient pdc = ProvisioningDeviceClient.Create(parameters.DpsEndpoint, parameters.DpsIdScope, symmetricKeyProvider, mqttTransportHandler); - - var pnpPayload = new ProvisioningRegistrationAdditionalData - { - JsonData = PnpConvention.CreateDpsPayload(ModelId), - }; - return await pdc.RegisterAsync(pnpPayload, cancellationToken); - } - - // Initialize the device client instance using connection string based authentication, over Mqtt protocol (TCP, with fallback over Websocket) and - // setting the ModelId into ClientOptions.This method also sets a connection status change callback, that will get triggered any time the device's - // connection status changes. - private static DeviceClient InitializeDeviceClient(string deviceConnectionString) - { - // Specify a custom System.Text.Json based PayloadConvention to be used. - var options = new ClientOptions(SystemTextJsonPayloadConvention.Instance) - { - ModelId = ModelId, - }; - - DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(deviceConnectionString, TransportType.Mqtt, options); - deviceClient.SetConnectionStatusChangesHandler((status, reason) => - { - s_logger.LogDebug($"Connection status change registered - status={status}, reason={reason}."); - }); - - return deviceClient; - } - - // Initialize the device client instance using symmetric key based authentication, over Mqtt protocol (TCP, with fallback over Websocket) - // and setting the ModelId into ClientOptions. This method also sets a connection status change callback, that will get triggered any time the device's connection status changes. - private static DeviceClient InitializeDeviceClient(string hostname, IAuthenticationMethod authenticationMethod) - { - var options = new ClientOptions - { - ModelId = ModelId, - }; - - DeviceClient deviceClient = DeviceClient.Create(hostname, authenticationMethod, TransportType.Mqtt, options); - deviceClient.SetConnectionStatusChangesHandler((status, reason) => - { - s_logger.LogDebug($"Connection status change registered - status={status}, reason={reason}."); - }); - - return deviceClient; - } - } -} diff --git a/iothub/device/samples/convention-based-samples/TemperatureController/Properties/launchSettings.template.json b/iothub/device/samples/convention-based-samples/TemperatureController/Properties/launchSettings.template.json deleted file mode 100644 index 6ae8d6e736..0000000000 --- a/iothub/device/samples/convention-based-samples/TemperatureController/Properties/launchSettings.template.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "profiles": { - "Hub": { - "commandName": "Project", - "environmentVariables": { - "IOTHUB_DEVICE_SECURITY_TYPE": "connectionString", - "IOTHUB_DEVICE_CONNECTION_STRING": "" - } - }, - "DPS": { - "commandName": "Project", - "environmentVariables": { - "IOTHUB_DEVICE_SECURITY_TYPE": "dps", - "IOTHUB_DEVICE_DPS_ID_SCOPE": "", - "IOTHUB_DEVICE_DPS_DEVICE_ID": "", - "IOTHUB_DEVICE_DPS_DEVICE_KEY": "", - "IOTHUB_DEVICE_DPS_ENDPOINT": "global.azure-devices-provisioning.net" - }, - "sqlDebugging": false - } - } -} \ No newline at end of file diff --git a/iothub/device/samples/convention-based-samples/TemperatureController/SystemTextJsonPayloadConvention.cs b/iothub/device/samples/convention-based-samples/TemperatureController/SystemTextJsonPayloadConvention.cs deleted file mode 100644 index 02cced0002..0000000000 --- a/iothub/device/samples/convention-based-samples/TemperatureController/SystemTextJsonPayloadConvention.cs +++ /dev/null @@ -1,19 +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 Microsoft.Azure.Devices.Shared; - -namespace Microsoft.Azure.Devices.Client.Samples -{ - /// - /// A that uses . - /// - public class SystemTextJsonPayloadConvention : PayloadConvention - { - public static readonly SystemTextJsonPayloadConvention Instance = new SystemTextJsonPayloadConvention(); - - public override PayloadSerializer PayloadSerializer { get; } = SystemTextJsonPayloadSerializer.Instance; - - public override PayloadEncoder PayloadEncoder { get; } = Utf8PayloadEncoder.Instance; - } -} diff --git a/iothub/device/samples/convention-based-samples/TemperatureController/SystemTextJsonPayloadSerializer.cs b/iothub/device/samples/convention-based-samples/TemperatureController/SystemTextJsonPayloadSerializer.cs deleted file mode 100644 index c3cad86935..0000000000 --- a/iothub/device/samples/convention-based-samples/TemperatureController/SystemTextJsonPayloadSerializer.cs +++ /dev/null @@ -1,67 +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.Text.Json; -using Microsoft.Azure.Devices.Shared; - -namespace Microsoft.Azure.Devices.Client.Samples -{ - /// - /// A implementation. - /// - public class SystemTextJsonPayloadSerializer : PayloadSerializer - { - /// - /// The Content Type string. - /// - internal const string ApplicationJson = "application/json"; - - /// - /// The default instance of this class. - /// - public static readonly SystemTextJsonPayloadSerializer Instance = new SystemTextJsonPayloadSerializer(); - - /// - public override string ContentType => ApplicationJson; - - /// - public override string SerializeToString(object objectToSerialize) - { - return JsonSerializer.Serialize(objectToSerialize); - } - - /// - public override T DeserializeToType(string stringToDeserialize) - { - return JsonSerializer.Deserialize(stringToDeserialize); - } - - /// - public override T ConvertFromObject(object objectToConvert) - { - return DeserializeToType(SerializeToString(objectToConvert)); - } - - /// - public override bool TryGetNestedObjectValue(object nestedObject, string propertyName, out T outValue) - { - outValue = default; - if (nestedObject == null || string.IsNullOrEmpty(propertyName)) - { - return false; - } - if (((JsonElement)nestedObject).TryGetProperty(propertyName, out JsonElement element)) - { - outValue = DeserializeToType(element.GetRawText()); - return true; - } - return false; - } - - /// - public override IWritablePropertyResponse CreateWritablePropertyResponse(object value, int statusCode, long version, string description = null) - { - return new SystemTextJsonWritablePropertyResponse(value, statusCode, version, description); - } - } -} diff --git a/iothub/device/samples/convention-based-samples/TemperatureController/SystemTextJsonWritablePropertyResponse.cs b/iothub/device/samples/convention-based-samples/TemperatureController/SystemTextJsonWritablePropertyResponse.cs deleted file mode 100644 index 97a8a84d64..0000000000 --- a/iothub/device/samples/convention-based-samples/TemperatureController/SystemTextJsonWritablePropertyResponse.cs +++ /dev/null @@ -1,57 +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.Text.Json.Serialization; -using Microsoft.Azure.Devices.Shared; - -namespace Microsoft.Azure.Devices.Client.Samples -{ - /// - /// An optional, helper class for constructing a writable property response. - /// - /// - /// This helper class will only work with . - /// It uses based to define the JSON property names. - /// - public sealed class SystemTextJsonWritablePropertyResponse : IWritablePropertyResponse - { - /// - /// Convenience constructor for specifying the properties. - /// - /// The unserialized property value. - /// The acknowledgment code, usually an HTTP Status Code e.g. 200, 400. - /// The acknowledgment version, as supplied in the property update request. - /// The acknowledgment description, an optional, human-readable message about the result of the property update. - public SystemTextJsonWritablePropertyResponse(object value, int ackCode, long ackVersion, string ackDescription = default) - { - Value = value; - AckCode = ackCode; - AckVersion = ackVersion; - AckDescription = ackDescription; - } - - /// - /// The unserialized property value. - /// - [JsonPropertyName(ConventionBasedConstants.ValuePropertyName)] - public object Value { get; set; } - - /// - /// The acknowledgment code, usually an HTTP Status Code e.g. 200, 400. - /// - [JsonPropertyName(ConventionBasedConstants.AckCodePropertyName)] - public int AckCode { get; set; } - - /// - /// The acknowledgment version, as supplied in the property update request. - /// - [JsonPropertyName(ConventionBasedConstants.AckVersionPropertyName)] - public long AckVersion { get; set; } - - /// - /// The acknowledgment description, an optional, human-readable message about the result of the property update. - /// - [JsonPropertyName(ConventionBasedConstants.AckDescriptionPropertyName)] - public string AckDescription { get; set; } - } -} diff --git a/iothub/device/samples/convention-based-samples/TemperatureController/TemperatureController.csproj b/iothub/device/samples/convention-based-samples/TemperatureController/TemperatureController.csproj deleted file mode 100644 index d0fbcbc34a..0000000000 --- a/iothub/device/samples/convention-based-samples/TemperatureController/TemperatureController.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - Exe - netcoreapp3.1 - $(MSBuildProjectDirectory)\..\..\..\..\.. - - - - - - - - - - - - - diff --git a/iothub/device/samples/convention-based-samples/TemperatureController/TemperatureControllerSample.cs b/iothub/device/samples/convention-based-samples/TemperatureController/TemperatureControllerSample.cs deleted file mode 100644 index 041adeffb3..0000000000 --- a/iothub/device/samples/convention-based-samples/TemperatureController/TemperatureControllerSample.cs +++ /dev/null @@ -1,412 +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.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Azure.Devices.Shared; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; - -namespace Microsoft.Azure.Devices.Client.Samples -{ - public class TemperatureControllerSample - { - private const string Thermostat1 = "thermostat1"; - private const string Thermostat2 = "thermostat2"; - - private static readonly Random s_random = new Random(); - private static readonly TimeSpan s_sleepDuration = TimeSpan.FromSeconds(5); - - private readonly DeviceClient _deviceClient; - private readonly ILogger _logger; - - // Dictionary to hold the temperature updates sent over each "Thermostat" component. - // NOTE: Memory constrained devices should leverage storage capabilities of an external service to store this - // information and perform computation. - // See https://docs.microsoft.com/en-us/azure/event-grid/compare-messaging-services for more details. - private readonly Dictionary> _temperatureReadingsDateTimeOffset = - new Dictionary>(); - - // Dictionary to hold the current temperature for each "Thermostat" component. - private readonly Dictionary _temperature = new Dictionary(); - - // Dictionary to hold the max temperature since last reboot, for each "Thermostat" component. - private readonly Dictionary _maxTemp = new Dictionary(); - - public TemperatureControllerSample(DeviceClient deviceClient, ILogger logger) - { - _deviceClient = deviceClient ?? throw new ArgumentNullException($"{nameof(deviceClient)} cannot be null."); - _logger = logger ?? LoggerFactory.Create(builer => builer.AddConsole()).CreateLogger(); - } - - public async Task PerformOperationsAsync(CancellationToken cancellationToken) - { - // Set handler to receive and respond to writable property update requests. - _logger.LogDebug("Subscribe to writable property updates."); - await _deviceClient.SubscribeToWritablePropertiesEventAsync(HandlePropertyUpdatesAsync, null, cancellationToken); - - // Set handler to receive and respond to commands. - _logger.LogDebug($"Subscribe to commands."); - await _deviceClient.SubscribeToCommandsAsync(HandleCommandsAsync, null, cancellationToken); - - // Report device information on "deviceInformation" component. - // This is a component-level property update call. - await UpdateDeviceInformationPropertyAsync(cancellationToken); - - // Verify if the device has previously reported the current value for property "serialNumber". - // If the expected value has not been previously reported then send device serial number over property update. - // This is a top-level property update call. - await SendDeviceSerialNumberPropertyIfNotCurrentAsync(cancellationToken); - - bool temperatureReset = true; - _maxTemp[Thermostat1] = 0d; - _maxTemp[Thermostat2] = 0d; - - // Periodically send "temperature" over telemetry - on "Thermostat" components. - // Send "maxTempSinceLastReboot" over property update, when a new max temperature is reached - on "Thermostat" components. - while (!cancellationToken.IsCancellationRequested) - { - if (temperatureReset) - { - // Generate a random value between 5.0°C and 45.0°C for the current temperature reading for each "Thermostat" component. - _temperature[Thermostat1] = GenerateTemperatureWithinRange(45, 5); - _temperature[Thermostat2] = GenerateTemperatureWithinRange(45, 5); - } - - // Send temperature updates over telemetry and the value of max temperature since last reboot over property update. - // Both of these are component-level calls. - await SendTemperatureAsync(Thermostat1, cancellationToken); - await SendTemperatureAsync(Thermostat2, cancellationToken); - - // Send working set of device memory over telemetry. - // This is a top-level telemetry call. - await SendDeviceMemoryTelemetryAsync(cancellationToken); - - temperatureReset = _temperature[Thermostat1] == 0 && _temperature[Thermostat2] == 0; - await Task.Delay(s_sleepDuration); - } - } - - // The callback to handle property update requests. - private async Task HandlePropertyUpdatesAsync(ClientPropertyCollection writableProperties, object userContext) - { - foreach (KeyValuePair writableProperty in writableProperties) - { - // The dispatcher key will be either a top-level property name or a component name. - switch (writableProperty.Key) - { - case Thermostat1: - case Thermostat2: - const string targetTemperatureProperty = "targetTemperature"; - if (writableProperties.TryGetValue(writableProperty.Key, targetTemperatureProperty, out double targetTemperatureRequested)) - { - await HandleTargetTemperatureUpdateRequestAsync(writableProperty.Key, targetTemperatureRequested, writableProperties.Version, userContext); - break; - } - else - { - _logger.LogWarning($"Property: Received an unrecognized property update from service for component {writableProperty.Key}:" + - $"\n[ {writableProperty.Value} ]."); - break; - } - - default: - _logger.LogWarning($"Property: Received an unrecognized property update from service:" + - $"\n[ {writableProperty.Key}: {writableProperty.Value} ]."); - break; - } - } - } - - // The callback to handle target temperature property update requests for a component. - private async Task HandleTargetTemperatureUpdateRequestAsync(string componentName, double targetTemperature, long version, object userContext) - { - const string targetTemperatureProperty = "targetTemperature"; - _logger.LogDebug($"Property: Received - component=\"{componentName}\", [ \"{targetTemperatureProperty}\": {targetTemperature}°C ]."); - - _temperature[componentName] = targetTemperature; - IWritablePropertyResponse writableResponse = _deviceClient - .PayloadConvention - .PayloadSerializer - .CreateWritablePropertyResponse(_temperature[componentName], CommonClientResponseCodes.OK, version, "Successfully updated target temperature."); - - var reportedProperty = new ClientPropertyCollection(); - reportedProperty.AddComponentProperty(componentName, targetTemperatureProperty, writableResponse); - - ClientPropertiesUpdateResponse updateResponse = await _deviceClient.UpdateClientPropertiesAsync(reportedProperty); - - _logger.LogDebug($"Property: Update - component=\"{componentName}\", {reportedProperty.GetSerializedString()} is {nameof(CommonClientResponseCodes.OK)} " + - $"with a version of {updateResponse.Version}."); - } - - // The callback to handle command invocation requests. - private Task HandleCommandsAsync(CommandRequest commandRequest, object userContext) - { - // In this approach, we'll first switch through the component name returned and handle each component-level command. - // For the "default" case, we'll first check if the component name is null. - // If null, then this would be a top-level command request, so we'll switch through each top-level command. - // If not null, then this is a component-level command that has not been implemented. - - // Switch through CommandRequest.ComponentName to handle all component-level commands. - switch (commandRequest.ComponentName) - { - case Thermostat1: - case Thermostat2: - // For each component, switch through CommandRequest.CommandName to handle the specific component-level command. - switch (commandRequest.CommandName) - { - case "getMaxMinReport": - return HandleMaxMinReportCommandAsync(commandRequest, userContext); - - default: - _logger.LogWarning($"Received a command request that isn't" + - $" implemented - component name = {commandRequest.ComponentName}, command name = {commandRequest.CommandName}"); - - return Task.FromResult(new CommandResponse(CommonClientResponseCodes.NotFound)); - } - - // For the default case, first check if CommandRequest.ComponentName is null. - default: - // If CommandRequest.ComponentName is null, then this is a top-level command request. - if (commandRequest.ComponentName == null) - { - // Switch through CommandRequest.CommandName to handle all top-level commands. - switch (commandRequest.CommandName) - { - case "reboot": - return HandleRebootCommandAsync(commandRequest, userContext); - - default: - _logger.LogWarning($"Received a command request that isn't" + - $" implemented - command name = {commandRequest.CommandName}"); - - return Task.FromResult(new CommandResponse(CommonClientResponseCodes.NotFound)); - } - } - else - { - _logger.LogWarning($"Received a command request that isn't" + - $" implemented - component name = {commandRequest.ComponentName}, command name = {commandRequest.CommandName}"); - - return Task.FromResult(new CommandResponse(CommonClientResponseCodes.NotFound)); - } - } - } - - // The callback to handle top-level "reboot" command. - // This method will send a temperature update (of 0°C) over telemetry for both associated components. - private async Task HandleRebootCommandAsync(CommandRequest commandRequest, object userContext) - { - try - { - int delay = commandRequest.GetData(); - - _logger.LogDebug($"Command: Received - Rebooting thermostat (resetting temperature reading to 0°C after {delay} seconds)."); - await Task.Delay(delay * 1000); - - _temperature[Thermostat1] = _maxTemp[Thermostat1] = 0; - _temperature[Thermostat2] = _maxTemp[Thermostat2] = 0; - - _temperatureReadingsDateTimeOffset.Clear(); - _logger.LogDebug($"Command: Reboot completed."); - - return new CommandResponse(CommonClientResponseCodes.OK); - } - catch (JsonReaderException ex) - { - _logger.LogDebug($"Command input for {commandRequest.CommandName} is invalid: {ex.Message}."); - return new CommandResponse(CommonClientResponseCodes.BadRequest); - } - } - - // The callback to handle component-level "getMaxMinReport" command. - // This method will returns the max, min and average temperature from the specified time to the current time. - private Task HandleMaxMinReportCommandAsync(CommandRequest commandRequest, object userContext) - { - try - { - DateTimeOffset sinceInUtc = commandRequest.GetData(); - _logger.LogDebug($"Command: Received - Generating max, min and avg temperature report since " + - $"{sinceInUtc.LocalDateTime}."); - - if (_temperatureReadingsDateTimeOffset.ContainsKey(commandRequest.ComponentName)) - { - Dictionary allReadings = _temperatureReadingsDateTimeOffset[commandRequest.ComponentName]; - Dictionary filteredReadings = allReadings.Where(i => i.Key > sinceInUtc) - .ToDictionary(i => i.Key, i => i.Value); - - if (filteredReadings != null && filteredReadings.Any()) - { - var report = new TemperatureReport - { - MaximumTemperature = filteredReadings.Values.Max(), - MinimumTemperature = filteredReadings.Values.Min(), - AverageTemperature = filteredReadings.Values.Average(), - StartTime = filteredReadings.Keys.Min(), - EndTime = filteredReadings.Keys.Max(), - }; - - _logger.LogDebug($"Command: component=\"{commandRequest.ComponentName}\", MaxMinReport since {sinceInUtc.LocalDateTime}:" + - $" maxTemp={report.MaximumTemperature}, minTemp={report.MinimumTemperature}, avgTemp={report.AverageTemperature}, " + - $"startTime={report.StartTime.LocalDateTime}, endTime={report.EndTime.LocalDateTime}"); - - return Task.FromResult(new CommandResponse(report, CommonClientResponseCodes.OK)); - } - - _logger.LogDebug($"Command: component=\"{commandRequest.ComponentName}\"," + - $" no relevant readings found since {sinceInUtc.LocalDateTime}, cannot generate any report."); - - return Task.FromResult(new CommandResponse(CommonClientResponseCodes.NotFound)); - } - - _logger.LogDebug($"Command: component=\"{commandRequest.ComponentName}\", no temperature readings sent yet," + - $" cannot generate any report."); - - return Task.FromResult(new CommandResponse(CommonClientResponseCodes.NotFound)); - } - catch (JsonReaderException ex) - { - _logger.LogError($"Command input for {commandRequest.CommandName} is invalid: {ex.Message}."); - - return Task.FromResult(new CommandResponse(CommonClientResponseCodes.BadRequest)); - } - } - - // Report the property values on "deviceInformation" component. - // This is a component-level property update call. - private async Task UpdateDeviceInformationPropertyAsync(CancellationToken cancellationToken) - { - const string componentName = "deviceInformation"; - var deviceInformationProperties = new Dictionary - { - { "manufacturer", "element15" }, - { "model", "ModelIDxcdvmk" }, - { "swVersion", "1.0.0" }, - { "osName", "Windows 10" }, - { "processorArchitecture", "64-bit" }, - { "processorManufacturer", "Intel" }, - { "totalStorage", 256 }, - { "totalMemory", 1024 }, - }; - var deviceInformation = new ClientPropertyCollection(); - deviceInformation.AddComponentProperties(componentName, deviceInformationProperties); - - ClientPropertiesUpdateResponse updateResponse = await _deviceClient.UpdateClientPropertiesAsync(deviceInformation, cancellationToken); - - _logger.LogDebug($"Property: Update - component = '{componentName}', properties update is complete " + - $"with a version of {updateResponse.Version}."); - } - - // Send working set of device memory over telemetry. - // This is a top-level telemetry call. - private async Task SendDeviceMemoryTelemetryAsync(CancellationToken cancellationToken) - { - const string workingSetName = "workingSet"; - long workingSet = Process.GetCurrentProcess().PrivateMemorySize64 / 1024; - using var telemetryMessage = new TelemetryMessage - { - Telemetry = { [workingSetName] = workingSet }, - }; - - await _deviceClient.SendTelemetryAsync(telemetryMessage, cancellationToken); - - _logger.LogDebug($"Telemetry: Sent - {telemetryMessage.Telemetry.GetSerializedString()} in KB."); - } - - // Verify if the device has previously reported the current value for property "serialNumber". - // If the expected value has not been previously reported then send device serial number over property update. - // This is a top-level property update call. - private async Task SendDeviceSerialNumberPropertyIfNotCurrentAsync(CancellationToken cancellationToken) - { - const string serialNumber = "serialNumber"; - const string currentSerialNumber = "SR-123456"; - - // Verify if the device has previously reported the current value for property "serialNumber". - // If the expected value has not been previously reported then report it. - - // Retrieve the device's properties. - ClientProperties properties = await _deviceClient.GetClientPropertiesAsync(cancellationToken); - - if (!properties.TryGetValue(serialNumber, out string serialNumberReported) - || serialNumberReported != currentSerialNumber) - { - var reportedProperties = new ClientPropertyCollection(); - reportedProperties.AddRootProperty(serialNumber, currentSerialNumber); - - ClientPropertiesUpdateResponse updateResponse = await _deviceClient.UpdateClientPropertiesAsync(reportedProperties, cancellationToken); - - _logger.LogDebug($"Property: Update - {reportedProperties.GetSerializedString()} is complete " + - $"with a version of {updateResponse.Version}."); - } - } - - // Send temperature updates over telemetry. - // This also sends the value of max temperature since last reboot over property update. - private async Task SendTemperatureAsync(string componentName, CancellationToken cancellationToken) - { - await SendTemperatureTelemetryAsync(componentName, cancellationToken); - - double maxTemp = _temperatureReadingsDateTimeOffset[componentName].Values.Max(); - if (maxTemp > _maxTemp[componentName]) - { - _maxTemp[componentName] = maxTemp; - await UpdateMaxTemperatureSinceLastRebootAsync(componentName, cancellationToken); - } - } - - // Send temperature update over telemetry. - // This is a component-level telemetry call. - private async Task SendTemperatureTelemetryAsync(string componentName, CancellationToken cancellationToken) - { - const string telemetryName = "temperature"; - double currentTemperature = _temperature[componentName]; - - using var telemtryMessage = new TelemetryMessage(componentName) - { - Telemetry = { [telemetryName] = currentTemperature }, - }; - - await _deviceClient.SendTelemetryAsync(telemtryMessage, cancellationToken); - - _logger.LogDebug($"Telemetry: Sent - component=\"{componentName}\", {telemtryMessage.Telemetry.GetSerializedString()} in °C."); - - if (_temperatureReadingsDateTimeOffset.ContainsKey(componentName)) - { - _temperatureReadingsDateTimeOffset[componentName].TryAdd(DateTimeOffset.UtcNow, currentTemperature); - } - else - { - _temperatureReadingsDateTimeOffset.TryAdd( - componentName, - new Dictionary - { - { DateTimeOffset.UtcNow, currentTemperature }, - }); - } - } - - // Send temperature over reported property update. - // This is a component-level property update. - private async Task UpdateMaxTemperatureSinceLastRebootAsync(string componentName, CancellationToken cancellationToken) - { - const string propertyName = "maxTempSinceLastReboot"; - double maxTemp = _maxTemp[componentName]; - var reportedProperties = new ClientPropertyCollection(); - reportedProperties.AddComponentProperty(componentName, propertyName, maxTemp); - - ClientPropertiesUpdateResponse updateResponse = await _deviceClient.UpdateClientPropertiesAsync(reportedProperties, cancellationToken); - - _logger.LogDebug($"Property: Update - component=\"{componentName}\", {reportedProperties.GetSerializedString()}" + - $" in °C is complete with a version of {updateResponse.Version}."); - } - - private static double GenerateTemperatureWithinRange(int max = 50, int min = 0) - { - return Math.Round(s_random.NextDouble() * (max - min) + min, 1); - } - } -} diff --git a/iothub/device/samples/convention-based-samples/TemperatureController/TemperatureReport.cs b/iothub/device/samples/convention-based-samples/TemperatureController/TemperatureReport.cs deleted file mode 100644 index f7b25e9ec7..0000000000 --- a/iothub/device/samples/convention-based-samples/TemperatureController/TemperatureReport.cs +++ /dev/null @@ -1,26 +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.Text.Json.Serialization; - -namespace Microsoft.Azure.Devices.Client.Samples -{ - public class TemperatureReport - { - [JsonPropertyName("maxTemp")] - public double MaximumTemperature { get; set; } - - [JsonPropertyName("minTemp")] - public double MinimumTemperature { get; set; } - - [JsonPropertyName("avgTemp")] - public double AverageTemperature { get; set; } - - [JsonPropertyName("startTime")] - public DateTimeOffset StartTime { get; set; } - - [JsonPropertyName("endTime")] - public DateTimeOffset EndTime { get; set; } - } -} diff --git a/iothub/device/samples/convention-based-samples/Thermostat/Models/Thermostat.json b/iothub/device/samples/convention-based-samples/Thermostat/Models/Thermostat.json deleted file mode 100644 index 46b21f85cb..0000000000 --- a/iothub/device/samples/convention-based-samples/Thermostat/Models/Thermostat.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "@context": "dtmi:dtdl:context;2", - "@id": "dtmi:com:example:Thermostat;1", - "@type": "Interface", - "displayName": "Thermostat", - "description": "Reports current temperature and provides desired temperature control.", - "contents": [ - { - "@type": [ - "Telemetry", - "Temperature" - ], - "name": "temperature", - "displayName" : "Temperature", - "description" : "Temperature in degrees Celsius.", - "schema": "double", - "unit": "degreeCelsius" - }, - { - "@type": [ - "Property", - "Temperature" - ], - "name": "targetTemperature", - "schema": "double", - "displayName": "Target Temperature", - "description": "Allows to remotely specify the desired target temperature.", - "unit" : "degreeCelsius", - "writable": true - }, - { - "@type": [ - "Property", - "Temperature" - ], - "name": "maxTempSinceLastReboot", - "schema": "double", - "unit" : "degreeCelsius", - "displayName": "Max temperature since last reboot.", - "description": "Returns the max temperature since last device reboot." - }, - { - "@type": "Command", - "name": "getMaxMinReport", - "displayName": "Get Max-Min report.", - "description": "This command returns the max, min and average temperature from the specified time to the current time.", - "request": { - "name": "since", - "displayName": "Since", - "description": "Period to return the max-min report.", - "schema": "dateTime" - }, - "response": { - "name" : "tempReport", - "displayName": "Temperature Report", - "schema": { - "@type": "Object", - "fields": [ - { - "name": "maxTemp", - "displayName": "Max temperature", - "schema": "double" - }, - { - "name": "minTemp", - "displayName": "Min temperature", - "schema": "double" - }, - { - "name" : "avgTemp", - "displayName": "Average Temperature", - "schema": "double" - }, - { - "name" : "startTime", - "displayName": "Start Time", - "schema": "dateTime" - }, - { - "name" : "endTime", - "displayName": "End Time", - "schema": "dateTime" - } - ] - } - } - } - ] -} diff --git a/iothub/device/samples/convention-based-samples/Thermostat/Parameter.cs b/iothub/device/samples/convention-based-samples/Thermostat/Parameter.cs deleted file mode 100644 index 9f8f5b99ff..0000000000 --- a/iothub/device/samples/convention-based-samples/Thermostat/Parameter.cs +++ /dev/null @@ -1,86 +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 CommandLine; -using Microsoft.Extensions.Logging; -using System; - -namespace Microsoft.Azure.Devices.Client.Samples -{ - /// - /// Parameters for the application supplied via command line arguments. - /// If the parameter is not supplied via command line args, it will look for it in environment variables. - /// - internal class Parameters - { - [Option( - "DeviceSecurityType", - HelpText = "(Required) The flow that will be used for connecting the device for the sample. Possible case-insensitive values include: dps, connectionString." + - "\nDefaults to environment variable \"IOTHUB_DEVICE_SECURITY_TYPE\".")] - public string DeviceSecurityType { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_SECURITY_TYPE"); - - [Option( - 'p', - "PrimaryConnectionString", - HelpText = "(Required if DeviceSecurityType is \"connectionString\"). \nThe primary connection string for the device to simulate." + - "\nDefaults to environment variable \"IOTHUB_DEVICE_CONNECTION_STRING\".")] - public string PrimaryConnectionString { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_CONNECTION_STRING"); - - [Option( - 'e', - "DpsEndpoint", - HelpText = "(Required if DeviceSecurityType is \"dps\"). \nThe DPS endpoint to use during device provisioning." + - "\nDefaults to environment variable \"IOTHUB_DEVICE_DPS_ENDPOINT\".")] - public string DpsEndpoint { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_DPS_ENDPOINT"); - - [Option( - 'i', - "DpsIdScope", - HelpText = "(Required if DeviceSecurityType is \"dps\"). \nThe DPS ID Scope to use during device provisioning." + - "\nDefaults to environment variable \"IOTHUB_DEVICE_DPS_ID_SCOPE\".")] - public string DpsIdScope { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_DPS_ID_SCOPE"); - - [Option( - 'd', - "DeviceId", - HelpText = "(Required if DeviceSecurityType is \"dps\"). \nThe device registration Id to use during device provisioning." + - "\nDefaults to environment variable \"IOTHUB_DEVICE_DPS_DEVICE_ID\".")] - public string DeviceId { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_DPS_DEVICE_ID"); - - [Option( - 'k', - "DeviceSymmetricKey", - HelpText = "(Required if DeviceSecurityType is \"dps\"). \nThe device symmetric key to use during device provisioning." + - "\nDefaults to environment variable \"IOTHUB_DEVICE_DPS_DEVICE_KEY\".")] - public string DeviceSymmetricKey { get; set; } = Environment.GetEnvironmentVariable("IOTHUB_DEVICE_DPS_DEVICE_KEY"); - - [Option( - 'r', - "Application running time (in seconds)", - Required = false, - HelpText = "The running time for this console application. Leave it unassigned to run the application until it is explicitly canceled using Control+C.")] - public double? ApplicationRunningTime { get; set; } - - public bool Validate(ILogger logger) - { - if (string.IsNullOrWhiteSpace(DeviceSecurityType)) - { - logger.LogWarning("Device provisioning type not set, please set the environment variable \"IOTHUB_DEVICE_SECURITY_TYPE\"" + - "or pass in \"-s | --DeviceSecurityType\" through command line. \nWill default to using \"dps\" flow."); - - DeviceSecurityType = "dps"; - } - - return (DeviceSecurityType.ToLowerInvariant()) switch - { - "dps" => !string.IsNullOrWhiteSpace(DpsEndpoint) - && !string.IsNullOrWhiteSpace(DpsIdScope) - && !string.IsNullOrWhiteSpace(DeviceId) - && !string.IsNullOrWhiteSpace(DeviceSymmetricKey), - "connectionstring" => !string.IsNullOrWhiteSpace(PrimaryConnectionString), - _ => throw new ArgumentException($"Unrecognized value for device provisioning received: {DeviceSecurityType}." + - $" It should be either \"dps\" or \"connectionString\" (case-insensitive)."), - }; - } - } -} diff --git a/iothub/device/samples/convention-based-samples/Thermostat/Program.cs b/iothub/device/samples/convention-based-samples/Thermostat/Program.cs deleted file mode 100644 index 69dff54071..0000000000 --- a/iothub/device/samples/convention-based-samples/Thermostat/Program.cs +++ /dev/null @@ -1,162 +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 CommandLine; -using Microsoft.Azure.Devices.Provisioning.Client; -using Microsoft.Azure.Devices.Provisioning.Client.Transport; -using Microsoft.Azure.Devices.Shared; -using Microsoft.Extensions.Logging; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Microsoft.Azure.Devices.Client.Samples -{ - public class Program - { - // DTDL interface used: https://github.com/Azure/iot-plugandplay-models/blob/main/dtmi/com/example/thermostat-1.json - private const string ModelId = "dtmi:com:example:Thermostat;1"; - - private static ILogger s_logger; - - public static async Task Main(string[] args) - { - // Parse application parameters - Parameters parameters = null; - ParserResult result = Parser.Default.ParseArguments(args) - .WithParsed(parsedParams => - { - parameters = parsedParams; - }) - .WithNotParsed(errors => - { - Environment.Exit(1); - }); - - s_logger = InitializeConsoleDebugLogger(); - if (!parameters.Validate(s_logger)) - { - throw new ArgumentException("Required parameters are not set. Please recheck required variables by using \"--help\""); - } - - var runningTime = parameters.ApplicationRunningTime != null - ? TimeSpan.FromSeconds((double)parameters.ApplicationRunningTime) - : Timeout.InfiniteTimeSpan; - - s_logger.LogInformation("Press Control+C to quit the sample."); - using var cts = new CancellationTokenSource(runningTime); - Console.CancelKeyPress += (sender, eventArgs) => - { - eventArgs.Cancel = true; - cts.Cancel(); - s_logger.LogInformation("Sample execution cancellation requested; will exit."); - }; - - s_logger.LogDebug($"Set up the device client."); - using DeviceClient deviceClient = await SetupDeviceClientAsync(parameters, cts.Token); - var sample = new ThermostatSample(deviceClient, s_logger); - await sample.PerformOperationsAsync(cts.Token); - - // PerformOperationsAsync is designed to run until cancellation has been explicitly requested, either through - // cancellation token expiration or by Console.CancelKeyPress. - // As a result, by the time the control reaches the call to close the device client, the cancellation token source would - // have already had cancellation requested. - // Hence, if you want to pass a cancellation token to any subsequent calls, a new token needs to be generated. - // For device client APIs, you can also call them without a cancellation token, which will set a default - // cancellation timeout of 4 minutes: https://github.com/Azure/azure-iot-sdk-csharp/blob/64f6e9f24371bc40ab3ec7a8b8accbfb537f0fe1/iothub/device/src/InternalClient.cs#L1922 - await deviceClient.CloseAsync(); - } - - private static ILogger InitializeConsoleDebugLogger() - { - ILoggerFactory loggerFactory = LoggerFactory.Create(builder => - { - builder - .AddFilter(level => level >= LogLevel.Debug) - .AddConsole(options => - { - options.TimestampFormat = "[MM/dd/yyyy HH:mm:ss]"; - }); - }); - - return loggerFactory.CreateLogger(); - } - - private static async Task SetupDeviceClientAsync(Parameters parameters, CancellationToken cancellationToken) - { - DeviceClient deviceClient; - switch (parameters.DeviceSecurityType.ToLowerInvariant()) - { - case "dps": - s_logger.LogDebug($"Initializing via DPS"); - DeviceRegistrationResult dpsRegistrationResult = await ProvisionDeviceAsync(parameters, cancellationToken); - var authMethod = new DeviceAuthenticationWithRegistrySymmetricKey(dpsRegistrationResult.DeviceId, parameters.DeviceSymmetricKey); - deviceClient = InitializeDeviceClient(dpsRegistrationResult.AssignedHub, authMethod); - break; - - case "connectionstring": - s_logger.LogDebug($"Initializing via IoT Hub connection string"); - deviceClient = InitializeDeviceClient(parameters.PrimaryConnectionString); - break; - - default: - throw new ArgumentException($"Unrecognized value for device provisioning received: {parameters.DeviceSecurityType}." + - $" It should be either \"dps\" or \"connectionString\" (case-insensitive)."); - } - - return deviceClient; - } - - // Provision a device via DPS, by sending the PnP model Id as DPS payload. - private static async Task ProvisionDeviceAsync(Parameters parameters, CancellationToken cancellationToken) - { - SecurityProvider symmetricKeyProvider = new SecurityProviderSymmetricKey(parameters.DeviceId, parameters.DeviceSymmetricKey, null); - ProvisioningTransportHandler mqttTransportHandler = new ProvisioningTransportHandlerMqtt(); - ProvisioningDeviceClient pdc = ProvisioningDeviceClient.Create(parameters.DpsEndpoint, parameters.DpsIdScope, - symmetricKeyProvider, mqttTransportHandler); - - var pnpPayload = new ProvisioningRegistrationAdditionalData - { - JsonData = $"{{ \"modelId\": \"{ModelId}\" }}", - }; - return await pdc.RegisterAsync(pnpPayload, cancellationToken); - } - - // Initialize the device client instance using connection string based authentication, over Mqtt protocol (TCP, with fallback over Websocket) - // and setting the ModelId into ClientOptions. - // This method also sets a connection status change callback, that will get triggered any time the device's connection status changes. - private static DeviceClient InitializeDeviceClient(string deviceConnectionString) - { - var options = new ClientOptions - { - ModelId = ModelId, - }; - - DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(deviceConnectionString, TransportType.Mqtt, options); - deviceClient.SetConnectionStatusChangesHandler((status, reason) => - { - s_logger.LogDebug($"Connection status change registered - status={status}, reason={reason}."); - }); - - return deviceClient; - } - - // Initialize the device client instance using symmetric key based authentication, over Mqtt protocol (TCP, with fallback over Websocket) and setting the ModelId into ClientOptions. - // This method also sets a connection status change callback, that will get triggered any time the device's connection status changes. - private static DeviceClient InitializeDeviceClient(string hostname, IAuthenticationMethod authenticationMethod) - { - var options = new ClientOptions - { - ModelId = ModelId, - }; - - DeviceClient deviceClient = DeviceClient.Create(hostname, authenticationMethod, TransportType.Mqtt, options); - deviceClient.SetConnectionStatusChangesHandler((status, reason) => - { - s_logger.LogDebug($"Connection status change registered - status={status}, reason={reason}."); - }); - - return deviceClient; - } - } -} diff --git a/iothub/device/samples/convention-based-samples/Thermostat/Properties/launchSettings.template.json b/iothub/device/samples/convention-based-samples/Thermostat/Properties/launchSettings.template.json deleted file mode 100644 index 6ae8d6e736..0000000000 --- a/iothub/device/samples/convention-based-samples/Thermostat/Properties/launchSettings.template.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "profiles": { - "Hub": { - "commandName": "Project", - "environmentVariables": { - "IOTHUB_DEVICE_SECURITY_TYPE": "connectionString", - "IOTHUB_DEVICE_CONNECTION_STRING": "" - } - }, - "DPS": { - "commandName": "Project", - "environmentVariables": { - "IOTHUB_DEVICE_SECURITY_TYPE": "dps", - "IOTHUB_DEVICE_DPS_ID_SCOPE": "", - "IOTHUB_DEVICE_DPS_DEVICE_ID": "", - "IOTHUB_DEVICE_DPS_DEVICE_KEY": "", - "IOTHUB_DEVICE_DPS_ENDPOINT": "global.azure-devices-provisioning.net" - }, - "sqlDebugging": false - } - } -} \ No newline at end of file diff --git a/iothub/device/samples/convention-based-samples/Thermostat/TemperatureReport.cs b/iothub/device/samples/convention-based-samples/Thermostat/TemperatureReport.cs deleted file mode 100644 index 44996a7961..0000000000 --- a/iothub/device/samples/convention-based-samples/Thermostat/TemperatureReport.cs +++ /dev/null @@ -1,26 +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 Newtonsoft.Json; - -namespace Microsoft.Azure.Devices.Client.Samples -{ - public class TemperatureReport - { - [JsonProperty("maxTemp")] - public double MaximumTemperature { get; set; } - - [JsonProperty("minTemp")] - public double MinimumTemperature { get; set; } - - [JsonProperty("avgTemp")] - public double AverageTemperature { get; set; } - - [JsonProperty("startTime")] - public DateTimeOffset StartTime { get; set; } - - [JsonProperty("endTime")] - public DateTimeOffset EndTime { get; set; } - } -} diff --git a/iothub/device/samples/convention-based-samples/Thermostat/Thermostat.csproj b/iothub/device/samples/convention-based-samples/Thermostat/Thermostat.csproj deleted file mode 100644 index d0fbcbc34a..0000000000 --- a/iothub/device/samples/convention-based-samples/Thermostat/Thermostat.csproj +++ /dev/null @@ -1,19 +0,0 @@ - - - - Exe - netcoreapp3.1 - $(MSBuildProjectDirectory)\..\..\..\..\.. - - - - - - - - - - - - - diff --git a/iothub/device/samples/convention-based-samples/Thermostat/ThermostatSample.cs b/iothub/device/samples/convention-based-samples/Thermostat/ThermostatSample.cs deleted file mode 100644 index d4977479d0..0000000000 --- a/iothub/device/samples/convention-based-samples/Thermostat/ThermostatSample.cs +++ /dev/null @@ -1,205 +0,0 @@ -// 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.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Microsoft.Azure.Devices.Shared; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; - -namespace Microsoft.Azure.Devices.Client.Samples -{ - public class ThermostatSample - { - private static readonly Random s_random = new Random(); - private static readonly TimeSpan s_sleepDuration = TimeSpan.FromSeconds(5); - - private double _temperature = 0d; - private double _maxTemp = 0d; - - // Dictionary to hold the temperature updates sent over. - // NOTE: Memory constrained devices should leverage storage capabilities of an external service to store this information and perform computation. - // See https://docs.microsoft.com/en-us/azure/event-grid/compare-messaging-services for more details. - private readonly Dictionary _temperatureReadingsDateTimeOffset = new Dictionary(); - - private readonly DeviceClient _deviceClient; - private readonly ILogger _logger; - - public ThermostatSample(DeviceClient deviceClient, ILogger logger) - { - _deviceClient = deviceClient ?? throw new ArgumentNullException($"{nameof(deviceClient)} cannot be null."); - _logger = logger ?? LoggerFactory.Create(builer => builer.AddConsole()).CreateLogger(); - } - - public async Task PerformOperationsAsync(CancellationToken cancellationToken) - { - // Set handler to receive and respond to writable property update requests. - _logger.LogDebug($"Subscribe to writable property updates."); - await _deviceClient.SubscribeToWritablePropertiesEventAsync(HandlePropertyUpdatesAsync, null, cancellationToken); - - // Set handler to receive and respond to commands. - _logger.LogDebug($"Subscribe to commands."); - await _deviceClient.SubscribeToCommandsAsync(HandleCommandsAsync, null, cancellationToken); - - bool temperatureReset = true; - - // Periodically send "temperature" over telemetry. - // Send "maxTempSinceLastReboot" over property update, when a new max temperature is reached. - while (!cancellationToken.IsCancellationRequested) - { - if (temperatureReset) - { - // Generate a random value between 5.0°C and 45.0°C for the current temperature reading. - _temperature = GenerateTemperatureWithinRange(45, 5); - temperatureReset = false; - } - - // Send temperature updates over telemetry and the value of max temperature since last reboot over property update. - await SendTemperatureAsync(); - - await Task.Delay(s_sleepDuration); - } - } - - // The callback to handle property update requests. - private async Task HandlePropertyUpdatesAsync(ClientPropertyCollection writableProperties, object userContext) - { - foreach (KeyValuePair writableProperty in writableProperties) - { - switch (writableProperty.Key) - { - case "targetTemperature": - const string targetTemperatureProperty = "targetTemperature"; - double targetTemperatureRequested = Convert.ToDouble(writableProperty.Value); - _logger.LogDebug($"Property: Received - [ \"{targetTemperatureProperty}\": {targetTemperatureRequested}°C ]."); - - _temperature = targetTemperatureRequested; - IWritablePropertyResponse writableResponse = _deviceClient - .PayloadConvention - .PayloadSerializer - .CreateWritablePropertyResponse(_temperature, CommonClientResponseCodes.OK, writableProperties.Version, "Successfully updated target temperature"); - - var reportedProperty = new ClientPropertyCollection(); - reportedProperty.AddRootProperty(targetTemperatureProperty, writableResponse); - - ClientPropertiesUpdateResponse updateResponse = await _deviceClient.UpdateClientPropertiesAsync(reportedProperty); - - _logger.LogDebug($"Property: Update - {reportedProperty.GetSerializedString()} is {nameof(CommonClientResponseCodes.OK)} " + - $"with a version of {updateResponse.Version}."); - - break; - - default: - _logger.LogWarning($"Property: Received an unrecognized property update from service:\n[ {writableProperty.Key}: {writableProperty.Value} ]."); - break; - } - } - } - - // The callback to handle command invocation requests. - private Task HandleCommandsAsync(CommandRequest commandRequest, object userContext) - { - // In this approach, we'll switch through the command name returned and handle each top-level command. - switch (commandRequest.CommandName) - { - case "getMaxMinReport": - try - { - DateTimeOffset sinceInUtc = commandRequest.GetData(); - _logger.LogDebug($"Command: Received - Generating max, min and avg temperature report since " + - $"{sinceInUtc.LocalDateTime}."); - - Dictionary filteredReadings = _temperatureReadingsDateTimeOffset - .Where(i => i.Key > sinceInUtc) - .ToDictionary(i => i.Key, i => i.Value); - - if (filteredReadings != null && filteredReadings.Any()) - { - var report = new TemperatureReport - { - MaximumTemperature = filteredReadings.Values.Max(), - MinimumTemperature = filteredReadings.Values.Min(), - AverageTemperature = filteredReadings.Values.Average(), - StartTime = filteredReadings.Keys.Min(), - EndTime = filteredReadings.Keys.Max(), - }; - - _logger.LogDebug($"Command: MaxMinReport since {sinceInUtc.LocalDateTime}:" + - $" maxTemp={report.MaximumTemperature}, minTemp={report.MinimumTemperature}, avgTemp={report.AverageTemperature}, " + - $"startTime={report.StartTime.LocalDateTime}, endTime={report.EndTime.LocalDateTime}"); - - return Task.FromResult(new CommandResponse(report, CommonClientResponseCodes.OK)); - } - - _logger.LogDebug($"Command: No relevant readings found since {sinceInUtc.LocalDateTime}, cannot generate any report."); - - return Task.FromResult(new CommandResponse(CommonClientResponseCodes.NotFound)); - } - catch (JsonReaderException ex) - { - _logger.LogError($"Command input for {commandRequest.CommandName} is invalid: {ex.Message}."); - - return Task.FromResult(new CommandResponse(CommonClientResponseCodes.BadRequest)); - } - - default: - _logger.LogWarning($"Received a command request that isn't" + - $" implemented - command name = {commandRequest.CommandName}"); - - return Task.FromResult(new CommandResponse(CommonClientResponseCodes.NotFound)); - } - } - - // Send temperature updates over telemetry. - // This also sends the value of max temperature since last reboot over property update. - private async Task SendTemperatureAsync() - { - await SendTemperatureTelemetryAsync(); - - double maxTemp = _temperatureReadingsDateTimeOffset.Values.Max(); - if (maxTemp > _maxTemp) - { - _maxTemp = maxTemp; - await UpdateMaxTemperatureSinceLastRebootPropertyAsync(); - } - } - - // Send temperature update over telemetry. - private async Task SendTemperatureTelemetryAsync() - { - const string telemetryName = "temperature"; - - using var telemetryMessage = new TelemetryMessage - { - Telemetry = { [telemetryName] = _temperature } - }; - await _deviceClient.SendTelemetryAsync(telemetryMessage); - - _logger.LogDebug($"Telemetry: Sent - {telemetryMessage.Telemetry.GetSerializedString()}."); - _temperatureReadingsDateTimeOffset.Add(DateTimeOffset.Now, _temperature); - } - - // Send temperature over reported property update. - private async Task UpdateMaxTemperatureSinceLastRebootPropertyAsync() - { - const string propertyName = "maxTempSinceLastReboot"; - var reportedProperties = new ClientPropertyCollection(); - reportedProperties.AddRootProperty(propertyName, _maxTemp); - - ClientPropertiesUpdateResponse updateResponse = await _deviceClient.UpdateClientPropertiesAsync(reportedProperties); - - _logger.LogDebug($"Property: Update - {reportedProperties.GetSerializedString()} is {nameof(CommonClientResponseCodes.OK)} " + - $"with a version of {updateResponse.Version}."); - } - - private static double GenerateTemperatureWithinRange(int max = 50, int min = 0) - { - return Math.Round(s_random.NextDouble() * (max - min) + min, 1); - } - } -} diff --git a/iothub/device/samples/convention-based-samples/readme.md b/iothub/device/samples/convention-based-samples/readme.md deleted file mode 100644 index b72498415d..0000000000 --- a/iothub/device/samples/convention-based-samples/readme.md +++ /dev/null @@ -1,577 +0,0 @@ ---- -page_type: sample -description: "A set of samples that show how a device that uses the IoT Plug and Play conventions interacts with either IoT Hub or IoT Central." -languages: -- csharp -products: -- azure -- azure-iot-hub -- azure-iot-central -- azure-iot-pnp -- dotnet -urlFragment: azure-iot-pnp-device-samples-for-csharp-net ---- - -# IoT Plug And Play (PnP) device/module APIs - -Device(s)/module(s) connecting to IoT Hub that announce their DTDL model ID during initialization can now perform convention-based operations. One such convention supported is [IoT Plug and Play][pnp-convention]. - -These devices/modules can now use the native PnP APIs in the Azure IoT device SDKs to directly exchange messages with an IoT Hub, without having to manually format these messages to follow the PnP convention. - -## Table of Contents - -- [Client initialization](#client-initialization) - - [Announce model ID during client initialization (same as in latest `master` release)](#announce-model-ID-during-client-initialization-same-as-in-latest-master-release) - - [Define the serialization and encoding convention that the client follows (newly introduced in `preview`)](#define-the-serialization-and-encoding-convention-that-the-client-follows-newly-introduced-in-preview) -- [Terms used](#terms-used) -- [Comparison of API calls - non-convention-aware APIs (old) vs convention-aware APIs (new):](#comparison-of-api-calls---non-convention-aware-apis-old-vs-convention-aware-apis-new) - - [Telemetry](#telemetry) - - [Commands](#commands) - - [Properties](#properties) -- [IoT Plug And Play device samples](#iot-plug-and-play-device-samples) - -## Client initialization - -### Announce model ID during client initialization (same as in latest [`master`][latest-master-release] release) - -```csharp -var options = new ClientOptions -{ - ModelId = ModelId, -}; - -DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(deviceConnectionString, TransportType.Mqtt, options); -``` - -### Define the serialization and encoding convention that the client follows (newly introduced in [`preview`][latest-preview-release]) - -```csharp -// Specify a custom System.Text.Json serialization and Utf8 encoding based PayloadConvention to be used. -// If not specified, the library defaults to a convention that uses Newtonsoft.Json-based serializer and Utf8-based encoder. -var options = new ClientOptions(SystemTextJsonPayloadConvention.Instance) -{ - ModelId = ModelId, -}; - -DeviceClient deviceClient = DeviceClient.CreateFromConnectionString(deviceConnectionString, TransportType.Mqtt, options); -``` - -## Terms used: -Telemetry, commands, properties and components can all be defined in the contents section of the main interface of a DTDL v2 model. Components enable interfaces to be composed of other interfaces. - -In DTDL v2, a component cannot contain another component. The maximum depth of components is 1. - -- Top-level telemetry/commands/properties - - These refer to the telemetry, commands and properties that are defined directly in the contents section of the main interface of a DTDL v2 model. In case of a model with no components, the main interface refers to the default component. - - When working with this category of telemetry, commands and properties, you do not need to specify any component name. -- Component-level telemetry/commands/properties - - These refer to the telemetry, commands and properties that are defined in the contents section of an interface, which itself is defined as a component within the main interface. - - When working with this category of telemetry, commands and properties, you need to specify the name of the component that these contents belong to. - -## Comparison of API calls - non-convention-aware APIs (old) vs convention-aware APIs (new): - -The following section provides a comparison between the older non-convention-aware APIs (as per latest [`master`][latest-master-release] release) and the newly introduced convention-aware APIs (as per latest [`preview`][latest-preview-release] release). - -## Telemetry - -### Send top-level telemetry: - -#### Using non-convention-aware API (old): - -```csharp -// Send telemetry "temperature". -double temperature = 70.0D; -var telemetry = new Dictionary -{ - ["temperature"] = temperature, -}; - -using var message = new Message(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(telemetry))) -{ - MessageId = s_random.Next().ToString(), - ContentEncoding = "utf-8", - ContentType = "application/json", -}; -await _deviceClient.SendEventAsync(message, cancellationToken); -``` - -#### Using convention-aware API (new): - -```csharp -// Send telemetry "temperature". -double temperature = 70.0D; -using var telemetryMessage = new TelemetryMessage -{ - MessageId = Guid.NewGuid().ToString(), - Telemetry = { ["temperature"] = temperature }, -}; - -await _deviceClient.SendTelemetryAsync(telemetryMessage, cancellationToken); -``` - -### Send component-level telemetry: - -#### Using non-convention-aware API (old): - -```csharp -// Send telemetry "temperature" under component "thermostat1". -double temperature = 70.0D; -var telemetry = new Dictionary() -{ - ["temperature"] = temperature, -}; - -using var message = new Message(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(telemetry))) -{ - MessageId = s_random.Next().ToString(), - ContentEncoding = "utf-8", - ContentType = "application/json", - ComponentName = "thermostat1", -}; -await _deviceClient.SendEventAsync(message, cancellationToken); -``` - -#### Using convention-aware API (new): - -```csharp -// Send telemetry "temperature" under component "thermostat1". -double temperature = 70.0D; -using var telemtryMessage = new TelemetryMessage("thermostat1") -{ - MessageId = Guid.NewGuid().ToString(), - Telemetry = { ["temperature"] = temperature }, -}; - -await _deviceClient.SendTelemetryAsync(telemtryMessage, cancellationToken); -``` - -## Commands - -### Respond to top-level commands: - -#### Using non-convention-aware API (old): - -```csharp -// Subscribe and respond to command "reboot". -await _deviceClient.SetMethodHandlerAsync( - "reboot", - async (methodRequest, userContext) => - { - try - { - int delay = JsonConvert.DeserializeObject(methodRequest.DataAsJson); - await Task.Delay(TimeSpan.FromSeconds(delay)); - - // Application code ... - - return new MethodResponse(CommonClientResponseCodes.OK); - } - catch (JsonReaderException) - { - return new MethodResponse(CommonClientResponseCodes.BadRequest); - } - }, - null, - cancellationToken); -``` - -#### Using convention-aware API (new): - -```csharp -// Subscribe and respond to command "reboot". -await _deviceClient.SubscribeToCommandsAsync( - async (commandRequest, userContext) => - { - // This API does not support setting command-level callbacks. - // For this reason we'll need to inspect the commandRequest.CommandName for the request command and perform the actions accordingly. - // Refer to the ThermostatSample.cs for a complete sample implementation. - - if (commandRequest.CommandName == "reboot") - { - try - { - int delay = commandRequest.GetData(); - await Task.Delay(delay * 1000); - - // Application code ... - - return new CommandResponse(CommonClientResponseCodes.OK); - } - catch (JsonReaderException) - { - return new CommandResponse(CommonClientResponseCodes.BadRequest); - } - } - else - { - return new CommandResponse(CommonClientResponseCodes.NotFound); - } - }, - null, - cancellationToken); -``` - -### Respond to component-level commands: - -#### Using non-convention-aware API (old): - -```csharp -// Subscribe and respond to command "getMaxMinReport" under component "thermostat1". -// The method that the application subscribes to is in the format {componentName}*{commandName}. -await _deviceClient.SetMethodHandlerAsync( - "thermostat1*getMaxMinReport", - (commandRequest, userContext) => - { - try - { - DateTimeOffset sinceInUtc = JsonConvert.DeserializeObject(commandRequest.DataAsJson); - - // Application code ... - Report report = GetMaxMinReport(sinceInUtc); - - return Task.FromResult( - new MethodResponse( - Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(report)), - CommonClientResponseCodes.OK)); - } - catch (JsonReaderException) - { - return Task.FromResult(new MethodResponse(CommonClientResponseCodes.BadRequest)); - } - }, - null, - cancellationToken); -``` - -#### Using convention-aware API (new): - -```csharp -// Subscribe and respond to command "getMaxMinReport" under component "thermostat1". -await _deviceClient.SubscribeToCommandsAsync( - (commandRequest, userContext) => - { - // This API does not support setting command-level callbacks. - // For this reason we'll need to inspect both commandRequest.ComponentName and commandRequest.CommandName, and perform the actions accordingly. - // Refer to the TemperatureControllerSample.cs for a complete sample implementation. - - if (commandRequest.ComponentName == "thermostat1" - && commandRequest.CommandName == "getMaxMinReport") - { - try - { - DateTimeOffset sinceInUtc = commandRequest.GetData(); - - // Application code ... - Report report = GetMaxMinReport(sinceInUtc); - - return Task.FromResult(new CommandResponse(report, CommonClientResponseCodes.OK)); - } - catch (JsonReaderException) - { - return Task.FromResult(new CommandResponse(CommonClientResponseCodes.BadRequest)); - } - } - else - { - return Task.FromResult(new CommandResponse(CommonClientResponseCodes.NotFound)); - } - } -); -``` - -## Properties - -### Retrive top-level client properties: - -#### Using non-convention-aware API (old): - -```csharp -// Retrieve the client's properties. -Twin properties = await _deviceClient.GetTwinAsync(cancellationToken); - -// To fetch the value of client reported property "serialNumber". -bool isSerialNumberReported = properties.Properties.Reported.Contains("serialNumber"); -if (isSerialNumberReported) -{ - string serialNumberReported = properties.Properties.Reported["serialNumber"]; -} - -// To fetch the value of service requested "targetTemperature" value. -bool isTargetTemperatureUpdateRequested = properties.Properties.Desired.Contains("targetTemperature"); -if (isTargetTemperatureUpdateRequested) -{ - double targetTemperatureUpdateRequest = properties.Properties.Desired["targetTemperature"]; -} -``` - -#### Using convention-aware API (new): - -```csharp -// Retrieve the client's properties. - ClientProperties properties = await _deviceClient.GetClientPropertiesAsync(cancellationToken); - -// To fetch the value of client reported property "serialNumber". -bool isSerialNumberReported = properties.TryGetValue("serialNumber", out string serialNumberReported); - - -// To fetch the value of service requested "targetTemperature" value. -bool isTargetTemperatureUpdateRequested = properties.Writable.TryGetValue("targetTemperature", out double targetTemperatureUpdateRequest); -``` - -### Retrive component-level client properties: - -#### Using non-convention-aware API (old): - -```csharp -// Retrieve the client's properties. -Twin properties = await _deviceClient.GetTwinAsync(cancellationToken); - -// To fetch the value of client reported property "serialNumber" under component "thermostat1". -JToken serialNumberJToken = null; -bool isSerialNumberReported = properties.Properties.Reported.Contains("thermostat1") - && ((JObject)properties.Properties.Reported["thermostat1"]).TryGetValue("serialNumber", out serialNumberJToken); - -if (isSerialNumberReported) -{ - string serialNumberReported = serialNumberJToken?.ToObject(); -} - -// To fetch the value of service requested "targetTemperature" value under component "thermostat1". -JToken targetTemperatureUpdateRequestJToken = null; -bool isTargetTemperatureUpdateRequested = properties.Properties.Desired.Contains("thermostat1") - && ((JObject)properties.Properties.Desired["thermostat1"]).TryGetValue("targetTemperature", out targetTemperatureUpdateRequestJToken); - -if (isTargetTemperatureUpdateRequested) -{ - double targetTemperatureUpdateRequest = (double)(targetTemperatureUpdateRequestJToken?.ToObject()); -} -``` - -#### Using convention-aware API (new): - -```csharp -// Retrieve the client's properties. - ClientProperties properties = await _deviceClient.GetClientPropertiesAsync(cancellationToken); - -// To fetch the value of client reported property "serialNumber" under component "thermostat1". -bool isSerialNumberReported = properties.TryGetValue("thermostat1", "serialNumber", out string serialNumberReported); - - -// To fetch the value of service requested "targetTemperature" value under component "thermostat1". -bool isTargetTemperatureUpdateRequested = properties.Writable.TryGetValue("thermostat1", "targetTemperature", out double targetTemperatureUpdateRequest); -``` - -### Update top-level property: - -#### Using non-convention-aware API (old): - -```csharp -// Update the property "serialNumber". -var propertiesToBeUpdated = new TwinCollection -{ - ["serialNumber"] = "SR-1234", -}; -await _deviceClient.UpdateReportedPropertiesAsync(propertiesToBeUpdated, cancellationToken); -``` - -#### Using convention-aware API (new): - -```csharp -// Update the property "serialNumber". -var propertiesToBeUpdated = new ClientPropertyCollection -{ - ["serialNumber"] = "SR-1234", -}; -ClientPropertiesUpdateResponse updateResponse = await _deviceClient - .UpdateClientPropertiesAsync(propertiesToBeUpdated, cancellationToken); -long updatedVersion = updateResponse.Version; -``` - -### Update component-level properties: - -#### Using non-convention-aware API (old): - -```csharp -// Update the property "serialNumber" under component "thermostat1". -// When calling the UpdateReportedPropertiesAsync API the component-level property update requests must -// include the {"__t": "c"} marker to indicate that the element refers to a component. -var thermostatProperties = new TwinCollection -{ - ["__t"] = "c", - ["serialNumber"] = "SR-1234", -}; -var propertiesToBeUpdated = new TwinCollection -{ - ["thermostat1"] = thermostatProperties -}; -await _deviceClient.UpdateReportedPropertiesAsync(propertiesToBeUpdated, cancellationToken); -``` - -#### Using convention-aware API (new): - -```csharp -// Update the property "serialNumber" under component "thermostat1". -var propertiesToBeUpdated = new ClientPropertyCollection(); -propertiesToBeUpdated.AddComponentProperty("thermostat1", "serialNumber", "SR-1234"); - -ClientPropertiesUpdateResponse updateResponse = await _deviceClient - .UpdateClientPropertiesAsync(propertiesToBeUpdated, cancellationToken); -long updatedVersion = updateResponse.Version; -``` - -### Respond to top-level property update requests: - -#### Using non-convention-aware API (old): - -```csharp -// Subscribe and respond to event for writable property "targetTemperature". -// This writable property update response should follow the format specified here: https://docs.microsoft.com/azure/iot-pnp/concepts-convention#writable-properties. -await _deviceClient.SetDesiredPropertyUpdateCallbackAsync( - async (desired, userContext) => - { - if (desired.Contains("targetTemperature")) - { - double targetTemperature = desired["targetTemperature"]; - - var targetTemperatureUpdateResponse = new TwinCollection - { - ["value"] = targetTemperature, - ["ac"] = CommonClientResponseCodes.OK, - ["av"] = desired.Version, - ["ad"] = "The operation completed successfully." - }; - var propertiesToBeUpdated = new TwinCollection() - { - ["targetTemperature"] = targetTemperatureUpdateResponse, - }; - - await _deviceClient.UpdateReportedPropertiesAsync(propertiesToBeUpdated, cancellationToken); - } - }, - null, - cancellationToken); -``` - -#### Using convention-aware API (new): - -```csharp -// Subscribe and respond to event for writable property "targetTemperature". -// This writable property update response should follow the format specified here: https://docs.microsoft.com/azure/iot-pnp/concepts-convention#writable-properties. -await _deviceClient.SubscribeToWritablePropertiesEventAsync( - async (writableProperties, userContext) => - { - if (writableProperties.TryGetValue("targetTemperature", out double targetTemperature)) - { - IWritablePropertyResponse writableResponse = _deviceClient - .PayloadConvention - .PayloadSerializer - .CreateWritablePropertyResponse(targetTemperature, CommonClientResponseCodes.OK, writableProperties.Version, "The operation completed successfully."); - - var propertiesToBeUpdated = new ClientPropertyCollection(); - propertiesToBeUpdated.AddRootProperty("targetTemperature", writableResponse); - - ClientPropertiesUpdateResponse updateResponse = await _deviceClient.UpdateClientPropertiesAsync(propertiesToBeUpdated, cancellationToken); - } - }, - null, - cancellationToken); -``` - -### Respond to component-level property update requests: - -#### Using non-convention-aware API (old): - -```csharp -// Subscribe and respond to event for writable property "targetTemperature" -// under component "thermostat1". -// This writable property update response should follow the format specified here: https://docs.microsoft.com/azure/iot-pnp/concepts-convention#writable-properties. -// When calling the UpdateReportedPropertiesAsync API the component-level property update requests must -// include the {"__t": "c"} marker to indicate that the element refers to a component. -await _deviceClient.SetDesiredPropertyUpdateCallbackAsync( - async (desired, userContext) => - { - if (desired.Contains("thermostat1") - && ((JObject)desired["thermostat1"]) - .TryGetValue("targetTemperature", out JToken targetTemperatureRequested)) - { - double targetTemperature = targetTemperatureRequested - .ToObject(); - - var targetTemperatureUpdateResponse = new TwinCollection - { - ["value"] = targetTemperature, - ["ac"] = CommonClientResponseCodes.OK, - ["av"] = desired.Version, - ["ad"] = "The operation completed successfully." - }; - var thermostatProperties = new TwinCollection() - { - ["__t"] = "c", - ["targetTemperature"] = targetTemperatureUpdateResponse, - }; - var propertiesToBeUpdated = new TwinCollection() - { - ["thermostat1"] = thermostatProperties, - }; - - await _deviceClient.UpdateReportedPropertiesAsync(propertiesToBeUpdated, cancellationToken); - } - }, - null, - cancellationToken); -``` - -#### Using convention-aware API (new): - -```csharp -// Subscribe and respond to event for writable property "targetTemperature" -// under component "thermostat1". -// This writable property update response should follow the format specified here: https://docs.microsoft.com/azure/iot-pnp/concepts-convention#writable-properties. -await _deviceClient.SubscribeToWritablePropertiesEventAsync( - async (writableProperties, userContext) => - { - if (writableProperties.TryGetValue("thermostat1", "targetTemperature", out double targetTemperature)) - { - IWritablePropertyResponse writableResponse = _deviceClient - .PayloadConvention - .PayloadSerializer - .CreateWritablePropertyResponse(targetTemperature, CommonClientResponseCodes.OK, writableProperties.Version, "The operation completed successfully."); - - var propertiesToBeUpdated = new ClientPropertyCollection(); - propertiesToBeUpdated.AddComponentProperty("thermostat1", "targetTemperature", writableResponse); - - ClientPropertiesUpdateResponse updateResponse = await _deviceClient.UpdateClientPropertiesAsync(propertiesToBeUpdated, cancellationToken); - } - }, - null, - cancellationToken); -``` - -# IoT Plug And Play device samples - -These samples demonstrate how a device that follows the [IoT Plug and Play conventions][pnp-convention] interacts with IoT Hub or IoT Central, to: - -- Send telemetry. -- Update client properties and be notified of service requested property update requests. -- Respond to command invocation. - -The samples demonstrate two scenarios: - -- An IoT Plug and Play device that implements the [Thermostat][d-thermostat] model. This model has a single interface (the default component) that defines telemetry, properties and commands. -- An IoT Plug and Play device that implements the [Temperature controller][d-temperature-controller] model. This model defines multiple interfaces: - - The top-level interface defines telemetry, properties and commands. - - The model includes two [Thermostat][thermostat-model] components, and a [device information][d-device-info] component. - -> NOTE: These samples are only meant to demonstrate the usage of Plug and Play APIs. If you are looking for a good device sample to get started with, please see the [device reconnection sample][device-reconnection-sample]. It shows how to connect a device, handle disconnect events, cases to handle when making calls, and when to re-initialize the `DeviceClient`. - -[pnp-convention]: https://docs.microsoft.com/azure/iot-pnp/concepts-convention -[d-thermostat]: ./Thermostat -[d-temperature-controller]: ./TemperatureController -[thermostat-model]: /iot-hub/Samples/device/convention-based-samples/Thermostat/Models/Thermostat.json -[d-device-info]: https://devicemodels.azure.com/dtmi/azure/devicemanagement/deviceinformation-1.json -[thermostat-hub-qs]: https://docs.microsoft.com/azure/iot-pnp/quickstart-connect-device?pivots=programming-language-csharp -[temp-controller-hub-tutorial]: https://docs.microsoft.com/azure/iot-pnp/tutorial-multiple-components?pivots=programming-language-csharp -[temp-controller-central-tutorial]: https://docs.microsoft.com/azure/iot-central/core/tutorial-connect-device?pivots=programming-language-csharp -[device-reconnection-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/master/iot-hub/Samples/device/DeviceReconnectionSample -[latest-master-release]: https://github.com/Azure/azure-iot-sdk-csharp/tree/2021-05-13 -[latest-preview-release]: https://github.com/Azure/azure-iot-sdk-csharp/tree/preview_2021-6-8 diff --git a/iothub/device/samples/readme.md b/iothub/device/samples/readme.md index 3908583189..0fd55b6269 100644 --- a/iothub/device/samples/readme.md +++ b/iothub/device/samples/readme.md @@ -1,5 +1,10 @@ -This folder contains simple samples showing how to use the various features of Microsoft Azure IoT Hub service, from a device running C# code. +This folder contains simple samples showing how to use the various preview features of Microsoft Azure IoT Hub .NET SDK. + +The following features are currently in preview: +- .NET 5.0 support. +- Support for convention-based operations. +- Device Streaming. ### [Device samples][device-samples] @@ -10,10 +15,9 @@ This folder contains simple samples showing how to use the various features of M - [Receive message sample][d-receive-message-sample] - [Twin sample][d-twin-sample] - [File upload sample][d-file-upload-sample] -- [Import/export devices sample][d-import-export-devices-sample] - [Connect with X509 certificate sample][d-x509-cert-sample] -- [Plug and Play device samples][d-pnp-sample] -- [Xamarin sample][d-xamarin-sample] +- [Convention based operations samples][d-convention-based-operations-sample] +- [Device streaming sample][d-device-streaming-sample] ### Module sample @@ -65,18 +69,17 @@ You need to clone the repository or download the sample (the one you want to try dotnet run ``` -[device-samples]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/master/iot-hub/Samples/device -[d-message-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/master/iot-hub/Samples/device/DeviceReconnectionSample -[d-receive-message-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/master/iot-hub/Samples/device/MessageReceiveSample -[d-method-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/master/iot-hub/Samples/device/MethodSample -[d-twin-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/master/iot-hub/Samples/device/TwinSample -[d-file-upload-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/master/iot-hub/Samples/device/FileUploadSample -[d-x509-cert-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/master/iot-hub/Samples/device/X509DeviceCertWithChainSample -[d-import-export-devices-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/master/iot-hub/Samples/device/ImportExportDevicesSample -[d-pnp-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/master/iot-hub/Samples/device/PnpDeviceSamples -[d-xamarin-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/master/iot-hub/Samples/device/XamarinSample - -[m-message-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/master/iot-hub/Samples/module/ModuleSample +[device-samples]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/preview/iot-hub/Samples/device +[d-message-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/preview/iot-hub/Samples/device/DeviceReconnectionSample +[d-receive-message-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/preview/iot-hub/Samples/device/MessageReceiveSample +[d-method-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/preview/iot-hub/Samples/device/MethodSample +[d-twin-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/preview/iot-hub/Samples/device/TwinSample +[d-file-upload-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/preview/iot-hub/Samples/device/FileUploadSample +[d-x509-cert-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/preview/iot-hub/Samples/device/X509DeviceCertWithChainSample +[d-convention-based-operations-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/preview/iot-hub/Samples/device/ConventionBasedOperations +[d-device-streaming-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/preview/iot-hub/Samples/device/DeviceStreamingSample + +[m-message-sample]: https://github.com/Azure-Samples/azure-iot-samples-csharp/tree/preview/iot-hub/Samples/module/ModuleSample [lnk-setup-iot-hub]: https://aka.ms/howtocreateazureiothub [lnk-manage-iot-device]: https://github.com/Azure/azure-iot-device-ecosystem/blob/master/setup_iothub.md#create-new-device-in-the-iot-hub-device-identity-registry \ No newline at end of file diff --git a/iothub/device/src/Transport/AmqpIot/AmqpIotExceptionAdapter.cs b/iothub/device/src/Transport/AmqpIot/AmqpIotExceptionAdapter.cs index fff5e23334..57babf2e13 100644 --- a/iothub/device/src/Transport/AmqpIot/AmqpIotExceptionAdapter.cs +++ b/iothub/device/src/Transport/AmqpIot/AmqpIotExceptionAdapter.cs @@ -15,17 +15,26 @@ internal static Exception ConvertToIotHubException(Exception exception) { return new IotHubCommunicationException(exception.Message, exception); } - else if (exception is UnauthorizedAccessException) + + if (exception is UnauthorizedAccessException) { return new UnauthorizedException(exception.Message, exception); } - else + + if (exception is OperationCanceledException + && exception.InnerException is AmqpException innerAmqpException + && innerAmqpException != null) { - var amqpException = exception as AmqpException; - return amqpException == null - ? exception - : AmqpIotErrorAdapter.ToIotHubClientContract(amqpException); + return AmqpIotErrorAdapter.ToIotHubClientContract(innerAmqpException); } + + if (exception is AmqpException amqpException + && amqpException != null) + { + return AmqpIotErrorAdapter.ToIotHubClientContract(amqpException); + } + + return exception; } internal static Exception ConvertToIotHubException(Exception exception, AmqpObject source) diff --git a/iothub/device/src/Transport/Mqtt/MqttTransportSettings.cs b/iothub/device/src/Transport/Mqtt/MqttTransportSettings.cs index a29e9cc92a..99fe30cbd9 100644 --- a/iothub/device/src/Transport/Mqtt/MqttTransportSettings.cs +++ b/iothub/device/src/Transport/Mqtt/MqttTransportSettings.cs @@ -26,7 +26,10 @@ public class MqttTransportSettings : ITransportSettings private const int DefaultMaxPendingInboundMessages = 50; private const QualityOfService DefaultPublishToServerQoS = QualityOfService.AtLeastOnce; private const QualityOfService DefaultReceivingQoS = QualityOfService.AtLeastOnce; - private static readonly TimeSpan s_defaultConnectArrivalTimeout = TimeSpan.FromSeconds(10); + + // The CONNACK timeout has been chosen to be 60 seconds to be in alignment with the service implemented timeout for processing connection requests. + private static readonly TimeSpan s_defaultConnectArrivalTimeout = TimeSpan.FromSeconds(60); + private static readonly TimeSpan s_defaultDeviceReceiveAckTimeout = TimeSpan.FromSeconds(300); private static readonly TimeSpan s_defaultReceiveTimeout = TimeSpan.FromMinutes(1); @@ -157,8 +160,15 @@ public bool CertificateRevocationCheck /// /// The time to wait for receiving an acknowledgment for a CONNECT packet. - /// The default is 10 seconds. + /// The default is 60 seconds. /// + /// + /// In the event that IoT Hub receives burst traffic, it will implement traffic shaping in order to process the incoming requests. + /// In such cases, during client connection the CONNECT requests can have a delay in being acknowledged and processed by IoT Hub. + /// The ConnectArrivalTimeout governs the duration the client will wait for a CONNACK packet before disconnecting and reopening the connection. + /// To know more about IoT Hub's throttling limits and traffic shaping feature, see + /// . + /// public TimeSpan ConnectArrivalTimeout { get; set; } /// @@ -239,4 +249,4 @@ public TransportType GetTransportType() /// internal string AuthenticationChain { get; set; } } -} \ No newline at end of file +} diff --git a/iothub/device/src/Transport/ProtocolRoutingDelegatingHandler.cs b/iothub/device/src/Transport/ProtocolRoutingDelegatingHandler.cs index d0b624cd8e..20636f8ef3 100644 --- a/iothub/device/src/Transport/ProtocolRoutingDelegatingHandler.cs +++ b/iothub/device/src/Transport/ProtocolRoutingDelegatingHandler.cs @@ -10,8 +10,8 @@ namespace Microsoft.Azure.Devices.Client.Transport { /// - /// Transport handler router. - /// Tries to open the connection in the protocol order it was set. + /// Transport handler router. + /// Tries to open the connection in the protocol order it was set. /// If fails tries to open the next one, etc. /// internal class ProtocolRoutingDelegatingHandler : DefaultDelegatingHandler @@ -22,6 +22,7 @@ internal class ProtocolRoutingDelegatingHandler : DefaultDelegatingHandler /// After we've verified that we could open the transport for any operation, we will stop attempting others in the list. /// private bool _transportSelectionComplete; + private int _nextTransportIndex; private SemaphoreSlim _handlerLock = new SemaphoreSlim(1, 1); diff --git a/iothub/service/src/Device.cs b/iothub/service/src/Device.cs index 610416e563..b5b3c7ccfd 100644 --- a/iothub/service/src/Device.cs +++ b/iothub/service/src/Device.cs @@ -114,7 +114,7 @@ public Device(string id) /// relationship. /// /// - /// For leaf devices, the value to set a parent edge device can be retrieved from the parent edge device's property. + /// For leaf devices, the value to set a parent edge device can be retrieved from the parent edge device's Scope property. /// /// For more information, see . /// diff --git a/iothub/service/src/ExportImportDevice.cs b/iothub/service/src/ExportImportDevice.cs index 8d8ddb7e42..eacfc12b6d 100644 --- a/iothub/service/src/ExportImportDevice.cs +++ b/iothub/service/src/ExportImportDevice.cs @@ -5,13 +5,14 @@ // --------------------------------------------------------------- using System; +using System.Diagnostics.CodeAnalysis; using Microsoft.Azure.Devices.Shared; using Newtonsoft.Json; namespace Microsoft.Azure.Devices { /// - /// Contains device properties specified during export/import operation + /// Contains device properties specified during export/import job operation. /// public sealed class ExportImportDevice { @@ -21,7 +22,7 @@ public sealed class ExportImportDevice /// /// Property container /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "Public property. No behavior changes allowed.")] + [SuppressMessage("Design", "CA1034:Nested types should not be visible", Justification = "Public property. No behavior changes allowed.")] public sealed class PropertyContainer { /// @@ -38,14 +39,14 @@ public sealed class PropertyContainer } /// - /// Create an ExportImportDevice + /// Create an ExportImportDevice. /// public ExportImportDevice() { } /// - /// Create an ExportImportDevice + /// Create an ExportImportDevice. /// /// Device properties /// Identifies the behavior when merging a device to the registry during import actions. @@ -66,13 +67,13 @@ public ExportImportDevice(Device device, ImportMode importmode) } /// - /// Id of the device + /// Id of the device. /// [JsonProperty(PropertyName = "id", Required = Required.Always)] public string Id { get; set; } /// - /// Module Id for the object + /// Module Id for the object. /// [JsonProperty(PropertyName = "moduleId", NullValueHandling = NullValueHandling.Ignore)] public string ModuleId { get; set; } @@ -88,31 +89,31 @@ public string ETag } /// - /// ImportMode of the device + /// Import mode of the device. /// [JsonProperty(PropertyName = "importMode", DefaultValueHandling = DefaultValueHandling.IgnoreAndPopulate)] public ImportMode ImportMode { get; set; } /// - /// Status of the device + /// Status of the device. /// [JsonProperty(PropertyName = "status", Required = Required.Always)] public DeviceStatus Status { get; set; } /// - /// StatusReason of the device + /// Status reason of the device. /// [JsonProperty(PropertyName = "statusReason", NullValueHandling = NullValueHandling.Ignore)] public string StatusReason { get; set; } /// - /// AuthenticationMechanism of the device + /// Authentication mechanism of the device. /// [JsonProperty(PropertyName = "authentication")] public AuthenticationMechanism Authentication { get; set; } /// - /// string representing a Twin ETag for the entity, as per RFC7232. + /// String representing a Twin ETag for the entity, as per RFC7232. /// [JsonProperty(PropertyName = "twinETag", NullValueHandling = NullValueHandling.Ignore)] public string TwinETag @@ -128,17 +129,29 @@ public string TwinETag public TwinCollection Tags { get; set; } /// - /// Desired and Reported property bags + /// Desired and reported property bags /// [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 /// [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. + /// + /// + /// 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 . + /// + [JsonProperty(PropertyName = "deviceScope", NullValueHandling = NullValueHandling.Include)] + public string DeviceScope { get; set; } + private static string SanitizeETag(string eTag) { if (!string.IsNullOrWhiteSpace(eTag)) diff --git a/provisioning/device/src/ProvisioningDeviceClient.cs b/provisioning/device/src/ProvisioningDeviceClient.cs index 3c7fbb3f66..1703ff729f 100644 --- a/provisioning/device/src/ProvisioningDeviceClient.cs +++ b/provisioning/device/src/ProvisioningDeviceClient.cs @@ -3,6 +3,7 @@ using Microsoft.Azure.Devices.Provisioning.Client.Transport; using Microsoft.Azure.Devices.Shared; +using System; using System.Threading; using System.Threading.Tasks; @@ -63,45 +64,79 @@ private ProvisioningDeviceClient( /// /// Registers the current device using the Device Provisioning Service and assigns it to an IoT Hub. /// + /// The maximum amount of time to allow this operation to run for before timing out. + /// + /// Due to the AMQP library used by this library uses not accepting cancellation tokens, this overload and + /// are the only overloads for this method that allow for a specified timeout to be respected in the middle of an AMQP operation such as opening + /// the AMQP connection. MQTT and HTTPS connections do not share that same limitation, though. + /// /// The registration result. - public Task RegisterAsync() + public Task RegisterAsync(TimeSpan timeout) { - return RegisterAsync(CancellationToken.None); + return RegisterAsync(null, timeout); } /// /// Registers the current device using the Device Provisioning Service and assigns it to an IoT Hub. /// - /// The optional additional data. + /// + /// The optional additional data that is passed through to the custom allocation policy webhook if + /// a custom allocation policy webhook is setup for this enrollment. + /// + /// The maximum amount of time to allow this operation to run for before timing out. + /// + /// Due to the AMQP library used by this library uses not accepting cancellation tokens, this overload and + /// are the only overloads for this method that allow for a specified timeout to be respected in the middle of an AMQP operation such as opening + /// the AMQP connection. MQTT and HTTPS connections do not share that same limitation, though. + /// /// The registration result. - public Task RegisterAsync(ProvisioningRegistrationAdditionalData data) + public Task RegisterAsync(ProvisioningRegistrationAdditionalData data, TimeSpan timeout) { - return RegisterAsync(data, CancellationToken.None); + Logging.RegisterAsync(this, _globalDeviceEndpoint, _idScope, _transport, _security); + + var request = new ProvisioningTransportRegisterMessage(_globalDeviceEndpoint, _idScope, _security, data?.JsonData) + { + ProductInfo = ProductInfo, + }; + + return _transport.RegisterAsync(request, timeout); } /// /// Registers the current device using the Device Provisioning Service and assigns it to an IoT Hub. /// /// The cancellation token. + /// + /// Due to the AMQP library used by this library uses not accepting cancellation tokens, the provided cancellation token will only be checked + /// for cancellation in between AMQP operations, and not during. In order to have a timeout for this operation that is checked during AMQP operations + /// (such as opening the connection), you must use instead. MQTT and HTTPS connections do not have the same + /// behavior as AMQP connections in this regard. MQTT and HTTPS connections will check this cancellation token for cancellation during their protocol level operations. + /// /// The registration result. - public Task RegisterAsync(CancellationToken cancellationToken) + public Task RegisterAsync(CancellationToken cancellationToken = default) { Logging.RegisterAsync(this, _globalDeviceEndpoint, _idScope, _transport, _security); - var request = new ProvisioningTransportRegisterMessage(_globalDeviceEndpoint, _idScope, _security) - { - ProductInfo = ProductInfo - }; - return _transport.RegisterAsync(request, cancellationToken); + return RegisterAsync(null, cancellationToken); } /// /// Registers the current device using the Device Provisioning Service and assigns it to an IoT Hub. /// - /// The custom content. + /// + /// The optional additional data that is passed through to the custom allocation policy webhook if + /// a custom allocation policy webhook is setup for this enrollment. + /// /// The cancellation token. + /// + /// Due to the AMQP library used by this library uses not accepting cancellation tokens, the provided cancellation token will only be checked + /// for cancellation in between AMQP operations, and not during. In order to have a timeout for this operation that is checked during AMQP operations + /// (such as opening the connection), you must use this overload instead. + /// MQTT and HTTPS connections do not have the same behavior as AMQP connections in this regard. MQTT and HTTPS connections will check this cancellation + /// token for cancellation during their protocol level operations. + /// /// The registration result. - public Task RegisterAsync(ProvisioningRegistrationAdditionalData data, CancellationToken cancellationToken) + public Task RegisterAsync(ProvisioningRegistrationAdditionalData data, CancellationToken cancellationToken = default) { Logging.RegisterAsync(this, _globalDeviceEndpoint, _idScope, _transport, _security); @@ -109,6 +144,7 @@ public Task RegisterAsync(ProvisioningRegistrationAddi { ProductInfo = ProductInfo, }; + return _transport.RegisterAsync(request, cancellationToken); } } diff --git a/provisioning/device/src/ProvisioningTransportHandler.cs b/provisioning/device/src/ProvisioningTransportHandler.cs index 8208932cad..1ddc085cfb 100644 --- a/provisioning/device/src/ProvisioningTransportHandler.cs +++ b/provisioning/device/src/ProvisioningTransportHandler.cs @@ -85,6 +85,19 @@ public virtual Task RegisterAsync( return _innerHandler.RegisterAsync(message, cancellationToken); } + /// + /// Registers a device described by the message. + /// + /// The provisioning message. + /// The maximum amount of time to allow this operation to run for before timing out. + /// The registration result. + public virtual Task RegisterAsync( + ProvisioningTransportRegisterMessage message, + TimeSpan timeout) + { + return _innerHandler.RegisterAsync(message, timeout); + } + /// /// Releases the unmanaged resources and disposes of the managed resources used by the invoker. /// diff --git a/provisioning/transport/amqp/src/ProvisioningTransportHandlerAmqp.cs b/provisioning/transport/amqp/src/ProvisioningTransportHandlerAmqp.cs index 740d4265a2..675412e509 100644 --- a/provisioning/transport/amqp/src/ProvisioningTransportHandlerAmqp.cs +++ b/provisioning/transport/amqp/src/ProvisioningTransportHandlerAmqp.cs @@ -22,7 +22,9 @@ namespace Microsoft.Azure.Devices.Provisioning.Client.Transport /// public class ProvisioningTransportHandlerAmqp : ProvisioningTransportHandler { - private static readonly TimeSpan s_defaultOperationPoolingInterval = TimeSpan.FromSeconds(2); + // This polling interval is the default time between checking if the device has reached a terminal state in its registration process + // DPS will generally send a retry-after header that overrides this default value though. + private static readonly TimeSpan s_defaultOperationPollingInterval = TimeSpan.FromSeconds(2); private static readonly TimeSpan s_timeoutConstant = TimeSpan.FromMinutes(1); /// @@ -43,6 +45,19 @@ public ProvisioningTransportHandlerAmqp( Proxy = DefaultWebProxySettings.Instance; } + /// + /// Registers a device described by the message. + /// + /// The provisioning message. + /// The maximum amount of time to allow this operation to run for before timing out. + /// The registration result. + public override async Task RegisterAsync( + ProvisioningTransportRegisterMessage message, + TimeSpan timeout) + { + return await RegisterAsync(message, timeout, CancellationToken.None).ConfigureAwait(false); + } + /// /// Registers a device described by the message. /// @@ -52,6 +67,22 @@ public ProvisioningTransportHandlerAmqp( public override async Task RegisterAsync( ProvisioningTransportRegisterMessage message, CancellationToken cancellationToken) + { + return await RegisterAsync(message, s_timeoutConstant, cancellationToken).ConfigureAwait(false); + } + + /// + /// Registers a device described by the message. Because the AMQP library does not accept cancellation tokens, the provided cancellation token + /// will only be checked for cancellation between AMQP operations. The timeout will be respected during the AMQP operations. + /// + /// The provisioning message. + /// The maximum amount of time to allow this operation to run for before timing out. + /// The cancellation token. + /// The registration result. + private async Task RegisterAsync( + ProvisioningTransportRegisterMessage message, + TimeSpan timeout, + CancellationToken cancellationToken) { if (Logging.IsEnabled) { @@ -106,19 +137,21 @@ public override async Task RegisterAsync( string linkEndpoint = $"{message.IdScope}/registrations/{registrationId}"; using AmqpClientConnection connection = authStrategy.CreateConnection(builder.Uri, message.IdScope); - await authStrategy.OpenConnectionAsync(connection, s_timeoutConstant, useWebSocket, Proxy, RemoteCertificateValidationCallback).ConfigureAwait(false); + await authStrategy.OpenConnectionAsync(connection, timeout, useWebSocket, Proxy, RemoteCertificateValidationCallback).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); await CreateLinksAsync( connection, linkEndpoint, - message.ProductInfo).ConfigureAwait(false); + message.ProductInfo, + timeout).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); string correlationId = Guid.NewGuid().ToString(); DeviceRegistration deviceRegistration = (message.Payload != null && message.Payload.Length > 0) ? new DeviceRegistration { Payload = new JRaw(message.Payload) } : null; - RegistrationOperationStatus operation = await RegisterDeviceAsync(connection, correlationId, deviceRegistration).ConfigureAwait(false); + RegistrationOperationStatus operation = await RegisterDeviceAsync(connection, correlationId, deviceRegistration, timeout).ConfigureAwait(false); // Poll with operationId until registration complete. int attempts = 0; @@ -131,7 +164,7 @@ await CreateLinksAsync( cancellationToken.ThrowIfCancellationRequested(); await Task.Delay( - operation.RetryAfter ?? RetryJitter.GenerateDelayWithJitterForRetry(s_defaultOperationPoolingInterval), + operation.RetryAfter ?? RetryJitter.GenerateDelayWithJitterForRetry(s_defaultOperationPollingInterval), cancellationToken).ConfigureAwait(false); try @@ -139,7 +172,8 @@ await Task.Delay( operation = await OperationStatusLookupAsync( connection, operationId, - correlationId).ConfigureAwait(false); + correlationId, + timeout).ConfigureAwait(false); } catch (ProvisioningTransportException e) when (e.ErrorDetails is ProvisioningErrorDetailsAmqp amqp && e.IsTransient) { @@ -154,7 +188,7 @@ await Task.Delay( authStrategy.SaveCredentials(operation); } - await connection.CloseAsync(s_timeoutConstant).ConfigureAwait(false); + await connection.CloseAsync(timeout).ConfigureAwait(false); return ConvertToProvisioningRegistrationResult(operation.RegistrationState); } @@ -179,30 +213,31 @@ await Task.Delay( } } - private static async Task CreateLinksAsync(AmqpClientConnection connection, string linkEndpoint, string productInfo) + private static async Task CreateLinksAsync(AmqpClientConnection connection, string linkEndpoint, string productInfo, TimeSpan timeout) { AmqpClientSession amqpDeviceSession = connection.CreateSession(); - await amqpDeviceSession.OpenAsync(s_timeoutConstant).ConfigureAwait(false); + await amqpDeviceSession.OpenAsync(timeout).ConfigureAwait(false); AmqpClientLink amqpReceivingLink = amqpDeviceSession.CreateReceivingLink(linkEndpoint); amqpReceivingLink.AddClientVersion(productInfo); amqpReceivingLink.AddApiVersion(ClientApiVersionHelper.ApiVersion); - await amqpReceivingLink.OpenAsync(s_timeoutConstant).ConfigureAwait(false); + await amqpReceivingLink.OpenAsync(timeout).ConfigureAwait(false); AmqpClientLink amqpSendingLink = amqpDeviceSession.CreateSendingLink(linkEndpoint); amqpSendingLink.AddClientVersion(productInfo); amqpSendingLink.AddApiVersion(ClientApiVersionHelper.ApiVersion); - await amqpSendingLink.OpenAsync(s_timeoutConstant).ConfigureAwait(false); + await amqpSendingLink.OpenAsync(timeout).ConfigureAwait(false); } private async Task RegisterDeviceAsync( AmqpClientConnection client, string correlationId, - DeviceRegistration deviceRegistration) + DeviceRegistration deviceRegistration, + TimeSpan timeout) { AmqpMessage amqpMessage = null; @@ -226,11 +261,11 @@ private async Task RegisterDeviceAsync( .SendMessageAsync( amqpMessage, new ArraySegment(Guid.NewGuid().ToByteArray()), - s_timeoutConstant) + timeout) .ConfigureAwait(false); ValidateOutcome(outcome); - AmqpMessage amqpResponse = await client.AmqpSession.ReceivingLink.ReceiveMessageAsync(s_timeoutConstant).ConfigureAwait(false); + AmqpMessage amqpResponse = await client.AmqpSession.ReceivingLink.ReceiveMessageAsync(timeout).ConfigureAwait(false); client.AmqpSession.ReceivingLink.AcceptMessage(amqpResponse); using var streamReader = new StreamReader(amqpResponse.BodyStream); @@ -238,7 +273,7 @@ private async Task RegisterDeviceAsync( .ReadToEndAsync() .ConfigureAwait(false); RegistrationOperationStatus status = JsonConvert.DeserializeObject(jsonResponse); - status.RetryAfter = ProvisioningErrorDetailsAmqp.GetRetryAfterFromApplicationProperties(amqpResponse, s_defaultOperationPoolingInterval); + status.RetryAfter = ProvisioningErrorDetailsAmqp.GetRetryAfterFromApplicationProperties(amqpResponse, s_defaultOperationPollingInterval); return status; } finally @@ -250,7 +285,8 @@ private async Task RegisterDeviceAsync( private async Task OperationStatusLookupAsync( AmqpClientConnection client, string operationId, - string correlationId) + string correlationId, + TimeSpan timeout) { using var amqpMessage = AmqpMessage.Create(new AmqpValue { Value = DeviceOperations.GetOperationStatus }); @@ -263,12 +299,12 @@ private async Task OperationStatusLookupAsync( .SendMessageAsync( amqpMessage, new ArraySegment(Guid.NewGuid().ToByteArray()), - s_timeoutConstant) + timeout) .ConfigureAwait(false); ValidateOutcome(outcome); - AmqpMessage amqpResponse = await client.AmqpSession.ReceivingLink.ReceiveMessageAsync(s_timeoutConstant) + AmqpMessage amqpResponse = await client.AmqpSession.ReceivingLink.ReceiveMessageAsync(timeout) .ConfigureAwait(false); client.AmqpSession.ReceivingLink.AcceptMessage(amqpResponse); @@ -277,7 +313,7 @@ private async Task OperationStatusLookupAsync( string jsonResponse = await streamReader.ReadToEndAsync().ConfigureAwait(false); RegistrationOperationStatus status = JsonConvert.DeserializeObject(jsonResponse); - status.RetryAfter = ProvisioningErrorDetailsAmqp.GetRetryAfterFromApplicationProperties(amqpResponse, s_defaultOperationPoolingInterval); + status.RetryAfter = ProvisioningErrorDetailsAmqp.GetRetryAfterFromApplicationProperties(amqpResponse, s_defaultOperationPollingInterval); return status; } @@ -314,7 +350,7 @@ private void ValidateOutcome(Outcome outcome) bool isTransient = statusCode >= (int)HttpStatusCode.InternalServerError || statusCode == 429; if (isTransient) { - errorDetails.RetryAfter = ProvisioningErrorDetailsAmqp.GetRetryAfterFromRejection(rejected, s_defaultOperationPoolingInterval); + errorDetails.RetryAfter = ProvisioningErrorDetailsAmqp.GetRetryAfterFromRejection(rejected, s_defaultOperationPollingInterval); } throw new ProvisioningTransportException( diff --git a/provisioning/transport/http/src/ProvisioningTransportHandlerHttp.cs b/provisioning/transport/http/src/ProvisioningTransportHandlerHttp.cs index 59b3af6723..cdbd2bec00 100644 --- a/provisioning/transport/http/src/ProvisioningTransportHandlerHttp.cs +++ b/provisioning/transport/http/src/ProvisioningTransportHandlerHttp.cs @@ -32,6 +32,20 @@ public ProvisioningTransportHandlerHttp() Proxy = DefaultWebProxySettings.Instance; } + /// + /// Registers a device described by the message. + /// + /// The provisioning message. + /// The maximum amount of time to allow this operation to run for before timing out. + /// The registration result. + public override async Task RegisterAsync( + ProvisioningTransportRegisterMessage message, + TimeSpan timeout) + { + using var cts = new CancellationTokenSource(timeout); + return await RegisterAsync(message, cts.Token).ConfigureAwait(false); + } + /// /// Registers a device described by the message. /// diff --git a/provisioning/transport/mqtt/src/ProvisioningTransportHandlerMqtt.cs b/provisioning/transport/mqtt/src/ProvisioningTransportHandlerMqtt.cs index d62ee25bd4..99b596d641 100644 --- a/provisioning/transport/mqtt/src/ProvisioningTransportHandlerMqtt.cs +++ b/provisioning/transport/mqtt/src/ProvisioningTransportHandlerMqtt.cs @@ -63,6 +63,20 @@ public ProvisioningTransportHandlerMqtt( Proxy = DefaultWebProxySettings.Instance; } + /// + /// Registers a device described by the message. + /// + /// The provisioning message. + /// The maximum amount of time to allow this operation to run for before timing out. + /// The registration result. + public override async Task RegisterAsync( + ProvisioningTransportRegisterMessage message, + TimeSpan timeout) + { + using var cts = new CancellationTokenSource(timeout); + return await RegisterAsync(message, cts.Token).ConfigureAwait(false); + } + /// /// Registers a device described by the message. /// diff --git a/readme.md b/readme.md index ab6d63c0f9..d0ca4c0a96 100644 --- a/readme.md +++ b/readme.md @@ -34,6 +34,15 @@ Due to security considerations, build logs are not publicly available. | Microsoft.Azure.Devices.DigitalTwin.Client | N/A | [![NuGet][pnp-device-prerelease]][pnp-device-nuget] | | Microsoft.Azure.Devices.DigitalTwin.Service | N/A | [![NuGet][pnp-service-prerelease]][pnp-service-nuget] | +> Note: +> Device streaming feature is not being included in our newer preview releases as there is no active development going on in the service. For more details on the feature, see [here](https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-device-streams-overview). It is not recommended to take dependency on preview nugets for production applications as breaking changes can be introduced in preview nugets. +> +> The feature has not been included in any preview release after [2020-10-14](https://github.com/Azure/azure-iot-sdk-csharp/releases/tag/preview_2020-10-14). However, the feature is still available under previews/deviceStreaming branch. +> +> The latest preview nuget versions that contain the feature are: +Microsoft.Azure.Devices.Client - 1.32.0-preview-001 +Microsoft.Azure.Devices - 1.28.0-preview-001 + The API reference documentation for .NET SDK is [here][dotnet-api-reference]. To find SDKs in other languages for Azure IoT, please refer to the [azure-iot-sdks][azure-iot-sdks] repository. @@ -63,28 +72,9 @@ If you would like to build or change the SDK source code, please follow the [dev ## OS platforms and hardware compatibility -> .NET Standard 1.3 (IoT Hub SDKs only) is last supported in the [2020-02-27](https://github.com/Azure/azure-iot-sdk-csharp/releases/tag/2020-2-27) and in the [2020-1-31 LTS](https://github.com/Azure/azure-iot-sdk-csharp/releases/tag/lts_2020-1-31) releases. - -The IoT Hub device SDK for .NET can be used with a broad range of device platforms and is officially supported on the following Operating Systems: - -* Windows versions officially supported by Microsoft. -* [Linux distributions](https://docs.microsoft.com/en-us/dotnet/core/install/linux) supported by .NET core. - -> Note: For Linux, we test our clients against Ubuntu 16.04.7 LTS. - -The NuGet packages provide support for the following .NET flavors: -- .NET 5.0 -- .NET Standard 2.1 -- .NET Standard 2.0 -- .NET Framework 4.7.2 (IoT Hub SDKs only) -- .NET Framework 4.5.1 (IoT Hub SDKs only) - -For details on .NET support see the [.NET Standard documentation](https://docs.microsoft.com/dotnet/standard/net-standard). -For details on OS support see the following resources: +For an official list of all the operating systems and .NET platforms that we support, please see [this document](./supported_platforms.md) -- [.NET Core Runtime ID Catalog](https://docs.microsoft.com/dotnet/core/rid-catalog) -- [.NET Framework System Requirements](https://docs.microsoft.com/dotnet/framework/get-started/system-requirements) -- [Configure TLS Protocol Version and Ciphers](./configure_tls_protocol_version_and_ciphers.md) +Note that you can configure your TLS protocol version and ciphers by following [this document](./configure_tls_protocol_version_and_ciphers.md) ## Key features and roadmap @@ -199,6 +189,7 @@ Below is a table showing the mapping of the LTS branches to the packages release | Release | Github Branch | LTS Status | LTS Start Date | Maintenance End Date | LTS End Date | | :-------------------------------------------------------------------------------------------: | :-----------: | :--------: | :------------: | :------------------: | :----------: | +| [2021-6-23](https://github.com/Azure/azure-iot-sdk-csharp/releases/tag/lts_2021-3-18_patch1) | lts_2021_03 | Active | 2020-03-18 | 2022-03-18 | 2024-03-17 | | [2021-3-18](https://github.com/Azure/azure-iot-sdk-csharp/releases/tag/lts_2021-3-18) | lts_2021_03 | Active | 2020-03-18 | 2022-03-18 | 2024-03-17 | | [2020-9-23](https://github.com/Azure/azure-iot-sdk-csharp/releases/tag/lts_2020-8-19_patch1) | lts_2020_08 | Active | 2020-08-19 | 2021-08-19 | 2023-08-19 | | [2020-8-19](https://github.com/Azure/azure-iot-sdk-csharp/releases/tag/lts_2020-8-19) | lts_2020_08 | Active | 2020-08-19 | 2021-08-19 | 2023-08-19 | diff --git a/supported_platforms.md b/supported_platforms.md new file mode 100644 index 0000000000..821b7a1977 --- /dev/null +++ b/supported_platforms.md @@ -0,0 +1,57 @@ +# Microsoft Azure IoT SDKs for .NET + +This SDK is tested nightly on a mix of .NET implementations on both Windows 10 and on Ubuntu 20.04. For additional details for each tested platform, see the respective sections below. + +## Supported .NET versions + +The NuGet packages provide support for the following .NET versions: +- .NET 5.0 +- .NET Standard 2.1 +- .NET Standard 2.0 +- .NET Framework 4.7.2 (IoT Hub SDKs only) +- .NET Framework 4.5.1 (IoT Hub SDKs only) + +This SDK _may_ work with newer versions of .NET, but there are no guarantees that they will _always_ work for those until we officially add support for them. + +Note that applications will resolve the dll that is being referenced based on the framework precedence rules. This means that .NET Framework targeting applications will look for the closet .NET Framework dll. In the absence of that, it will pick up the closest .NET Standard dll. Similarly for netcoreapp applications will look for the closest netcoreapp dll and in the absence of one will pick the closest .NET Standard dll. Since we publish the above list of dlls, you should target the appropriate net target to ensure you get the desired .NET API coverage. + +## Windows 10 + +Note that, while we only directly test on Windows 10, we do support other Windows versions officially supported by Microsoft. + +Nightly test platform details: + +.NET versions tested on +- .NET 5.0 +- .NET Core 3.1 +- .NET Core 2.1.18 +- .NET Framework 4.7.2 (only IoT Hub SDKs tested) +- .NET Framework 4.5.1 (only IoT Hub SDKs tested) + + +Default locale: en_US, platform encoding: Cp1252 + +OS name: "windows server 2019", version: "10.0", arch: "amd64", family: "windows" + +## Ubuntu 20.04 + +Note that, while we only directly test on Ubuntu 1604, we do generally support other [Linux distributions supported by .NET core](https://docs.microsoft.com/en-us/dotnet/core/install/linux). + +Nightly test platform details: + +.NET versions tested on: +- .NET 5.0 +- .NET Core 3.1 +- .NET Core 2.1.18 + +Default locale: en_US, platform encoding: UTF-8 + +OS name: "linux", kernel version: "5.8.0-1036-azure", arch: "amd64", family: "unix" + +## Miscellaneous support notes + +- This library does not officially support being run on MacOS. +- This library does not officially support being run in Xamarin applications. +- .NET Standard 1.3 (IoT Hub SDKs only) is last supported in the [2020-02-27](https://github.com/Azure/azure-iot-sdk-csharp/releases/tag/2020-2-27) and in the [2020-1-31 LTS](https://github.com/Azure/azure-iot-sdk-csharp/releases/tag/lts_2020-1-31) releases. +- [.NET Core Runtime ID Catalog](https://docs.microsoft.com/dotnet/core/rid-catalog) +- In order to run this SDK your device will need to meet the [.NET Framework System Requirements](https://docs.microsoft.com/dotnet/framework/get-started/system-requirements) diff --git a/vsts/vsts.yaml b/vsts/vsts.yaml index 1a8b27cbc1..3e4568ce24 100644 --- a/vsts/vsts.yaml +++ b/vsts/vsts.yaml @@ -34,6 +34,7 @@ jobs: 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 @@ -145,6 +146,7 @@ jobs: 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: |