Skip to content

Commit

Permalink
add multiple GET endpoints to API generation and changed them to new …
Browse files Browse the repository at this point in the history
…design. Use general schemas made earlier
  • Loading branch information
NickOvt committed Nov 2, 2023
1 parent 1db7528 commit 71c6496
Showing 1 changed file with 198 additions and 38 deletions.
236 changes: 198 additions & 38 deletions lib/api/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const { getMongoDBQuery /*, getElasticSearchQuery*/ } = require('../search-query

const BimiHandler = require('../bimi-handler');
const { Address, AddressOptionalNameArray, Header, Attachment, ReferenceWithAttachments, Bimi } = require('../schemas/request/messages-schemas');
const { userId, mailboxId, messageId, successRes } = require('../schemas/request/general-schemas');
const { MsgEnvelope } = require('../schemas/response/messages-schemas');

module.exports = (db, server, messageHandler, userHandler, storageHandler, settingsHandler) => {
let maildrop = new Maildropper({
Expand Down Expand Up @@ -440,7 +442,7 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti

const { requestBody, pathParams, queryParams } = req.route.spec.validationObjs;

const schema = Joi.object().keys({
const schema = Joi.object({
...requestBody,
...pathParams,
...queryParams
Expand Down Expand Up @@ -655,24 +657,74 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
});

server.get(
{ name: 'search', path: '/users/:user/search' },
{
name: 'search',
path: '/users/:user/search',
validationObjs: {
queryParams: {
...searchSchema.keys({
threadCounters: booleanSchema
.default(false)
.description('If true, then includes threadMessageCount in the response. Counters come with some overhead'),
limit: Joi.number().default(20).min(1).max(250).description('How many records to return'),
order: Joi.any()
.empty('')
.allow('asc', 'desc')
.optional()
.description('Ordering of the records by insert date. If no order is supplied, results are sorted by heir mongoDB ObjectId.'),
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
})
},
pathParams: { user: userId },
requestBody: {},
response: {
200: {
description: 'Success',
model: Joi.object({
success: booleanSchema.required().description('Indicates successful response'),
query: Joi.string().required('Query'),
total: Joi.number().required('How many results were found'),
page: Joi.number().required('Current page number. Derived from page query argument'),
previousCursor: Joi.alternatives()
.try(booleanSchema, Joi.string())
.required()
.description('Either a cursor string or false if there are not any previous results'),
nextCursor: Joi.alternatives()
.try(booleanSchema, Joi.string())
.required()
.description('Either a cursor string or false if there are not any next results'),
results: Joi.array()
.items(
Joi.object({
id: Joi.string().required().description('ID of the Domain Alias'),
alias: Joi.string().required().description('Domain Alias'),
domain: Joi.string().required().description('The domain this alias applies to')
}).$_setFlag('objectName', 'GetDomainAliasesResult')
)
.required()
.description('Aliases listing')
})
}
}
},
summary: 'Search for messages ',
description: 'This method allows searching for matching messages.',
tags: ['Messages']
},
tools.responseWrapper(async (req, res) => {
res.charSet('utf-8');

const schema = searchSchema.keys({
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
});
const { requestBody, queryParams, pathParams } = req.route.spec.validationObjs;

const schema = Joi.object({ ...requestBody, ...queryParams, ...pathParams });

const result = schema.validate(req.params, {
abortEarly: false,
Expand Down Expand Up @@ -932,19 +984,126 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
);

server.get(
{ name: 'message', path: '/users/:user/mailboxes/:mailbox/messages/:message' },
{
name: 'message',
path: '/users/:user/mailboxes/:mailbox/messages/:message',
summary: 'Request Message information',
validationObjs: {
queryParams: {
replaceCidLinks: booleanSchema.default(false).description('If true then replaces cid links'),
markAsSeen: booleanSchema.default(false).description('If true then marks message as seen'),
sess: sessSchema,
ip: sessIPSchema
},
pathParams: {
user: userId,
mailbox: mailboxId,
message: messageId
},
requestBody: {},
response: {
200: {
description: 'Success',
model: Joi.object({
success: successRes,
id: messageId,
mailbox: mailboxId,
user: userId,
envelope: MsgEnvelope.required(),
thread: Joi.string().required().description('ID of the Thread'),
from: Address.required(),
to: Address,
cc: Address,
bcc: Address,
subject: Joi.string().required().description('Message subject'),
messageId: Joi.string().required().description('Message-ID header'),
date: Joi.date().required().description('Date string from header'),
idate: Joi.date().description('Date string of receive time'),
list: Joi.object({
id: Joi.string().required().description('Value from List-ID header'),
unsubscribe: Joi.string().required().description('Value from List-Unsubscribe header')
})
.description('If set then this message is from a mailing list')
.$_setFlag('objectName', 'List'),
expires: Joi.string().description('Datestring, if set then indicates the time after this message is automatically deleted'),
seen: booleanSchema.required().description('Does this message have a \\Seen flag'),
deleted: booleanSchema.required().description('Does this message have a \\Deleted flag'),
flagged: booleanSchema.required().description('Does this message have a \\Flagged flag'),
draft: booleanSchema.required().description('Does this message have a \\Draft flag'),
html: Joi.array()
.items(Joi.string())
.description(
'An array of HTML string. Every array element is from a separate mime node, usually you would just join these to a single string'
),
text: Joi.string().description('Plaintext content of the message'),
attachments: Joi.array()
.items(
Joi.object({
id: Joi.string().required().description('Attachment ID'),
hash: Joi.string().description('SHA-256 hash of the contents of the attachment'),
filename: Joi.string().required().description('Filename of the attachment'),
contentType: Joi.string().required().description('MIME type'),
disposition: Joi.string().required().description('Attachment disposition'),
transferEncoding: Joi.string()
.required()
.description('Which transfer encoding was used (actual content when fetching attachments is not encoded)'),
related: booleanSchema
.required()
.description(
'Was this attachment found from a multipart/related node. This usually means that this is an embedded image'
),
sizeKb: Joi.number().required().description('Approximate size of the attachment in kilobytes')
})
)
.description('Attachments for the message'),
verificationResults: Joi.object({
tls: Joi.object({
name: Joi.object().required().description('Cipher name, eg "ECDHE-RSA-AES128-GCM-SHA256"'),
version: Joi.object().required().description('TLS version, eg "TLSv1/SSLv3"')
})
.$_setFlag('objectName', 'Tls')
.required()
.description('TLS information. Value is false if TLS was not used'),
spf: Joi.object({})
.required()
.description('Domain name (either MFROM or HELO) of verified SPF or false if no SPF match was found'),
dkim: Joi.object({}).required().description('Domain name of verified DKIM signature or false if no valid signature was found')
}).description(
'Security verification info if message was received from MX. If this property is missing then do not automatically assume invalid TLS, SPF or DKIM.'
),
bimi: Joi.object({
certified: booleanSchema.description('If true, then this logo is from a VMC file'),
url: Joi.string().description('URL of the resource the logo was retrieved from'),
image: Joi.string().description('Data URL for the SVG image')
}).description('BIMI logo info. If logo validation failed in any way, then this property is not set'),
contentType: Joi.object({
value: Joi.string().required().description('MIME type of the message, eg. "multipart/mixed'),
params: Joi.object({}).required().description('An object with Content-Type params as key-value pairs')
})
.required()
.description('Parsed Content-Type header. Usually needed to identify encrypted messages and such'),
metaData: Joi.object({}).description('Custom metadata object set for this message'),
references: Joi.object({}),
files: Joi.object({}).description(
'List of files added to this message as attachments. Applies to Drafts, normal messages do not have this property. Needed to prevent uploading the same attachment every time a draft is updated'
),
outbound: Joi.array().items(Joi.object({})).description('Outbound queue entries'),
forwardTargets: Joi.object({}),
reference: Joi.object({}).description('Referenced message info'),
answered: booleanSchema.required(),
forwarded: booleanSchema.required()
})
}
}
},
tags: ['Messages']
},
tools.responseWrapper(async (req, res) => {
res.charSet('utf-8');

const schema = Joi.object().keys({
user: Joi.string().hex().lowercase().length(24).required(),
mailbox: Joi.string().hex().lowercase().length(24).required(),
message: Joi.number().min(1).required(),
replaceCidLinks: booleanSchema.default(false),
markAsSeen: booleanSchema.default(false),
sess: sessSchema,
ip: sessIPSchema
});
const { requestBody, queryParams, pathParams } = req.route.spec.validationObjs;

const schema = Joi.object({ ...requestBody, ...queryParams, ...pathParams });

const result = schema.validate(req.params, {
abortEarly: false,
Expand Down Expand Up @@ -1663,8 +1822,8 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
'This method allows to upload either an RFC822 formatted message or a message structure to a mailbox. Raw message is stored unmodified, no headers are added or removed. If you want to generate the uploaded message from structured data fields, then do not use the raw property.',
validationObjs: {
pathParams: {
user: Joi.string().hex().lowercase().length(24).required().description('ID of the User'),
mailbox: Joi.string().hex().lowercase().length(24).required().description('ID of the Mailbox')
user: userId,
mailbox: mailboxId
},
requestBody: {
date: Joi.date(),
Expand Down Expand Up @@ -2109,9 +2268,9 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
path: '/users/:user/mailboxes/:mailbox/messages/:message/forward',
validationObjs: {
pathParams: {
user: Joi.string().hex().lowercase().length(24).required().description('ID of the User'),
mailbox: Joi.string().hex().lowercase().length(24).required().description('ID of the Mailbox'),
message: Joi.number().required().description('Message ID')
user: userId,
mailbox: mailboxId,
message: messageId
},
queryParams: {},
requestBody: {
Expand Down Expand Up @@ -2318,9 +2477,9 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
path: '/users/:user/mailboxes/:mailbox/messages/:message/submit',
validationObjs: {
pathParams: {
user: Joi.string().hex().lowercase().length(24).required().description('ID of the User'),
mailbox: Joi.string().hex().lowercase().length(24).required().description('ID of the Mailbox'),
message: Joi.number().required().description('Message ID')
user: userId,
mailbox: mailboxId,
message: messageId
},
queryParams: {},
requestBody: {
Expand Down Expand Up @@ -2873,7 +3032,7 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
'Initiates a restore task to move archived messages of a date range back to the mailboxes the messages were deleted from. If target mailbox does not exist, then the messages are moved to INBOX.',
validationObjs: {
pathParams: {
user: Joi.string().hex().lowercase().length(24).required().description('ID of the User')
user: userId
},
requestBody: {
start: Joi.date().label('Start time').required().description('Datestring'),
Expand All @@ -2886,7 +3045,8 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
200: {
description: 'Success',
model: Joi.object({
success: booleanSchema.required().description('Indicates successful response')
success: booleanSchema.required().description('Indicates successful response'),
task: Joi.string().required().description('Task ID')
})
}
}
Expand Down Expand Up @@ -2990,8 +3150,8 @@ module.exports = (db, server, messageHandler, userHandler, storageHandler, setti
},
queryParams: {},
pathParams: {
user: Joi.string().hex().lowercase().length(24).required().description('ID of the User'),
message: Joi.string().hex().lowercase().length(24).required().description('Message ID')
user: userId,
message: messageId
},
response: {
200: {
Expand Down

0 comments on commit 71c6496

Please sign in to comment.