private async Task <ChangesResult> GetChanges(string accessToken, string requestUri, string cursor) { string rootUri = GetOneDriveRootUri(requestUri); requestUri = string.Format(CultureInfo.InvariantCulture, "{0}/{1}", await GetItemUri(accessToken, requestUri, rootUri), "view.changes"); ChangesResult result = new ChangesResult(); using (var client = CreateHttpClient(accessToken)) { var next = cursor; var ids = new Dictionary <string, OneDriveModel.ItemInfo>(); Dictionary <string, object> changes = null; var serializer = new JavaScriptSerializer(); do { var uri = requestUri; if (!string.IsNullOrWhiteSpace(next)) { uri = string.Format(CultureInfo.InvariantCulture, "{0}?token={1}", requestUri, next); } using (var response = await client.GetAsync(uri)) { changes = await ProcessResponse <Dictionary <string, object> >("GetChanges", response); } if (changes.ContainsKey("@changes.resync")) { if (string.IsNullOrEmpty(next)) { throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.OneDriveUnableToSync, changes["@changes.resync"])); } // resync next = null; changes["@changes.hasMoreChanges"] = true; result = new ChangesResult(); continue; } var items = serializer.ConvertToType <OneDriveModel.OneDriveItemCollection>(changes); // changes if (items != null && items.value != null && items.value.Count > 0) { var subResults = GetChanges(items, ids, rootUri); result.DeletionChanges.AddRange(subResults.DeletionChanges); result.DirectoryChanges.AddRange(subResults.DirectoryChanges); result.FileChanges.AddRange(subResults.FileChanges); } // set next token next = (string)changes["@changes.token"]; } while ((bool)changes["@changes.hasMoreChanges"]); result.Cursor = next; return(result); } }
private NextAction Visit <TChange, TChangeContext>( IBreakingChangeDefinitions <TChange, TChangeContext> breakingChangeDefinitions, TChange typeChange, ChangeType changeType, TChangeContext typeChangeContext) { ChangesResult result = EvaluateAndStoreBreakingChanges(breakingChangeDefinitions, typeChange, typeChangeContext); if (result == ChangesResult.NoBreakingChanges && changeType == ChangeType.Matched) { return(NextAction.VisitChildTypes); } return((StopOnFirstBreakingChange && result == ChangesResult.SomeBreakingChanges) ? NextAction.Stop : NextAction.VisitNextSibling); }
private static ChangesResult GetChanges(OneDriveModel.OneDriveItemCollection items, Dictionary <string, OneDriveModel.ItemInfo> ids, string rootUri) { ChangesResult result = new ChangesResult(); foreach (var item in items.value) { ids[item.id] = new OneDriveModel.ItemInfo { name = item.name, parentId = item.parentReference.id }; var path = GetPath(ids, item); if (item.deleted != null) { result.DeletionChanges.Add(new OneDriveModel.OneDriveChange { Path = path, IsDeleted = true }); } else { var change = new OneDriveModel.OneDriveChange { ContentUri = string.Format(CultureInfo.InvariantCulture, "{0}/items/{1}", rootUri, item.id), Path = path, IsFile = item.file != null, LastModifiedUtc = item.lastModifiedDateTime.ToUniversalTime() }; if (change.IsFile) { result.FileChanges.Add(change); } else { result.DirectoryChanges.Add(change); } } } return(result); }
internal async Task <ChangeSet> Sync(OneDriveInfo info, IRepository repository, ITracer tracer) { ChangeSet changeSet = null; string cursor = _settings.GetValue(CursorKey); ChangesResult changes = null; // use incoming tracer since it is background work _tracer = tracer; // We truncate cursor value in filename but keep it unharmed in the trace content using (_tracer.Step("Getting delta changes with cursor: {0}...", cursor.Truncate(5))) using (_tracer.Step("cursor: {0}", cursor)) { changes = await GetChanges(info.TargetChangeset.Id, info.AccessToken, info.RepositoryUrl, cursor); } if (changes == null || changes.Count == 0) { _tracer.Trace("No changes need to be applied."); LogMessage(Resources.OneDriveNoChangesFound); return(changeSet); } // for simplicity, use file changes as effective total _totals = changes.FileChanges.Count > 0 ? changes.FileChanges.Count : changes.Count; string hoststarthtml = Path.Combine(_environment.WebRootPath, Constants.HostingStartHtml); FileSystemHelpers.DeleteFileSafe(hoststarthtml); using (new Timer(UpdateStatusFile, state: info.TargetChangeset.Id, dueTime: TimeSpan.FromSeconds(5), period: TimeSpan.FromSeconds(5))) using (_tracer.Step("Applying {0} changes ...", _totals)) { LogMessage(Resources.OneDriveApplyingChanges, _totals); // perform action seperately, so that can ensure timestamp on directory // e.g two changes: // (new) file /a/b/c.txt // (new) dir /a/b // if created dir first then create file. file creation will trigger folder timestamp change. // which will result in "/a/b" has timestamp from the monent of file creation instead of the timestamp value from server, where value supposed to be set by code specifically. await ApplyChangesParallel(changes.DeletionChanges, info.AccessToken, maxParallelCount : 1, countSuccess : changes.FileChanges.Count == 0); await ApplyChangesParallel(changes.FileChanges, info.AccessToken, maxParallelCount : MaxConcurrentRequests, countSuccess : true); // apply folder changes at last to maintain same timestamp as in OneDrive await ApplyChangesParallel(changes.DirectoryChanges, info.AccessToken, maxParallelCount : 1, countSuccess : changes.FileChanges.Count == 0); _tracer.Trace("{0} succeeded, {1} failed", _successCount, _failedCount); LogMessage(Resources.OneDriveApplyResult, _successCount, _failedCount); string message = _failedCount > 0 ? string.Format(CultureInfo.CurrentCulture, Resources.OneDrive_SynchronizedWithFailure, _successCount, _totals, _failedCount) : string.Format(CultureInfo.CurrentCulture, Resources.OneDrive_Synchronized, _totals); // Commit anyway even partial change if (repository.Commit(message, info.AuthorName, info.AuthorEmail)) { changeSet = repository.GetChangeSet("HEAD"); } if (_failedCount > 0) { // signal deployment failied throw new Exception(string.Format(CultureInfo.CurrentCulture, Resources.OneDriveApplyResult, _successCount, _failedCount)); } } // finally keep a copy of the new cursor _settings.SetValue(CursorKey, changes.Cursor); return(changeSet); }