public async Task UpdateGroupAsync(GroupMemberDiff memberDiff, bool ignoreChangeLimit = false) { if (memberDiff is null) { throw new ArgumentNullException(nameof(memberDiff)); } if (!ignoreChangeLimit && memberDiff.ChangeRatio < _changeRatioLowerLimit) { throw new ChangeRatioException(); } IGroupStore store = GetGroupStore(memberDiff.Document); foreach (GroupMember member in memberDiff.Remove) { await store.RemoveGroupMemberAsync(member, memberDiff.Document.GroupId); await _logger?.StoreOperationalLogItemAsync(new OperationalLogItem(memberDiff.Document, GroupMemberOperations.Remove, member)); } foreach (GroupMember member in memberDiff.Add) { await store.AddGroupMemberAsync(member, memberDiff.Document.GroupId); await _logger?.StoreOperationalLogItemAsync(new OperationalLogItem(memberDiff.Document, GroupMemberOperations.Add, member)); } }
public async Task <IActionResult> GetDiffAsync(bool unchanged) { Grouper backend = GetGrouperBackend(); GroupMemberDiff diff = await backend.GetMemberDiffAsync(await Helper.MakeDocumentAsync(Request), unchanged); return(Ok(diff)); }
public async Task <IActionResult> GetDiffForStoredDocumentAsync(Guid id, bool unchanged) { GrouperDocumentEntry entry = (await GetDocumentDb().GetEntriesByDocumentIdAsync(id)).FirstOrDefault(); if (entry == null) { return(BadRequest()); } // Grouper backend = GetGrouperBackend(); GroupMemberDiff diff = await _grouperBackend.GetMemberDiffAsync(entry.Document, unchanged); return(Ok(diff)); }
public void TestSerializedPropertyNames() { GroupMemberDiff diff = new GroupMemberDiff( document: TestHelpers.MakeDocument(), addMemberCollection: new GroupMemberCollection(), removeMemberCollection: new GroupMemberCollection(), unchangedMemberCollection: new GroupMemberCollection(), changeRatio: 0 ); string serializedDiff = JsonConvert.SerializeObject(diff); JObject obj = JObject.Parse(serializedDiff); Assert.True(obj.ContainsKey("document")); Assert.True(obj.ContainsKey("add")); Assert.True(obj.ContainsKey("remove")); Assert.True(obj.ContainsKey("unchanged")); Assert.True(obj.ContainsKey("ratio")); }
public async Task <IActionResult> InvokeGrouper(bool ignoreChangelimit) { GrouperDocument document = await Helper.MakeDocumentAsync(Request); Grouper grouper = GetGrouperBackend(); GroupMemberDiff diff = await grouper.GetMemberDiffAsync(document); // await grouper.UpdateGroupAsync(diff, ignoreChangelimit); var changes = new List <OperationalLogItem>(); foreach (GroupMember member in diff.Add) { changes.Add(new OperationalLogItem(document, GroupMemberOperations.Add, member)); } foreach (GroupMember member in diff.Remove) { changes.Add(new OperationalLogItem(document, GroupMemberOperations.Remove, member)); } return(Ok(changes)); }
public void TestConstruction() { double ratio = 0.5; GrouperDocument doc = TestHelpers.MakeDocument(); GroupMemberCollection add = new GroupMemberCollection(); add.Add(new GroupMember(Guid.Empty, "M1", GroupMemberTypes.AzureAd)); GroupMemberCollection remove = new GroupMemberCollection(); remove.Add(new GroupMember(Guid.Empty, "M2", GroupMemberTypes.AzureAd)); GroupMemberCollection unchanged = new GroupMemberCollection(); unchanged.Add(new GroupMember(Guid.Empty, "M3", GroupMemberTypes.AzureAd)); GroupMemberDiff diff = new GroupMemberDiff(doc, add, remove, unchanged, ratio); Assert.NotNull(diff.Add.FirstOrDefault(m => m.DisplayName == "M1")); Assert.NotNull(diff.Remove.FirstOrDefault(m => m.DisplayName == "M2")); Assert.NotNull(diff.Unchanged.FirstOrDefault(m => m.DisplayName == "M3")); Assert.Equal(ratio, diff.ChangeRatio); Assert.Equal(doc, diff.Document); }
private void InvokeGrouper(object source, ElapsedEventArgs e) { Timer timer = (Timer)source; List <GrouperDocumentEntry> entries = new List <GrouperDocumentEntry>(); bool processAllDocuments = ShouldProcessAllDocuments(); if (processAllDocuments) { _lastFullProcessHour = DateTime.Now.Hour; entries.AddRange(_documentDb.GetAllEntriesAsync().GetAwaiter().GetResult()); } else { // Get documents that changed since the last timer event. // It doesn't matter if we miss a document. It will be processed in the next full run DateTime start = DateTime.Now.AddMilliseconds(-_workInterval); entries.AddRange(_documentDb.GetEntriesByAgeAsync(start).GetAwaiter().GetResult()); // Get documents that have a processing interval hint and // where the interval has passed. IEnumerable <GrouperDocumentEntry> entriesWithInterval = _documentDb.GetEntriesByProcessingInterval(min: 1).GetAwaiter().GetResult(); foreach (GrouperDocumentEntry entry in entriesWithInterval) { if (_lastProcessedDictionary.TryGetValue(entry.Document.Id, out DateTime lastProcessed)) { if (lastProcessed.AddMinutes(entry.Document.ProcessingInterval) < DateTime.Now) { entries.Add(entry); } // If it's not in the dictionary, process interval was added to the document // after the service started and the document should be picked up by the // document age check above (this includes both new and changed documents) } } } foreach (GrouperDocumentEntry entry in entries) { DebugPrint("Processing group {0}", entry.GroupName); if (_stopRequested) { return; } try { GroupMemberDiff diff = _grouper.GetMemberDiffAsync(entry.Document).GetAwaiter().GetResult(); _grouper.UpdateGroupAsync(diff).GetAwaiter().GetResult(); if (entry.Document.ProcessingInterval > 0) { _lastProcessedDictionary[entry.Document.Id] = DateTime.Now; } DebugPrint("Processed group {0}. {1} added, {2} removed.", entry.GroupName, diff.Add.Count(), diff.Remove.Count()); } catch (Exception ex) { string message = ex.Message; if (ex.InnerException != null) { message = ex.InnerException.Message; } WriteToLogDb(entry.Document, message, LogLevels.Error); if (!(ex is GroupNotFoundException || ex is MemberNotFoundException || ex is ChangeRatioException)) { WriteToEventLog(message, EventLogEntryType.Error); } } } // There is a memory leak somewhere. Likely in Exo. So we tear down Grouper and build a new one after // every full run and see if that helps narrow down the leak. if (processAllDocuments) { SetupGrouper(); } if (!_stopRequested) { timer.Enabled = true; } }