Temporary repo to track my changes on LTS functions app porting
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

327 lines
12 KiB

  1. using Microsoft.Azure.Cosmos;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace CDP
  8. {
  9. public class ContactsDB
  10. {
  11. private static Lazy<CosmosClient> lazyClient = new Lazy<CosmosClient>(InitializeCosmosClient);
  12. private static CosmosClient cosmosClient => lazyClient.Value;
  13. private static string DatabaseName = "CDP";
  14. private static string ContainerName = "Contacts";
  15. private static CosmosClient InitializeCosmosClient()
  16. {
  17. // Perform any initialization here
  18. var uri = "https://cdplite.documents.azure.com:443/";
  19. var authKey = "VPbg8RpzyI3XwhC2o0dIUtYFs33ghxORCqZeNAyg8vg4HWUBjM41BUxP0qLFXEvFh6ewQY1uKv52ACDbsEN1AQ==";
  20. return new CosmosClient(uri, authKey);
  21. }
  22. public static async Task<List<ContactRecord>> GetUserContacts(string AppKey, string UserId)
  23. {
  24. var results = new List<ContactRecord>();
  25. Container container = cosmosClient.GetContainer(DatabaseName, ContainerName);
  26. // Fetch the metadata document for the customer ID
  27. var metadataDocumentId = GetMetaDocumentKey(AppKey, UserId);
  28. MetadataDocumentContact metadataDocument = null;
  29. try
  30. {
  31. var metadataDocumentResponse =
  32. await container.ReadItemAsync<MetadataDocumentContact>(metadataDocumentId, new PartitionKey(metadataDocumentId));
  33. metadataDocument = metadataDocumentResponse.Resource;
  34. }
  35. catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
  36. {
  37. }
  38. catch (Exception e)
  39. {
  40. // Helpers.LogIt(e.Message);
  41. }
  42. if (metadataDocument == null)
  43. return results;
  44. // Determine the partition keys within the date range
  45. var partitionKeysInDocument = metadataDocument.PartitionKeys;
  46. // Fetch the audit records for each partition key within the date range
  47. foreach (var partitionKey in partitionKeysInDocument)
  48. {
  49. ItemResponse<ContactsDocument> response = await container.ReadItemAsync<ContactsDocument>(partitionKey, new PartitionKey(partitionKey));
  50. if (response == null)
  51. continue;
  52. ContactsDocument t = response.Resource;
  53. results.AddRange(t.Records);
  54. }
  55. return results;
  56. }
  57. public static async Task<bool> AppendRecord(string appKey, string userId, ContactRecord rec)
  58. {
  59. try
  60. {
  61. var metadataDocument = await GetMetadataDocument(appKey, userId);
  62. if (metadataDocument == null)
  63. return false;
  64. string dayKey = metadataDocument.GetLatestKey(appKey, userId);
  65. ContactsDocument al = await GetContactDocument(dayKey);
  66. if (al == null)
  67. {
  68. al = new ContactsDocument();
  69. al.AppKey = appKey;
  70. al.UserId = userId;
  71. }
  72. al.Records.Add(rec);
  73. await UpdateContactsDocument(al);
  74. return true;
  75. }
  76. catch (Exception e)
  77. {
  78. return false;
  79. }
  80. }
  81. public static async Task<bool> RemoveRecord(string appKey, string userId, string EmailId)
  82. {
  83. Container container = cosmosClient.GetContainer(DatabaseName, ContainerName);
  84. // Fetch the metadata document for the customer ID
  85. var metadataDocumentId = GetMetaDocumentKey(appKey, userId);
  86. MetadataDocumentContact metadataDocument = null;
  87. try
  88. {
  89. var metadataDocumentResponse = await container.ReadItemAsync<MetadataDocumentContact>(metadataDocumentId, new PartitionKey(metadataDocumentId));
  90. metadataDocument = metadataDocumentResponse.Resource;
  91. }
  92. catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
  93. {
  94. }
  95. catch (Exception e)
  96. {
  97. // Helpers.LogIt(e.Message);
  98. }
  99. if (metadataDocument == null)
  100. return false;
  101. // Determine the partition keys within the date range
  102. var partitionKeysInDocument = metadataDocument.PartitionKeys;
  103. // Fetch the audit records for each partition key within the date range
  104. foreach (var partitionKey in partitionKeysInDocument)
  105. {
  106. ItemResponse<ContactsDocument> response = await container.ReadItemAsync<ContactsDocument>(partitionKey, new PartitionKey(partitionKey));
  107. if (response == null)
  108. continue;
  109. ContactsDocument t = response.Resource;
  110. var originalCount = t.Records.Count;
  111. t.Records = t.Records.Where(item => item.Email != EmailId).ToList();
  112. var afterFilterCount = t.Records.Count;
  113. if (originalCount != afterFilterCount)
  114. {
  115. ItemResponse<ContactsDocument> updateResponse = await container.ReplaceItemAsync(
  116. item: t,
  117. id: t.id,
  118. partitionKey: new PartitionKey(partitionKey));
  119. return true;
  120. }
  121. }
  122. return false;
  123. }
  124. public static async Task UpdateContactsDocument(ContactsDocument al)
  125. {
  126. if (al.Records.Count == 0)
  127. return;
  128. List<ContactsDocument> lal = await SplitAuditlog(al);
  129. Container container = cosmosClient.GetContainer(DatabaseName, ContainerName);
  130. foreach (ContactsDocument ial in lal)
  131. {
  132. ItemResponse<ContactsDocument> r = await container.UpsertItemAsync(ial, new PartitionKey(ial.id));
  133. }
  134. await UpdateMetadata(container, lal);
  135. }
  136. static async Task UpdateMetadata(Container container, List<ContactsDocument> lal)
  137. {
  138. bool update = false;
  139. string pKey = GetMetaDocumentKey(lal[0].AppKey, lal[0].UserId);
  140. MetadataDocument md = null;
  141. try
  142. {
  143. ItemResponse<MetadataDocument> response =
  144. await container.ReadItemAsync<MetadataDocument>(pKey, new PartitionKey(pKey));
  145. md = response.Resource;
  146. }
  147. catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
  148. {
  149. md = new MetadataDocument()
  150. {
  151. id = GetMetaDocumentKey(lal[0].AppKey, lal[0].UserId),
  152. PartitionKeys = new List<string>()
  153. };
  154. }
  155. catch (Exception e)
  156. {
  157. // Helpers.LogIt(e.Message);
  158. return;
  159. }
  160. if (md == null)
  161. {
  162. // Helpers.LogIt("Something ugly happened!");
  163. return;
  164. }
  165. foreach (ContactsDocument log in lal)
  166. {
  167. if (md.PartitionKeys.Contains(log.id))
  168. continue;
  169. md.PartitionKeys.Add(log.id);
  170. update = true;
  171. }
  172. if (update)
  173. {
  174. try
  175. {
  176. ItemResponse<MetadataDocument> r = await container.UpsertItemAsync(md, new PartitionKey(pKey));
  177. }
  178. catch (Exception e)
  179. {
  180. // Helpers.LogIt(e.Message);
  181. return;
  182. }
  183. }
  184. }
  185. static async Task<List<ContactsDocument>> SplitAuditlog(ContactsDocument al)
  186. {
  187. List<ContactsDocument> lal = new List<ContactsDocument>();
  188. var sortedRecords = al.Records.OrderBy(record => record.EventTime).ToList();
  189. var currentGroup = new List<ContactRecord>();
  190. var currentGroupSize = 0;
  191. int MaxDocumentSizeInBytes = 2 * 1024 * 1024; // 2MB
  192. int index = al.Index; // start for index passed in
  193. foreach (var record in sortedRecords)
  194. {
  195. var recordSize = record.CalculateRecordSize();
  196. if (currentGroupSize + recordSize > MaxDocumentSizeInBytes)
  197. {
  198. ContactsDocument i = new ContactsDocument();
  199. i.Index = index++;
  200. i.Records = currentGroup;
  201. i.AppKey = al.AppKey;
  202. i.UserId = al.UserId;
  203. lal.Add(i);
  204. currentGroup = new List<ContactRecord>();
  205. currentGroupSize = 0;
  206. }
  207. currentGroup.Add(record);
  208. currentGroupSize += recordSize;
  209. }
  210. if (currentGroup.Any())
  211. {
  212. ContactsDocument i = new ContactsDocument();
  213. i.Index = index++;
  214. i.Records = currentGroup;
  215. i.AppKey = al.AppKey;
  216. i.UserId = al.UserId;
  217. lal.Add(i);
  218. }
  219. return lal;
  220. }
  221. public static async Task<ContactsDocument> GetContactDocument(string key)
  222. {
  223. try
  224. {
  225. Container container = cosmosClient.GetContainer(DatabaseName, ContainerName);
  226. ItemResponse<ContactsDocument> response = await container.ReadItemAsync<ContactsDocument>(key, new PartitionKey(key));
  227. if (response == null)
  228. return null;
  229. ContactsDocument t = response.Resource;
  230. return t;
  231. }
  232. catch (Exception e)
  233. {
  234. return null;
  235. }
  236. }
  237. static async Task<MetadataDocumentContact> GetMetadataDocument(string appKey, string userId)
  238. {
  239. MetadataDocumentContact md = null;
  240. string pKey = GetMetaDocumentKey(appKey, userId);
  241. string id = GetDocumentId(appKey, userId);
  242. PartitionKey partitionKey = new PartitionKeyBuilder()
  243. .Add(pKey)
  244. .Build();
  245. Container container = cosmosClient.GetContainer(DatabaseName, ContainerName);
  246. try
  247. {
  248. ItemResponse<MetadataDocumentContact> response =
  249. await container.ReadItemAsync<MetadataDocumentContact>(pKey, partitionKey);
  250. md = response.Resource;
  251. }
  252. catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
  253. {
  254. md = new MetadataDocumentContact()
  255. {
  256. id = id,
  257. PartitionKeys = new List<string>()
  258. };
  259. }
  260. catch (Exception e)
  261. {
  262. // Helpers.LogIt(e.Message);
  263. return null;
  264. }
  265. if (md == null)
  266. {
  267. // Helpers.LogIt("Something ugly happened!");
  268. return null;
  269. }
  270. return md;
  271. }
  272. static string GetMetaDocumentKey(string appKey, string userId)
  273. {
  274. return $"{appKey}-{userId}-meta";
  275. }
  276. static string GetDocumentId(string appKey, string userId)
  277. {
  278. return $"{appKey}-{userId}";
  279. }
  280. }
  281. }