private Task ProcessDriveChangesAsync(ref string deltaLink, Func <DriveItem, bool> relevancePredicate, Func <IEnumerable <DriveItem>, Task> action, string resetLink) { var graph = graphClientContext.GraphClient; IDriveItemDeltaRequest deltaRequest = new DriveItemDeltaRequest(deltaLink, graph, null); IDriveItemDeltaCollectionPage delta = null; var preResult = new List <Task>(); do { try { delta = deltaRequest.GetAsync().Result; } catch (Exception e) when((e.InnerException as ServiceException)?.Error?.Code == "resyncRequired") { delta = new DriveItemDeltaRequest(resetLink, graph, null).GetAsync().Result; } var relevantItems = delta.Where(relevancePredicate); if (!relevantItems.Any()) { continue; } preResult.Add(action.Invoke(relevantItems)); } while ((deltaRequest = delta.NextPageRequest) != null); deltaLink = (string)delta.AdditionalData["@odata.deltaLink"]; return(Task.WhenAll(preResult)); }
/// <summary> /// Request the delta stream from OneDrive to find files that have changed between notifications for this account /// </summary> /// <param name="state">Our internal state information for the subscription we're processing.</param> /// <param name="client">Graph client for the attached user.</param> /// <param name="log">Tracewriter for debug output</param> /// <returns></returns> private static async Task <List <DriveItem> > FindChangedDriveItemsAsync(StoredSubscriptionState state, GraphServiceClient client, TraceWriter log) { string DefaultLatestDeltaUrl = idaMicrosoftGraphUrl + "/v1.0/drives/" + state.DriveId + "/root/delta?token=latest"; // We default to reading the "latest" state of the drive, so we don't have to process all the files in the drive // when a new subscription comes in. string deltaUrl = state?.LastDeltaToken ?? DefaultLatestDeltaUrl; List <DriveItem> changedDriveItems = new List <DriveItem>(); // Create an SDK request using the URL, instead of building up the request using the SDK IDriveItemDeltaRequest request = new DriveItemDeltaRequest(deltaUrl, client, null); // We max out at 50 requests of delta responses, just for demo purposes. const int MaxLoopCount = 50; for (int loopCount = 0; loopCount < MaxLoopCount && request != null; loopCount++) { log.Info($"Making request for '{state.SubscriptionId}' to '{deltaUrl}' "); // Get the next page of delta results IDriveItemDeltaCollectionPage deltaResponse = await request.GetAsync(); // Filter to the audio files we're interested in working with and add them to our list var changedFiles = (from f in deltaResponse where f.File != null && f.Name != null && (f.Name.EndsWith(acceptedAudioFileExtension) || f.Audio != null) && f.Deleted == null select f); changedDriveItems.AddRange(changedFiles); // Figure out how to proceed, whether we have more pages of changes to retrieve or not. if (null != deltaResponse.NextPageRequest) { request = deltaResponse.NextPageRequest; } else if (null != deltaResponse.AdditionalData["@odata.deltaLink"]) { string deltaLink = (string)deltaResponse.AdditionalData["@odata.deltaLink"]; log.Verbose($"All changes requested, nextDeltaUrl: {deltaLink}"); state.LastDeltaToken = deltaLink; return(changedDriveItems); } else { // Shouldn't get here, but just in case, we don't want to get stuck in a loop forever. request = null; } } // If we exit the For loop without returning, that means we read MaxLoopCount pages without finding a deltaToken log.Info($"Read through MaxLoopCount pages without finding an end. Too much data has changed and we're going to start over on the next notification."); state.LastDeltaToken = DefaultLatestDeltaUrl; return(changedDriveItems); }