Esempio n. 1
0
        private async Task <ReadDeltaResult> ReadDelta(ISyncContext context, IDocumentStorageProvider storage, IClient dropbox, Func <Task> progressAction)
        {
            Entries delta;

            // we will load local state lazily upon first use
            var localState = new Lazy <LocalState>(() => new LocalState(storage.StateSnapshot));

            var numChangesProcessed = 0;
            var cursor = context.Cursor;

            do
            {
                delta = await dropbox.Core.Metadata.DeltaAsync(cursor, path_prefix : _options.Path, include_media_info : true);

                Debug.Assert(delta != null);
                Debug.Assert(delta.entries != null);

                // if this is a reset notification (or we start with an empty cursor),
                // we should reset local state and then restore it by iterating through delta
                if (delta.reset || String.IsNullOrEmpty(cursor))
                {
                    localState.Value.Reset();
                    numChangesProcessed++;
                }

                foreach (var entry in delta.entries.Where(IsDeleteOrAddFileDelta))
                {
                    numChangesProcessed++;

                    var path = entry.Item1.ToLowerInvariant();

                    if (entry.Item2 == null)
                    {
                        localState.Value.RemoveAtPath(path);
                    }
                    else if (!entry.Item2.is_dir)
                    {
                        localState.Value.AddAtPath(path, entry.Item2);
                    }
                }

                cursor = delta.cursor;

                if (progressAction != null)
                {
                    await progressAction();
                }
            } while (delta.has_more);

            context.Cursor = cursor;


            return(new ReadDeltaResult
            {
                ChangesProcessed = numChangesProcessed,
                PendingChanges = localState.IsValueCreated
                    ? localState.Value.ChangeList.Values
                    : null // no changes to process
            });
        }
Esempio n. 2
0
        public async Task <SyncResult> Sync(
            ISyncContext syncContext,
            IDocumentStorageProvider storage,
            Func <Task> progressAction = null)
        {
            if (syncContext == null)
            {
                throw new ArgumentNullException("syncContext");
            }
            if (syncContext.AccessToken == null)
            {
                throw new ArgumentException("An access token must be provided");
            }

            if (String.IsNullOrEmpty(syncContext.AccessToken))
            {
                throw new InvalidOperationException("Specified data context does not have valid token.");
            }

            var dropbox = _clientFactory(syncContext.AccessToken);

            // read delta using last indexing state
            // NB: this may update sync context, which should be persisted by the caller to enable reuse of the cursor
            var readResult = await ReadDelta(syncContext, storage, dropbox, progressAction);

            Debug.Assert(readResult != null);

            if (readResult.ChangesProcessed <= 0)
            {
                return(new SyncResult());
            }

            Debug.Assert(readResult.PendingChanges != null);

            // apply accumulated changes
            var applyResult = await ApplyPendingChanges(storage, dropbox, readResult.PendingChanges, progressAction);

            return(applyResult.ToIndexingResult(readResult.ChangesProcessed));
        }
Esempio n. 3
0
        /// <summary>
        /// Process pending changes - download thumbnails and update destination with batch document operations.
        /// </summary>
        private async Task <ApplyChangesResult> ApplyPendingChanges(
            IDocumentStorageProvider storage,
            IClient dropbox,
            ICollection <PendingChange> pendingChanges,
            Func <Task> batchProgressAction)
        {
            var result = new ApplyChangesResult();

            // filter changes according to specified options
            var filteredChanges = pendingChanges
                                  .Where(c =>
                                         c.Change == DocumentAction.Delete ||
                                         !_options.WithThumbnailsOnly || c.Meta.thumb_exists ||
                                         _options.IncludeSharedFolders || String.IsNullOrEmpty(c.Meta.parent_shared_folder_id)
                                         );

            var itemsProcessed = 0;

            // split accumulated changes to smaller batches to enable progress reporting and status checks
            foreach (var batch in filteredChanges.Chunkify(_options.ItemsPerBatch))
            {
                Debug.WriteLine("Processing next batch of changes [{0} / {1}]", itemsProcessed, pendingChanges.Count);

                // these will buffer metadata updates
                var documentsToDelete = new List <Document>();
                var documentsToUpdate = new List <Document>();
                var documentsToAdd    = new List <Document>();

                // create a list for thumbnail download tasks
                var thumbsToDownload = new List <PendingChange>();

                // sort through updates and adds
                foreach (var change in batch)
                {
                    if (change.Change != DocumentAction.Delete &&
                        _options.DownloadThumbnails && change.Meta.thumb_exists)
                    {
                        thumbsToDownload.Add(change);
                    }
                    else
                    {
                        var document = change.ToDocument();

                        switch (change.Change)
                        {
                        case DocumentAction.Add:
                            documentsToAdd.Add(document);
                            break;

                        case DocumentAction.Update:
                            documentsToUpdate.Add(document);
                            break;

                        case DocumentAction.Delete:
                            document.FilePath = change.DeletedFilePath;
                            documentsToDelete.Add(document);
                            break;
                        }
                    }
                }

                var downloadCount = await DownloadThumbnails(dropbox, thumbsToDownload, documentsToAdd, documentsToUpdate);

                result.Skipped += thumbsToDownload.Count - downloadCount;

                // now flush accumulated changes
                if (documentsToDelete.Count > 0)
                {
                    Debug.WriteLine("Deleting documents [{0}]", documentsToDelete.Count);
                    result.Deleted += await storage.DeleteAsync(documentsToDelete);
                }

                if (documentsToAdd.Count > 0)
                {
                    Debug.WriteLine("Adding documents [{0}]", documentsToUpdate.Count + documentsToAdd.Count);
                    result.Added += await storage.AddAsync(documentsToAdd);
                }
                if (documentsToUpdate.Count > 0)
                {
                    Debug.WriteLine("Updating documents [{0}]", documentsToUpdate.Count + documentsToAdd.Count);
                    result.Updated += await storage.UpdateAsync(documentsToUpdate);
                }

                if (batchProgressAction != null)
                {
                    await batchProgressAction();
                }

                itemsProcessed += _options.ItemsPerBatch;
            }

            return(result);
        }