Support for /threads?view=count/expanded, thread & message JSON includes folders

This commit is contained in:
Ben Gotow 2016-06-29 12:02:31 -07:00
parent 9ff7d3aea5
commit 53d6c6284e
6 changed files with 86 additions and 23 deletions

View file

@ -24,10 +24,11 @@ module.exports = (server) => {
},
handler: (request, reply) => {
request.getAccountDatabase().then((db) => {
const {Message} = db;
const {Message, Category} = db;
Message.findAll({
limit: request.query.limit,
offset: request.query.offset,
include: {model: Category},
}).then((messages) => {
reply(Serialization.jsonStringify(messages));
})
@ -55,20 +56,20 @@ module.exports = (server) => {
},
},
handler: (request, reply) => {
request.getAccountDatabase()
.then((db) => {
const {headers: {accept}} = request
const {params: {id}} = request
const account = request.auth.credentials
request.getAccountDatabase().then((db) => {
const {Message, Category} = db;
const {headers: {accept}} = request;
const {params: {id}} = request;
const account = request.auth.credentials;
db.Message.findOne({where: {id}})
.then((message) => {
Message.findOne({where: {id}, include: {model: Category}}).then((message) => {
if (!message) {
return reply.notFound(`Message ${id} not found`)
}
if (accept === 'message/rfc822') {
return message.fetchRaw({account, db})
.then((rawMessage) => reply(rawMessage))
return message.fetchRaw({account, db}).then((rawMessage) =>
reply(rawMessage)
)
}
return reply(Serialization.jsonStringify(message));
})

View file

@ -1,4 +1,5 @@
const Joi = require('joi');
const _ = require('underscore');
const Serialization = require('../serialization');
const {createSyncbackRequest} = require('../route-helpers')
@ -13,6 +14,7 @@ module.exports = (server) => {
validate: {
query: {
'id': Joi.number().integer().min(0),
'view': Joi.string().valid('expanded', 'count'),
'subject': Joi.string(),
'unread': Joi.boolean(),
'starred': Joi.boolean(),
@ -21,17 +23,24 @@ module.exports = (server) => {
'lastMessageBefore': Joi.date().timestamp(),
'lastMessageAfter': Joi.date().timestamp(),
'in': Joi.string().allow(Joi.number()),
'limit': Joi.number().integer().min(1).max(2000).default(100),
'offset': Joi.number().integer().min(0).default(0),
},
},
response: {
schema: Joi.array().items(
Serialization.jsonSchema('Thread')
),
schema: Joi.alternatives().try([
Joi.array().items(
Serialization.jsonSchema('Thread')
),
Joi.object().keys({
count: Joi.number().integer().min(0),
}),
]),
},
},
handler: (request, reply) => {
request.getAccountDatabase().then((db) => {
const {Thread, Category} = db;
const {Thread, Category, Message} = db;
const query = request.query;
const where = {};
const include = [];
@ -93,11 +102,45 @@ module.exports = (server) => {
include.push({model: Category})
}
if (query.view === 'expanded') {
include.push({
model: Message,
as: 'messages',
attributes: _.without(Object.keys(Message.attributes), 'body'),
})
} else {
include.push({
model: Message,
as: 'messages',
attributes: ['id'],
})
}
if (query.view === 'count') {
Thread.count({
where: where,
include: include,
}).then((count) => {
reply(Serialization.jsonStringify({count: count}));
});
return;
}
Thread.findAll({
limit: request.query.limit,
offset: request.query.offset,
where: where,
include: include,
limit: 50,
}).then((threads) => {
// if the user requested the expanded viw, fill message.category using
// thread.category, since it must be a superset.
if (query.view === 'expanded') {
for (const thread of threads) {
for (const msg of thread.messages) {
msg.category = thread.categories.find(c => c.id === msg.categoryId);
}
}
}
reply(Serialization.jsonStringify(threads));
})
})

View file

@ -15,6 +15,8 @@ function jsonSchema(modelName) {
id: Joi.number(),
object: Joi.string(),
email_address: Joi.string(),
provider: Joi.string(),
organization_unit: Joi.string(),
connection_settings: Joi.object(),
sync_policy: Joi.object(),
sync_error: Joi.object(),

View file

@ -61,17 +61,21 @@ module.exports = (sequelize, Sequelize) => {
},
toJSON: function toJSON() {
if (this.category_id && !this.category) {
throw new Error("Message.toJSON called on a message where category were not eagerly loaded.")
}
return {
id: this.id,
object: 'message',
account_id: this.accountId,
object: 'message',
body: this.body,
subject: this.subject,
snippet: this.snippet,
date: this.date.getTime() / 1000.0,
unread: this.unread,
starred: this.starred,
category_id: this.categoryId,
folder: this.category,
};
},
},

View file

@ -23,16 +23,18 @@ module.exports = (sequelize, Sequelize) => {
},
instanceMethods: {
toJSON: function toJSON() {
if (!this.categories) {
if (!(this.categories instanceof Array)) {
throw new Error("Thread.toJSON called on a thread where categories were not eagerly loaded.")
}
const folders = this.categories.filter(c => c.type === 'folder');
const labels = this.categories.filter(c => c.type === 'label');
return {
if (!(this.messages instanceof Array)) {
throw new Error("Thread.toJSON called on a thread where messages were not eagerly loaded. (Only need the IDs!)")
}
const response = {
id: this.id,
object: 'thread',
folders: folders,
labels: labels,
folders: this.categories.filter(c => c.type === 'folder'),
labels: this.categories.filter(c => c.type === 'label'),
account_id: this.accountId,
participants: this.participants,
subject: this.subject,
@ -43,6 +45,15 @@ module.exports = (sequelize, Sequelize) => {
last_message_sent_timestamp: this.lastMessageSentDate ? this.lastMessageSentDate.getTime() / 1000.0 : null,
last_message_received_timestamp: this.lastMessageReceivedDate ? this.lastMessageReceivedDate.getTime() / 1000.0 : null,
};
const expanded = this.messages[0] ? !!this.messages[0].accountId : false;
if (expanded) {
response.messages = this.messages;
} else {
response.message_ids = this.messages.map(m => m.id);
}
return response;
},
},
});

View file

@ -23,6 +23,8 @@ module.exports = (sequelize, Sequelize) => {
return {
id: this.id,
object: 'account',
organization_unit: (this.provider === 'gmail') ? 'label' : 'folder',
provider: this.provider,
email_address: this.emailAddress,
connection_settings: this.connectionSettings,
sync_policy: this.syncPolicy,