diff --git a/internal_packages/worker-sync/lib/nylas-sync-worker.coffee b/internal_packages/worker-sync/lib/nylas-sync-worker.coffee index aa8e4303b..1cadfe03b 100644 --- a/internal_packages/worker-sync/lib/nylas-sync-worker.coffee +++ b/internal_packages/worker-sync/lib/nylas-sync-worker.coffee @@ -137,11 +137,14 @@ class NylasSyncWorker requestOptions = error: (err) => return if @_terminated - @_resumeTimer.backoff() - @_resumeTimer.start() - @updateTransferState(model, {busy: false, complete: false, error: err.toString()}) + @_fetchCollectionPageError(model, err) success: (json) => return if @_terminated + + if model in ["labels", "folders"] and @_hasNoInbox(json) + @_fetchCollectionPageError(model, "No inbox in #{model}") + return + lastReceivedIndex = params.offset + json.length if json.length is params.limit nextParams = _.extend({}, params, {offset: lastReceivedIndex}) @@ -156,6 +159,18 @@ class NylasSyncWorker else @_api.getCollection(@_account.id, model, params, requestOptions) + # It's occasionally possible for the NylasAPI's labels or folders + # endpoint to not return an "inbox" label. Since that's a core part of + # the app and it doesn't function without it, keep retrying until we see + # it. + _hasNoInbox: (json) -> + return not _.any(json, (obj) -> obj.name is "inbox") + + _fetchCollectionPageError: (model, err) -> + @_resumeTimer.backoff() + @_resumeTimer.start() + @updateTransferState(model, {busy: false, complete: false, error: err.toString()}) + updateTransferState: (model, {busy, error, complete, fetched, count}) -> @_state[model] = _.defaults({busy, error, complete, fetched, count}, @_state[model]) @writeState() diff --git a/internal_packages/worker-sync/spec/nylas-sync-worker-spec.coffee b/internal_packages/worker-sync/spec/nylas-sync-worker-spec.coffee index b619d20b1..bc1b46c64 100644 --- a/internal_packages/worker-sync/spec/nylas-sync-worker-spec.coffee +++ b/internal_packages/worker-sync/spec/nylas-sync-worker-spec.coffee @@ -101,6 +101,28 @@ describe "NylasSyncWorker", -> advanceClock(30000); expect(@worker.resumeFetches.callCount).toBe(4) advanceClock(30000); expect(@worker.resumeFetches.callCount).toBe(5) + it "handles the request as a failure if we try and grab labels or folders without an 'inbox'", -> + + spyOn(@worker, 'resumeFetches').andCallThrough() + @worker.start() + expect(@worker.resumeFetches.callCount).toBe(1) + request = _.findWhere(@apiRequests, model: 'labels') + request.requestOptions.success([]) + expect(@worker.resumeFetches.callCount).toBe(1) + advanceClock(30000) + expect(@worker.resumeFetches.callCount).toBe(2) + + it "handles the request as a success if we try and grab labels or folders and it includes the 'inbox'", -> + + spyOn(@worker, 'resumeFetches').andCallThrough() + @worker.start() + expect(@worker.resumeFetches.callCount).toBe(1) + request = _.findWhere(@apiRequests, model: 'labels') + request.requestOptions.success([{name: "inbox"}, {name: "archive"}]) + expect(@worker.resumeFetches.callCount).toBe(1) + advanceClock(30000) + expect(@worker.resumeFetches.callCount).toBe(1) + describe "when a count request completes", -> beforeEach -> @worker.start()