Skip to content

Commit

Permalink
Added GET /outbox/queueId
Browse files Browse the repository at this point in the history
  • Loading branch information
andris9 committed Jul 3, 2024
1 parent 56e68da commit a315ba3
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 51 deletions.
48 changes: 47 additions & 1 deletion lib/outbox.js
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,50 @@ async function del(options) {
return false;
}

module.exports = { list, del };
async function get(options) {
options = options || {};

let logger = options.logger;

if (!options.queueId) {
return false;
}

let job = await submitQueue.getJob(options.queueId);
if (!job) {
return false;
}

let scheduled = job.timestamp + (Number(job.opts.delay) || 0);

let backoffDelay = Number(job.opts.backoff && job.opts.backoff.delay) || 0;
let nextAttempt = job.attemptsMade ? Math.round(job.processedOn + Math.pow(2, job.attemptsMade) * backoffDelay) : scheduled;

if (job.opts.attempts <= job.attemptsMade) {
nextAttempt = false;
}

try {
let queueEntryBuf = await redis.hgetBuffer(`${REDIS_PREFIX}iaq:${job.data.account}`, job.data.queueId);
if (!queueEntryBuf) {
return false;
}
} catch (err) {
logger.error({ msg: 'Failed to retrieve queued message', account: job.data.account, queueId: job.data.queueId, messageId: job.data.messageId, err });
throw err;
}

let response = Object.assign(job.data, {
created: new Date(Number(job.created || job.timestamp)).toISOString(),
//status: job.name,
progress: job.progress,
attemptsMade: job.attemptsMade,
attempts: job.opts.attempts,
scheduled: new Date(scheduled).toISOString(),
nextAttempt: nextAttempt ? new Date(nextAttempt).toISOString() : false
});

return response;
}

module.exports = { list, del, get };
40 changes: 39 additions & 1 deletion lib/schemas.js
Original file line number Diff line number Diff line change
Expand Up @@ -1328,6 +1328,43 @@ const defaultAccountTypeSchema = Joi.string()
.description('Display the form for the specified account type (either "imap" or an OAuth2 app ID) instead of allowing the user to choose')
.label('DefaultAccountType');

const outboxEntrySchema = Joi.object({
queueId: Joi.string().example('1869c5692565f756b33').description('Outbox queue ID'),
account: accountIdSchema.required(),
source: Joi.string().example('smtp').valid('smtp', 'api').description('How this message was added to the queue'),

messageId: Joi.string().max(996).example('<[email protected]>').description('Message ID'),
envelope: Joi.object({
from: Joi.string().email().allow('').example('[email protected]'),
to: Joi.array().items(Joi.string().email().required().example('[email protected]'))
}).description('SMTP envelope'),

subject: Joi.string()
.allow('')
.max(10 * 1024)
.example('What a wonderful message')
.description('Message subject'),

created: Joi.date().iso().example('2021-02-17T13:43:18.860Z').description('The time this message was queued'),
scheduled: Joi.date().iso().example('2021-02-17T13:43:18.860Z').description('When this message is supposed to be delivered'),
nextAttempt: Joi.date().iso().example('2021-02-17T13:43:18.860Z').description('Next delivery attempt'),

attemptsMade: Joi.number().integer().example(3).description('How many times EmailEngine has tried to deliver this email'),
attempts: Joi.number().integer().example(3).description('How many delivery attempts to make until message is considered as failed'),

progress: Joi.object({
status: Joi.string().valid('queued', 'processing', 'submitted', 'error').example('queued').description('Current state of the sending'),
response: Joi.string().example('250 Message Accepted').description('Response from the SMTP server. Only if state=processing'),
error: Joi.object({
message: Joi.string().example('Authentication failed').description('Error message'),
code: Joi.string().example('EAUTH').description('Error code'),
statusCode: Joi.string().example(502).description('SMTP response code')
})
.label('OutboxListProgressError')
.description('Error information if state=error')
}).label('OutboxEntryProgress')
}).label('OutboxEntry');

module.exports = {
ADDRESS_STRATEGIES,

Expand Down Expand Up @@ -1361,7 +1398,8 @@ module.exports = {
accountPathSchema,
messageSpecialUseSchema,
defaultAccountTypeSchema,
fromAddressSchema
fromAddressSchema,
outboxEntrySchema
};

/*
Expand Down
111 changes: 62 additions & 49 deletions workers/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ const {
accountCountersSchema,
accountPathSchema,
defaultAccountTypeSchema,
fromAddressSchema
fromAddressSchema,
outboxEntrySchema
} = require('../lib/schemas');

const OAuth2ProviderSchema = Joi.string()
Expand Down Expand Up @@ -5837,55 +5838,67 @@ When making API calls remember that requests against the same account are queued
page: Joi.number().integer().example(0).description('Current page (0-based index)').label('PageNumber'),
pages: Joi.number().integer().example(24).description('Total page count').label('PagesNumber'),

messages: Joi.array()
.items(
Joi.object({
queueId: Joi.string().example('1869c5692565f756b33').description('Outbox queue ID'),
account: accountIdSchema.required(),
source: Joi.string().example('smtp').valid('smtp', 'api').description('How this message was added to the queue'),
messages: Joi.array().items(outboxEntrySchema).label('OutboxListEntries')
}).label('OutboxListResponse'),
failAction: 'log'
}
}
});

messageId: Joi.string().max(996).example('<[email protected]>').description('Message ID'),
envelope: Joi.object({
from: Joi.string().email().allow('').example('[email protected]'),
to: Joi.array().items(Joi.string().email().required().example('[email protected]'))
}).description('SMTP envelope'),
server.route({
method: 'GET',
path: '/v1/outbox/{queueId}',

subject: Joi.string()
.allow('')
.max(10 * 1024)
.example('What a wonderful message')
.description('Message subject'),

created: Joi.date().iso().example('2021-02-17T13:43:18.860Z').description('The time this message was queued'),
scheduled: Joi.date().iso().example('2021-02-17T13:43:18.860Z').description('When this message is supposed to be delivered'),
nextAttempt: Joi.date().iso().example('2021-02-17T13:43:18.860Z').description('Next delivery attempt'),

attemptsMade: Joi.number().integer().example(3).description('How many times EmailEngine has tried to deliver this email'),
attempts: Joi.number()
.integer()
.example(3)
.description('How many delivery attempts to make until message is considered as failed'),

progress: Joi.object({
status: Joi.string()
.valid('queued', 'processing', 'submitted', 'error')
.example('queued')
.description('Current state of the sending'),
response: Joi.string()
.example('250 Message Accepted')
.description('Response from the SMTP server. Only if state=processing'),
error: Joi.object({
message: Joi.string().example('Authentication failed').description('Error message'),
code: Joi.string().example('EAUTH').description('Error code'),
statusCode: Joi.string().example(502).description('SMTP response code')
})
.label('OutboxListProgressError')
.description('Error information if state=error')
}).label('OutboxListProgress')
}).label('OutboxListItem')
)
.label('OutboxListEntries')
}).label('OutboxListResponse'),
async handler(request) {
try {
let outboxEntry = await outbox.get({ queueId: request.params.queueId, logger });
if (!outboxEntry) {
let message = 'Requested queue entry was not found';
let error = Boom.boomify(new Error(message), { statusCode: 404 });
throw error;
}
return outboxEntry;
} catch (err) {
request.logger.error({ msg: 'API request failed', err });
if (Boom.isBoom(err)) {
throw err;
}
let error = Boom.boomify(err, { statusCode: err.statusCode || 500 });
if (err.code) {
error.output.payload.code = err.code;
}
throw error;
}
},

options: {
description: 'Get queued message',
notes: 'Gets a queued message in the Outbox',
tags: ['api', 'Outbox'],

plugins: {},

auth: {
strategy: 'api-token',
mode: 'required'
},
cors: CORS_CONFIG,

validate: {
options: {
stripUnknown: false,
abortEarly: false,
convert: true
},
failAction,

params: Joi.object({
queueId: Joi.string().max(100).example('d41f0423195f271f').description('Queue identifier for scheduled email').required()
}).label('OutboxEntryParams')
},

response: {
schema: outboxEntrySchema,
failAction: 'log'
}
}
Expand Down Expand Up @@ -5935,7 +5948,7 @@ When making API calls remember that requests against the same account are queued

params: Joi.object({
queueId: Joi.string().max(100).example('d41f0423195f271f').description('Queue identifier for scheduled email').required()
}).label('DeleteOutboxEntry')
}).label('OutboxEntryParams')
},

response: {
Expand Down

0 comments on commit a315ba3

Please sign in to comment.