public async Task ConvertAsyncThrowsOnEmptyDevices()
        {
            string tenantId = this.rand.NextString();
            List <ValueApiModel> deviceGroupsList = new List <ValueApiModel>
            {
                this.entityHelper.CreateDeviceGroup(),
                this.entityHelper.CreateDeviceGroup(),
            };
            ValueListApiModel deviceGroups = new ValueListApiModel
            {
                Items = deviceGroupsList,
            };

            this.mockStorageAdapterClient
            .Setup(c => c.GetAllAsync(
                       It.Is <string>(s => s == this.converter.Entity),
                       It.IsAny <string>()))
            .ReturnsAsync(deviceGroups);

            this.mockIotHubManagerClient
            .Setup(c => c.GetListAsync(
                       It.IsAny <IEnumerable <DeviceGroupConditionModel> >(),
                       It.Is <string>(s => s == tenantId)))
            .ReturnsAsync(new DeviceListModel {
                Items = new List <DeviceModel> {
                }
            });                                                                            // return empty device lists, should cause the exception

            Func <Task> conversion = async() => await this.converter.ConvertAsync(tenantId);

            await Assert.ThrowsAsync <ResourceNotFoundException>(conversion);
        }
コード例 #2
0
        public async Task VerifyGetBehaviorIfDoIncludeDeleted()
        {
            // Arrange
            Rule test = new Rule
            {
                Enabled = false,
                Deleted = true,
                Id      = "id",
                ETag    = "123"
            };
            string        ruleString = JsonConvert.SerializeObject(test);
            ValueApiModel model      = new ValueApiModel
            {
                Data = ruleString,
                ETag = "123",
                Key  = "id"
            };
            ValueListApiModel result = new ValueListApiModel();

            result.Items = new List <ValueApiModel> {
                model
            };
            this.storageAdapter.Setup(x => x.GetAllAsync(It.IsAny <string>()))
            .Returns(Task.FromResult(result));

            // Act
            List <Rule> rulesList = await this.rules.GetListAsync("asc", 0, LIMIT, null, true);

            // Assert
            Assert.Single(rulesList);
            this.storageAdapter.Verify(x => x.GetAllAsync(It.IsAny <string>()), Times.Once);
        }
コード例 #3
0
        public async Task DeleteRule_RetriesLogOnError()
        {
            // Arrange
            ValueListApiModel fakeRules = new ValueListApiModel();

            fakeRules.Items.Add(this.CreateFakeRule("rule1"));
            fakeRules.Items.Add(this.CreateFakeRule("rule2"));
            this.storageAdapter.Setup(x => x.GetAllAsync(It.IsAny <string>())).Returns(Task.FromResult(fakeRules));
            this.httpClientMock.SetupSequence(x => x.PostAsync(It.IsAny <HttpRequest>()))
            .Throws <Exception>()
            .ReturnsAsync(new HttpResponse(HttpStatusCode.ServiceUnavailable, "", null))
            .ReturnsAsync(new HttpResponse(HttpStatusCode.OK, "", null))
            .ReturnsAsync(new HttpResponse(HttpStatusCode.OK, "", null));

            Rule test = new Rule
            {
                Enabled = true,
                Deleted = false
            };

            this.SetUpStorageAdapterGet(test);

            // Act
            await this.rules.DeleteAsync("id");

            // Assert
            this.storageAdapter.Verify(x => x.GetAllAsync(It.IsAny <string>()), Times.Once);
            this.httpClientMock.Verify(x => x.PostAsync(It.IsAny <HttpRequest>()), Times.Exactly(4));
        }
コード例 #4
0
        public async Task DeleteRule_QueriesRuleCountAndLogs()
        {
            // Arrange
            ValueListApiModel fakeRules = new ValueListApiModel();

            fakeRules.Items.Add(this.CreateFakeRule("rule1"));
            fakeRules.Items.Add(this.CreateFakeRule("rule2"));
            this.storageAdapter.Setup(x => x.GetAllAsync(It.IsAny <string>())).Returns(Task.FromResult(fakeRules));
            IHttpResponse fakeOkResponse = new HttpResponse(HttpStatusCode.OK, "", null);

            this.httpClientMock.Setup(x => x.PostAsync(It.IsAny <HttpRequest>())).ReturnsAsync(fakeOkResponse);

            Rule test = new Rule
            {
                Enabled = true,
                Deleted = false
            };

            this.SetUpStorageAdapterGet(test);

            // Act
            await this.rules.DeleteAsync("id");

            // Assert
            this.storageAdapter.Verify(x => x.GetAllAsync(It.IsAny <string>()), Times.Once);
            this.httpClientMock.Verify(x => x.PostAsync(It.IsAny <HttpRequest>()), Times.Exactly(2));
        }
        public async Task ConvertAsyncReturnsExpectedModel()
        {
            string tenantId = this.rand.NextString();
            List <ValueApiModel> deviceGroupsList = new List <ValueApiModel>
            {
                this.entityHelper.CreateDeviceGroup(),
                this.entityHelper.CreateDeviceGroup(),
            };
            ValueListApiModel deviceGroups = new ValueListApiModel
            {
                Items = deviceGroupsList,
            };

            this.mockStorageAdapterClient
            .Setup(c => c.GetAllAsync(
                       It.Is <string>(s => s == this.converter.Entity)))
            .ReturnsAsync(deviceGroups);

            this.mockBlobStorageClient
            .Setup(c => c.CreateBlobAsync(
                       It.IsAny <string>(),
                       It.IsAny <string>(),
                       It.IsAny <string>()))
            .Returns(Task.CompletedTask);

            this.mockIotHubManagerClient
            .Setup(c => c.GetListAsync(
                       It.IsAny <IEnumerable <DeviceGroupConditionModel> >(),
                       It.Is <string>(s => s == tenantId)))
            .ReturnsAsync(new DeviceListModel {
                Items = new List <DeviceModel> {
                    this.entityHelper.CreateDevice(), this.entityHelper.CreateDevice()
                }
            });                                                                                                                                               // return a device for each device group

            ConversionApiModel conversionResponse = await this.converter.ConvertAsync(tenantId);

            this.mockStorageAdapterClient
            .Verify(
                c => c.GetAllAsync(
                    It.Is <string>(s => s == this.converter.Entity)),
                Times.Once);
            this.mockBlobStorageClient
            .Verify(
                c => c.CreateBlobAsync(
                    It.IsAny <string>(),
                    It.IsAny <string>(),
                    It.IsAny <string>()),
                Times.Once);
            this.mockIotHubManagerClient
            .Verify(
                c => c.GetListAsync(
                    It.IsAny <IEnumerable <DeviceGroupConditionModel> >(),
                    It.Is <string>(s => s == tenantId)),
                Times.Exactly(deviceGroups.Items.Count));

            Assert.Equal(conversionResponse.Entities, deviceGroups);
            Assert.Equal(conversionResponse.TenantId, tenantId);
        }
コード例 #6
0
        public void UpsertUsesOptimisticConcurrency()
        {
            // Arrange
            const string ETAG1 = "001";
            const string ETAG2 = "002";

            // Initial simulation
            var simulation1 = new SimulationModel {
                Id = SIMULATION_ID, ETag = ETAG1
            };
            var storageRecord1 = new ValueApiModel
            {
                Key  = SIMULATION_ID,
                Data = JsonConvert.SerializeObject(simulation1),
                ETag = simulation1.ETag
            };
            var storageList1 = new ValueListApiModel();

            storageList1.Items.Add(storageRecord1);

            // Simulation after update
            var simulation2 = new SimulationModel {
                Id = SIMULATION_ID, ETag = ETAG2
            };
            var storageRecord2 = new ValueApiModel
            {
                Key  = SIMULATION_ID,
                Data = JsonConvert.SerializeObject(simulation2),
                ETag = simulation2.ETag
            };
            var storageList2 = new ValueListApiModel();

            storageList2.Items.Add(storageRecord2);

            // Initial setup - the ETag matches
            this.storage.Setup(x => x.GetAllAsync(STORAGE_COLLECTION)).ReturnsAsync(storageList1);
            this.storage.Setup(x => x.UpdateAsync(It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <string>()))
            .ReturnsAsync(storageRecord2);

            // Act - No exception because ETag matches
            // Note: the call to UpsertAsync modifies the object, don't reuse the variable later
            this.target.UpsertAsync(simulation1).Wait(Constants.TEST_TIMEOUT);

            // Arrange - the ETag won't match
            this.storage.Setup(x => x.GetAllAsync(STORAGE_COLLECTION)).ReturnsAsync(storageList2);

            // Act + Assert
            var simulationOutOfDate = new SimulationModel {
                Id = SIMULATION_ID, ETag = ETAG1
            };

            Assert.ThrowsAsync <ResourceOutOfDateException>(
                async() => await this.target.UpsertAsync(simulationOutOfDate))
            .Wait(Constants.TEST_TIMEOUT);
        }
コード例 #7
0
        private async Task<int> GetRuleCountAsync()
        {
            ValueListApiModel rules = await this.storage.GetAllAsync(StorageCollection);
            int ruleCount = 0;
            foreach (var item in rules.Items)
            {
                var rule = this.Deserialize(item.Data);
                if (!rule.Deleted)
                {
                    ruleCount++;
                }
            }

            return ruleCount;
        }
コード例 #8
0
        private void SetupAListOfInvalidDeviceModelScriptsInStorage()
        {
            var list  = new ValueListApiModel();
            var value = new ValueApiModel
            {
                Key  = "key1",
                Data = "{ 'invalid': json",
                ETag = "etag"
            };

            list.Items.Add(value);

            this.storage
            .Setup(x => x.GetAllAsync(It.IsAny <string>()))
            .ReturnsAsync(list);
        }
コード例 #9
0
        public async Task ConvertAsyncThrowsOnEmptyRules()
        {
            string            tenantId = this.rand.NextString();
            ValueListApiModel rules    = new ValueListApiModel
            {
                Items = new List <ValueApiModel>(),
            };

            this.mockStorageAdapterClient
            .Setup(c => c.GetAllAsync(
                       It.Is <string>(s => s == this.converter.Entity)))
            .ReturnsAsync(rules);

            Func <Task> conversion = async() => await this.converter.ConvertAsync(tenantId);

            await Assert.ThrowsAsync <ResourceNotFoundException>(conversion);
        }
コード例 #10
0
        public async Task ConvertAsyncReturnsExpectedModel()
        {
            string tenantId = this.rand.NextString();
            List <ValueApiModel> rulesList = new List <ValueApiModel>
            {
                this.entityHelper.CreateRule(),
                this.entityHelper.CreateRule(),
            };
            ValueListApiModel rules = new ValueListApiModel
            {
                Items = rulesList,
            };

            this.mockStorageAdapterClient
            .Setup(c => c.GetAllAsync(
                       It.Is <string>(s => s == this.converter.Entity),
                       It.IsAny <string>()))
            .ReturnsAsync(rules);

            this.mockBlobStorageClient
            .Setup(c => c.CreateBlobAsync(
                       It.IsAny <string>(),
                       It.IsAny <string>(),
                       It.IsAny <string>()))
            .Returns(Task.CompletedTask);

            ConversionApiModel conversionResponse = await this.converter.ConvertAsync(tenantId);

            this.mockStorageAdapterClient
            .Verify(
                c => c.GetAllAsync(
                    It.Is <string>(s => s == this.converter.Entity),
                    It.IsAny <string>()),
                Times.Once);
            this.mockBlobStorageClient
            .Verify(
                c => c.CreateBlobAsync(
                    It.IsAny <string>(),
                    It.IsAny <string>(),
                    It.IsAny <string>()),
                Times.Once);

            Assert.Equal(conversionResponse.Entities, rules);
            Assert.Equal(conversionResponse.TenantId, tenantId);
        }
コード例 #11
0
        private void SetupAListOfDeviceModelsInStorage(string etag)
        {
            var deviceModel = new DeviceModel {
                Id = "id", ETag = etag
            };
            var list  = new ValueListApiModel();
            var value = new ValueApiModel
            {
                Key  = "key",
                Data = JsonConvert.SerializeObject(deviceModel),
                ETag = deviceModel.ETag
            };

            list.Items.Add(value);

            this.storage
            .Setup(x => x.GetAllAsync(It.IsAny <string>()))
            .ReturnsAsync(list);
        }
コード例 #12
0
        /// <summary>
        /// Get List of deviceProperties from cache
        /// </summary>
        public async Task <List <string> > GetListAsync()
        {
            ValueListApiModel response = new ValueListApiModel();

            try
            {
                response = await this.storageClient.GetAllAsync(CACHE_COLLECTION_ID);
            }
            catch (ResourceNotFoundException)
            {
                this.log.Debug($"Cache get: cache {CACHE_COLLECTION_ID}:{CACHE_KEY} was not found",
                               () => { });
            }
            catch (Exception e)
            {
                throw new ExternalDependencyException(
                          $"Cache get: unable to get device-twin-properties cache", e);
            }

            DevicePropertyServiceModel properties = new DevicePropertyServiceModel();

            try
            {
                properties = JsonConvert.DeserializeObject <DevicePropertyServiceModel>(response.Items.FirstOrDefault().Data);
            }
            catch (Exception e)
            {
                throw new InvalidInputException("Unable to deserialize deviceProperties from CosmosDB", e);
            }
            List <string> result = new List <string>();

            foreach (string tag in properties.Tags)
            {
                result.Add(TAG_PREFIX + tag);
            }
            foreach (string reported in properties.Reported)
            {
                result.Add(REPORTED_PREFIX + reported);
            }
            return(result);
        }
コード例 #13
0
        private void ThereIsAnEnabledSimulationInTheStorage()
        {
            var simulation = new SimulationModel
            {
                Id       = SIMULATION_ID,
                Created  = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(10)),
                Modified = DateTimeOffset.UtcNow.Subtract(TimeSpan.FromDays(10)),
                ETag     = "ETag0",
                Enabled  = true
            };

            var list  = new ValueListApiModel();
            var value = new ValueApiModel
            {
                Key  = SIMULATION_ID,
                Data = JsonConvert.SerializeObject(simulation),
                ETag = simulation.ETag
            };

            list.Items.Add(value);

            this.mockStorageAdapterClient.Setup(x => x.GetAllAsync(STORAGE_COLLECTION)).ReturnsAsync(list);
        }
コード例 #14
0
        public override async Task <ConversionApiModel> ConvertAsync(string tenantId, string operationId = null)
        {
            ValueListApiModel rules = null;

            try
            {
                rules = await this.StorageAdapterClient.GetAllAsync(this.Entity);
            }
            catch (Exception e)
            {
                this.Logger.LogError(e, "Unable to query {entity} using storage adapter. OperationId: {operationId}. TenantId: {tenantId}", this.Entity, operationId, tenantId);
                throw e;
            }

            if (rules.Items.Count() == 0 || rules == null)
            {
                string errorMessage = $"No entities were receieved from storage adapter to convert to {this.Entity}. OperationId: {operationId}. TenantId: {tenantId}";
                this.Logger.LogError(new Exception(errorMessage), errorMessage);
                throw new ResourceNotFoundException("No entities were receieved from storage adapter to convert to rules.");
            }

            List <RuleReferenceDataModel> jsonRulesList = new List <RuleReferenceDataModel>();

            try
            {
                foreach (ValueApiModel item in rules.Items)
                {
                    try
                    {
                        RuleDataModel dataModel = JsonConvert.DeserializeObject <RuleDataModel>(item.Data);
                        RuleModel     ruleModel = new RuleModel(item.Key, dataModel);

                        // return a RuleReferenceModel which is a conversion from the RuleModel into a SAjob readable format with additional metadata
                        RuleReferenceDataModel referenceModel = new RuleReferenceDataModel(ruleModel);
                        jsonRulesList.Add(referenceModel);
                    }
                    catch (Exception)
                    {
                        this.Logger.LogInformation("Unable to convert a rule to the proper reference data model for {entity}. OperationId: {operationId}. TenantId: {tenantId}", this.Entity, operationId, tenantId);
                    }
                }

                if (jsonRulesList.Count() == 0)
                {
                    throw new ResourceNotSupportedException("No rules were able to be converted to the proper rule reference data model.");
                }
            }
            catch (Exception e)
            {
                this.Logger.LogError(e, "Unable to convert {entity} queried from storage adapter to appropriate data model. OperationId: {operationId}. TenantId: {tenantId}", this.Entity, operationId, tenantId);
                throw e;
            }

            string fileContent = null;

            try
            {
                fileContent = JsonConvert.SerializeObject(jsonRulesList, Formatting.Indented);
            }
            catch (Exception e)
            {
                this.Logger.LogError(e, "Unable to serialize the IEnumerable of {entity} data models for the temporary file content. OperationId: {operationId}. TenantId: {tenantId}", this.Entity, operationId, tenantId);
                throw e;
            }

            string blobFilePath = await this.WriteFileContentToBlobAsync(fileContent, tenantId, operationId);

            ConversionApiModel conversionResponse = new ConversionApiModel
            {
                TenantId     = tenantId,
                BlobFilePath = blobFilePath,
                Entities     = rules,
                OperationId  = operationId,
            };

            this.Logger.LogInformation("Successfully Completed {entity} conversion\n{model}", this.Entity, JsonConvert.SerializeObject(conversionResponse));
            return(conversionResponse);
        }
コード例 #15
0
        /// <summary>
        /// Update Cache when devices are modified/created
        /// </summary>
        public async Task <DevicePropertyServiceModel> UpdateListAsync(
            DevicePropertyServiceModel deviceProperties)
        {
            // To simplify code, use empty set to replace null set
            deviceProperties.Tags     = deviceProperties.Tags ?? new HashSet <string>();
            deviceProperties.Reported = deviceProperties.Reported ?? new HashSet <string>();

            string etag = null;

            while (true)
            {
                ValueListApiModel model = null;
                try
                {
                    model = await this.storageClient.GetAllAsync(CACHE_COLLECTION_ID);
                }
                catch (ResourceNotFoundException)
                {
                    this.log.Info($"Cache updating: cache {CACHE_COLLECTION_ID}:{CACHE_KEY} was not found",
                                  () => { });
                }

                if (model != null)
                {
                    DevicePropertyServiceModel devicePropertiesFromStorage;

                    try
                    {
                        devicePropertiesFromStorage = JsonConvert.
                                                      DeserializeObject <DevicePropertyServiceModel>(model.Items.FirstOrDefault().Data);
                    }
                    catch
                    {
                        devicePropertiesFromStorage = new DevicePropertyServiceModel();
                    }
                    devicePropertiesFromStorage.Tags = devicePropertiesFromStorage.Tags ??
                                                       new HashSet <string>();
                    devicePropertiesFromStorage.Reported = devicePropertiesFromStorage.Reported ??
                                                           new HashSet <string>();

                    deviceProperties.Tags.UnionWith(devicePropertiesFromStorage.Tags);
                    deviceProperties.Reported.UnionWith(devicePropertiesFromStorage.Reported);
                    etag = model.Items.FirstOrDefault().ETag;
                    // If the new set of deviceProperties are already there in cache, return
                    if (deviceProperties.Tags.Count == devicePropertiesFromStorage.Tags.Count &&
                        deviceProperties.Reported.Count == devicePropertiesFromStorage.Reported.Count)
                    {
                        return(deviceProperties);
                    }
                }

                var value = JsonConvert.SerializeObject(deviceProperties);
                try
                {
                    //var checkcacheList = await this.storageClient.GetAllAsync(CACHE_COLLECTION_ID);
                    //var updatedata = checkcacheList.Items.FirstOrDefault();
                    var response = await this.storageClient.UpdateAsync(
                        CACHE_COLLECTION_ID, model.Items.FirstOrDefault().objectid, value, etag);

                    return(JsonConvert.DeserializeObject <DevicePropertyServiceModel>(response.Data));
                }
                catch (ConflictingResourceException)
                {
                    this.log.Info("Cache updating: failed due to conflict. Retry soon", () => { });
                }
                catch (Exception e)
                {
                    this.log.Info("Cache updating: failed", () => e);
                    throw new Exception("Cache updating: failed");
                }
            }
        }
        public override async Task <ConversionApiModel> ConvertAsync(string tenantId, string operationId = null)
        {
            ValueListApiModel deviceGroups = null;

            try
            {
                deviceGroups = await this.StorageAdapterClient.GetAllAsync(this.Entity, tenantId);
            }
            catch (Exception e)
            {
                this.Logger.LogError(e, "Unable to query {entity} using storage adapter. OperationId: {operationId}. TenantId: {tenantId}", this.Entity, operationId, tenantId);
                throw e;
            }

            if (deviceGroups.Items.Count() == 0 || deviceGroups == null)
            {
                string errorMessage = $"No entities were receieved from storage adapter to convert to {this.Entity}. OperationId: {operationId}. TenantId: {tenantId}";
                this.Logger.LogError(new Exception(errorMessage), errorMessage);
                throw new ResourceNotFoundException("No entities were receieved from storage adapter to convert to rules.");
            }

            DeviceGroupListModel deviceGroupModels = new DeviceGroupListModel();

            try
            {
                List <DeviceGroupModel> items = new List <DeviceGroupModel>();
                foreach (ValueApiModel group in deviceGroups.Items)
                {
                    try
                    {
                        DeviceGroupDataModel dataModel       = JsonConvert.DeserializeObject <DeviceGroupDataModel>(group.Data);
                        DeviceGroupModel     individualModel = new DeviceGroupModel(group.Key, group.ETag, dataModel);
                        items.Add(individualModel);
                    }
                    catch (Exception)
                    {
                        this.Logger.LogInformation("Unable to convert a device group to the proper reference data model for {entity}. OperationId: {operationId}. TenantId: {tenantId}", this.Entity, operationId, tenantId);
                    }
                }

                if (items.Count() == 0)
                {
                    throw new ResourceNotSupportedException("No device groups were able to be converted to the proper rule reference data model.");
                }

                deviceGroupModels.Items = items;
            }
            catch (Exception e)
            {
                this.Logger.LogError(e, "Unable to convert {entity} queried from storage adapter to appropriate data model. OperationId: {operationId}. TenantId: {tenantId}", this.Entity, operationId, tenantId);
                throw e;
            }

            Dictionary <DeviceGroupModel, DeviceListModel> deviceMapping = new Dictionary <DeviceGroupModel, DeviceListModel>();

            foreach (DeviceGroupModel deviceGroup in deviceGroupModels.Items)
            {
                try
                {
                    DeviceListModel devicesList = await this.iotHubManager.GetListAsync(deviceGroup.Conditions, tenantId);

                    if (devicesList.Items.Count() > 0)
                    {
                        deviceMapping.Add(deviceGroup, devicesList);
                    }
                }
                catch (Exception e)
                {
                    // Do not throw an exception here, attempt to query other device groups instead to get as much data as possible
                    // Log all device groups that could not be retreived
                    this.Logger.LogError(e, "Unable to get list of devices for devicegroup {deviceGroup} from IotHubManager. OperationId: {operationId}. TenantId: {tenantId}", deviceGroup.Id, operationId, tenantId);
                }
            }

            if (deviceMapping.Count() == 0)
            {
                string groups       = $"[{string.Join(", ", deviceGroupModels.Items.Select(group => group.Id))}]";
                string errorMessage = $"No Devices were found for any {this.Entity}. OperationId: {operationId}. TenantId: {tenantId}\n{groups}";
                this.Logger.LogError(new Exception(errorMessage), errorMessage);
                throw new ResourceNotFoundException($"No Devices were found for any {this.Entity}.");
            }

            string fileContent = null;

            try
            {
                // Write a file in csv format:
                // deviceId,groupId
                // mapping contains devices groups, and a list model of all devices within each device group
                // create a new csv row for each device and device group combination
                string fileContentRows = string.Join("\n", deviceMapping.Select(mapping =>
                {
                    return(string.Join("\n", mapping.Value.Items.Select(device => $"{device.Id},{mapping.Key.Id}")));
                }));

                // Add the rows and the header together to complete the csv file content
                fileContent = $"{CsvHeader}\n{fileContentRows}";
            }
            catch (Exception e)
            {
                this.Logger.LogError(e, "Unable to serialize the {entity} data models for the temporary file content. OperationId: {operationId}. TenantId: {tenantId}", this.Entity, operationId, tenantId);
                throw e;
            }

            string blobFilePath = await this.WriteFileContentToBlobAsync(fileContent, tenantId, operationId);

            ConversionApiModel conversionResponse = new ConversionApiModel
            {
                TenantId     = tenantId,
                BlobFilePath = blobFilePath,
                Entities     = deviceGroups,
                OperationId  = operationId,
            };

            this.Logger.LogInformation("Successfully Completed {entity} conversion\n{model}", this.Entity, JsonConvert.SerializeObject(conversionResponse));
            return(conversionResponse);
        }