< Summary

Class:Azure.Iot.Hub.Service.ModulesClient
Assembly:Azure.Iot.Hub.Service
File(s):C:\Git\azure-sdk-for-net\sdk\iot\Azure.Iot.Hub.Service\src\ModulesClient.cs
Covered lines:106
Uncovered lines:64
Coverable lines:170
Total lines:583
Line coverage:62.3% (106 of 170)
Covered branches:4
Total branches:20
Branch coverage:20% (4 of 20)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.ctor()-100%100%
.ctor(...)-100%100%
CreateOrUpdateIdentityAsync(...)-100%100%
CreateOrUpdateIdentity(...)-100%100%
GetIdentityAsync(...)-100%100%
GetIdentity(...)-100%100%
GetIdentitiesAsync(...)-100%100%
GetIdentities(...)-100%100%
DeleteIdentityAsync(...)-0%100%
DeleteIdentity(...)-0%100%
CreateIdentitiesWithTwinAsync(...)-100%100%
CreateIdentitiesWithTwin(...)-100%100%
CreateIdentitiesAsync(...)-100%100%
CreateIdentities(...)-100%100%
UpdateIdentitiesAsync(...)-100%50%
UpdateIdentities(...)-100%50%
DeleteIdentitiesAsync(...)-0%0%
DeleteIdentities(...)-0%0%
<GetTwinsAsync()-100%50%
<GetTwinsAsync()-0%0%
GetTwinsAsync(...)-100%100%
GetTwins(...)-100%100%
GetTwinAsync(...)-100%100%
GetTwin(...)-100%100%
UpdateTwinAsync(...)-100%100%
UpdateTwin(...)-100%100%
UpdateTwinsAsync(...)-0%0%
UpdateTwins(...)-0%0%
InvokeMethodAsync(...)-0%100%
InvokeMethod(...)-0%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\iot\Azure.Iot.Hub.Service\src\ModulesClient.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System.Collections.Generic;
 5using System.Globalization;
 6using System.Linq;
 7using System.Threading;
 8using System.Threading.Tasks;
 9using Azure.Core;
 10using Azure.Iot.Hub.Service.Models;
 11
 12namespace Azure.Iot.Hub.Service
 13{
 14    /// <summary>
 15    /// Modules client to interact with device modules and module twins including CRUD operations and method invocations
 16    /// See <see href="https://docs.microsoft.com/en-us/azure/iot-hub/iot-hub-portal-csharp-module-twin-getstarted"> Get
 17    /// </summary>
 18    public class ModulesClient
 19    {
 20        private const string ContinuationTokenHeader = "x-ms-continuation";
 21        private const string HubModuleQuery = "select * from devices.modules";
 22
 23        private readonly DevicesRestClient _devicesRestClient;
 24        private readonly ModulesRestClient _modulesRestClient;
 25        private readonly QueryRestClient _queryRestClient;
 26
 27        /// <summary>
 28        /// Initializes a new instance of ModulesClient.
 29        /// </summary>
 19030        protected ModulesClient()
 31        {
 19032        }
 33
 34        /// <summary>
 35        /// Initializes a new instance of DevicesClient.
 36        /// <param name="devicesRestClient"> The REST client to perform bulk operations on the module. </param>
 37        /// <param name="modulesRestClient"> The REST client to perform module and module twin operations. </param>
 38        /// <param name="queryRestClient"> The REST client to perform query operations for the device. </param>
 39        /// </summary>
 7640        internal ModulesClient(DevicesRestClient devicesRestClient, ModulesRestClient modulesRestClient, QueryRestClient
 41        {
 7642            Argument.AssertNotNull(devicesRestClient, nameof(devicesRestClient));
 7643            Argument.AssertNotNull(modulesRestClient, nameof(modulesRestClient));
 7644            Argument.AssertNotNull(queryRestClient, nameof(queryRestClient));
 45
 7646            _devicesRestClient = devicesRestClient;
 7647            _modulesRestClient = modulesRestClient;
 7648            _queryRestClient = queryRestClient;
 7649        }
 50
 51        /// <summary>
 52        /// Create a module identity.
 53        /// </summary>
 54        /// <param name="moduleIdentity">The module identity to create or update.</param>
 55        /// <param name="precondition">The condition on which to perform this operation.
 56        /// In case of create, the condition must be equal to <see cref="IfMatchPrecondition.IfMatch"/>.
 57        /// In case of update, if no ETag is present on the device, then the condition must be equal to <see cref="IfMat
 58        /// </param>
 59        /// <param name="cancellationToken">The cancellation token.</param>
 60        /// <returns>The created module identity and the http response <see cref="Response{T}"/>.</returns>
 61        public virtual Task<Response<ModuleIdentity>> CreateOrUpdateIdentityAsync(
 62            ModuleIdentity moduleIdentity,
 63            IfMatchPrecondition precondition = IfMatchPrecondition.IfMatch,
 64            CancellationToken cancellationToken = default)
 65        {
 4666            Argument.AssertNotNull(moduleIdentity, nameof(moduleIdentity));
 4667            string ifMatchHeaderValue = IfMatchPreconditionExtensions.GetIfMatchHeaderValue(precondition, moduleIdentity
 4668            return _modulesRestClient.CreateOrUpdateIdentityAsync(moduleIdentity.DeviceId, moduleIdentity.ModuleId, modu
 69        }
 70
 71        /// <summary>
 72        /// Create a module identity.
 73        /// </summary>
 74        /// <param name="moduleIdentity">The module identity to create or update.</param>
 75        /// <param name="precondition">The condition on which to perform this operation.
 76        /// In case of create, the condition must be equal to <see cref="IfMatchPrecondition.IfMatch"/>.
 77        /// In case of update, if no ETag is present on the device, then the condition must be equal to <see cref="IfMat
 78        /// </param>
 79        /// <param name="cancellationToken">The cancellation token.</param>
 80        /// <returns>The created module identity and the http response <see cref="Response{T}"/>.</returns>
 81        public virtual Response<ModuleIdentity> CreateOrUpdateIdentity(
 82            ModuleIdentity moduleIdentity,
 83            IfMatchPrecondition precondition = IfMatchPrecondition.IfMatch,
 84            CancellationToken cancellationToken = default)
 85        {
 4686            Argument.AssertNotNull(moduleIdentity, nameof(moduleIdentity));
 4687            string ifMatchHeaderValue = IfMatchPreconditionExtensions.GetIfMatchHeaderValue(precondition, moduleIdentity
 4688            return _modulesRestClient.CreateOrUpdateIdentity(moduleIdentity.DeviceId, moduleIdentity.ModuleId, moduleIde
 89        }
 90
 91        /// <summary>
 92        /// Get a single module identity.
 93        /// </summary>
 94        /// <param name="deviceId">The unique identifier of the device identity.</param>
 95        /// <param name="moduleId">The unique identifier of the module to get.</param>
 96        /// <param name="cancellationToken">The cancellation token.</param>
 97        /// <returns>The retrieved module identity and the http response <see cref="Response{T}"/>.</returns>
 98        public virtual Task<Response<ModuleIdentity>> GetIdentityAsync(string deviceId, string moduleId, CancellationTok
 99        {
 22100            return _modulesRestClient.GetIdentityAsync(deviceId, moduleId, cancellationToken);
 101        }
 102
 103        /// <summary>
 104        /// Get a single module identity.
 105        /// </summary>
 106        /// <param name="deviceId">The unique identifier of the device identity.</param>
 107        /// <param name="moduleId">The unique identifier of the module to get.</param>
 108        /// <param name="cancellationToken">The cancellation token.</param>
 109        /// <returns>The retrieved module identity and the http response <see cref="Response{T}"/>.</returns>
 110        public virtual Response<ModuleIdentity> GetIdentity(string deviceId, string moduleId, CancellationToken cancella
 111        {
 22112            return _modulesRestClient.GetIdentity(deviceId, moduleId, cancellationToken);
 113        }
 114
 115        /// <summary>
 116        /// Get a set of module identities for a specific device.
 117        /// </summary>
 118        /// <param name="deviceId">The unique identifier of the device.</param>
 119        /// <param name="cancellationToken">The cancellation token.</param>
 120        /// <returns>A list of modules identities within a device and the http response <see cref="Response{T}"/>.</retu
 121        public virtual Task<Response<IReadOnlyList<ModuleIdentity>>> GetIdentitiesAsync(string deviceId, CancellationTok
 122        {
 2123            Argument.AssertNotNullOrEmpty(deviceId, nameof(deviceId));
 2124            return _modulesRestClient.GetModulesOnDeviceAsync(deviceId, cancellationToken);
 125        }
 126
 127        /// <summary>
 128        /// Get a set of module identities for a specific device.
 129        /// </summary>
 130        /// <param name="deviceId">The unique identifier of the device.</param>
 131        /// <param name="cancellationToken">The cancellation token.</param>
 132        /// <returns>A list of modules identities within a device and the http response <see cref="Response{T}"/>.</retu
 133        public virtual Response<IReadOnlyList<ModuleIdentity>> GetIdentities(string deviceId, CancellationToken cancella
 134        {
 2135            Argument.AssertNotNullOrEmpty(deviceId, nameof(deviceId));
 2136            return _modulesRestClient.GetModulesOnDevice(deviceId, cancellationToken);
 137        }
 138
 139        /// <summary>
 140        /// Delete a single module identity.
 141        /// </summary>
 142        /// <param name="moduleIdentity">The module identity to delete. If no ETag is present on the module identity, th
 143        /// <param name="precondition">The condition on which to delete the module identity.</param>
 144        /// <param name="cancellationToken">The cancellation token.</param>
 145        /// <returns>The http response <see cref="Response{T}"/>.</returns>
 146        public virtual Task<Response> DeleteIdentityAsync(
 147            ModuleIdentity moduleIdentity,
 148            IfMatchPrecondition precondition = IfMatchPrecondition.IfMatch,
 149            CancellationToken cancellationToken = default)
 150        {
 0151            Argument.AssertNotNull(moduleIdentity, nameof(moduleIdentity));
 0152            string ifMatchHeaderValue = IfMatchPreconditionExtensions.GetIfMatchHeaderValue(precondition, moduleIdentity
 0153            return _modulesRestClient.DeleteIdentityAsync(moduleIdentity.DeviceId, moduleIdentity.ModuleId, ifMatchHeade
 154        }
 155
 156        /// <summary>
 157        /// Delete a single module identity.
 158        /// </summary>
 159        /// <param name="moduleIdentity">The module identity to delete. If no ETag is present on the module identity, th
 160        /// <param name="precondition">The condition on which to delete the module identity.</param>
 161        /// <param name="cancellationToken">The cancellation token.</param>
 162        /// <returns>The http response <see cref="Response{T}"/>.</returns>
 163        public virtual Response DeleteIdentity(
 164            ModuleIdentity moduleIdentity,
 165            IfMatchPrecondition precondition = IfMatchPrecondition.IfMatch,
 166            CancellationToken cancellationToken = default)
 167        {
 0168            Argument.AssertNotNull(moduleIdentity, nameof(moduleIdentity));
 0169            string ifMatchHeaderValue = IfMatchPreconditionExtensions.GetIfMatchHeaderValue(precondition, moduleIdentity
 0170            return _modulesRestClient.DeleteIdentity(moduleIdentity.DeviceId, moduleIdentity.ModuleId, ifMatchHeaderValu
 171        }
 172
 173        /// <summary>
 174        /// Create multiple modules with an initial twin. A maximum of 100 creations can be done per call,
 175        /// and each creation must have a unique module identity. Multiple modules may be created on a single device.
 176        /// All devices that these new modules will belong to must already exist.
 177        /// For larger scale operations, consider using <see href="https://docs.microsoft.com/en-us/azure/iot-hub/iot-hu
 178        /// </summary>
 179        /// <param name="modules">The pairs of modules and their twins that will be created. For fields such as deviceId
 180        /// where device and twin have a definition, the device value will override the twin value.</param>
 181        /// <param name="cancellationToken">The cancellation token.</param>
 182        /// <returns>The result of the bulk operation and the http response <see cref="Response{T}"/>.</returns>
 183        public virtual Task<Response<BulkRegistryOperationResponse>> CreateIdentitiesWithTwinAsync(IDictionary<ModuleIde
 184        {
 2185            IEnumerable<ExportImportDevice> registryOperations = modules
 22186                .Select(x => new ExportImportDevice()
 22187                {
 22188                    Id = x.Key.DeviceId,
 22189                    ModuleId = x.Key.ModuleId,
 22190                    Authentication = x.Key.Authentication,
 22191                    ImportMode = ExportImportDeviceImportMode.Create
 22192                }.WithTags(x.Value.Tags).WithPropertiesFrom(x.Value.Properties));
 193
 2194            return _devicesRestClient.BulkRegistryOperationsAsync(registryOperations, cancellationToken);
 195        }
 196
 197        /// <summary>
 198        /// Create multiple modules with an initial twin. A maximum of 100 creations can be done per call,
 199        /// and each creation must have a unique module identity. Multiple modules may be created on a single device.
 200        /// All devices that these new modules will belong to must already exist.
 201        /// For larger scale operations, consider using <see href="https://docs.microsoft.com/en-us/azure/iot-hub/iot-hu
 202        /// </summary>
 203        /// <param name="modules">The pairs of modules and their twins that will be created. For fields such as deviceId
 204        /// where device and twin have a definition, the device value will override the twin value.</param>
 205        /// <param name="cancellationToken">The cancellation token.</param>
 206        /// <returns>The result of the bulk operation and the http response <see cref="Response{T}"/>.</returns>
 207        public virtual Response<BulkRegistryOperationResponse> CreateIdentitiesWithTwin(IDictionary<ModuleIdentity, Twin
 208        {
 2209            IEnumerable<ExportImportDevice> registryOperations = modules
 22210                .Select(x => new ExportImportDevice()
 22211                {
 22212                    Id = x.Key.DeviceId,
 22213                    ModuleId = x.Key.ModuleId,
 22214                    Authentication = x.Key.Authentication,
 22215                    ImportMode = ExportImportDeviceImportMode.Create
 22216                }.WithTags(x.Value.Tags).WithPropertiesFrom(x.Value.Properties));
 217
 2218            return _devicesRestClient.BulkRegistryOperations(registryOperations, cancellationToken);
 219        }
 220
 221        /// <summary>
 222        /// Create multiple modules. A maximum of 100 creations can be done per call, and each module identity must be u
 223        /// All devices that these modules will belong to must already exist. Multiple modules can be created at a time 
 224        /// For larger scale operations, consider using <see href="https://docs.microsoft.com/en-us/azure/iot-hub/iot-hu
 225        /// </summary>
 226        /// <param name="moduleIdentities">The module identities to create.</param>
 227        /// <param name="cancellationToken">The cancellation token.</param>
 228        /// <returns>The result of the bulk operation and the http response <see cref="Response{T}"/>.</returns>
 229        public virtual Task<Response<BulkRegistryOperationResponse>> CreateIdentitiesAsync(IEnumerable<ModuleIdentity> m
 230        {
 6231            IEnumerable<ExportImportDevice> registryOperations = moduleIdentities
 66232                .Select(x => new ExportImportDevice()
 66233                {
 66234                    Id = x.DeviceId,
 66235                    ModuleId = x.ModuleId,
 66236                    Authentication = x.Authentication,
 66237                    ImportMode = ExportImportDeviceImportMode.Create
 66238                });
 239
 6240            return _devicesRestClient.BulkRegistryOperationsAsync(registryOperations, cancellationToken);
 241        }
 242
 243        /// <summary>
 244        /// Create multiple modules. A maximum of 100 creations can be done per call, and each module identity must be u
 245        /// All devices that these modules will belong to must already exist. Multiple modules can be created at a time 
 246        /// For larger scale operations, consider using <see href="https://docs.microsoft.com/en-us/azure/iot-hub/iot-hu
 247        /// </summary>
 248        /// <param name="moduleIdentities">The module identities to create.</param>
 249        /// <param name="cancellationToken">The cancellation token.</param>
 250        /// <returns>The result of the bulk operation and the http response <see cref="Response{T}"/>.</returns>
 251        public virtual Response<BulkRegistryOperationResponse> CreateIdentities(IEnumerable<ModuleIdentity> moduleIdenti
 252        {
 6253            IEnumerable<ExportImportDevice> registryOperations = moduleIdentities
 66254                .Select(x => new ExportImportDevice()
 66255                {
 66256                    Id = x.DeviceId,
 66257                    ModuleId = x.ModuleId,
 66258                    Authentication = x.Authentication,
 66259                    ImportMode = ExportImportDeviceImportMode.Create
 66260                });
 261
 6262            return _devicesRestClient.BulkRegistryOperations(registryOperations, cancellationToken);
 263        }
 264
 265        /// <summary>
 266        /// Update multiple modules. A maximum of 100 updates can be done per call, and each operation must be done on a
 267        /// </summary>
 268        /// <param name="moduleIdentities">The modules to update.</param>
 269        /// <param name="precondition">The condition on which to update each module identity.</param>
 270        /// <param name="cancellationToken">The cancellation token.</param>
 271        /// <returns>The result of the bulk operation and the http response <see cref="Response{T}"/>.</returns>
 272        public virtual Task<Response<BulkRegistryOperationResponse>> UpdateIdentitiesAsync(
 273            IEnumerable<ModuleIdentity> moduleIdentities,
 274            BulkIfMatchPrecondition precondition = BulkIfMatchPrecondition.IfMatch,
 275            CancellationToken cancellationToken = default)
 276        {
 2277            IEnumerable<ExportImportDevice> registryOperations = moduleIdentities
 22278                .Select(x => new ExportImportDevice()
 22279                {
 22280                    Id = x.DeviceId,
 22281                    ModuleId = x.ModuleId,
 22282                    Authentication = x.Authentication,
 22283                    ImportMode = precondition == BulkIfMatchPrecondition.Unconditional ? ExportImportDeviceImportMode.Up
 22284                });
 285
 2286            return _devicesRestClient.BulkRegistryOperationsAsync(registryOperations, cancellationToken);
 287        }
 288
 289        /// <summary>
 290        /// Update multiple modules. A maximum of 100 updates can be done per call, and each operation must be done on a
 291        /// </summary>
 292        /// <param name="moduleIdentities">The modules to update.</param>
 293        /// <param name="precondition">The condition on which to update each module identity.</param>
 294        /// <param name="cancellationToken">The cancellation token.</param>
 295        /// <returns>The result of the bulk operation and the http response <see cref="Response{T}"/>.</returns>
 296        public virtual Response<BulkRegistryOperationResponse> UpdateIdentities(
 297            IEnumerable<ModuleIdentity> moduleIdentities,
 298            BulkIfMatchPrecondition precondition = BulkIfMatchPrecondition.IfMatch,
 299            CancellationToken cancellationToken = default)
 300        {
 2301            IEnumerable<ExportImportDevice> registryOperations = moduleIdentities
 22302                .Select(x => new ExportImportDevice()
 22303                {
 22304                    Id = x.DeviceId,
 22305                    ModuleId = x.ModuleId,
 22306                    Authentication = x.Authentication,
 22307                    ImportMode = precondition == BulkIfMatchPrecondition.Unconditional ? ExportImportDeviceImportMode.Up
 22308                });
 309
 2310            return _devicesRestClient.BulkRegistryOperations(registryOperations, cancellationToken);
 311        }
 312
 313        /// <summary>
 314        /// Delete multiple modules. A maximum of 100 deletions can be done per call. For larger scale operations, consi
 315        /// </summary>
 316        /// <param name="moduleIdentities">The modules to delete.</param>
 317        /// <param name="precondition">The condition on which to delete each device identity.</param>
 318        /// <param name="cancellationToken">The cancellation token.</param>
 319        /// <returns>The result of the bulk deletion and the http response <see cref="Response{T}"/>.</returns>
 320        public virtual Task<Response<BulkRegistryOperationResponse>> DeleteIdentitiesAsync(
 321            IEnumerable<ModuleIdentity> moduleIdentities,
 322            BulkIfMatchPrecondition precondition = BulkIfMatchPrecondition.IfMatch,
 323            CancellationToken cancellationToken = default)
 324        {
 0325            IEnumerable<ExportImportDevice> registryOperations = moduleIdentities
 0326                .Select(x => new ExportImportDevice()
 0327                {
 0328                    Id = x.DeviceId,
 0329                    ModuleId = x.ModuleId,
 0330                    ETag = x.Etag,
 0331                    ImportMode = precondition == BulkIfMatchPrecondition.Unconditional
 0332                        ? ExportImportDeviceImportMode.Delete
 0333                        : ExportImportDeviceImportMode.DeleteIfMatchETag
 0334                });
 335
 0336            return _devicesRestClient.BulkRegistryOperationsAsync(registryOperations, cancellationToken);
 337        }
 338
 339        /// <summary>
 340        /// Delete multiple modules. A maximum of 100 deletions can be done per call. For larger scale operations, consi
 341        /// </summary>
 342        /// <param name="moduleIdentities">The devices to delete.</param>
 343        /// <param name="precondition">The condition on which to delete each device identity.</param>
 344        /// <param name="cancellationToken">The cancellation token.</param>
 345        /// <returns>The result of the bulk deletion and the http response <see cref="Response{T}"/>.</returns>
 346        public virtual Response<BulkRegistryOperationResponse> DeleteIdentities(
 347            IEnumerable<ModuleIdentity> moduleIdentities,
 348            BulkIfMatchPrecondition precondition = BulkIfMatchPrecondition.IfMatch,
 349            CancellationToken cancellationToken = default)
 350        {
 0351            IEnumerable<ExportImportDevice> registryOperations = moduleIdentities
 0352                .Select(x => new ExportImportDevice()
 0353                {
 0354                    Id = x.DeviceId,
 0355                    ModuleId = x.ModuleId,
 0356                    ETag = x.Etag,
 0357                    ImportMode = precondition == BulkIfMatchPrecondition.Unconditional
 0358                        ? ExportImportDeviceImportMode.Delete
 0359                        : ExportImportDeviceImportMode.DeleteIfMatchETag
 0360                });
 361
 0362            return _devicesRestClient.BulkRegistryOperations(registryOperations, cancellationToken);
 363        }
 364
 365        /// <summary>
 366        /// List a set of module twins.
 367        /// </summary>
 368        /// <param name="pageSize">The size of each page to be retrieved from the service. Service may override this siz
 369        /// <param name="cancellationToken">The cancellation token.</param>
 370        /// <returns>A pageable set of module twins <see cref="AsyncPageable{T}"/>.</returns>
 371        public virtual AsyncPageable<TwinData> GetTwinsAsync(int? pageSize = null, CancellationToken cancellationToken =
 372        {
 373            async Task<Page<TwinData>> FirstPageFunc(int? pageSizeHint)
 374            {
 2375                var querySpecification = new QuerySpecification
 2376                {
 2377                    Query = HubModuleQuery
 2378                };
 379
 2380                Response<IReadOnlyList<TwinData>> response =
 2381                    await _queryRestClient.GetTwinsAsync(querySpecification, null, pageSizeHint?.ToString(CultureInfo.In
 382
 2383                response.GetRawResponse().Headers.TryGetValue(ContinuationTokenHeader, out string continuationToken);
 384
 2385                return Page.FromValues(response.Value, continuationToken, response.GetRawResponse());
 2386            }
 387
 388            async Task<Page<TwinData>> NextPageFunc(string nextLink, int? pageSizeHint)
 389            {
 0390                var querySpecification = new QuerySpecification();
 391
 0392                Response<IReadOnlyList<TwinData>> response =
 0393                    await _queryRestClient.GetTwinsAsync(querySpecification, nextLink, pageSizeHint?.ToString(CultureInf
 394
 0395                response.GetRawResponse().Headers.TryGetValue(ContinuationTokenHeader, out string continuationToken);
 0396                return Page.FromValues(response.Value, continuationToken, response.GetRawResponse());
 0397            }
 398
 2399            return PageableHelpers.CreateAsyncEnumerable(FirstPageFunc, NextPageFunc, pageSize);
 400        }
 401
 402        /// <summary>
 403        /// List a set of module twins.
 404        /// </summary>
 405        /// <param name="pageSize">The size of each page to be retrieved from the service. Service may override this siz
 406        /// <param name="cancellationToken">The cancellation token.</param>
 407        /// <returns>A pageable set of module twins <see cref="Pageable{T}"/>.</returns>
 408        public virtual Pageable<TwinData> GetTwins(int? pageSize = null, CancellationToken cancellationToken = default)
 409        {
 410            Page<TwinData> FirstPageFunc(int? pageSizeHint)
 411            {
 4412                var querySpecification = new QuerySpecification
 4413                {
 4414                    Query = HubModuleQuery
 4415                };
 416
 4417                Response<IReadOnlyList<TwinData>> response = _queryRestClient.GetTwins(
 4418                    querySpecification,
 4419                    null,
 4420                    pageSizeHint?.ToString(CultureInfo.InvariantCulture),
 4421                    cancellationToken);
 422
 4423                response.GetRawResponse().Headers.TryGetValue(ContinuationTokenHeader, out string continuationToken);
 424
 4425                return Page.FromValues(response.Value, continuationToken, response.GetRawResponse());
 426            }
 427
 428            Page<TwinData> NextPageFunc(string nextLink, int? pageSizeHint)
 429            {
 0430                var querySpecification = new QuerySpecification();
 0431                Response<IReadOnlyList<TwinData>> response = _queryRestClient.GetTwins(
 0432                    querySpecification,
 0433                    nextLink,
 0434                    pageSizeHint?.ToString(CultureInfo.InvariantCulture),
 0435                    cancellationToken);
 436
 0437                response.GetRawResponse().Headers.TryGetValue(ContinuationTokenHeader, out string continuationToken);
 0438                return Page.FromValues(response.Value, continuationToken, response.GetRawResponse());
 439            }
 440
 4441            return PageableHelpers.CreateEnumerable(FirstPageFunc, NextPageFunc, pageSize);
 442        }
 443
 444        /// <summary>
 445        /// Get a module's twin.
 446        /// </summary>
 447        /// <param name="deviceId">The unique identifier of the device identity.</param>
 448        /// <param name="moduleId">The unique identifier of the module identity.</param>
 449        /// <param name="cancellationToken">The cancellation token.</param>
 450        /// <returns>The module's twin, including reported properties and desired properties and the http response <see 
 451        public virtual Task<Response<TwinData>> GetTwinAsync(string deviceId, string moduleId, CancellationToken cancell
 452        {
 6453            return _modulesRestClient.GetTwinAsync(deviceId, moduleId, cancellationToken);
 454        }
 455
 456        /// <summary>
 457        /// Get a module's twin.
 458        /// </summary>
 459        /// <param name="deviceId">The unique identifier of the device identity.</param>
 460        /// <param name="moduleId">The unique identifier of the module identity.</param>
 461        /// <param name="cancellationToken">The cancellation token.</param>
 462        /// <returns>The module's twin, including reported properties and desired properties and the http response <see 
 463        public virtual Response<TwinData> GetTwin(string deviceId, string moduleId, CancellationToken cancellationToken 
 464        {
 6465            return _modulesRestClient.GetTwin(deviceId, moduleId, cancellationToken);
 466        }
 467
 468        /// <summary>
 469        /// Update a module's twin.
 470        /// </summary>
 471        /// <param name="twinUpdate">The properties to update. Any existing properties not referenced by this patch will
 472        /// <param name="precondition">The condition for which this operation will execute.</param>
 473        /// <param name="cancellationToken">The cancellation token.</param>
 474        /// <returns>The new representation of the module's twin and the http response <see cref="Response{T}"/>.</retur
 475        public virtual Task<Response<TwinData>> UpdateTwinAsync(
 476            TwinData twinUpdate,
 477            IfMatchPrecondition precondition = IfMatchPrecondition.IfMatch,
 478            CancellationToken cancellationToken = default)
 479        {
 6480            Argument.AssertNotNull(twinUpdate, nameof(twinUpdate));
 6481            string ifMatchHeaderValue = IfMatchPreconditionExtensions.GetIfMatchHeaderValue(precondition, twinUpdate.Eta
 6482            return _modulesRestClient.UpdateTwinAsync(twinUpdate.DeviceId, twinUpdate.ModuleId, twinUpdate, ifMatchHeade
 483        }
 484
 485        /// <summary>
 486        /// Update a module's twin.
 487        /// </summary>
 488        /// <param name="twinUpdate">The properties to update. Any existing properties not referenced by this patch will
 489        /// <param name="precondition">The condition for which this operation will execute.</param>
 490        /// <param name="cancellationToken">The cancellation token.</param>
 491        /// <returns>The new representation of the module's twin and the http response <see cref="Response{T}"/>.</retur
 492        public virtual Response<TwinData> UpdateTwin(TwinData twinUpdate, IfMatchPrecondition precondition = IfMatchPrec
 493        {
 6494            Argument.AssertNotNull(twinUpdate, nameof(twinUpdate));
 6495            string ifMatchHeaderValue = IfMatchPreconditionExtensions.GetIfMatchHeaderValue(precondition, twinUpdate.Eta
 6496            return _modulesRestClient.UpdateTwin(twinUpdate.DeviceId, twinUpdate.ModuleId, twinUpdate, ifMatchHeaderValu
 497        }
 498
 499        /// <summary>
 500        /// Update multiple modules' twins. A maximum of 100 updates can be done per call, and each operation must be do
 501        /// </summary>
 502        /// <param name="twinUpdates">The new twins to replace the twins on existing devices.</param>
 503        /// <param name="precondition">The condition on which to update each device twin.</param>
 504        /// <param name="cancellationToken">The cancellation token.</param>
 505        /// <returns>The result of the bulk operation and the http response <see cref="Response{T}"/>.</returns>
 506        public virtual Task<Response<BulkRegistryOperationResponse>> UpdateTwinsAsync(
 507            IEnumerable<TwinData> twinUpdates,
 508            BulkIfMatchPrecondition precondition = BulkIfMatchPrecondition.IfMatch,
 509            CancellationToken cancellationToken = default)
 510        {
 0511            IEnumerable<ExportImportDevice> registryOperations = twinUpdates
 0512                .Select(x => new ExportImportDevice()
 0513                {
 0514                    Id = x.DeviceId,
 0515                    ModuleId = x.ModuleId,
 0516                    TwinETag = x.Etag,
 0517                    ImportMode = precondition == BulkIfMatchPrecondition.Unconditional ? ExportImportDeviceImportMode.Up
 0518                }.WithTags(x.Tags).WithPropertiesFrom(x.Properties));
 519
 0520            return _devicesRestClient.BulkRegistryOperationsAsync(registryOperations, cancellationToken);
 521        }
 522
 523        /// <summary>
 524        /// Update multiple modules' twins. A maximum of 100 updates can be done per call, and each operation must be do
 525        /// </summary>
 526        /// <param name="twinUpdates">The new twins to replace the twins on existing devices.</param>
 527        /// <param name="precondition">The condition on which to update each device twin.</param>
 528        /// <param name="cancellationToken">The cancellation token.</param>
 529        /// <returns>The result of the bulk operation and the http response <see cref="Response{T}"/>.</returns>
 530        public virtual Response<BulkRegistryOperationResponse> UpdateTwins(
 531            IEnumerable<TwinData> twinUpdates,
 532            BulkIfMatchPrecondition precondition = BulkIfMatchPrecondition.IfMatch,
 533            CancellationToken cancellationToken = default)
 534        {
 0535            IEnumerable<ExportImportDevice> registryOperations = twinUpdates
 0536                .Select(x => new ExportImportDevice()
 0537                {
 0538                    Id = x.DeviceId,
 0539                    ModuleId = x.ModuleId,
 0540                    TwinETag = x.Etag,
 0541                    ImportMode = precondition == BulkIfMatchPrecondition.Unconditional
 0542                        ? ExportImportDeviceImportMode.UpdateTwin
 0543                        : ExportImportDeviceImportMode.UpdateTwinIfMatchETag
 0544                }.WithTags(x.Tags).WithPropertiesFrom(x.Properties));
 545
 0546            return _devicesRestClient.BulkRegistryOperations(registryOperations, cancellationToken);
 547        }
 548
 549        /// <summary>
 550        /// Invoke a method on a module.
 551        /// </summary>
 552        /// <param name="deviceId">The unique identifier of the device.</param>
 553        /// <param name="moduleId">The unique identifier of the module identity to invoke the method on.</param>
 554        /// <param name="directMethodRequest">The details of the method to invoke.</param>
 555        /// <param name="cancellationToken">The cancellation token.</param>
 556        /// <returns>The result of the method invocation and the http response <see cref="Response{T}"/>.</returns>
 557        public virtual Task<Response<CloudToDeviceMethodResponse>> InvokeMethodAsync(
 558            string deviceId,
 559            string moduleId,
 560            CloudToDeviceMethodRequest directMethodRequest,
 561            CancellationToken cancellationToken = default)
 562        {
 0563            return _modulesRestClient.InvokeMethodAsync(deviceId, moduleId, directMethodRequest, cancellationToken);
 564        }
 565
 566        /// <summary>
 567        /// Invoke a method on a module.
 568        /// </summary>
 569        /// <param name="deviceId">The unique identifier of the device.</param>
 570        /// <param name="moduleId">The unique identifier of the module identity to invoke the method on.</param>
 571        /// <param name="directMethodRequest">The details of the method to invoke.</param>
 572        /// <param name="cancellationToken">The cancellation token.</param>
 573        /// <returns>The result of the method invocation and the http response <see cref="Response{T}"/>.</returns>
 574        public virtual Response<CloudToDeviceMethodResponse> InvokeMethod(
 575            string deviceId,
 576            string moduleId,
 577            CloudToDeviceMethodRequest directMethodRequest,
 578            CancellationToken cancellationToken = default)
 579        {
 0580            return _modulesRestClient.InvokeMethod(deviceId, moduleId, directMethodRequest, cancellationToken);
 581        }
 582    }
 583}