Allow including specific header fields in message listing

This commit is contained in:
Andris Reinman 2023-07-20 13:40:01 +03:00
parent 6e0e882ee0
commit 39560ae9dc
2 changed files with 124 additions and 38 deletions

View file

@ -1711,6 +1711,12 @@ paths:
description: Ordering of the records by insert date
schema:
$ref: '#/components/schemas/Order'
- name: includeHeaders
in: query
description: 'Comma separated list of header keys to include in the response'
schema:
type: string
example: 'List-ID, MIME-version'
- name: next
in: query
description: 'Cursor value for next page, retrieved from nextCursor response value'
@ -2037,6 +2043,12 @@ paths:
description: Ordering of the records by insert date. If no order is supplied, results are sorted by heir mongoDB ObjectId.
schema:
$ref: '#/components/schemas/Order'
- name: includeHeaders
in: query
description: 'Comma separated list of header keys to include in the response'
schema:
type: string
example: 'List-ID, MIME-version'
- name: page
in: query
description: 'Current page number. Informational only, page numbers start from 1'
@ -6918,6 +6930,9 @@ components:
metaData:
type: object
description: Custom metadata value. Included if metaData query argument was true
headers:
type: object
description: Header object keys requested with the includeHeaders argument
GetFilesResult:
required:
- id

View file

@ -387,6 +387,8 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
let sortAscending = result.value.order === 'asc';
let filterUnseen = result.value.unseen;
let includeHeaders = result.value.includeHeaders ? result.value.includeHeaders.split(',') : false;
let mailboxData;
try {
mailboxData = await db.database.collection('mailboxes').findOne(
@ -443,13 +445,6 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
hdate: true,
idate: true,
subject: true,
'mimeTree.parsedHeader.from': true,
'mimeTree.parsedHeader.sender': true,
'mimeTree.parsedHeader.to': true,
'mimeTree.parsedHeader.cc': true,
'mimeTree.parsedHeader.bcc': true,
'mimeTree.parsedHeader.content-type': true,
'mimeTree.parsedHeader.references': true,
ha: true,
size: true,
intro: true,
@ -466,6 +461,24 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
sortAscending
};
if (includeHeaders) {
// get all headers
opts.fields.projection['mimeTree.parsedHeader'] = true;
} else {
// get only required headers
for (let requiredHeader of [
'mimeTree.parsedHeader.from',
'mimeTree.parsedHeader.sender',
'mimeTree.parsedHeader.to',
'mimeTree.parsedHeader.cc',
'mimeTree.parsedHeader.bcc',
'mimeTree.parsedHeader.content-type',
'mimeTree.parsedHeader.references'
]) {
opts.fields.projection[requiredHeader] = true;
}
}
if (pageNext) {
opts.next = pageNext;
} else if ((!page || page > 1) && pagePrevious) {
@ -500,7 +513,7 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
previousCursor: listing.hasPrevious ? listing.previous : false,
nextCursor: listing.hasNext ? listing.next : false,
specialUse: mailboxData.specialUse,
results: (listing.results || []).map(formatMessageListing)
results: (listing.results || []).map(entry => formatMessageListing(entry, includeHeaders))
};
return res.json(response);
@ -532,6 +545,12 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
attachments: booleanSchema,
flagged: booleanSchema,
unseen: booleanSchema,
includeHeaders: Joi.string()
.max(1024)
.trim()
.empty('')
.example('List-ID, MIME-Version')
.description('Comma separated list of header keys to include in the response'),
searchable: booleanSchema,
sess: sessSchema,
ip: sessIPSchema
@ -546,6 +565,12 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
threadCounters: booleanSchema.default(false),
limit: Joi.number().default(20).min(1).max(250),
order: Joi.any().empty('').allow('asc', 'desc').optional(),
includeHeaders: Joi.string()
.max(1024)
.trim()
.empty('')
.example('List-ID, MIME-Version')
.description('Comma separated list of header keys to include in the response'),
next: nextPageCursorSchema,
previous: previousPageCursorSchema,
page: pageNrSchema
@ -581,6 +606,8 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
let pagePrevious = result.value.previous;
let order = result.value.order;
let includeHeaders = result.value.includeHeaders ? result.value.includeHeaders.split(',') : false;
let filter;
let query;
@ -588,29 +615,24 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
let hasESFeatureFlag = await db.redis.sismember(`feature:indexing`, user.toString());
if (hasESFeatureFlag) {
// search from ElasticSearch
/*
// TODO: paging and cursors for ElasticSearch results
let searchQuery = await getElasticSearchQuery(db, user, result.value.q);
const esclient = getClient();
console.log(
util.inspect(
{
index: config.elasticsearch.index,
body: { query: searchQuery, sort: { uid: 'desc' } }
},
false,
22,
true
)
);
let searchResult = await esclient.search({
const searchOpts = {
index: config.elasticsearch.index,
body: { query: searchQuery, sort: { uid: 'desc' } }
});
};
let searchResult = await esclient.search(searchOpts);
const searchHits = searchResult && searchResult.body && searchResult.body.hits;
console.log('ES RESULTS');
console.log(util.inspect(searchResult, false, 22, true));
*/
}
filter = await getMongoDBQuery(db, user, result.value.q);
@ -640,13 +662,6 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
hdate: true,
idate: true,
subject: true,
'mimeTree.parsedHeader.from': true,
'mimeTree.parsedHeader.sender': true,
'mimeTree.parsedHeader.to': true,
'mimeTree.parsedHeader.cc': true,
'mimeTree.parsedHeader.bcc': true,
'mimeTree.parsedHeader.content-type': true,
'mimeTree.parsedHeader.references': true,
ha: true,
intro: true,
size: true,
@ -663,6 +678,24 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
sortAscending: order === 'asc' ? true : undefined
};
if (includeHeaders) {
// get all headers
opts.fields.projection['mimeTree.parsedHeader'] = true;
} else {
// get only required headers
for (let requiredHeader of [
'mimeTree.parsedHeader.from',
'mimeTree.parsedHeader.sender',
'mimeTree.parsedHeader.to',
'mimeTree.parsedHeader.cc',
'mimeTree.parsedHeader.bcc',
'mimeTree.parsedHeader.content-type',
'mimeTree.parsedHeader.references'
]) {
opts.fields.projection[requiredHeader] = true;
}
}
if (pageNext) {
opts.next = pageNext;
} else if ((!page || page > 1) && pagePrevious) {
@ -697,7 +730,7 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
page,
previousCursor: listing.hasPrevious ? listing.previous : false,
nextCursor: listing.hasNext ? listing.next : false,
results: (listing.results || []).map(formatMessageListing)
results: (listing.results || []).map(entry => formatMessageListing(entry, includeHeaders))
};
return res.json(response);
@ -2499,6 +2532,12 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
next: nextPageCursorSchema,
previous: previousPageCursorSchema,
order: Joi.any().empty('').allow('asc', 'desc').default('desc'),
includeHeaders: Joi.string()
.max(1024)
.trim()
.empty('')
.example('List-ID, MIME-Version')
.description('Comma separated list of header keys to include in the response'),
page: pageNrSchema,
sess: sessSchema,
ip: sessIPSchema
@ -2533,6 +2572,8 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
let pagePrevious = result.value.previous;
let sortAscending = result.value.order === 'asc';
let includeHeaders = result.value.includeHeaders ? result.value.includeHeaders.split(',') : false;
let total = await db.database.collection('archived').countDocuments({ user });
let opts = {
@ -2551,13 +2592,6 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
hdate: true,
idate: true,
subject: true,
'mimeTree.parsedHeader.from': true,
'mimeTree.parsedHeader.sender': true,
'mimeTree.parsedHeader.to': true,
'mimeTree.parsedHeader.cc': true,
'mimeTree.parsedHeader.bcc': true,
'mimeTree.parsedHeader.content-type': true,
'mimeTree.parsedHeader.references': true,
ha: true,
intro: true,
size: true,
@ -2573,6 +2607,24 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
sortAscending
};
if (includeHeaders) {
// get all headers
opts.fields.projection['mimeTree.parsedHeader'] = true;
} else {
// get only required headers
for (let requiredHeader of [
'mimeTree.parsedHeader.from',
'mimeTree.parsedHeader.sender',
'mimeTree.parsedHeader.to',
'mimeTree.parsedHeader.cc',
'mimeTree.parsedHeader.bcc',
'mimeTree.parsedHeader.content-type',
'mimeTree.parsedHeader.references'
]) {
opts.fields.projection[requiredHeader] = true;
}
}
if (pageNext) {
opts.next = pageNext;
} else if ((!page || page > 1) && pagePrevious) {
@ -2606,7 +2658,7 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
m.uid = m._id;
return m;
})
.map(formatMessageListing)
.map(entry => formatMessageListing(entry, includeHeaders))
};
return res.json(response);
@ -3127,7 +3179,17 @@ function leftPad(val, chr, len) {
return chr.repeat(len - val.toString().length) + val;
}
function formatMessageListing(messageData) {
function formatMessageListing(messageData, includeHeaders) {
includeHeaders = []
.concat(includeHeaders || [])
.map(entry => {
if (typeof entry !== 'string') {
return false;
}
return entry.toLowerCase().trim();
})
.filter(entry => entry);
let parsedHeader = (messageData.mimeTree && messageData.mimeTree.parsedHeader) || {};
let from = parsedHeader.from ||
@ -3176,6 +3238,15 @@ function formatMessageListing(messageData) {
bimi: messageData.bimi
};
if (includeHeaders.length) {
response.headers = {};
for (let headerKey of includeHeaders) {
if (parsedHeader[headerKey]) {
response.headers[headerKey] = parsedHeader[headerKey];
}
}
}
if (messageData.meta && 'custom' in messageData.meta) {
response.metaData = tools.formatMetaData(messageData.meta.custom);
}