Skip to content

Commit 7f21a33

Browse files
committed
Fix GAL extraction and make extraction resilient to errors
1. Fixed People API query: Use 'scoredEmailAddresses' instead of 'emailAddresses' which doesn't exist on the Person type 2. Made each extraction source independent with its own error handling so one failure doesn't prevent other sources from being processed or statistics from being calculated 3. Statistics are now always calculated regardless of individual extraction failures https://claude.ai/code/session_01M1VXcPRu18nAEEtxeCfsqj
1 parent 6dae46b commit 7f21a33

2 files changed

Lines changed: 50 additions & 38 deletions

File tree

src/FindMyContacts/Services/ContactExtractionService.cs

Lines changed: 49 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -40,83 +40,95 @@ public async Task<ExtractionResult> ExtractContactsAsync(ExtractionOptions optio
4040

4141
_logger.LogInformation("Starting contact extraction...");
4242

43-
try
43+
// Extract from inbox
44+
if (options.IncludeInbox)
4445
{
45-
// Extract from inbox
46-
if (options.IncludeInbox)
46+
await ExtractWithErrorHandling("inbox", result, async () =>
4747
{
48-
_logger.LogInformation("Extracting contacts from inbox...");
4948
await foreach (var contact in _emailExtractor.ExtractFromInboxAsync(options, cancellationToken))
5049
{
5150
_aggregator.AddContact(contact);
5251
result.Statistics.TotalEmailsProcessed++;
5352
}
54-
}
53+
});
54+
}
5555

56-
// Extract from sent items
57-
if (options.IncludeSentItems)
56+
// Extract from sent items
57+
if (options.IncludeSentItems)
58+
{
59+
await ExtractWithErrorHandling("sent items", result, async () =>
5860
{
59-
_logger.LogInformation("Extracting contacts from sent items...");
6061
await foreach (var contact in _emailExtractor.ExtractFromSentItemsAsync(options, cancellationToken))
6162
{
6263
_aggregator.AddContact(contact);
6364
}
64-
}
65+
});
66+
}
6567

66-
// Extract from calendar events
67-
_logger.LogInformation("Extracting contacts from calendar events...");
68+
// Extract from calendar events
69+
await ExtractWithErrorHandling("calendar events", result, async () =>
70+
{
6871
await foreach (var contact in _meetingExtractor.ExtractFromEventsAsync(options, cancellationToken))
6972
{
7073
_aggregator.AddContact(contact);
7174
result.Statistics.TotalMeetingsProcessed++;
7275
}
76+
});
7377

74-
// Extract from Outlook personal contacts
75-
if (options.IncludeOutlookContacts)
78+
// Extract from Outlook personal contacts
79+
if (options.IncludeOutlookContacts)
80+
{
81+
await ExtractWithErrorHandling("Outlook contacts", result, async () =>
7682
{
77-
_logger.LogInformation("Extracting Outlook personal contacts...");
7883
await foreach (var contact in _outlookExtractor.ExtractContactsAsync(options, cancellationToken))
7984
{
8085
_aggregator.AddContact(contact);
8186
result.Statistics.TotalOutlookContactsProcessed++;
8287
}
83-
}
88+
});
89+
}
8490

85-
// Extract from Global Address List
86-
if (options.IncludeGal)
91+
// Extract from Global Address List
92+
if (options.IncludeGal)
93+
{
94+
await ExtractWithErrorHandling("Global Address List", result, async () =>
8795
{
88-
_logger.LogInformation("Extracting contacts from Global Address List...");
8996
await foreach (var contact in _galExtractor.ExtractContactsAsync(options, cancellationToken))
9097
{
9198
_aggregator.AddContact(contact);
9299
result.Statistics.TotalGalContactsProcessed++;
93100
}
94-
}
101+
});
102+
}
95103

96-
// Get deduplicated contacts
97-
result.Contacts = _aggregator.GetDeduplicatedContacts().ToList();
98-
result.Statistics.TotalContactsFound = _aggregator.TotalContactsAdded;
99-
result.Statistics.UniqueContactsAfterDeduplication = _aggregator.UniqueContactCount;
104+
// Always calculate final statistics
105+
result.Contacts = _aggregator.GetDeduplicatedContacts().ToList();
106+
result.Statistics.TotalContactsFound = _aggregator.TotalContactsAdded;
107+
result.Statistics.UniqueContactsAfterDeduplication = _aggregator.UniqueContactCount;
108+
CalculateStatistics(result);
100109

101-
// Calculate enrichment statistics
102-
CalculateStatistics(result);
110+
result.ExtractionCompletedUtc = DateTime.UtcNow;
103111

104-
_logger.LogInformation(
105-
"Extraction complete. Found {Total} contacts, {Unique} unique after deduplication",
106-
result.Statistics.TotalContactsFound,
107-
result.Statistics.UniqueContactsAfterDeduplication);
108-
}
109-
catch (Exception ex)
112+
_logger.LogInformation(
113+
"Extraction complete. Found {Total} contacts, {Unique} unique after deduplication",
114+
result.Statistics.TotalContactsFound,
115+
result.Statistics.UniqueContactsAfterDeduplication);
116+
117+
return result;
118+
}
119+
120+
private async Task ExtractWithErrorHandling(string source, ExtractionResult result, Func<Task> extractAction)
121+
{
122+
try
110123
{
111-
_logger.LogError(ex, "Error during contact extraction");
112-
result.Errors.Add(ex.Message);
124+
_logger.LogInformation("Extracting contacts from {Source}...", source);
125+
await extractAction();
113126
}
114-
finally
127+
catch (Exception ex)
115128
{
116-
result.ExtractionCompletedUtc = DateTime.UtcNow;
129+
_logger.LogError(ex, "Error extracting from {Source}", source);
130+
result.Errors.Add($"Error extracting from {source}: {ex.Message}");
117131
}
118-
119-
return result;
120132
}
121133

122134
private static void CalculateStatistics(ExtractionResult result)

src/FindMyContacts/Services/GalContactExtractor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public async IAsyncEnumerable<Contact> ExtractContactsAsync(
4040
requestConfig.QueryParameters.Top = 100;
4141
requestConfig.QueryParameters.Select =
4242
[
43-
"id", "displayName", "givenName", "surname", "emailAddresses",
43+
"id", "displayName", "givenName", "surname", "scoredEmailAddresses",
4444
"companyName", "jobTitle", "department", "officeLocation",
4545
"phones", "userPrincipalName"
4646
];

0 commit comments

Comments
 (0)