Import: Better onerror logging, save attachments serially (#1768)
* Import: Proper error handling and reporting from IndexedDB APIs * Import: Load attachments one at a time, not per-conversation
This commit is contained in:
parent
f0ec75eef4
commit
bd65932d94
1 changed files with 90 additions and 35 deletions
125
js/backup.js
125
js/backup.js
|
@ -188,7 +188,14 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
var transaction = idb_db.transaction(storeNames, 'readwrite');
|
var transaction = idb_db.transaction(storeNames, 'readwrite');
|
||||||
transaction.onerror = reject;
|
transaction.onerror = function() {
|
||||||
|
var error = transaction.error;
|
||||||
|
console.log(
|
||||||
|
'importFromJsonString error:',
|
||||||
|
error && error.stack ? error.stack : error
|
||||||
|
);
|
||||||
|
reject(error || new Error('importFromJsonString: transaction.onerror'));
|
||||||
|
};
|
||||||
transaction.oncomplete = finish.bind(null, 'transaction complete');
|
transaction.oncomplete = finish.bind(null, 'transaction complete');
|
||||||
|
|
||||||
_.each(storeNames, function(storeName) {
|
_.each(storeNames, function(storeName) {
|
||||||
|
@ -216,14 +223,16 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
request.onerror = function(error) {
|
request.onerror = function() {
|
||||||
|
var error = request.error;
|
||||||
console.log(
|
console.log(
|
||||||
'Error adding object to store',
|
'Error adding object to store',
|
||||||
storeName,
|
storeName,
|
||||||
':',
|
':',
|
||||||
toAdd
|
toAdd,
|
||||||
|
error && error.stack ? error.stack : error
|
||||||
);
|
);
|
||||||
reject(error);
|
reject(error || new Error('importFromJsonString: request.onerror'));
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -383,14 +392,16 @@
|
||||||
return createFileAndWriter(dir, 'messages.json').then(function(writer) {
|
return createFileAndWriter(dir, 'messages.json').then(function(writer) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
var transaction = idb_db.transaction('messages', 'readwrite');
|
var transaction = idb_db.transaction('messages', 'readwrite');
|
||||||
transaction.onerror = function(e) {
|
transaction.onerror = function() {
|
||||||
|
var error = transaction.error;
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
'exportConversation transaction error for conversation',
|
'exportConversation transaction error for conversation',
|
||||||
name,
|
name,
|
||||||
':',
|
':',
|
||||||
e && e.stack ? e.stack : e
|
error && error.stack ? error.stack : error
|
||||||
);
|
);
|
||||||
return reject(e);
|
return reject(error || new Error('exportConversation: transaction.onerror'));
|
||||||
};
|
};
|
||||||
transaction.oncomplete = function() {
|
transaction.oncomplete = function() {
|
||||||
// this doesn't really mean anything - we may have attachment processing to do
|
// this doesn't really mean anything - we may have attachment processing to do
|
||||||
|
@ -407,14 +418,16 @@
|
||||||
var stream = createOutputStream(writer);
|
var stream = createOutputStream(writer);
|
||||||
stream.write('{"messages":[');
|
stream.write('{"messages":[');
|
||||||
|
|
||||||
request.onerror = function(e) {
|
request.onerror = function() {
|
||||||
|
var error = request.error;
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
'exportConversation: error pulling messages for conversation',
|
'exportConversation: error pulling messages for conversation',
|
||||||
name,
|
name,
|
||||||
':',
|
':',
|
||||||
e && e.stack ? e.stack : e
|
error && error.stack ? error.stack : error
|
||||||
);
|
);
|
||||||
return reject(e);
|
return reject(error || new Error('exportConversation: request.onerror'));
|
||||||
};
|
};
|
||||||
request.onsuccess = function(event) {
|
request.onsuccess = function(event) {
|
||||||
var cursor = event.target.result;
|
var cursor = event.target.result;
|
||||||
|
@ -497,12 +510,13 @@
|
||||||
function exportConversations(idb_db, parentDir) {
|
function exportConversations(idb_db, parentDir) {
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
var transaction = idb_db.transaction('conversations', 'readwrite');
|
var transaction = idb_db.transaction('conversations', 'readwrite');
|
||||||
transaction.onerror = function(e) {
|
transaction.onerror = function() {
|
||||||
|
var error = transaction.error;
|
||||||
console.log(
|
console.log(
|
||||||
'exportConversations: transaction error:',
|
'exportConversations: transaction error:',
|
||||||
e && e.stack ? e.stack : e
|
error && error.stack ? error.stack : error
|
||||||
);
|
);
|
||||||
return reject(e);
|
return reject(error || new Error('exportConversations: transaction.onerror'));
|
||||||
};
|
};
|
||||||
transaction.oncomplete = function() {
|
transaction.oncomplete = function() {
|
||||||
// not really very useful - fires at unexpected times
|
// not really very useful - fires at unexpected times
|
||||||
|
@ -511,12 +525,13 @@
|
||||||
var promiseChain = Promise.resolve();
|
var promiseChain = Promise.resolve();
|
||||||
var store = transaction.objectStore('conversations');
|
var store = transaction.objectStore('conversations');
|
||||||
var request = store.openCursor();
|
var request = store.openCursor();
|
||||||
request.onerror = function(e) {
|
request.onerror = function() {
|
||||||
|
var error = request.error;
|
||||||
console.log(
|
console.log(
|
||||||
'exportConversations: error pulling conversations:',
|
'exportConversations: error pulling conversations:',
|
||||||
e && e.stack ? e.stack : e
|
error && error.stack ? error.stack : error
|
||||||
);
|
);
|
||||||
return reject(e);
|
return reject(error || new Error('exportConversations: request.onerror'));
|
||||||
};
|
};
|
||||||
request.onsuccess = function(event) {
|
request.onsuccess = function(event) {
|
||||||
var cursor = event.target.result;
|
var cursor = event.target.result;
|
||||||
|
@ -601,12 +616,14 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
var transaction = idb_db.transaction('messages', 'readwrite');
|
var transaction = idb_db.transaction('messages', 'readwrite');
|
||||||
transaction.onerror = function(e) {
|
transaction.onerror = function() {
|
||||||
|
var error = transaction.error;
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
'saveAllMessages transaction error:',
|
'saveAllMessages transaction error:',
|
||||||
e && e.stack ? e.stack : e
|
error && error.stack ? error.stack : error
|
||||||
);
|
);
|
||||||
return reject(e);
|
return reject(error || new Error('saveAllMessages: transaction.onerror'));
|
||||||
};
|
};
|
||||||
transaction.oncomplete = finish.bind(null, 'transaction complete');
|
transaction.oncomplete = finish.bind(null, 'transaction complete');
|
||||||
|
|
||||||
|
@ -620,7 +637,7 @@
|
||||||
count += 1;
|
count += 1;
|
||||||
if (count === messages.length) {
|
if (count === messages.length) {
|
||||||
console.log(
|
console.log(
|
||||||
'Done importing',
|
'Saved',
|
||||||
messages.length,
|
messages.length,
|
||||||
'messages for conversation',
|
'messages for conversation',
|
||||||
// Don't know if group or private conversation, so we blindly redact
|
// Don't know if group or private conversation, so we blindly redact
|
||||||
|
@ -629,35 +646,63 @@
|
||||||
finish('puts scheduled');
|
finish('puts scheduled');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
request.onerror = function(event) {
|
request.onerror = function() {
|
||||||
console.log('Error adding object to store:', event);
|
var event = request.error;
|
||||||
reject(new Error('saveAllMessage: onerror fired'));
|
console.log(
|
||||||
|
'Error adding object to store:',
|
||||||
|
error && error.stack ? error.stack : error
|
||||||
|
);
|
||||||
|
reject(error || new Error('saveAllMessages: request.onerror'));
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// To reduce the memory impact of attachments, we make individual saves to the
|
||||||
|
// database for every message with an attachment. We load the attachment for a
|
||||||
|
// message, save it, and only then do we move on to the next message. Thus, every
|
||||||
|
// message with attachments needs to be removed from our overall message save with the
|
||||||
|
// filter() call.
|
||||||
function importConversation(idb_db, dir) {
|
function importConversation(idb_db, dir) {
|
||||||
return readFileAsText(dir, 'messages.json').then(function(contents) {
|
return readFileAsText(dir, 'messages.json').then(function(contents) {
|
||||||
var promiseChain = Promise.resolve();
|
var promiseChain = Promise.resolve();
|
||||||
|
|
||||||
var json = JSON.parse(contents);
|
var json = JSON.parse(contents);
|
||||||
var messages = json.messages;
|
var conversationId;
|
||||||
_.forEach(messages, function(message) {
|
if (json.messages && json.messages.length) {
|
||||||
|
conversationId = json.messages[0].conversationId;
|
||||||
|
}
|
||||||
|
|
||||||
|
var messages = _.filter(json.messages, function(message) {
|
||||||
message = unstringify(message);
|
message = unstringify(message);
|
||||||
|
|
||||||
if (message.attachments && message.attachments.length) {
|
if (message.attachments && message.attachments.length) {
|
||||||
var process = function() {
|
var process = function() {
|
||||||
return loadAttachments(dir, message);
|
return loadAttachments(dir, message).then(function() {
|
||||||
|
return saveAllMessages(idb_db, [message]);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
promiseChain = promiseChain.then(process);
|
promiseChain = promiseChain.then(process);
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
});
|
});
|
||||||
|
|
||||||
return promiseChain.then(function() {
|
return saveAllMessages(idb_db, messages)
|
||||||
return saveAllMessages(idb_db, messages);
|
.then(function() {
|
||||||
});
|
return promiseChain;
|
||||||
|
})
|
||||||
|
.then(function() {
|
||||||
|
console.log(
|
||||||
|
'Finished importing conversation',
|
||||||
|
// Don't know if group or private conversation, so we blindly redact
|
||||||
|
conversationId ? '[REDACTED]' + conversationId.slice(-3) : 'with no messages'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
}, function() {
|
}, function() {
|
||||||
console.log('Warning: could not access messages.json in directory: ' + dir);
|
console.log('Warning: could not access messages.json in directory: ' + dir);
|
||||||
});
|
});
|
||||||
|
@ -689,10 +734,18 @@
|
||||||
var storeNames = idb_db.objectStoreNames;
|
var storeNames = idb_db.objectStoreNames;
|
||||||
var transaction = idb_db.transaction(storeNames, 'readwrite');
|
var transaction = idb_db.transaction(storeNames, 'readwrite');
|
||||||
|
|
||||||
transaction.oncomplete = function() {
|
var finished = false;
|
||||||
// unused
|
var finish = function(via) {
|
||||||
|
console.log('clearing all stores done via', via);
|
||||||
|
if (finished) {
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
finished = true;
|
||||||
};
|
};
|
||||||
transaction.onerror = function(error) {
|
|
||||||
|
transaction.oncomplete = finish.bind(null, 'transaction complete');
|
||||||
|
transaction.onerror = function() {
|
||||||
|
var error = transaction.error;
|
||||||
console.log(
|
console.log(
|
||||||
'saveAllMessages transaction error:',
|
'saveAllMessages transaction error:',
|
||||||
error && error.stack ? error.stack : error
|
error && error.stack ? error.stack : error
|
||||||
|
@ -711,16 +764,18 @@
|
||||||
|
|
||||||
if (count >= storeNames.length) {
|
if (count >= storeNames.length) {
|
||||||
console.log('Done clearing all indexeddb stores');
|
console.log('Done clearing all indexeddb stores');
|
||||||
return resolve();
|
return finish('clears complete');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
request.onerror = function(error) {
|
request.onerror = function() {
|
||||||
|
var error = request.error;
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
'clearAllStores transaction error:',
|
'clearAllStores transaction error:',
|
||||||
error && error.stack ? error.stack : error
|
error && error.stack ? error.stack : error
|
||||||
);
|
);
|
||||||
return reject(error);
|
return reject(error || new Error('clearAllStores: request.onerror'));
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue