mirror of
https://github.com/Foundry376/Mailspring.git
synced 2024-09-22 00:06:06 +08:00
More sync error handling WIP
This commit is contained in:
parent
63b929e59b
commit
1fe7fdf4b1
|
@ -35,7 +35,7 @@ class DatabaseConnector {
|
|||
|
||||
_sequelizeForAccount(accountId) {
|
||||
if (!accountId) {
|
||||
throw new Error(`You need to pass an accountId to init the database!`)
|
||||
return Promise.reject(new Error(`You need to pass an accountId to init the database!`))
|
||||
}
|
||||
const sequelize = new Sequelize(accountId, '', '', {
|
||||
storage: path.join(STORAGE_DIR, `a-${accountId}.sqlite`),
|
||||
|
|
18
packages/nylas-core/extendable-error.js
Normal file
18
packages/nylas-core/extendable-error.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
class ExtendableError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
this.name = this.constructor.name;
|
||||
this.message = message;
|
||||
Error.captureStackTrace(this, this.constructor);
|
||||
}
|
||||
|
||||
toJSON() {
|
||||
const obj = {}
|
||||
Object.getOwnPropertyNames(this).forEach((key) => {
|
||||
obj[key] = this[key];
|
||||
});
|
||||
return obj
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ExtendableError
|
|
@ -1,7 +1,7 @@
|
|||
const Rx = require('rx')
|
||||
const Imap = require('imap');
|
||||
const _ = require('underscore');
|
||||
const xoauth2 = require("xoauth2");
|
||||
const xoauth2 = require('xoauth2');
|
||||
const EventEmitter = require('events');
|
||||
|
||||
|
||||
|
@ -273,7 +273,7 @@ class IMAPConnection extends EventEmitter {
|
|||
})
|
||||
.catch((err) => {
|
||||
this._currentOperation = null;
|
||||
console.error(err);
|
||||
console.log(`Task errored: ${operation.description()}`)
|
||||
reject(err);
|
||||
})
|
||||
.finally(() => {
|
||||
|
|
|
@ -7,4 +7,5 @@ module.exports = {
|
|||
SyncPolicy: require('./sync-policy'),
|
||||
SchedulerUtils: require('./scheduler-utils'),
|
||||
Config: require(`./config/${process.env.ENV || 'development'}`),
|
||||
ExtendableError: require('./extendable-error'),
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ module.exports = (sequelize, Sequelize) => {
|
|||
},
|
||||
},
|
||||
instanceMethods: {
|
||||
fetchRaw({account, db}) {
|
||||
fetchRaw: function fetchRaw({account, db}) {
|
||||
const settings = Object.assign({}, account.connectionSettings, account.decryptedCredentials())
|
||||
return Promise.props({
|
||||
category: this.getCategory(),
|
||||
|
@ -57,6 +57,7 @@ module.exports = (sequelize, Sequelize) => {
|
|||
.finally(() => connection.end())
|
||||
})
|
||||
},
|
||||
|
||||
toJSON: function toJSON() {
|
||||
return {
|
||||
id: this.id,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
const crypto = require('crypto');
|
||||
const {JSONType} = require('../../database-types');
|
||||
const {JSONType, JSONARRAYType} = require('../../database-types');
|
||||
|
||||
const algorithm = 'aes-256-ctr';
|
||||
const password = 'd6F3Efeq';
|
||||
|
@ -11,6 +11,7 @@ module.exports = (sequelize, Sequelize) => {
|
|||
connectionSettings: JSONType('connectionSettings'),
|
||||
connectionCredentials: Sequelize.STRING,
|
||||
syncPolicy: JSONType('syncPolicy'),
|
||||
syncErrors: JSONARRAYType('syncErrors'),
|
||||
}, {
|
||||
classMethods: {
|
||||
associate: ({AccountToken}) => {
|
||||
|
@ -24,9 +25,14 @@ module.exports = (sequelize, Sequelize) => {
|
|||
email_address: this.emailAddress,
|
||||
connection_settings: this.connectionSettings,
|
||||
sync_policy: this.syncPolicy,
|
||||
sync_errors: this.syncErrors,
|
||||
}
|
||||
},
|
||||
|
||||
errored: function errored() {
|
||||
return this.syncErrors.length > 0
|
||||
},
|
||||
|
||||
setCredentials: function setCredentials(json) {
|
||||
if (!(json instanceof Object)) {
|
||||
throw new Error("Call setCredentials with JSON!")
|
||||
|
|
|
@ -114,7 +114,6 @@ class FetchMessagesInCategory {
|
|||
|
||||
_fetchMessagesAndQueueForProcessing(range) {
|
||||
const messagesObservable = this._box.fetch(range)
|
||||
// TODO Error handling here
|
||||
messagesObservable.subscribe(this._processMessage.bind(this))
|
||||
return messagesObservable.toPromise()
|
||||
}
|
||||
|
|
|
@ -3,12 +3,20 @@ const {
|
|||
IMAPConnection,
|
||||
PubsubConnector,
|
||||
DatabaseConnector,
|
||||
ExtendableError,
|
||||
} = require('nylas-core');
|
||||
|
||||
const FetchCategoryList = require('./imap/fetch-category-list')
|
||||
const FetchMessagesInCategory = require('./imap/fetch-messages-in-category')
|
||||
const SyncbackTaskFactory = require('./syncback-task-factory')
|
||||
|
||||
class SyncAllCategoriesError extends ExtendableError {
|
||||
constructor(message, failures) {
|
||||
super(message)
|
||||
this.failures = failures
|
||||
}
|
||||
}
|
||||
|
||||
class SyncWorker {
|
||||
constructor(account, db) {
|
||||
this._db = db;
|
||||
|
@ -47,11 +55,8 @@ class SyncWorker {
|
|||
|
||||
if (afterSync === 'idle') {
|
||||
return this.getInboxCategory()
|
||||
.then((inboxCategory) => {
|
||||
this._conn.openBox(inboxCategory.name).then(() => {
|
||||
console.log("SyncWorker: - Idling on inbox category");
|
||||
})
|
||||
});
|
||||
.then((inboxCategory) => this._conn.openBox(inboxCategory.name))
|
||||
.then(() => console.log("SyncWorker: - Idling on inbox category"))
|
||||
} else if (afterSync === 'close') {
|
||||
console.log("SyncWorker: - Closing connection");
|
||||
this._conn.end();
|
||||
|
@ -95,7 +100,7 @@ class SyncWorker {
|
|||
});
|
||||
|
||||
this._conn = conn;
|
||||
resolve(this._conn.connect());
|
||||
return resolve(this._conn.connect());
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -118,7 +123,7 @@ class SyncWorker {
|
|||
});
|
||||
}
|
||||
|
||||
fetchMessagesInCategory() {
|
||||
syncAllCategories() {
|
||||
const {Category} = this._db;
|
||||
const {folderSyncOptions} = this._account.syncPolicy;
|
||||
|
||||
|
@ -132,25 +137,47 @@ class SyncWorker {
|
|||
// ['[Gmail]/All Mail', '[Gmail]/Trash', '[Gmail]/Spam'].includes(cat.name)
|
||||
// )
|
||||
|
||||
// TODO Don't accumulate errors, just bail on the first error and clear
|
||||
// the queue and the connection
|
||||
const failures = []
|
||||
return Promise.all(categoriesToSync.map((cat) =>
|
||||
this._conn.runOperation(new FetchMessagesInCategory(cat, folderSyncOptions))
|
||||
.catch((error) => failures.push({error, category: cat.name}))
|
||||
))
|
||||
.then(() => {
|
||||
if (failures.length > 0) {
|
||||
const error = new SyncAllCategoriesError(
|
||||
`Failed to sync all categories for ${this._account.emailAddress}`, failures
|
||||
)
|
||||
return Promise.reject(error)
|
||||
}
|
||||
return Promise.resolve()
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
syncNow() {
|
||||
clearTimeout(this._syncTimer);
|
||||
|
||||
this.ensureConnection()
|
||||
.then(this.fetchCategoryList.bind(this))
|
||||
.then(this.syncbackMessageActions.bind(this))
|
||||
.then(this.fetchMessagesInCategory.bind(this))
|
||||
// TODO Update account sync state in this error handler
|
||||
.catch(console.error)
|
||||
.then(this.syncAllCategories.bind(this))
|
||||
.catch((error) => {
|
||||
// TODO
|
||||
// Distinguish between temporary and critical errors
|
||||
// Update account sync state for critical errors
|
||||
// Handle connection errors separately
|
||||
console.log('----------------------------------')
|
||||
console.log('Erroring where you are supposed to')
|
||||
console.log(error)
|
||||
console.log('----------------------------------')
|
||||
})
|
||||
.finally(() => {
|
||||
this._lastSyncTime = Date.now()
|
||||
this.onSyncDidComplete()
|
||||
.catch((error) => console.error('SyncWorker.syncNow: Unhandled error while cleaning up sync: ', error))
|
||||
.catch((error) => (
|
||||
console.error('SyncWorker.syncNow: Unhandled error while cleaning up after sync: ', error)
|
||||
))
|
||||
.finally(() => this.scheduleNextSync())
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue