public async Task RefreshCacheWithRefreshRequestTest()
        {
            // Arrange
            var store = GetEntityStore("cache");
            var serviceAuthentication  = new ServiceAuthentication(ServiceAuthenticationType.None);
            Func <ServiceIdentity> si1 = () => new ServiceIdentity("d1", "1234", Enumerable.Empty <string>(), serviceAuthentication, ServiceIdentityStatus.Enabled);
            Func <ServiceIdentity> si2 = () => new ServiceIdentity("d2", "m1", "2345", Enumerable.Empty <string>(), serviceAuthentication, ServiceIdentityStatus.Enabled);
            Func <ServiceIdentity> si3 = () => new ServiceIdentity("d3", "5678", Enumerable.Empty <string>(), serviceAuthentication, ServiceIdentityStatus.Enabled);
            Func <ServiceIdentity> si4 = () => new ServiceIdentity("d2", "m4", "9898", Enumerable.Empty <string>(), serviceAuthentication, ServiceIdentityStatus.Enabled);

            var iterator1 = new Mock <IServiceIdentitiesIterator>();

            iterator1.SetupSequence(i => i.HasNext)
            .Returns(true)
            .Returns(true)
            .Returns(false);
            iterator1.SetupSequence(i => i.GetNext())
            .ReturnsAsync(
                new List <ServiceIdentity>
            {
                si1(),
                si2()
            })
            .ReturnsAsync(
                new List <ServiceIdentity>
            {
                si3(),
                si4()
            });

            var iterator2 = new Mock <IServiceIdentitiesIterator>();

            iterator2.SetupSequence(i => i.HasNext)
            .Returns(true)
            .Returns(false);
            iterator2.SetupSequence(i => i.GetNext())
            .ReturnsAsync(
                new List <ServiceIdentity>
            {
                si1(),
                si2(),
                si3()
            });

            var iterator3 = new Mock <IServiceIdentitiesIterator>();

            iterator3.SetupSequence(i => i.HasNext)
            .Returns(true)
            .Returns(false);
            iterator3.SetupSequence(i => i.GetNext())
            .ReturnsAsync(
                new List <ServiceIdentity>
            {
                si1(),
                si2()
            });

            var iterator4 = new Mock <IServiceIdentitiesIterator>();

            iterator4.SetupSequence(i => i.HasNext)
            .Returns(true)
            .Returns(false);
            iterator4.SetupSequence(i => i.GetNext())
            .ReturnsAsync(
                new List <ServiceIdentity>
            {
                si3(),
                si4()
            });

            var serviceProxy = new Mock <IServiceProxy>();

            serviceProxy.SetupSequence(s => s.GetServiceIdentitiesIterator())
            .Returns(iterator1.Object)
            .Returns(iterator2.Object)
            .Returns(iterator3.Object)
            .Returns(iterator4.Object);
            var updatedIdentities = new List <ServiceIdentity>();
            var removedIdentities = new List <string>();

            // Act
            IDeviceScopeIdentitiesCache deviceScopeIdentitiesCache = await DeviceScopeIdentitiesCache.Create(serviceProxy.Object, store, TimeSpan.FromSeconds(7));

            deviceScopeIdentitiesCache.ServiceIdentityUpdated += (sender, identity) => updatedIdentities.Add(identity);
            deviceScopeIdentitiesCache.ServiceIdentityRemoved += (sender, s) => removedIdentities.Add(s);

            // Wait for refresh to complete
            await Task.Delay(TimeSpan.FromSeconds(3));

            Option <ServiceIdentity> receivedServiceIdentity1 = await deviceScopeIdentitiesCache.GetServiceIdentity("d1");

            Option <ServiceIdentity> receivedServiceIdentity2 = await deviceScopeIdentitiesCache.GetServiceIdentity("d2/m1");

            Option <ServiceIdentity> receivedServiceIdentity3 = await deviceScopeIdentitiesCache.GetServiceIdentity("d3");

            Option <ServiceIdentity> receivedServiceIdentity4 = await deviceScopeIdentitiesCache.GetServiceIdentity("d2/m4");

            // Assert
            Assert.True(si1().Equals(receivedServiceIdentity1.OrDefault()));
            Assert.True(si2().Equals(receivedServiceIdentity2.OrDefault()));
            Assert.True(si3().Equals(receivedServiceIdentity3.OrDefault()));
            Assert.True(si4().Equals(receivedServiceIdentity4.OrDefault()));

            Assert.Empty(updatedIdentities);
            Assert.Empty(removedIdentities);

            // Act - Signal refresh cache multiple times. It should get picked up twice.
            deviceScopeIdentitiesCache.InitiateCacheRefresh();
            deviceScopeIdentitiesCache.InitiateCacheRefresh();
            deviceScopeIdentitiesCache.InitiateCacheRefresh();
            deviceScopeIdentitiesCache.InitiateCacheRefresh();

            // Wait for the 2 refresh cycles to complete, this time because of the refresh request
            await Task.Delay(TimeSpan.FromSeconds(5));

            receivedServiceIdentity1 = await deviceScopeIdentitiesCache.GetServiceIdentity("d1");

            receivedServiceIdentity2 = await deviceScopeIdentitiesCache.GetServiceIdentity("d2/m1");

            receivedServiceIdentity3 = await deviceScopeIdentitiesCache.GetServiceIdentity("d3");

            receivedServiceIdentity4 = await deviceScopeIdentitiesCache.GetServiceIdentity("d2/m4");

            // Assert
            Assert.True(si1().Equals(receivedServiceIdentity1.OrDefault()));
            Assert.True(si2().Equals(receivedServiceIdentity2.OrDefault()));
            Assert.False(receivedServiceIdentity3.HasValue);
            Assert.False(receivedServiceIdentity4.HasValue);

            Assert.Empty(updatedIdentities);
            Assert.Equal(2, removedIdentities.Count);
            Assert.Contains("d2/m4", removedIdentities);
            Assert.Contains("d3", removedIdentities);

            // Wait for another refresh cycle to complete, this time because timeout
            await Task.Delay(TimeSpan.FromSeconds(8));

            receivedServiceIdentity1 = await deviceScopeIdentitiesCache.GetServiceIdentity("d1");

            receivedServiceIdentity2 = await deviceScopeIdentitiesCache.GetServiceIdentity("d2/m1");

            receivedServiceIdentity3 = await deviceScopeIdentitiesCache.GetServiceIdentity("d3");

            receivedServiceIdentity4 = await deviceScopeIdentitiesCache.GetServiceIdentity("d2/m4");

            // Assert
            Assert.True(si3().Equals(receivedServiceIdentity3.OrDefault()));
            Assert.True(si4().Equals(receivedServiceIdentity4.OrDefault()));
            Assert.False(receivedServiceIdentity1.HasValue);
            Assert.False(receivedServiceIdentity2.HasValue);

            Assert.Empty(updatedIdentities);
            Assert.Equal(4, removedIdentities.Count);
            Assert.Contains("d2/m1", removedIdentities);
            Assert.Contains("d1", removedIdentities);
        }
Example #2
0
        async Task <EdgeHubScopeResult> HandleGetDeviceAndModuleOnBehalfOfAsync(string actorDeviceId, string actorModuleId, IdentityOnBehalfOfRequest request)
        {
            Events.ReceivedIdentityOnBehalfOfRequest(actorDeviceId, actorModuleId, request);
            Preconditions.CheckNonWhiteSpace(request.TargetDeviceId, nameof(request.TargetDeviceId));

            bool   isModule = false;
            string targetId = request.TargetDeviceId;

            if (!request.TargetModuleId.IsNullOrWhiteSpace())
            {
                isModule  = true;
                targetId += "/" + request.TargetModuleId;
            }

            IEdgeHub edgeHub = await this.edgeHubGetter;

            // Check if the actor has provided the originating EdgeHub ID,
            // if not, then we must be by definition by the origin.
            string originatingEdge = edgeHub.GetEdgeDeviceId();

            if (this.Request.Headers.TryGetValue(Constants.OriginEdgeHeaderKey, out StringValues originHeader) && originHeader.Count > 0)
            {
                originatingEdge = originHeader.First();
            }

            // We must always forward the call further upstream first,
            // as this is invoked for refreshing an identity on-demand,
            // and we don't know whether our cache is out-of-date.
            IDeviceScopeIdentitiesCache identitiesCache = edgeHub.GetDeviceScopeIdentitiesCache();
            await identitiesCache.RefreshServiceIdentityOnBehalfOf(targetId, originatingEdge);

            Option <ServiceIdentity> targetIdentity = await identitiesCache.GetServiceIdentity(targetId);

            if (!targetIdentity.HasValue)
            {
                // Identity still doesn't exist, this can happen if the identity
                // is newly added and we couldn't refresh the individual identity
                // because we don't know where it resides in the nested hierarchy.
                // In this case our only recourse is to refresh the whole cache
                // and hope the identity shows up.
                identitiesCache.InitiateCacheRefresh();
                await identitiesCache.WaitForCacheRefresh(TimeSpan.FromSeconds(100));

                targetIdentity = await identitiesCache.GetServiceIdentity(targetId);
            }

            // Add the identity to the result
            var identityList = new List <ServiceIdentity>();

            targetIdentity.ForEach(i => identityList.Add(i));

            // If the target is a module, we also need to
            // include the parent device as well to match
            // IoT Hub API behavior
            if (isModule)
            {
                Option <ServiceIdentity> device = await identitiesCache.GetServiceIdentity(request.TargetDeviceId);

                device.ForEach(i => identityList.Add(i));
            }

            Events.SendingScopeResult(targetId, identityList);
            return(MakeResultFromIdentities(identityList));
        }