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); }
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)); }