signal-desktop/js/modules/migrations/17/index.js
Daniel Gasienica 2642844c27 Rewrite migration 17 without idb
We ran into issues when doing async operations inside of an IndexedDB
`onupgradeneeded` handler. The errors were ‘The transaction is not active’ or
‘Transaction has finished’. The following documentation confirmed that
transactions are committed/terminated when control returns to the event loop:

Spec
- https://www.w3.org/TR/IndexedDB/#transaction-lifetime-concept
- https://www.w3.org/TR/IndexedDB/#upgrade-transaction-construct

Stack Overflow
- https://stackoverflow.com/a/11059085
- https://stackoverflow.com/a/27338944

Since the initial database migration is so critical, I decided to avoid `idb`
with promise support for IndexedDB for now, but will reconsider using it for
other tasks in the future to improve readability of IndexedDB code.
2018-03-19 19:27:59 -04:00

63 lines
2 KiB
JavaScript

const Message = require('../../types/message');
exports.run = async (transaction) => {
const messagesStore = transaction.objectStore('messages');
console.log('Initialize messages schema version');
const numUpgradedMessages = await _initializeMessageSchemaVersion(messagesStore);
console.log('Complete messages schema version initialization', { numUpgradedMessages });
console.log('Create index from attachment schema version to attachment');
messagesStore.createIndex('schemaVersion', 'schemaVersion', { unique: false });
};
const _initializeMessageSchemaVersion = messagesStore =>
new Promise((resolve, reject) => {
const messagePutOperations = [];
const cursorRequest = messagesStore.openCursor();
cursorRequest.onsuccess = (event) => {
const cursor = event.target.result;
const hasMoreData = Boolean(cursor);
if (!hasMoreData) {
// eslint-disable-next-line more/no-then
return Promise.all(messagePutOperations)
.then(() => resolve(messagePutOperations.length));
}
const message = cursor.value;
const messageWithSchemaVersion = Message.initializeSchemaVersion(message);
messagePutOperations.push(new Promise((resolvePut) => {
console.log(
'Initialize schema version for message:',
messageWithSchemaVersion.id
);
resolvePut(putItem(
messagesStore,
messageWithSchemaVersion,
messageWithSchemaVersion.id
));
}));
return cursor.continue();
};
cursorRequest.onerror = event =>
reject(event.target.error);
});
// putItem :: IDBObjectStore -> Item -> Key -> Promise Item
const putItem = (store, item, key) =>
new Promise((resolve, reject) => {
try {
const request = store.put(item, key);
request.onsuccess = event =>
resolve(event.target.result);
request.onerror = event =>
reject(event.target.error);
} catch (error) {
reject(error);
}
});