/// <inheritdoc/>
        public async Task HandleDeviceTwinEventAsync(DeviceTwinEvent ev)
        {
            if (ev.Handled)
            {
                return;
            }
            if (string.IsNullOrEmpty(ev.Twin.Id) || string.IsNullOrEmpty(ev.Twin.ModuleId))
            {
                return;
            }
            var type = ev.Twin.Properties?.Reported.GetValueOrDefault <string>(
                TwinProperty.Type, null);

            if ((ev.Event != DeviceTwinEventType.Delete && ev.IsPatch) || string.IsNullOrEmpty(type))
            {
                try {
                    ev.Twin = await _iothub.GetAsync(ev.Twin.Id, ev.Twin.ModuleId);

                    ev.IsPatch = false;
                    type       = ev.Twin.Properties?.Reported?.GetValueOrDefault <string>(
                        TwinProperty.Type, null);
                }
                catch (Exception ex) {
                    _logger.Verbose(ex, "Failed to materialize twin");
                }
            }
            if (IdentityType.Publisher.EqualsIgnoreCase(type))
            {
                var ctx = new RegistryOperationContextModel {
                    AuthorityId = ev.AuthorityId,
                    Time        = ev.Timestamp
                };
                switch (ev.Event)
                {
                case DeviceTwinEventType.New:
                    break;

                case DeviceTwinEventType.Create:
                    await _broker.NotifyAllAsync(l => l.OnPublisherNewAsync(ctx,
                                                                            ev.Twin.ToPublisherRegistration(false).ToServiceModel()));

                    break;

                case DeviceTwinEventType.Update:
                    await _broker.NotifyAllAsync(l => l.OnPublisherUpdatedAsync(ctx,
                                                                                ev.Twin.ToPublisherRegistration(false).ToServiceModel()));

                    break;

                case DeviceTwinEventType.Delete:
                    await _broker.NotifyAllAsync(l => l.OnPublisherDeletedAsync(ctx,
                                                                                PublisherModelEx.CreatePublisherId(
                                                                                    ev.Twin.Id, ev.Twin.ModuleId)));

                    break;
                }
                ev.Handled = true;
            }
        }
        /// <inheritdoc/>
        public async Task <PublisherModel> GetPublisherAsync(string id,
                                                             bool onlyServerState, CancellationToken ct)
        {
            if (string.IsNullOrEmpty(id))
            {
                throw new ArgumentException(nameof(id));
            }
            var deviceId = PublisherModelEx.ParseDeviceId(id, out var moduleId);
            var device   = await _iothub.GetAsync(deviceId, moduleId, ct);

            var registration = device.ToEntityRegistration(onlyServerState)
                               as PublisherRegistration;

            if (registration == null)
            {
                throw new ResourceNotFoundException(
                          $"{id} is not a supervisor registration.");
            }
            return(registration.ToServiceModel());
        }
        /// <inheritdoc/>
        public async Task <(string, EndpointModel)> FindPublisherEndpoint(
            string endpointId, CancellationToken ct)
        {
            // Get the endpoint and the endpoints supervisor
            var device = await _iothub.GetAsync(endpointId, null, ct);

            var registration = device.ToEndpointRegistration(false);

            var endpoint     = registration.ToServiceModel();
            var supervisorId = endpoint?.Registration?.SupervisorId;

            if (string.IsNullOrEmpty(supervisorId))
            {
                // No supervisor set for the retrieved endpoint
                throw new ResourceInvalidStateException(
                          $"Endpoint {endpointId} has no supervisor");
            }

            // Get iotedge device
            var deviceId = SupervisorModelEx.ParseDeviceId(supervisorId, out _);

            // Query for the publisher in the same edge device
            var query = "SELECT * FROM devices.modules WHERE " +
                        $"properties.reported.{TwinProperty.Type} = '{IdentityType.Publisher}' " +
                        $"AND deviceId = '{deviceId}'";
            var devices = await _iothub.QueryAllDeviceTwinsAsync(query, ct);

            device = devices.SingleOrDefault();
            if (device == null)
            {
                throw new ResourceNotFoundException(
                          $"No publisher found for {endpointId} in {deviceId}");
            }
            var publisherId = PublisherModelEx.CreatePublisherId(
                device.Id, device.ModuleId);

            return(publisherId, endpoint.Registration.Endpoint);
        }
        /// <summary>
        /// Helper to create fixtures
        /// </summary>
        /// <param name="site"></param>
        /// <param name="discoverer"></param>
        /// <param name="existing"></param>
        /// <param name="found"></param>
        /// <param name="registry"></param>
        /// <param name="countDevices"></param>
        /// <param name="fixup"></param>
        /// <param name="disable"></param>
        private void CreateFixtures(out string site, out string discoverer,
                                    out string supervisor, out string publisher, out string gateway,
                                    out List <ApplicationRegistrationModel> existing, out List <DiscoveryEventModel> found,
                                    out IoTHubServices registry, int countDevices = -1,
                                    Func <ApplicationRegistrationModel, ApplicationRegistrationModel> fixup = null,
                                    bool disable = false)
        {
            var fix = new Fixture();

            // Create template applications and endpoints
            fix.Customizations.Add(new TypeRelay(typeof(VariantValue), typeof(VariantValue)));
            fix.Behaviors.OfType <ThrowingRecursionBehavior>().ToList()
            .ForEach(b => fix.Behaviors.Remove(b));
            fix.Behaviors.Add(new OmitOnRecursionBehavior());
            var sitex = site = fix.Create <string>();

            gateway = fix.Create <string>();
            var Gateway = (new GatewayModel {
                SiteId = site,
                Id = gateway
            }.ToGatewayRegistration().ToDeviceTwin(),
                           new DeviceModel {
                Id = gateway
            });
            var module      = fix.Create <string>();
            var discovererx = discoverer = DiscovererModelEx.CreateDiscovererId(gateway, module);
            var Discoverer  = (new DiscovererModel {
                SiteId = site,
                Id = discovererx
            }.ToDiscovererRegistration().ToDeviceTwin(_serializer),
                               new DeviceModel {
                Id = gateway, ModuleId = module
            });

            module = fix.Create <string>();
            var supervisorx = supervisor = SupervisorModelEx.CreateSupervisorId(gateway, module);
            var Supervisor  = (new SupervisorModel {
                SiteId = site,
                Id = supervisorx
            }.ToSupervisorRegistration().ToDeviceTwin(_serializer),
                               new DeviceModel {
                Id = gateway, ModuleId = module
            });

            module = fix.Create <string>();
            var publisherx = publisher = PublisherModelEx.CreatePublisherId(gateway, module);
            var Publisher  = (new PublisherModel {
                SiteId = site,
                Id = publisherx
            }.ToPublisherRegistration().ToDeviceTwin(_serializer),
                              new DeviceModel {
                Id = gateway, ModuleId = module
            });

            var template = fix
                           .Build <ApplicationRegistrationModel>()
                           .Without(x => x.Application)
                           .Do(c => c.Application = fix
                                                    .Build <ApplicationInfoModel>()
                                                    .Without(x => x.NotSeenSince)
                                                    .With(x => x.SiteId, sitex)
                                                    .With(x => x.DiscovererId, discovererx)
                                                    .Create())
                           .Without(x => x.Endpoints)
                           .Do(c => c.Endpoints = fix
                                                  .Build <EndpointRegistrationModel>()
                                                  .With(x => x.SiteId, sitex)
                                                  .With(x => x.DiscovererId, discovererx)
                                                  .With(x => x.SupervisorId, supervisorx)
                                                  .CreateMany(5)
                                                  .ToList())
                           .CreateMany(5)
                           .ToList();

            template.ForEach(a =>
                             a.Application.ApplicationId =
                                 ApplicationInfoModelEx.CreateApplicationId(a.Application)
                             );

            // Create discovery results from template
            var i = 0; var now = DateTime.UtcNow;

            found = template
                    .SelectMany(a => a.Endpoints.Select(
                                    e => new DiscoveryEventModel {
                Application  = a.Application,
                Registration = e,
                Index        = i++,
                TimeStamp    = now
            }))
                    .ToList();

            // Clone and fixup existing applications as per test case
            existing = template
                       .Select(e => e.Clone())
                       .Select(fixup ?? (a => a))
                       .ToList();
            // and fill registry with them...
            var appdevices = existing
                             .Select(a => a.Application.ToApplicationRegistration(disable))
                             .Select(a => a.ToDeviceTwin(_serializer))
                             .Select(d => (d, new DeviceModel {
                Id = d.Id
            }));
            var epdevices = existing
                            .SelectMany(a => a.Endpoints
                                        .Select(e =>
                                                new EndpointInfoModel {
                ApplicationId = a.Application.ApplicationId,
                Registration  = e
            }.ToEndpointRegistration(_serializer, disable))
                                        .Select(e => e.ToDeviceTwin(_serializer)))
                            .Select(d => (d, new DeviceModel {
                Id = d.Id
            }));

            appdevices = appdevices.Concat(epdevices);
            if (countDevices != -1)
            {
                appdevices = appdevices.Take(countDevices);
            }
            registry = IoTHubServices.Create(appdevices
                                             .Concat(Gateway.YieldReturn())
                                             .Concat(Discoverer.YieldReturn())
                                             .Concat(Supervisor.YieldReturn())
                                             .Concat(Publisher.YieldReturn()));
        }
        public void ProcessDiscoveryWithNoResultsAndNoExistingApplications()
        {
            var found = new List <DiscoveryEventModel>();
            var fix   = new Fixture();

            var gateway = fix.Create <string>();
            var Gateway = (new GatewayModel {
                Id = gateway
            }.ToGatewayRegistration().ToDeviceTwin(),
                           new DeviceModel {
                Id = gateway
            });
            var module     = fix.Create <string>();
            var discoverer = DiscovererModelEx.CreateDiscovererId(gateway, module);
            var Discoverer = (new DiscovererModel {
                Id = discoverer
            }.ToDiscovererRegistration().ToDeviceTwin(_serializer),
                              new DeviceModel {
                Id = gateway, ModuleId = module
            });

            module = fix.Create <string>();
            var supervisor = SupervisorModelEx.CreateSupervisorId(gateway, module);
            var Supervisor = (new SupervisorModel {
                Id = supervisor
            }.ToSupervisorRegistration().ToDeviceTwin(_serializer),
                              new DeviceModel {
                Id = gateway, ModuleId = module
            });

            module = fix.Create <string>();
            var publisher = PublisherModelEx.CreatePublisherId(gateway, module);
            var Publisher = (new PublisherModel {
                Id = publisher
            }.ToPublisherRegistration().ToDeviceTwin(_serializer),
                             new DeviceModel {
                Id = gateway, ModuleId = module
            });

            var registry = IoTHubServices.Create(Gateway.YieldReturn() // Single device
                                                 .Append(Discoverer)
                                                 .Append(Supervisor)
                                                 .Append(Publisher));

            using (var mock = AutoMock.GetLoose(builder => {
                // Setup
                builder.RegisterInstance(registry).As <IIoTHubTwinServices>();
                builder.RegisterType <ApplicationTwins>().As <IApplicationRepository>();
                builder.RegisterType <DiscovererRegistry>().As <IDiscovererRegistry>();
                builder.RegisterType <SupervisorRegistry>().As <ISupervisorRegistry>();
                builder.RegisterType <PublisherRegistry>().As <IPublisherRegistry>();
                builder.RegisterType <GatewayRegistry>().As <IGatewayRegistry>();
                builder.RegisterType <ApplicationRegistry>().As <IApplicationBulkProcessor>();
                builder.RegisterType <EndpointRegistry>().As <IEndpointBulkProcessor>();
            })) {
                var service = mock.Create <DiscoveryProcessor>();

                // Run
                service.ProcessDiscoveryResultsAsync(discoverer, new DiscoveryResultModel(), found).Wait();

                // Assert
                Assert.Single(registry.Devices);
                Assert.Equal(gateway, registry.Devices.First().Device.Id);
            }
        }