Quotes: The full pipeline into the database

1. MessageReceiver always pulls down thumbnails included in quotes
2. Message.upgradeSchema has a new schema that puts all thumbnails on
   disk just like happens with full attachments.
3. handleDataMessage pipes quote from dataMessage into the final message
   destined for the database
pull/1/head
Scott Nonnenberg 7 years ago
parent e69586200a
commit 054d3887a1
No known key found for this signature in database
GPG Key ID: 5F82280C35134661

@ -447,6 +447,7 @@
body : dataMessage.body, body : dataMessage.body,
conversationId : conversation.id, conversationId : conversation.id,
attachments : dataMessage.attachments, attachments : dataMessage.attachments,
quote : dataMessage.quote,
decrypted_at : now, decrypted_at : now,
flags : dataMessage.flags, flags : dataMessage.flags,
errors : [] errors : []

@ -26,7 +26,7 @@ const INITIAL_SCHEMA_VERSION = 0;
// add more upgrade steps, we could design a pipeline that does this // add more upgrade steps, we could design a pipeline that does this
// incrementally, e.g. from version 0 / unknown -> 1, 1 --> 2, etc., similar to // incrementally, e.g. from version 0 / unknown -> 1, 1 --> 2, etc., similar to
// how we do database migrations: // how we do database migrations:
exports.CURRENT_SCHEMA_VERSION = 3; exports.CURRENT_SCHEMA_VERSION = 4;
// Public API // Public API
@ -149,6 +149,26 @@ exports._mapAttachments = upgradeAttachment => async (message, context) => {
return Object.assign({}, message, { attachments }); return Object.assign({}, message, { attachments });
}; };
// _mapQuotedAttachments :: (QuotedAttachment -> Promise QuotedAttachment) ->
// (Message, Context) ->
//
exports._mapQuotedAttachments = upgradeAttachment => async (message, context) => {
if (!message.quote) {
return message;
}
const upgradeWithContext = attachment =>
upgradeAttachment(attachment, context);
const quotedAttachments = (message.quote && message.quote.attachments) || [];
const attachments = await Promise.all(quotedAttachments.map(upgradeWithContext));
return Object.assign({}, message, {
quote: Object.assign({}, message.quote, {
attachments,
}),
});
};
const toVersion0 = async message => const toVersion0 = async message =>
exports.initializeSchemaVersion(message); exports.initializeSchemaVersion(message);
@ -164,17 +184,29 @@ const toVersion3 = exports._withSchemaVersion(
3, 3,
exports._mapAttachments(Attachment.migrateDataToFileSystem) exports._mapAttachments(Attachment.migrateDataToFileSystem)
); );
const toVersion4 = exports._withSchemaVersion(
4,
exports._mapQuotedAttachments(Attachment.migrateDataToFileSystem)
);
// UpgradeStep // UpgradeStep
exports.upgradeSchema = async (message, { writeNewAttachmentData } = {}) => { exports.upgradeSchema = async (rawMessage, { writeNewAttachmentData } = {}) => {
if (!isFunction(writeNewAttachmentData)) { if (!isFunction(writeNewAttachmentData)) {
throw new TypeError('`context.writeNewAttachmentData` is required'); throw new TypeError('`context.writeNewAttachmentData` is required');
} }
return toVersion3( let message = rawMessage;
await toVersion2(await toVersion1(await toVersion0(message))), const versions = [toVersion0, toVersion1, toVersion2, toVersion3, toVersion4];
{ writeNewAttachmentData }
); for (let i = 0, max = versions.length; i < max; i += 1) {
const currentVersion = versions[i];
// We really do want this intra-loop await because this is a chained async action,
// each step dependent on the previous
// eslint-disable-next-line no-await-in-loop
message = await currentVersion(message, { writeNewAttachmentData });
}
return message;
}; };
exports.createAttachmentLoader = (loadAttachmentData) => { exports.createAttachmentLoader = (loadAttachmentData) => {

@ -1006,6 +1006,18 @@ MessageReceiver.prototype.extend({
const attachment = decrypted.attachments[i]; const attachment = decrypted.attachments[i];
promises.push(this.handleAttachment(attachment)); promises.push(this.handleAttachment(attachment));
} }
if (decrypted.quote && decrypted.quote.attachments) {
const { attachments } = decrypted.quote;
for (let i = 0, max = attachments.length; i < max; i += 1) {
const attachment = attachments[i];
if (attachment.thumbnail) {
promises.push(this.handleAttachment(attachment.thumbnail));
}
}
}
return Promise.all(promises).then(() => decrypted); return Promise.all(promises).then(() => decrypted);
/* eslint-enable no-bitwise, no-param-reassign */ /* eslint-enable no-bitwise, no-param-reassign */
}, },

@ -1,4 +1,5 @@
const { assert } = require('chai'); const { assert } = require('chai');
const sinon = require('sinon');
const Message = require('../../../js/modules/types/message'); const Message = require('../../../js/modules/types/message');
const { stringToArrayBuffer } = require('../../../js/modules/string_to_array_buffer'); const { stringToArrayBuffer } = require('../../../js/modules/string_to_array_buffer');
@ -308,4 +309,81 @@ describe('Message', () => {
assert.deepEqual(actual, expected); assert.deepEqual(actual, expected);
}); });
}); });
describe('_mapQuotedAttachments', () => {
it('handles message with no quote', async () => {
const upgradeAttachment = sinon.stub().throws(new Error("Shouldn't be called"));
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
const message = {
body: 'hey there!',
};
const result = await upgradeVersion(message);
assert.deepEqual(result, message);
});
it('handles quote with no attachments', async () => {
const upgradeAttachment = sinon.stub().throws(new Error("Shouldn't be called"));
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
const message = {
body: 'hey there!',
quote: {
text: 'hey!',
},
};
const expected = {
body: 'hey there!',
quote: {
text: 'hey!',
attachments: [],
},
};
const result = await upgradeVersion(message);
assert.deepEqual(result, expected);
});
it('handles zero attachments', async () => {
const upgradeAttachment = sinon.stub().throws(new Error("Shouldn't be called"));
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
const message = {
body: 'hey there!',
quote: {
text: 'hey!',
attachments: [],
},
};
const result = await upgradeVersion(message);
assert.deepEqual(result, message);
});
it('calls provided async function for each quoted attachment', async () => {
const upgradeAttachment = sinon.stub().returns(Promise.resolve({
path: '/new/path/on/disk',
}));
const upgradeVersion = Message._mapQuotedAttachments(upgradeAttachment);
const message = {
body: 'hey there!',
quote: {
text: 'hey!',
attachments: [{
data: 'data is here',
}],
},
};
const expected = {
body: 'hey there!',
quote: {
text: 'hey!',
attachments: [{
path: '/new/path/on/disk',
}],
},
};
const result = await upgradeVersion(message);
assert.deepEqual(result, expected);
});
});
}); });

Loading…
Cancel
Save