using Microsoft.Azure.Cosmos; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CDP { public class ContactsDB { private static Lazy lazyClient = new Lazy(InitializeCosmosClient); private static CosmosClient cosmosClient => lazyClient.Value; private static string DatabaseName = "CDP"; private static string ContainerName = "Contacts"; private static CosmosClient InitializeCosmosClient() { // Perform any initialization here var uri = "https://cdplite.documents.azure.com:443/"; var authKey = "VPbg8RpzyI3XwhC2o0dIUtYFs33ghxORCqZeNAyg8vg4HWUBjM41BUxP0qLFXEvFh6ewQY1uKv52ACDbsEN1AQ=="; return new CosmosClient(uri, authKey); } public static async Task> GetUserContacts(string AppKey, string UserId) { var results = new List(); Container container = cosmosClient.GetContainer(DatabaseName, ContainerName); // Fetch the metadata document for the customer ID var metadataDocumentId = GetMetaDocumentKey(AppKey, UserId); MetadataDocumentContact metadataDocument = null; try { var metadataDocumentResponse = await container.ReadItemAsync(metadataDocumentId, new PartitionKey(metadataDocumentId)); metadataDocument = metadataDocumentResponse.Resource; } catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) { } catch (Exception e) { // Helpers.LogIt(e.Message); } if (metadataDocument == null) return results; // Determine the partition keys within the date range var partitionKeysInDocument = metadataDocument.PartitionKeys; // Fetch the audit records for each partition key within the date range foreach (var partitionKey in partitionKeysInDocument) { ItemResponse response = await container.ReadItemAsync(partitionKey, new PartitionKey(partitionKey)); if (response == null) continue; ContactsDocument t = response.Resource; results.AddRange(t.Records); } return results; } public static async Task AppendRecord(string appKey, string userId, ContactRecord rec) { try { var metadataDocument = await GetMetadataDocument(appKey, userId); if (metadataDocument == null) return false; string dayKey = metadataDocument.GetLatestKey(appKey, userId); ContactsDocument al = await GetContactDocument(dayKey); if (al == null) { al = new ContactsDocument(); al.AppKey = appKey; al.UserId = userId; } al.Records.Add(rec); await UpdateContactsDocument(al); return true; } catch (Exception e) { return false; } } public static async Task RemoveRecord(string appKey, string userId, string EmailId) { Container container = cosmosClient.GetContainer(DatabaseName, ContainerName); // Fetch the metadata document for the customer ID var metadataDocumentId = GetMetaDocumentKey(appKey, userId); MetadataDocumentContact metadataDocument = null; try { var metadataDocumentResponse = await container.ReadItemAsync(metadataDocumentId, new PartitionKey(metadataDocumentId)); metadataDocument = metadataDocumentResponse.Resource; } catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) { } catch (Exception e) { // Helpers.LogIt(e.Message); } if (metadataDocument == null) return false; // Determine the partition keys within the date range var partitionKeysInDocument = metadataDocument.PartitionKeys; // Fetch the audit records for each partition key within the date range foreach (var partitionKey in partitionKeysInDocument) { ItemResponse response = await container.ReadItemAsync(partitionKey, new PartitionKey(partitionKey)); if (response == null) continue; ContactsDocument t = response.Resource; var originalCount = t.Records.Count; t.Records = t.Records.Where(item => item.Email != EmailId).ToList(); var afterFilterCount = t.Records.Count; if (originalCount != afterFilterCount) { ItemResponse updateResponse = await container.ReplaceItemAsync( item: t, id: t.id, partitionKey: new PartitionKey(partitionKey)); return true; } } return false; } public static async Task UpdateContactsDocument(ContactsDocument al) { if (al.Records.Count == 0) return; List lal = await SplitAuditlog(al); Container container = cosmosClient.GetContainer(DatabaseName, ContainerName); foreach (ContactsDocument ial in lal) { ItemResponse r = await container.UpsertItemAsync(ial, new PartitionKey(ial.id)); } await UpdateMetadata(container, lal); } static async Task UpdateMetadata(Container container, List lal) { bool update = false; string pKey = GetMetaDocumentKey(lal[0].AppKey, lal[0].UserId); MetadataDocument md = null; try { ItemResponse response = await container.ReadItemAsync(pKey, new PartitionKey(pKey)); md = response.Resource; } catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) { md = new MetadataDocument() { id = GetMetaDocumentKey(lal[0].AppKey, lal[0].UserId), PartitionKeys = new List() }; } catch (Exception e) { // Helpers.LogIt(e.Message); return; } if (md == null) { // Helpers.LogIt("Something ugly happened!"); return; } foreach (ContactsDocument log in lal) { if (md.PartitionKeys.Contains(log.id)) continue; md.PartitionKeys.Add(log.id); update = true; } if (update) { try { ItemResponse r = await container.UpsertItemAsync(md, new PartitionKey(pKey)); } catch (Exception e) { // Helpers.LogIt(e.Message); return; } } } static async Task> SplitAuditlog(ContactsDocument al) { List lal = new List(); var sortedRecords = al.Records.OrderBy(record => record.EventTime).ToList(); var currentGroup = new List(); var currentGroupSize = 0; int MaxDocumentSizeInBytes = 2 * 1024 * 1024; // 2MB int index = al.Index; // start for index passed in foreach (var record in sortedRecords) { var recordSize = record.CalculateRecordSize(); if (currentGroupSize + recordSize > MaxDocumentSizeInBytes) { ContactsDocument i = new ContactsDocument(); i.Index = index++; i.Records = currentGroup; i.AppKey = al.AppKey; i.UserId = al.UserId; lal.Add(i); currentGroup = new List(); currentGroupSize = 0; } currentGroup.Add(record); currentGroupSize += recordSize; } if (currentGroup.Any()) { ContactsDocument i = new ContactsDocument(); i.Index = index++; i.Records = currentGroup; i.AppKey = al.AppKey; i.UserId = al.UserId; lal.Add(i); } return lal; } public static async Task GetContactDocument(string key) { try { Container container = cosmosClient.GetContainer(DatabaseName, ContainerName); ItemResponse response = await container.ReadItemAsync(key, new PartitionKey(key)); if (response == null) return null; ContactsDocument t = response.Resource; return t; } catch (Exception e) { return null; } } static async Task GetMetadataDocument(string appKey, string userId) { MetadataDocumentContact md = null; string pKey = GetMetaDocumentKey(appKey, userId); string id = GetDocumentId(appKey, userId); PartitionKey partitionKey = new PartitionKeyBuilder() .Add(pKey) .Build(); Container container = cosmosClient.GetContainer(DatabaseName, ContainerName); try { ItemResponse response = await container.ReadItemAsync(pKey, partitionKey); md = response.Resource; } catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound) { md = new MetadataDocumentContact() { id = id, PartitionKeys = new List() }; } catch (Exception e) { // Helpers.LogIt(e.Message); return null; } if (md == null) { // Helpers.LogIt("Something ugly happened!"); return null; } return md; } static string GetMetaDocumentKey(string appKey, string userId) { return $"{appKey}-{userId}-meta"; } static string GetDocumentId(string appKey, string userId) { return $"{appKey}-{userId}"; } } }