private async Task <TwinServiceModel> GetPreviousFirmwareReportedProperties(string tenantId, string deviceId)
        {
            var sql = QueryBuilder.GetDeploymentDeviceDocumentsSqlByKey("CollectionId", $"deviceDeploymentHistory-{deviceId}");

            List <Document> docs = new List <Document>();

            try
            {
                CosmosOperations storageClient = await CosmosOperations.GetClientAsync();

                docs = await storageClient.QueryDocumentsAsync(
                    "pcs-storage",
                    $"pcs-{tenantId}",
                    this.DefaultQueryOptions,
                    sql,
                    0,
                    100);

                var result = docs.Select(doc => new ValueServiceModel(doc));
                if (result != null && result.Count() > 0)
                {
                    var previousDeployment = JsonConvert.DeserializeObject <DeploymentHistoryModel>(result.FirstOrDefault()?.Data);
                    var previousTwin       = previousDeployment.Twin;
                    return(previousTwin != null ? previousTwin : null);
                }

                return(null);
            }
            catch (ResourceNotFoundException e)
            {
                throw new ResourceNotFoundException($"No deployments exist in CosmosDb. The telemetry collection {$"pcs-{tenantId}"} does not exist.", e);
            }
        }
        private async Task SaveDeviceTwins(string tenantId, string deploymentId, List <TwinServiceModel> deviceTwins)
        {
            if (deviceTwins != null)
            {
                CosmosOperations storageClient = await CosmosOperations.GetClientAsync();

                foreach (var deviceTwin in deviceTwins)
                {
                    var value = JsonConvert.SerializeObject(
                        deviceTwin,
                        Formatting.Indented,
                        new JsonSerializerSettings
                    {
                        NullValueHandling = NullValueHandling.Ignore,
                    });

                    try
                    {
                        await storageClient.SaveDocumentAsync(string.Format(DeploymentDevicePropertiesCollection, deploymentId), deviceTwin.DeviceId, new ValueServiceModel()
                        {
                            Data = value
                        }, this.GenerateCollectionLink(tenantId));
                    }
                    catch (Exception)
                    {
                    }
                }
            }
        }
        private async Task <TwinServiceListModel> GetTwins(string tenantId, string collectionId)
        {
            var         sql          = CosmosOperations.GetDocumentsByCollectionId("CollectionId", collectionId);
            FeedOptions queryOptions = new FeedOptions
            {
                EnableCrossPartitionQuery = true,
                EnableScanInQuery         = true,
            };

            List <Document> docs = new List <Document>();

            try
            {
                CosmosOperations storageClient = await CosmosOperations.GetClientAsync();

                docs = await storageClient.QueryAllDocumentsAsync(
                    "pcs-storage",
                    $"pcs-{tenantId}",
                    queryOptions,
                    sql);

                var result = docs == null ?
                             new List <TwinServiceModel>() :
                             docs
                             .Select(doc => new ValueServiceModel(doc)).Select(x => JsonConvert.DeserializeObject <TwinServiceModel>(x.Data))
                             .ToList();
                return(new TwinServiceListModel(result, null));
            }
            catch (ResourceNotFoundException e)
            {
                throw new ResourceNotFoundException($"No records exist in CosmosDb. The CollectionId {collectionId} does not exist.", e);
            }
        }
        public async Task SaveDeploymentFromHub(string tenantId, DeploymentServiceModel deploymentModel, List <TwinServiceModel> deviceTwins)
        {
            CosmosOperations storageClient = await CosmosOperations.GetClientAsync();

            foreach (TwinServiceModel deviceTwin in deviceTwins)
            {
                DeploymentHistoryModel modelToSave = new DeploymentHistoryModel
                {
                    DeploymentId           = deploymentModel.Id,
                    DeploymentName         = deploymentModel.Name,
                    DeviceId               = deviceTwin.DeviceId,
                    PreviousFirmwareTwin   = null,
                    LastUpdatedDateTimeUtc = DateTime.UtcNow,
                    Twin = deviceTwin,
                };

                var value = JsonConvert.SerializeObject(
                    modelToSave,
                    Formatting.None,
                    new JsonSerializerSettings
                {
                    NullValueHandling = NullValueHandling.Ignore,
                });

                await storageClient.SaveDocumentAsync(string.Format(DeploymentHistoryCollection, deviceTwin.DeviceId), deploymentModel.Id, new ValueServiceModel()
                {
                    Data = value
                }, this.GenerateCollectionLink(tenantId), Guid.NewGuid());
            }
        }
        private async Task SaveDeviceProperties(string tenantId, string deploymentId, List <TwinServiceModel> deviceTwins)
        {
            if (deviceTwins != null)
            {
                TwinServiceListModel existingDeviceTwins = await this.GetTwins(tenantId, string.Format(DeploymentDevicePropertiesCollection, deploymentId));

                if (existingDeviceTwins == null || (existingDeviceTwins != null && existingDeviceTwins.Items.Count == 0))
                {
                    await this.SaveDeviceTwins(tenantId, deploymentId, deviceTwins);
                }
                else
                {
                    foreach (var deviceTwin in deviceTwins)
                    {
                        CosmosOperations storageClient = await CosmosOperations.GetClientAsync();

                        var existingDeviceTwin = existingDeviceTwins.Items.FirstOrDefault(x => x.DeviceId == deviceTwin.DeviceId);
                        var value = JsonConvert.SerializeObject(
                            deviceTwin,
                            Formatting.Indented,
                            new JsonSerializerSettings
                        {
                            NullValueHandling = NullValueHandling.Ignore,
                        });

                        try
                        {
                            await storageClient.SaveDocumentAsync(string.Format(DeploymentDevicePropertiesCollection, deploymentId), deviceTwin.DeviceId, new ValueServiceModel()
                            {
                                Data = value, ETag = existingDeviceTwin.ETag
                            }, this.GenerateCollectionLink(tenantId));
                        }
                        catch (Exception)
                        {
                        }

                        // archive exisiting Device Twin
                        var archiveDeviceTwinValue = JsonConvert.SerializeObject(
                            existingDeviceTwin,
                            Formatting.Indented,
                            new JsonSerializerSettings
                        {
                            NullValueHandling = NullValueHandling.Ignore,
                        });
                        await storageClient.SaveDocumentAsync(string.Format(DeploymentHistoryPropertiesCollection, deploymentId, Guid.NewGuid().ToString()), deviceTwin.DeviceId, new ValueServiceModel()
                        {
                            Data = archiveDeviceTwinValue
                        }, this.GenerateCollectionLink(tenantId));
                    }
                }
            }
        }
        public static async Task <CosmosOperations> GetClientAsync()
        {
            await semaphoreSlim.WaitAsync();

            try
            {
                return(instance ?? (instance = CreateInstance()));
            }
            finally
            {
                semaphoreSlim.Release();
            }
        }
        private async Task <List <DeploymentServiceModel> > GetDeploymentsFromStorage(string tenantId, IEnumerable <string> deploymentIds)
        {
            var sql = QueryBuilder.GetDocumentsSql(
                "deployments",
                null,
                null,
                null,
                "_ts",
                null,
                "_ts",
                "asc",
                "_ts",
                0,
                100,
                deploymentIds.ToArray(),
                "Key");

            FeedOptions queryOptions = new FeedOptions
            {
                EnableCrossPartitionQuery = true,
                EnableScanInQuery         = true,
            };

            List <Document> docs = new List <Document>();

            try
            {
                CosmosOperations storageClient = await CosmosOperations.GetClientAsync();

                docs = await storageClient.QueryDocumentsAsync(
                    "pcs-storage",
                    $"pcs-{tenantId}",
                    queryOptions,
                    sql,
                    0,
                    100);

                var result = docs.Select(doc => new ValueServiceModel(doc));
                var deploymentFromStorage = result.Select(res => this.CreateDeploymentServiceModel(res)).ToList();

                return(deploymentFromStorage);
            }
            catch (ResourceNotFoundException e)
            {
                throw new ResourceNotFoundException($"No deployments exist in CosmosDb. The telemetry collection {$"pcs-{tenantId}"} does not exist.", e);
            }
        }
        private async Task SaveModuleTwin(string tenantId, string deploymentId, TwinServiceModel moduleTwin)
        {
            CosmosOperations storageClient = await CosmosOperations.GetClientAsync();

            var value = JsonConvert.SerializeObject(
                moduleTwin,
                Formatting.Indented,
                new JsonSerializerSettings
            {
                NullValueHandling = NullValueHandling.Ignore,
            });

            await storageClient.SaveDocumentAsync(string.Format(DeploymentEdgeModulePropertiesCollection, deploymentId), $"{moduleTwin.DeviceId}-{this.RemoveSpecialCharacters(moduleTwin.ModuleId)}", new ValueServiceModel()
            {
                Data = value
            }, this.GenerateCollectionLink(tenantId));
        }
        private async Task SaveDeployment(DeploymentServiceModel deployment, string tenantId)
        {
            CosmosOperations storageClient = await CosmosOperations.GetClientAsync();

            var value = JsonConvert.SerializeObject(
                deployment,
                Formatting.Indented,
                new JsonSerializerSettings
            {
                NullValueHandling = NullValueHandling.Ignore,
            });

            await storageClient.SaveDocumentAsync(DeploymentsCollection, deployment.Id, new ValueServiceModel()
            {
                Data = value, ETag = deployment.ETag
            }, this.GenerateCollectionLink(tenantId));
        }
        private async Task SaveDeviceStatuses(IDictionary <string, DeploymentStatus> deviceStatuses, string deploymentId, string tenantId)
        {
            CosmosOperations storageClient = await CosmosOperations.GetClientAsync();

            var sql = CosmosOperations.GetDocumentsByCollectionId("CollectionId", string.Format(DeviceStatusesCollection, deploymentId));

            var existingDeviceStatuses = await storageClient.QueryAllDocumentsAsync(
                "pcs-storage",
                $"pcs-{tenantId}",
                this.DefaultQueryOptions,
                sql);

            if (existingDeviceStatuses != null && existingDeviceStatuses.Count > 0)
            {
                foreach (var item in existingDeviceStatuses)
                {
                    await storageClient.DeleteDocumentAsync(item.Id, this.GenerateCollectionLink(tenantId));
                }
            }

            if (deviceStatuses != null)
            {
                for (int i = 0; i < deviceStatuses.Count; i = i + DeviceStatusLength)
                {
                    var items = deviceStatuses.Skip(i).Take(DeviceStatusLength).ToDictionary(p => p.Key, p => p.Value);
                    var value = JsonConvert.SerializeObject(
                        new DeviceStatusServiceModel
                    {
                        DeviceStatuses = items,
                        DeploymentId   = deploymentId,
                    },
                        Formatting.Indented,
                        new JsonSerializerSettings
                    {
                        NullValueHandling = NullValueHandling.Ignore,
                    });
                    await storageClient.SaveDocumentAsync(string.Format(DeviceStatusesCollection, deploymentId), Guid.NewGuid().ToString(), new ValueServiceModel()
                    {
                        Data = value
                    }, this.GenerateCollectionLink(tenantId));
                }
            }
        }
        public async Task <List <DeploymentServiceModel> > GetDeploymentsByIdFromStorage(string tenantId, string[] deploymentIds)
        {
            var sql = QueryBuilder.GetDocumentsSql(
                "deployments",
                null,
                null,
                null,
                "_ts",
                null,
                "_ts",
                "desc",
                "_ts",
                0,
                100,
                deploymentIds,
                "Key");

            List <Document> docs = new List <Document>();

            try
            {
                CosmosOperations storageClient = await CosmosOperations.GetClientAsync();

                docs = await storageClient.QueryDocumentsAsync(
                    "pcs-storage",
                    $"pcs-{tenantId}",
                    this.DefaultQueryOptions,
                    sql,
                    0,
                    100);

                var result = docs.Select(doc => new ValueServiceModel(doc));
                var deploymentFromStorage = result.Select(res => this.CreateDeploymentServiceModel(res)).ToList();

                return(deploymentFromStorage);
            }
            catch (ResourceNotFoundException e)
            {
                throw new ResourceNotFoundException($"No deployments exist in CosmosDb. The telemetry collection {$"pcs-{tenantId}"} does not exist.", e);
            }
        }
        private async Task <IDictionary <string, DeploymentStatus> > FetchDeviceStatuses(string tenantId, string deploymentId)
        {
            CosmosOperations storageClient = await CosmosOperations.GetClientAsync();

            var sql      = CosmosOperations.GetDocumentsByCollectionId("CollectionId", string.Format(DeviceStatusesCollection, deploymentId));
            var statuses = new Dictionary <string, DeploymentStatus>();

            var existingDeviceStatuses = await storageClient.QueryAllDocumentsAsync(
                "pcs-storage",
                $"pcs-{tenantId}",
                this.DefaultQueryOptions,
                sql);

            if (existingDeviceStatuses != null && existingDeviceStatuses.Count > 0)
            {
                foreach (var item in existingDeviceStatuses.Select(doc => new ValueServiceModel(doc)))
                {
                    statuses = statuses.Union(JsonConvert.DeserializeObject <DeviceStatusServiceModel>(item.Data).DeviceStatuses).ToDictionary(k => k.Key, v => v.Value);
                }
            }

            return(statuses);
        }
        private async Task <bool> DoesDeploymentTwinsExist(string tenantId, string deploymentId)
        {
            var         sql          = QueryBuilder.GetDeploymentDeviceDocumentsSqlByKey("Key", deploymentId);
            FeedOptions queryOptions = new FeedOptions
            {
                EnableCrossPartitionQuery = true,
                EnableScanInQuery         = true,
            };

            CosmosOperations storageClient = await CosmosOperations.GetClientAsync();

            List <Document> docs = new List <Document>();

            docs = await storageClient.QueryDocumentsAsync(
                "pcs-storage",
                $"pcs-{tenantId}",
                this.DefaultQueryOptions,
                sql,
                0,
                1);

            return(docs != null && docs.Count > 0);
        }
        private async Task StoreModuleTwinsInStorage(string tenantId, List <TwinServiceModel> moduleTwins, string deploymentId)
        {
            if (moduleTwins != null && moduleTwins.Count > 0)
            {
                TwinServiceListModel existingModuleTwins = await this.GetTwins(tenantId, string.Format(DeploymentEdgeModulePropertiesCollection, deploymentId));

                if (existingModuleTwins == null || (existingModuleTwins != null && existingModuleTwins.Items.Count == 0))
                {
                    foreach (var moduleTwin in moduleTwins)
                    {
                        await this.SaveModuleTwin(tenantId, deploymentId, moduleTwin);
                    }
                }
                else
                {
                    foreach (var moduleTwin in moduleTwins)
                    {
                        var existingModuleTwin         = existingModuleTwins.Items.FirstOrDefault(x => x.ModuleId == moduleTwin.ModuleId && x.DeviceId == moduleTwin.DeviceId);
                        CosmosOperations storageClient = await CosmosOperations.GetClientAsync();

                        var value = JsonConvert.SerializeObject(
                            moduleTwin,
                            Formatting.Indented,
                            new JsonSerializerSettings
                        {
                            NullValueHandling = NullValueHandling.Ignore,
                        });

                        try
                        {
                            await storageClient.SaveDocumentAsync(string.Format(DeploymentEdgeModulePropertiesCollection, deploymentId), $"{moduleTwin.DeviceId}-{this.RemoveSpecialCharacters(moduleTwin.ModuleId)}", new ValueServiceModel()
                            {
                                Data = value, ETag = existingModuleTwin.ETag
                            }, this.GenerateCollectionLink(tenantId));
                        }
                        catch (Exception)
                        {
                        }

                        // archive exisiting Device Twin
                        var archiveModuleTwinValue = JsonConvert.SerializeObject(
                            existingModuleTwin,
                            Formatting.Indented,
                            new JsonSerializerSettings
                        {
                            NullValueHandling = NullValueHandling.Ignore,
                        });
                        try
                        {
                            await storageClient.SaveDocumentAsync(string.Format(DeploymentModuleHistoryPropertiesCollection, deploymentId, Guid.NewGuid().ToString()), $"{moduleTwin.DeviceId}-{this.RemoveSpecialCharacters(moduleTwin.ModuleId)}", new ValueServiceModel()
                            {
                                Data = archiveModuleTwinValue
                            }, this.GenerateCollectionLink(tenantId));
                        }
                        catch (Exception)
                        {
                        }
                    }
                }
            }
        }