Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ZMS-114 #602

Merged
merged 6 commits into from
Jan 12, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
182 changes: 151 additions & 31 deletions lib/api/mailboxes.js
Expand Up @@ -9,6 +9,7 @@ const util = require('util');
const { sessSchema, sessIPSchema, booleanSchema } = require('../schemas');
const { userId, mailboxId } = require('../schemas/request/general-schemas');
const { successRes } = require('../schemas/response/general-schemas');
const { GetMailboxesResult } = require('../schemas/response/mailboxes-schemas');

module.exports = (db, server, mailboxHandler) => {
const getMailboxCounter = util.promisify(tools.getMailboxCounter);
Expand All @@ -17,18 +18,50 @@ module.exports = (db, server, mailboxHandler) => {
const createMailbox = mailboxHandler.createAsync.bind(mailboxHandler);

server.get(
'/users/:user/mailboxes',
{
path: '/users/:user/mailboxes',
tags: ['Mailboxes'],
summary: 'List Mailboxes for a User',
validationObjs: {
requestBody: {},
pathParams: {
user: userId
},
queryParams: {
specialUse: booleanSchema.default(false).description('Should the response include only folders with specialUse flag set.'),
showHidden: booleanSchema.default(false).description('Hidden folders are not included in the listing by default.'),
counters: booleanSchema
.default(false)
.description('Should the response include counters (total + unseen). Counters come with some overhead.'),
sizes: booleanSchema
.default(false)
.description(
'Should the response include mailbox size in bytes. Size numbers come with a lot of overhead as an aggregated query is ran.'
),
sess: sessSchema,
ip: sessIPSchema
},

response: {
200: {
description: 'Success',
model: Joi.object({
success: successRes,
results: Joi.array().items(GetMailboxesResult).description('List of user mailboxes').required()
})
}
}
}
},
tools.responseWrapper(async (req, res) => {
res.charSet('utf-8');

const schema = Joi.object().keys({
user: Joi.string().hex().lowercase().length(24).required(),
specialUse: booleanSchema.default(false),
showHidden: booleanSchema.default(false),
counters: booleanSchema.default(false),
sizes: booleanSchema.default(false),
sess: sessSchema,
ip: sessIPSchema
const { pathParams, requestBody, queryParams } = req.route.spec.validationObjs;

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

const result = schema.validate(req.params, {
Expand Down Expand Up @@ -329,15 +362,56 @@ module.exports = (db, server, mailboxHandler) => {
);

server.get(
'/users/:user/mailboxes/:mailbox',
{
path: '/users/:user/mailboxes/:mailbox',
summary: 'Request Mailbox information',
tags: ['Mailboxes'],
validationObjs: {
requestBody: {},
queryParams: {
path: Joi.string()
.regex(/\/{2,}|\/$/, { invert: true })
.description('If mailbox is specified as `resolve` in the path then use this param as mailbox path instead of the given mailbox id.'),
sess: sessSchema,
ip: sessIPSchema
},
pathParams: {
user: userId,
mailbox: mailboxId.allow('resolve')
},
response: {
200: {
description: 'Success',
model: Joi.object({
success: successRes,
id: mailboxId,
name: Joi.string().required().description('Name for the mailbox (unicode string)'),
path: Joi.string()
.required()
.description('Full path of the mailbox, folders are separated by slashes, ends with the mailbox name (unicode string)'),
specialUse: Joi.string()
.required()
.example('\\Draft')
.description('Either special use identifier or null. One of Drafts, Junk, Sent or Trash'),
modifyIndex: Joi.number().required().description('Modification sequence number. Incremented on every change in the mailbox.'),
subscribed: booleanSchema.required().description('Mailbox subscription status. IMAP clients may unsubscribe from a folder.'),
hidden: booleanSchema.required().description('Is the folder hidden or not'),
total: Joi.number().required().description('How many messages are stored in this mailbox'),
unseen: Joi.number().required().description('How many unseen messages are stored in this mailbox')
})
}
}
}
},
tools.responseWrapper(async (req, res) => {
res.charSet('utf-8');

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

const schema = Joi.object({
user: Joi.string().hex().lowercase().length(24).required(),
mailbox: Joi.string().hex().lowercase().length(24).allow('resolve').required(),
path: Joi.string().regex(/\/{2,}|\/$/, { invert: true }),
sess: sessSchema,
ip: sessIPSchema
...pathParams,
...requestBody,
...queryParams
});

const result = schema.validate(req.params, {
Expand Down Expand Up @@ -457,19 +531,47 @@ module.exports = (db, server, mailboxHandler) => {
);

server.put(
'/users/:user/mailboxes/:mailbox',
{
path: '/users/:user/mailboxes/:mailbox',
summary: 'Update Mailbox information',
tags: ['Mailboxes'],
validationObjs: {
requestBody: {
path: Joi.string()
.regex(/\/{2,}|\/$/, { invert: true })
.description('Full path of the mailbox, use this to rename an existing Mailbox'),
retention: Joi.number()
.empty('')
.min(0)
.description(
'Retention policy for the Mailbox (in ms). Changing retention value only affects messages added to this folder after the change'
),
subscribed: booleanSchema.description('Change Mailbox subscription state'),
hidden: booleanSchema.description('Is the folder hidden or not. Hidden folders can not be opened in IMAP.'),
sess: sessSchema,
ip: sessIPSchema
},
pathParams: { user: userId, mailbox: mailboxId },
queryParams: {},
response: {
200: {
description: 'Success',
model: Joi.object({
success: successRes
})
}
}
}
},
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(),
path: Joi.string().regex(/\/{2,}|\/$/, { invert: true }),
retention: Joi.number().empty('').min(0),
subscribed: booleanSchema,
hidden: booleanSchema,
sess: sessSchema,
ip: sessIPSchema
const { pathParams, requestBody, queryParams } = req.route.spec.validationObjs;

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

const result = schema.validate(req.params, {
Expand Down Expand Up @@ -521,15 +623,33 @@ module.exports = (db, server, mailboxHandler) => {
);

server.del(
'/users/:user/mailboxes/:mailbox',
{
path: '/users/:user/mailboxes/:mailbox',
summary: 'Delete a Mailbox',
tags: ['Mailboxes'],
validationObjs: {
requestBody: { user: userId, mailbox: mailboxId },
queryParams: { sess: sessSchema, ip: sessIPSchema },
pathParams: {},
response: {
200: {
description: 'Success',
model: Joi.object({
success: successRes
})
}
}
}
},
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(),
sess: sessSchema,
ip: sessIPSchema
const { pathParams, requestBody, queryParams } = req.route.spec.validationObjs;

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

const result = schema.validate(req.params, {
Expand Down
27 changes: 27 additions & 0 deletions lib/schemas/response/mailboxes-schemas.js
@@ -0,0 +1,27 @@
'use strict';

const Joi = require('joi');
const { mailboxId } = require('../request/general-schemas');
const { booleanSchema } = require('../../schemas');

const GetMailboxesResult = Joi.object({
id: mailboxId,
name: Joi.string().required().description('Name for the mailbox (unicode string)'),
path: Joi.string().required().description('Full path of the mailbox, folders are separated by slashes, ends with the mailbox name (unicode string)'),
specialUse: Joi.string().required().description('Either special use identifier or null. One of Drafts, Junk, Sent or Trash'),
modifyIndex: Joi.number().required().description('Modification sequence number. Incremented on every change in the mailbox.'),
subscribed: booleanSchema.required().description('Mailbox subscription status. IMAP clients may unsubscribe from a folder.'),
retention: Joi.number()
.required()
NickOvt marked this conversation as resolved.
Show resolved Hide resolved
.description(
'Default retention policy for this mailbox (in ms). If set then messages added to this maibox will be automatically deleted after retention time.'
),
hidden: booleanSchema.required().description('Is the folder hidden or not'),
total: Joi.number().required().description('How many messages are stored in this mailbox'),
unseen: Joi.number().required().description('How many unseen messages are stored in this mailbox'),
size: Joi.number().description('Total size of mailbox in bytes.')
});

module.exports = {
GetMailboxesResult
};