mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-21 15:56:10 +08:00
fix(sync): Pull down and associate metadata during initial sync
Summary: Snooze should wait for categories on all accounts Fix authPlugin to rembmer `plugin+accountId`, not pluginId, add specs categories() returned [], categories(acctId) returned {} dry up sync worker, fetch metadata before anything else Test Plan: Run tests Reviewers: drew, juan Reviewed By: juan Differential Revision: https://phab.nylas.com/D2693
This commit is contained in:
parent
041817c589
commit
f706d4d80f
|
@ -58,8 +58,8 @@ const SnoozeUtils = {
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
whenCategoriesReady() {
|
whenCategoriesReady(accountId) {
|
||||||
const categoriesReady = ()=> CategoryStore.categories().length > 0
|
const categoriesReady = ()=> CategoryStore.categories(accountId).length > 0;
|
||||||
if (!categoriesReady()) {
|
if (!categoriesReady()) {
|
||||||
return new Promise((resolve)=> {
|
return new Promise((resolve)=> {
|
||||||
const unsubscribe = CategoryStore.listen(()=> {
|
const unsubscribe = CategoryStore.listen(()=> {
|
||||||
|
@ -74,7 +74,7 @@ const SnoozeUtils = {
|
||||||
},
|
},
|
||||||
|
|
||||||
getSnoozeCategory(accountId, categoryName = SNOOZE_CATEGORY_NAME) {
|
getSnoozeCategory(accountId, categoryName = SNOOZE_CATEGORY_NAME) {
|
||||||
return SnoozeUtils.whenCategoriesReady()
|
return SnoozeUtils.whenCategoriesReady(accountId)
|
||||||
.then(()=> {
|
.then(()=> {
|
||||||
const allCategories = CategoryStore.categories(accountId)
|
const allCategories = CategoryStore.categories(accountId)
|
||||||
const category = _.findWhere(allCategories, {displayName: categoryName})
|
const category = _.findWhere(allCategories, {displayName: categoryName})
|
||||||
|
|
|
@ -106,23 +106,51 @@ class NylasSyncWorker
|
||||||
# we'll backoff and restart the timer.
|
# we'll backoff and restart the timer.
|
||||||
@_resumeTimer.cancel()
|
@_resumeTimer.cancel()
|
||||||
|
|
||||||
@fetchCollection('threads')
|
needed = [
|
||||||
if @_account.usesLabels()
|
{model: 'threads'},
|
||||||
@fetchCollection('labels', {initialPageSize: 1000})
|
{model: "#{@_account.organizationUnit}s", initialPageSize: 1000}
|
||||||
if @_account.usesFolders()
|
{model: 'drafts'},
|
||||||
@fetchCollection('folders', {initialPageSize: 1000})
|
{model: 'contacts'},
|
||||||
@fetchCollection('drafts')
|
{model: 'calendars'},
|
||||||
@fetchCollection('contacts')
|
{model: 'events'},
|
||||||
@fetchCollection('calendars')
|
].filter ({model}) =>
|
||||||
@fetchCollection('events')
|
@shouldFetchCollection(model)
|
||||||
|
|
||||||
fetchCollection: (model, options = {}) ->
|
return if needed.length is 0
|
||||||
return unless @_state
|
|
||||||
|
@fetchAllMetadata =>
|
||||||
|
needed.forEach ({model, initialPageSize}) =>
|
||||||
|
@fetchCollection(model, initialPageSize)
|
||||||
|
|
||||||
|
fetchAllMetadata: (finished) ->
|
||||||
|
@_metadata = {}
|
||||||
|
makeMetadataRequest = (offset) =>
|
||||||
|
limit = 200
|
||||||
|
@_fetchWithErrorHandling
|
||||||
|
path: "/metadata"
|
||||||
|
qs: {limit, offset}
|
||||||
|
success: (data) =>
|
||||||
|
for metadatum in data
|
||||||
|
@_metadata[metadatum.object_id] ?= []
|
||||||
|
@_metadata[metadatum.object_id].push(metadatum)
|
||||||
|
if data.length is limit
|
||||||
|
makeMetadataRequest(offset + limit)
|
||||||
|
else
|
||||||
|
console.log("Retrieved #{offset + data.length} metadata objects")
|
||||||
|
finished()
|
||||||
|
|
||||||
|
makeMetadataRequest(0)
|
||||||
|
|
||||||
|
shouldFetchCollection: (model) ->
|
||||||
|
return false unless @_state
|
||||||
state = @_state[model] ? {}
|
state = @_state[model] ? {}
|
||||||
|
|
||||||
return if state.complete and not options.force?
|
return false if state.complete
|
||||||
return if state.busy
|
return false if state.busy
|
||||||
|
return true
|
||||||
|
|
||||||
|
fetchCollection: (model, initialPageSize = INITIAL_PAGE_SIZE) ->
|
||||||
|
state = @_state[model] ? {}
|
||||||
state.complete = false
|
state.complete = false
|
||||||
state.error = null
|
state.error = null
|
||||||
state.busy = true
|
state.busy = true
|
||||||
|
@ -138,7 +166,7 @@ class NylasSyncWorker
|
||||||
@fetchCollectionPage(model, {limit, offset})
|
@fetchCollectionPage(model, {limit, offset})
|
||||||
else
|
else
|
||||||
@fetchCollectionPage(model, {
|
@fetchCollectionPage(model, {
|
||||||
limit: options.initialPageSize ? INITIAL_PAGE_SIZE,
|
limit: initialPageSize,
|
||||||
offset: 0
|
offset: 0
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -146,23 +174,17 @@ class NylasSyncWorker
|
||||||
@writeState()
|
@writeState()
|
||||||
|
|
||||||
fetchCollectionCount: (model) ->
|
fetchCollectionCount: (model) ->
|
||||||
@_api.makeRequest
|
@_fetchWithErrorHandling
|
||||||
accountId: @_account.id
|
|
||||||
path: "/#{model}"
|
path: "/#{model}"
|
||||||
returnsModel: false
|
qs: {view: 'count'}
|
||||||
qs:
|
|
||||||
view: 'count'
|
|
||||||
success: (response) =>
|
success: (response) =>
|
||||||
return if @_terminated
|
|
||||||
@updateTransferState(model, count: response.count)
|
@updateTransferState(model, count: response.count)
|
||||||
error: (err) =>
|
|
||||||
return if @_terminated
|
|
||||||
@_resumeTimer.backoff()
|
|
||||||
@_resumeTimer.start()
|
|
||||||
|
|
||||||
fetchCollectionPage: (model, params = {}) ->
|
fetchCollectionPage: (model, params = {}) ->
|
||||||
requestStartTime = Date.now()
|
requestStartTime = Date.now()
|
||||||
requestOptions =
|
requestOptions =
|
||||||
|
metadataToAttach: @_metadata
|
||||||
|
|
||||||
error: (err) =>
|
error: (err) =>
|
||||||
return if @_terminated
|
return if @_terminated
|
||||||
@_fetchCollectionPageError(model, params, err)
|
@_fetchCollectionPageError(model, params, err)
|
||||||
|
@ -203,6 +225,21 @@ class NylasSyncWorker
|
||||||
_hasNoInbox: (json) ->
|
_hasNoInbox: (json) ->
|
||||||
return not _.any(json, (obj) -> obj.name is "inbox")
|
return not _.any(json, (obj) -> obj.name is "inbox")
|
||||||
|
|
||||||
|
_fetchWithErrorHandling: ({path, qs, success, error}) ->
|
||||||
|
@_api.makeRequest
|
||||||
|
accountId: @_account.id
|
||||||
|
returnsModel: false
|
||||||
|
path: path
|
||||||
|
qs: qs
|
||||||
|
success: (response) =>
|
||||||
|
return if @_terminated
|
||||||
|
success(response) if success
|
||||||
|
error: (err) =>
|
||||||
|
return if @_terminated
|
||||||
|
@_resumeTimer.backoff()
|
||||||
|
@_resumeTimer.start()
|
||||||
|
error(err) if error
|
||||||
|
|
||||||
_fetchCollectionPageError: (model, params, err) ->
|
_fetchCollectionPageError: (model, params, err) ->
|
||||||
@_resumeTimer.backoff()
|
@_resumeTimer.backoff()
|
||||||
@_resumeTimer.start()
|
@_resumeTimer.start()
|
||||||
|
|
|
@ -17,6 +17,7 @@ describe "NylasSyncWorker", ->
|
||||||
@apiRequests.push({account, model:'threads', params, requestOptions})
|
@apiRequests.push({account, model:'threads', params, requestOptions})
|
||||||
|
|
||||||
@apiCursorStub = undefined
|
@apiCursorStub = undefined
|
||||||
|
spyOn(NylasSyncWorker.prototype, 'fetchAllMetadata').andCallFake (cb) -> cb()
|
||||||
spyOn(DatabaseTransaction.prototype, 'persistJSONBlob').andReturn(Promise.resolve())
|
spyOn(DatabaseTransaction.prototype, 'persistJSONBlob').andReturn(Promise.resolve())
|
||||||
spyOn(DatabaseStore, 'findJSONBlob').andCallFake (key) =>
|
spyOn(DatabaseStore, 'findJSONBlob').andCallFake (key) =>
|
||||||
if key is "NylasSyncWorker:#{TEST_ACCOUNT_ID}"
|
if key is "NylasSyncWorker:#{TEST_ACCOUNT_ID}"
|
||||||
|
@ -37,6 +38,7 @@ describe "NylasSyncWorker", ->
|
||||||
|
|
||||||
@account = new Account(clientId: TEST_ACCOUNT_CLIENT_ID, serverId: TEST_ACCOUNT_ID, organizationUnit: 'label')
|
@account = new Account(clientId: TEST_ACCOUNT_CLIENT_ID, serverId: TEST_ACCOUNT_ID, organizationUnit: 'label')
|
||||||
@worker = new NylasSyncWorker(@api, @account)
|
@worker = new NylasSyncWorker(@api, @account)
|
||||||
|
@worker._metadata = {"a": [{"id":"b"}]}
|
||||||
@connection = @worker.connection()
|
@connection = @worker.connection()
|
||||||
spyOn(@connection, 'start')
|
spyOn(@connection, 'start')
|
||||||
advanceClock()
|
advanceClock()
|
||||||
|
@ -177,35 +179,58 @@ describe "NylasSyncWorker", ->
|
||||||
expect(nextState.threads.count).toEqual(1001)
|
expect(nextState.threads.count).toEqual(1001)
|
||||||
|
|
||||||
describe "resumeFetches", ->
|
describe "resumeFetches", ->
|
||||||
it "should fetch collections", ->
|
it "should fetch metadata first and fetch other collections when metadata is ready", ->
|
||||||
|
fetchAllMetadataCallback = null
|
||||||
|
jasmine.unspy(NylasSyncWorker.prototype, 'fetchAllMetadata')
|
||||||
|
spyOn(NylasSyncWorker.prototype, 'fetchAllMetadata').andCallFake (cb) =>
|
||||||
|
fetchAllMetadataCallback = cb
|
||||||
spyOn(@worker, 'fetchCollection')
|
spyOn(@worker, 'fetchCollection')
|
||||||
|
@worker._state = {}
|
||||||
@worker.resumeFetches()
|
@worker.resumeFetches()
|
||||||
expect(@worker.fetchCollection.calls.map (call) -> call.args[0]).toEqual(['threads', 'labels', 'drafts', 'contacts', 'calendars', 'events'])
|
expect(@worker.fetchAllMetadata).toHaveBeenCalled()
|
||||||
|
expect(@worker.fetchCollection.calls.length).toBe(0)
|
||||||
|
fetchAllMetadataCallback()
|
||||||
|
expect(@worker.fetchCollection.calls.length).not.toBe(0)
|
||||||
|
|
||||||
|
it "should fetch collections for which `shouldFetchCollection` returns true", ->
|
||||||
|
spyOn(@worker, 'fetchCollection')
|
||||||
|
spyOn(@worker, 'shouldFetchCollection').andCallFake (collection) =>
|
||||||
|
return collection in ['threads', 'labels', 'drafts']
|
||||||
|
@worker.resumeFetches()
|
||||||
|
expect(@worker.fetchCollection.calls.map (call) -> call.args[0]).toEqual(['threads', 'labels', 'drafts'])
|
||||||
|
|
||||||
it "should be called when Actions.retryInitialSync is received", ->
|
it "should be called when Actions.retryInitialSync is received", ->
|
||||||
spyOn(@worker, 'resumeFetches').andCallThrough()
|
spyOn(@worker, 'resumeFetches').andCallThrough()
|
||||||
Actions.retryInitialSync()
|
Actions.retryInitialSync()
|
||||||
expect(@worker.resumeFetches).toHaveBeenCalled()
|
expect(@worker.resumeFetches).toHaveBeenCalled()
|
||||||
|
|
||||||
describe "fetchCollection", ->
|
describe "shouldFetchCollection", ->
|
||||||
beforeEach ->
|
it "should return false if the collection sync is already in progress", ->
|
||||||
@apiRequests = []
|
|
||||||
|
|
||||||
it "should not start if the collection sync is already in progress", ->
|
|
||||||
@worker._state.threads = {
|
@worker._state.threads = {
|
||||||
'busy': true
|
'busy': true
|
||||||
'complete': false
|
'complete': false
|
||||||
}
|
}
|
||||||
@worker.fetchCollection('threads')
|
expect(@worker.shouldFetchCollection('threads')).toBe(false)
|
||||||
expect(@apiRequests.length).toBe(0)
|
|
||||||
|
|
||||||
it "should not start if the collection sync is already complete", ->
|
it "should return false if the collection sync is already complete", ->
|
||||||
@worker._state.threads = {
|
@worker._state.threads = {
|
||||||
'busy': false
|
'busy': false
|
||||||
'complete': true
|
'complete': true
|
||||||
}
|
}
|
||||||
@worker.fetchCollection('threads')
|
expect(@worker.shouldFetchCollection('threads')).toBe(false)
|
||||||
expect(@apiRequests.length).toBe(0)
|
|
||||||
|
it "should return true otherwise", ->
|
||||||
|
@worker._state.threads = {
|
||||||
|
'busy': false
|
||||||
|
'complete': false
|
||||||
|
}
|
||||||
|
expect(@worker.shouldFetchCollection('threads')).toBe(true)
|
||||||
|
@worker._state.threads = undefined
|
||||||
|
expect(@worker.shouldFetchCollection('threads')).toBe(true)
|
||||||
|
|
||||||
|
describe "fetchCollection", ->
|
||||||
|
beforeEach ->
|
||||||
|
@apiRequests = []
|
||||||
|
|
||||||
it "should start the request for the model count", ->
|
it "should start the request for the model count", ->
|
||||||
@worker._state.threads = {
|
@worker._state.threads = {
|
||||||
|
@ -216,7 +241,16 @@ describe "NylasSyncWorker", ->
|
||||||
expect(@apiRequests[0].requestOptions.path).toBe('/threads')
|
expect(@apiRequests[0].requestOptions.path).toBe('/threads')
|
||||||
expect(@apiRequests[0].requestOptions.qs.view).toBe('count')
|
expect(@apiRequests[0].requestOptions.qs.view).toBe('count')
|
||||||
|
|
||||||
describe "when there is no errorRequestRange saved", ->
|
it "should pass any metadata it preloaded", ->
|
||||||
|
@worker._state.threads = {
|
||||||
|
'busy': false
|
||||||
|
'complete': false
|
||||||
|
}
|
||||||
|
@worker.fetchCollection('threads')
|
||||||
|
expect(@apiRequests[1].model).toBe('threads')
|
||||||
|
expect(@apiRequests[1].requestOptions.metadataToAttach).toBe(@worker._metadata)
|
||||||
|
|
||||||
|
describe "when there is not a previous page failure (`errorRequestRange`)", ->
|
||||||
it "should start the first request for models", ->
|
it "should start the first request for models", ->
|
||||||
@worker._state.threads = {
|
@worker._state.threads = {
|
||||||
'busy': false
|
'busy': false
|
||||||
|
@ -226,7 +260,7 @@ describe "NylasSyncWorker", ->
|
||||||
expect(@apiRequests[1].model).toBe('threads')
|
expect(@apiRequests[1].model).toBe('threads')
|
||||||
expect(@apiRequests[1].params.offset).toBe(0)
|
expect(@apiRequests[1].params.offset).toBe(0)
|
||||||
|
|
||||||
describe "when there is an errorRequestRange saved", ->
|
describe "when there is a previous page failure (`errorRequestRange`)", ->
|
||||||
beforeEach ->
|
beforeEach ->
|
||||||
@worker._state.threads =
|
@worker._state.threads =
|
||||||
'count': 1200
|
'count': 1200
|
||||||
|
|
|
@ -8,6 +8,110 @@ DatabaseStore = require '../src/flux/stores/database-store'
|
||||||
DatabaseTransaction = require '../src/flux/stores/database-transaction'
|
DatabaseTransaction = require '../src/flux/stores/database-transaction'
|
||||||
|
|
||||||
describe "NylasAPI", ->
|
describe "NylasAPI", ->
|
||||||
|
describe "authPlugin", ->
|
||||||
|
beforeEach ->
|
||||||
|
NylasAPI.pluginsSupported = true
|
||||||
|
@authGetResponse = null
|
||||||
|
@authPostResponse = null
|
||||||
|
@error = null
|
||||||
|
@resolved = false
|
||||||
|
spyOn(NylasEnv.config, 'set')
|
||||||
|
spyOn(NylasEnv.config, 'get').andReturn(null)
|
||||||
|
spyOn(NylasAPI, 'makeRequest').andCallFake (options) =>
|
||||||
|
return @authGetResponse if options.method is 'GET' and @authGetResponse
|
||||||
|
return @authPostResponse if options.method is 'POST' and @authPostResponse
|
||||||
|
return new Promise (resolve, reject) -> #never respond
|
||||||
|
|
||||||
|
it "should reject if the current environment does not support plugins", ->
|
||||||
|
NylasAPI.pluginsSupported = false
|
||||||
|
NylasAPI.authPlugin('PID', 'PSECRET', TEST_ACCOUNT_ID).catch (err) => @error = err
|
||||||
|
waitsFor =>
|
||||||
|
@error
|
||||||
|
runs =>
|
||||||
|
expect(@error.message).toEqual('Sorry, this feature is only available when N1 is running against the hosted version of the Nylas Sync Engine.')
|
||||||
|
|
||||||
|
it "should reject if no account can be found for the given accountOrId", ->
|
||||||
|
NylasAPI.authPlugin('PID', 'PSECRET', 'randomAccountId').catch (err) => @error = err
|
||||||
|
waitsFor =>
|
||||||
|
@error
|
||||||
|
runs =>
|
||||||
|
expect(@error.message).toEqual('Invalid account')
|
||||||
|
|
||||||
|
it "should resolve if the plugin has been successfully authed with accountOrId already", ->
|
||||||
|
jasmine.unspy(NylasEnv.config, 'get')
|
||||||
|
spyOn(NylasEnv.config, 'get').andCallFake (key) =>
|
||||||
|
return Date.now() if key is "plugins.PID.lastAuth.#{TEST_ACCOUNT_ID}"
|
||||||
|
return null
|
||||||
|
NylasAPI.authPlugin('PID', 'PSECRET', TEST_ACCOUNT_ID).then (err) =>
|
||||||
|
@resolved = true
|
||||||
|
waitsFor =>
|
||||||
|
@resolved
|
||||||
|
expect(NylasAPI.makeRequest).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
describe "check for existing auth", ->
|
||||||
|
it "should GET /auth/plugin to check if the plugin has been authed", ->
|
||||||
|
@authGetResponse = Promise.resolve({authed: true})
|
||||||
|
NylasAPI.authPlugin('PID', 'PSECRET', TEST_ACCOUNT_ID)
|
||||||
|
advanceClock()
|
||||||
|
expect(NylasAPI.makeRequest).toHaveBeenCalledWith({
|
||||||
|
returnsModel: false,
|
||||||
|
method: 'GET',
|
||||||
|
accountId: 'test-account-server-id',
|
||||||
|
path: '/auth/plugin?client_id=PID'
|
||||||
|
})
|
||||||
|
|
||||||
|
it "should record a successful auth in the config and resolve without making a POST", ->
|
||||||
|
@authGetResponse = Promise.resolve({authed: true})
|
||||||
|
@authPostResponse = null
|
||||||
|
NylasAPI.authPlugin('PID', 'PSECRET', TEST_ACCOUNT_ID).then => @resolved = true
|
||||||
|
waitsFor =>
|
||||||
|
@resolved
|
||||||
|
runs =>
|
||||||
|
expect(NylasAPI.makeRequest).toHaveBeenCalled()
|
||||||
|
expect(NylasEnv.config.set.mostRecentCall.args[0]).toEqual("plugins.PID.lastAuth.#{TEST_ACCOUNT_ID}")
|
||||||
|
|
||||||
|
it "should propagate any network errors back to the caller", ->
|
||||||
|
@authGetResponse = Promise.reject(new Error("Network failure!"))
|
||||||
|
NylasAPI.authPlugin('PID', 'PSECRET', TEST_ACCOUNT_ID).catch (err) => @error = err
|
||||||
|
advanceClock()
|
||||||
|
advanceClock()
|
||||||
|
expect(@error.message).toBe("Network failure!")
|
||||||
|
expect(NylasEnv.config.set).not.toHaveBeenCalled()
|
||||||
|
|
||||||
|
describe "request for auth", ->
|
||||||
|
it "should POST to /auth/plugin with the client id and record a successful auth", ->
|
||||||
|
@authGetResponse = Promise.resolve({authed: false})
|
||||||
|
@authPostResponse = Promise.resolve({authed: true})
|
||||||
|
NylasAPI.authPlugin('PID', 'PSECRET', TEST_ACCOUNT_ID).then => @resolved = true
|
||||||
|
waitsFor =>
|
||||||
|
@resolved
|
||||||
|
runs =>
|
||||||
|
expect(NylasAPI.makeRequest.calls[0].args[0]).toEqual({
|
||||||
|
returnsModel: false,
|
||||||
|
method: 'GET',
|
||||||
|
accountId: 'test-account-server-id',
|
||||||
|
path: '/auth/plugin?client_id=PID'
|
||||||
|
})
|
||||||
|
expect(NylasAPI.makeRequest.calls[1].args[0]).toEqual({
|
||||||
|
returnsModel: false,
|
||||||
|
method: 'POST',
|
||||||
|
accountId: 'test-account-server-id',
|
||||||
|
path: '/auth/plugin',
|
||||||
|
body: {client_id: 'PID'},
|
||||||
|
json: true
|
||||||
|
})
|
||||||
|
setCall = NylasEnv.config.set.mostRecentCall
|
||||||
|
expect(setCall.args[0]).toEqual("plugins.PID.lastAuth.#{TEST_ACCOUNT_ID}")
|
||||||
|
|
||||||
|
it "should propagate any network errors back to the caller", ->
|
||||||
|
@authGetResponse = Promise.resolve({authed: false})
|
||||||
|
@authPostResponse = Promise.reject(new Error("Network failure!"))
|
||||||
|
NylasAPI.authPlugin('PID', 'PSECRET', TEST_ACCOUNT_ID).catch (err) => @error = err
|
||||||
|
waitsFor =>
|
||||||
|
@error
|
||||||
|
runs =>
|
||||||
|
expect(@error.message).toBe("Network failure!")
|
||||||
|
|
||||||
describe "handleModel404", ->
|
describe "handleModel404", ->
|
||||||
it "should unpersist the model from the cache that was requested", ->
|
it "should unpersist the model from the cache that was requested", ->
|
||||||
model = new Thread(id: 'threadidhere')
|
model = new Thread(id: 'threadidhere')
|
||||||
|
|
|
@ -23,6 +23,13 @@ class PluginMetadata extends Model {
|
||||||
this.version = this.version || 0;
|
this.version = this.version || 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fromJSON(json) {
|
||||||
|
super.fromJSON(json);
|
||||||
|
|
||||||
|
// application_id is used in JSON coming down from the API
|
||||||
|
this.pluginId = this.pluginId || json.application_id;
|
||||||
|
}
|
||||||
|
|
||||||
get id() {
|
get id() {
|
||||||
return this.pluginId
|
return this.pluginId
|
||||||
}
|
}
|
||||||
|
|
|
@ -121,7 +121,6 @@ class NylasAPI
|
||||||
SampleTemporaryErrorCode: SampleTemporaryErrorCode
|
SampleTemporaryErrorCode: SampleTemporaryErrorCode
|
||||||
|
|
||||||
constructor: ->
|
constructor: ->
|
||||||
@_workers = []
|
|
||||||
@_lockTracker = new NylasAPIChangeLockTracker()
|
@_lockTracker = new NylasAPIChangeLockTracker()
|
||||||
|
|
||||||
NylasEnv.config.onDidChange('env', @_onConfigChanged)
|
NylasEnv.config.onDidChange('env', @_onConfigChanged)
|
||||||
|
@ -131,6 +130,7 @@ class NylasAPI
|
||||||
prev = {@AppID, @APIRoot, @APITokens}
|
prev = {@AppID, @APIRoot, @APITokens}
|
||||||
|
|
||||||
if NylasEnv.inSpecMode()
|
if NylasEnv.inSpecMode()
|
||||||
|
@pluginsSupported = true
|
||||||
env = "testing"
|
env = "testing"
|
||||||
else
|
else
|
||||||
env = NylasEnv.config.get('env')
|
env = NylasEnv.config.get('env')
|
||||||
|
@ -143,12 +143,15 @@ class NylasAPI
|
||||||
if env in ['production']
|
if env in ['production']
|
||||||
@AppID = 'eco3rpsghu81xdc48t5qugwq7'
|
@AppID = 'eco3rpsghu81xdc48t5qugwq7'
|
||||||
@APIRoot = 'https://api.nylas.com'
|
@APIRoot = 'https://api.nylas.com'
|
||||||
|
@pluginsSupported = true
|
||||||
else if env in ['staging', 'development']
|
else if env in ['staging', 'development']
|
||||||
@AppID = '54miogmnotxuo5st254trcmb9'
|
@AppID = '54miogmnotxuo5st254trcmb9'
|
||||||
@APIRoot = 'https://api-staging.nylas.com'
|
@APIRoot = 'https://api-staging.nylas.com'
|
||||||
|
@pluginsSupported = true
|
||||||
else if env in ['experimental']
|
else if env in ['experimental']
|
||||||
@AppID = 'c5dis00do2vki9ib6hngrjs18'
|
@AppID = 'c5dis00do2vki9ib6hngrjs18'
|
||||||
@APIRoot = 'https://api-staging-experimental.nylas.com'
|
@APIRoot = 'https://api-staging-experimental.nylas.com'
|
||||||
|
@pluginsSupported = true
|
||||||
else if env in ['local']
|
else if env in ['local']
|
||||||
@AppID = NylasEnv.config.get('syncEngine.AppID') or 'n/a'
|
@AppID = NylasEnv.config.get('syncEngine.AppID') or 'n/a'
|
||||||
@APIRoot = 'http://localhost:5555'
|
@APIRoot = 'http://localhost:5555'
|
||||||
|
@ -321,6 +324,12 @@ class NylasAPI
|
||||||
.then ->
|
.then ->
|
||||||
return Promise.resolve(responseModels)
|
return Promise.resolve(responseModels)
|
||||||
|
|
||||||
|
_attachMetadataToResponse: (jsons, metadataToAttach) ->
|
||||||
|
return unless metadataToAttach
|
||||||
|
for obj in jsons
|
||||||
|
if metadataToAttach[obj.id]
|
||||||
|
obj.metadata = metadataToAttach[obj.id]
|
||||||
|
|
||||||
_apiObjectToClassMap:
|
_apiObjectToClassMap:
|
||||||
"file": require('./models/file')
|
"file": require('./models/file')
|
||||||
"event": require('./models/event')
|
"event": require('./models/event')
|
||||||
|
@ -341,6 +350,7 @@ class NylasAPI
|
||||||
if result.messages
|
if result.messages
|
||||||
messages = messages.concat(result.messages)
|
messages = messages.concat(result.messages)
|
||||||
if messages.length > 0
|
if messages.length > 0
|
||||||
|
@_attachMetadataToResponse(messages, requestOptions.metadataToAttach)
|
||||||
@_handleModelResponse(messages)
|
@_handleModelResponse(messages)
|
||||||
if requestSuccess
|
if requestSuccess
|
||||||
requestSuccess(json)
|
requestSuccess(json)
|
||||||
|
@ -350,11 +360,17 @@ class NylasAPI
|
||||||
|
|
||||||
getCollection: (accountId, collection, params={}, requestOptions={}) ->
|
getCollection: (accountId, collection, params={}, requestOptions={}) ->
|
||||||
throw (new Error "getCollection requires accountId") unless accountId
|
throw (new Error "getCollection requires accountId") unless accountId
|
||||||
|
requestSuccess = requestOptions.success
|
||||||
@makeRequest _.extend requestOptions,
|
@makeRequest _.extend requestOptions,
|
||||||
path: "/#{collection}"
|
path: "/#{collection}"
|
||||||
accountId: accountId
|
accountId: accountId
|
||||||
qs: params
|
qs: params
|
||||||
returnsModel: true
|
returnsModel: false
|
||||||
|
success: (jsons) =>
|
||||||
|
@_attachMetadataToResponse(jsons, requestOptions.metadataToAttach)
|
||||||
|
@_handleModelResponse(jsons)
|
||||||
|
if requestSuccess
|
||||||
|
requestSuccess(jsons)
|
||||||
|
|
||||||
incrementRemoteChangeLock: (klass, id) ->
|
incrementRemoteChangeLock: (klass, id) ->
|
||||||
@_lockTracker.increment(klass, id)
|
@_lockTracker.increment(klass, id)
|
||||||
|
@ -385,14 +401,19 @@ class NylasAPI
|
||||||
# the plugin server couldn't be reached or failed to respond properly when authing
|
# the plugin server couldn't be reached or failed to respond properly when authing
|
||||||
# the account, or that the Nylas API couldn't be reached.
|
# the account, or that the Nylas API couldn't be reached.
|
||||||
authPlugin: (pluginId, pluginName, accountOrId) ->
|
authPlugin: (pluginId, pluginName, accountOrId) ->
|
||||||
account = if accountOrId instanceof Account
|
unless @pluginsSupported
|
||||||
accountOrId
|
return Promise.reject(new Error('Sorry, this feature is only available when N1 is running against the hosted version of the Nylas Sync Engine.'))
|
||||||
|
|
||||||
|
if accountOrId instanceof Account
|
||||||
|
account = accountOrId
|
||||||
else
|
else
|
||||||
AccountStore ?= require './stores/account-store'
|
AccountStore ?= require './stores/account-store'
|
||||||
AccountStore.accountForId(accountOrId)
|
account = AccountStore.accountForId(accountOrId)
|
||||||
Promise.reject(new Error('Invalid account')) unless account
|
|
||||||
|
|
||||||
cacheKey = "plugins.#{pluginId}.lastAuthTimestamp"
|
unless account
|
||||||
|
return Promise.reject(new Error('Invalid account'))
|
||||||
|
|
||||||
|
cacheKey = "plugins.#{pluginId}.lastAuth.#{account.id}"
|
||||||
if NylasEnv.config.get(cacheKey)
|
if NylasEnv.config.get(cacheKey)
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
|
|
||||||
|
@ -401,22 +422,24 @@ class NylasAPI
|
||||||
method: "GET",
|
method: "GET",
|
||||||
accountId: account.id,
|
accountId: account.id,
|
||||||
path: "/auth/plugin?client_id=#{pluginId}"
|
path: "/auth/plugin?client_id=#{pluginId}"
|
||||||
})
|
|
||||||
.then (result) =>
|
}).then (result) =>
|
||||||
if result.authed
|
if result.authed
|
||||||
NylasEnv.config.set(cacheKey, Date.now())
|
NylasEnv.config.set(cacheKey, Date.now())
|
||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
else
|
|
||||||
# Enable to show a prompt to the user
|
# Enable to show a prompt to the user
|
||||||
# return @_requestPluginAuth(pluginName, account).then =>
|
# return @_requestPluginAuth(pluginName, account).then =>
|
||||||
return @makeRequest({
|
return @makeRequest({
|
||||||
returnsModel: false,
|
returnsModel: false,
|
||||||
method: "POST",
|
method: "POST",
|
||||||
accountId: account.id,
|
accountId: account.id,
|
||||||
path: "/auth/plugin",
|
path: "/auth/plugin",
|
||||||
body: {client_id: pluginId},
|
body: {client_id: pluginId},
|
||||||
json: true
|
json: true
|
||||||
})
|
}).then (result) =>
|
||||||
|
NylasEnv.config.set(cacheKey, Date.now()) if result.authed
|
||||||
|
return Promise.resolve()
|
||||||
|
|
||||||
_requestPluginAuth: (pluginName, account) ->
|
_requestPluginAuth: (pluginName, account) ->
|
||||||
{dialog} = require('electron').remote
|
{dialog} = require('electron').remote
|
||||||
|
|
|
@ -32,7 +32,8 @@ class CategoryStore extends NylasStore
|
||||||
.subscribe(@_onCategoriesChanged)
|
.subscribe(@_onCategoriesChanged)
|
||||||
|
|
||||||
byId: (accountOrId, categoryId) ->
|
byId: (accountOrId, categoryId) ->
|
||||||
@categories(accountOrId)[categoryId]
|
categories = @_categoryCache[asAccountId(accountOrId)] ? {}
|
||||||
|
categories[categoryId]
|
||||||
|
|
||||||
# Public: Returns an array of all categories for an account, both
|
# Public: Returns an array of all categories for an account, both
|
||||||
# standard and user generated. The items returned by this function will be
|
# standard and user generated. The items returned by this function will be
|
||||||
|
@ -40,7 +41,7 @@ class CategoryStore extends NylasStore
|
||||||
#
|
#
|
||||||
categories: (accountOrId = null) ->
|
categories: (accountOrId = null) ->
|
||||||
if accountOrId
|
if accountOrId
|
||||||
@_categoryCache[asAccountId(accountOrId)] ? {}
|
_.values(@_categoryCache[asAccountId(accountOrId)]) ? []
|
||||||
else
|
else
|
||||||
all = []
|
all = []
|
||||||
for accountId, categories of @_categoryCache
|
for accountId, categories of @_categoryCache
|
||||||
|
|
Loading…
Reference in a new issue