azurite
Version:
An open source Azure Storage API compatible server
251 lines • 12.4 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
const v4_1 = tslib_1.__importDefault(require("uuid/v4"));
const QueueStorageContext_1 = tslib_1.__importDefault(require("../context/QueueStorageContext"));
const StorageErrorFactory_1 = tslib_1.__importDefault(require("../errors/StorageErrorFactory"));
const constants_1 = require("../utils/constants");
const utils_1 = require("../utils/utils");
const BaseHandler_1 = tslib_1.__importDefault(require("./BaseHandler"));
/**
* MessagesHandler handles Azure Storage messages related requests.
*
* @export
* @class MessagesHandler
* @implements {IMessagesHandler}
*/
class MessagesHandler extends BaseHandler_1.default {
/**
* Dequeue the messages.
*
* @param {Models.MessagesDequeueOptionalParams} options
* @param {Context} context
* @returns {Promise<Models.MessagesDequeueResponse>}
* @memberof MessagesHandler
*/
async dequeue(options, context) {
const queueCtx = new QueueStorageContext_1.default(context);
const accountName = queueCtx.account;
const queueName = queueCtx.queue;
const popReceipt = (0, utils_1.getPopReceipt)(context.startTime);
// Validate the query parameters.
const timeNextVisible = new Date(context.startTime.getTime() + constants_1.DEFAULT_DEQUEUE_VISIBILITYTIMEOUT * 1000 // 30s as default, convert to ms
);
if (options.visibilitytimeout !== undefined) {
if (options.visibilitytimeout < constants_1.DEQUEUE_VISIBILITYTIMEOUT_MIN ||
options.visibilitytimeout > constants_1.DEQUEUE_VISIBILITYTIMEOUT_MAX) {
throw StorageErrorFactory_1.default.getOutOfRangeQueryParameterValue(context.contextID, {
QueryParameterName: "visibilitytimeout",
QueryParameterValue: `${options.visibilitytimeout}`,
MinimumAllowed: `${constants_1.DEQUEUE_VISIBILITYTIMEOUT_MIN}`,
MaximumAllowed: `${constants_1.DEQUEUE_VISIBILITYTIMEOUT_MAX}`
});
}
timeNextVisible.setTime(context.startTime.getTime() + options.visibilitytimeout * 1000);
}
let numberOfMessages = 1;
if (options.numberOfMessages !== undefined) {
if (options.numberOfMessages < constants_1.DEQUEUE_NUMOFMESSAGES_MIN ||
options.numberOfMessages > constants_1.DEQUEUE_NUMOFMESSAGES_MAX) {
throw StorageErrorFactory_1.default.getOutOfRangeQueryParameterValue(context.contextID, {
QueryParameterName: "numofmessages",
QueryParameterValue: `${options.numberOfMessages}`,
MinimumAllowed: `${constants_1.DEQUEUE_NUMOFMESSAGES_MIN}`,
MaximumAllowed: `${constants_1.DEQUEUE_NUMOFMESSAGES_MAX}`
});
}
numberOfMessages = options.numberOfMessages;
}
// Get metadata.
const messages = await this.metadataStore.getMessages(accountName, queueName, timeNextVisible, popReceipt, numberOfMessages, context.startTime, context);
const response = [];
const responseArray = response;
const responseObject = response;
for (const message of messages) {
const textStream = await this.extentStore.readExtent(message.persistency, context.contextID);
const text = await (0, utils_1.readStreamToString)(textStream);
const dequeuedMessage = {
...message,
messageText: text
};
responseArray.push(dequeuedMessage);
}
responseObject.date = context.startTime;
responseObject.requestId = context.contextID;
responseObject.version = constants_1.QUEUE_API_VERSION;
responseObject.statusCode = 200;
responseObject.clientRequestId = options.requestId;
return response;
}
/**
* Clear the messages in a queue
*
* @param {Models.MessagesClearOptionalParams} options
* @param {Context} context
* @returns {Promise<Models.MessagesClearResponse>}
* @memberof MessagesHandler
*/
async clear(options, context) {
const queueCtx = new QueueStorageContext_1.default(context);
const accountName = queueCtx.account;
const queueName = queueCtx.queue;
await this.metadataStore.clearMessages(accountName, queueName, context);
const response = {
date: context.startTime,
requestId: queueCtx.contextID,
statusCode: 204,
version: constants_1.QUEUE_API_VERSION,
clientRequestId: options.requestId
};
return response;
}
/**
* Enqueue a message
*
* @param {Models.QueueMessage} queueMessage
* @param {Models.MessagesEnqueueOptionalParams} options
* @param {Context} context
* @returns {Promise<Models.MessagesEnqueueResponse>}
* @memberof MessagesHandler
*/
async enqueue(queueMessage, options, context) {
const queueCtx = new QueueStorageContext_1.default(context);
const accountName = queueCtx.account;
const queueName = queueCtx.queue;
if (queueMessage.messageText === undefined) {
const body = queueCtx.request.getBody();
// TODO: deserialize does not support the message text with only empty character.
// If the text is undefined, try to retrieve it from the XML body here.
const parsedBody = await (0, utils_1.parseXMLwithEmpty)(body || "");
for (const text in parsedBody) {
if (Object.hasOwnProperty.bind(parsedBody)(text) &&
text.toLowerCase() === "messagetext") {
queueMessage.messageText = parsedBody[text];
break;
}
}
}
if (queueMessage.messageText === undefined) {
throw StorageErrorFactory_1.default.getInvalidXmlDocument(queueCtx.contextID);
}
if ((0, utils_1.getUTF8ByteSize)(queueMessage.messageText) > constants_1.MESSAGETEXT_LENGTH_MAX) {
throw StorageErrorFactory_1.default.getRequestBodyTooLarge(queueCtx.contextID, {
MaxLimit: `${constants_1.MESSAGETEXT_LENGTH_MAX}`
});
}
const message = {
accountName,
queueName,
messageId: (0, v4_1.default)(),
insertionTime: new Date(context.startTime),
expirationTime: new Date(context.startTime.getTime() + constants_1.DEFAULT_MESSAGETTL * 1000), // Default ttl is 7 days.
dequeueCount: 0,
timeNextVisible: new Date(context.startTime),
popReceipt: (0, utils_1.getPopReceipt)(context.startTime),
persistency: constants_1.EMPTY_EXTENT_CHUNK // Provide an empty item to initialize the whole object.
};
if (options.visibilitytimeout !== undefined) {
if (options.visibilitytimeout < constants_1.ENQUEUE_VISIBILITYTIMEOUT_MIN ||
options.visibilitytimeout > constants_1.ENQUEUE_VISIBILITYTIMEOUT_MAX) {
throw StorageErrorFactory_1.default.getOutOfRangeQueryParameterValue(context.contextID, {
QueryParameterName: "visibilitytimeout",
QueryParameterValue: `${options.visibilitytimeout}`,
MinimumAllowed: `${constants_1.ENQUEUE_VISIBILITYTIMEOUT_MIN}`,
MaximumAllowed: `${constants_1.ENQUEUE_VISIBILITYTIMEOUT_MAX}`
});
}
message.timeNextVisible.setTime(context.startTime.getTime() + options.visibilitytimeout * 1000);
}
if (options.messageTimeToLive !== undefined) {
if (options.messageTimeToLive === -1) {
message.expirationTime = new Date(constants_1.NEVER_EXPIRE_DATE);
}
else if (options.messageTimeToLive < constants_1.MESSAGETTL_MIN) {
throw StorageErrorFactory_1.default.getInvalidQueryParameterValue(context.contextID, {
QueryParameterName: "messagettl",
QueryParameterValue: `${options.messageTimeToLive}`,
Reason: `Value must be greater than or equal to 1, or -1 to indicate an infinite TTL.`
});
}
else if (options.visibilitytimeout !== undefined &&
options.visibilitytimeout >= options.messageTimeToLive) {
throw StorageErrorFactory_1.default.getInvalidQueryParameterValue(context.contextID, {
QueryParameterName: "visibilitytimeout",
QueryParameterValue: `${options.visibilitytimeout}`,
Reason: `messagettl must be greater than visibilitytimeout.`
});
}
else {
if (new Date(constants_1.NEVER_EXPIRE_DATE).getTime() - context.startTime.getTime() <
options.messageTimeToLive * 1000) {
message.expirationTime = new Date(constants_1.NEVER_EXPIRE_DATE);
}
else {
message.expirationTime.setTime(context.startTime.getTime() + options.messageTimeToLive * 1000);
}
}
}
// Write data to file system after the validation pass.
const extentChunk = await this.extentStore.appendExtent(Buffer.from(queueMessage.messageText), context.contextID);
message.persistency = extentChunk;
await this.metadataStore.insertMessage(message);
const response = [];
const responseArray = response;
const responseObject = response;
const enqueuedMessage = message;
responseArray.push(enqueuedMessage);
responseObject.date = context.startTime;
responseObject.requestId = context.contextID;
responseObject.version = constants_1.QUEUE_API_VERSION;
responseObject.statusCode = 201;
responseObject.clientRequestId = options.requestId;
return response;
}
/**
* get the peek messages without altering the visibility
*
* @param {Models.MessagesPeekOptionalParams} options
* @param {Context} context
* @returns {Promise<Models.MessagesPeekResponse>}
* @memberof MessagesHandler
*/
async peek(options, context) {
const queueCtx = new QueueStorageContext_1.default(context);
const accountName = queueCtx.account;
const queueName = queueCtx.queue;
let numberOfMessages = 1;
if (options.numberOfMessages !== undefined) {
if (options.numberOfMessages < constants_1.DEQUEUE_NUMOFMESSAGES_MIN ||
options.numberOfMessages > constants_1.DEQUEUE_NUMOFMESSAGES_MAX) {
throw StorageErrorFactory_1.default.getOutOfRangeQueryParameterValue(context.contextID, {
QueryParameterName: "numofmessages",
QueryParameterValue: `${options.numberOfMessages}`,
MinimumAllowed: `${constants_1.DEQUEUE_NUMOFMESSAGES_MIN}`,
MaximumAllowed: `${constants_1.DEQUEUE_NUMOFMESSAGES_MAX}`
});
}
numberOfMessages = options.numberOfMessages;
}
const messages = await this.metadataStore.peekMessages(accountName, queueName, numberOfMessages, context.startTime, context);
const response = [];
const responseArray = response;
const responseObject = response;
for (const message of messages) {
const textStream = await this.extentStore.readExtent(message.persistency, context.contextID);
const text = await (0, utils_1.readStreamToString)(textStream);
const dequeuedMessage = {
...message,
messageText: text
};
responseArray.push(dequeuedMessage);
}
responseObject.date = context.startTime;
responseObject.requestId = context.contextID;
responseObject.version = constants_1.QUEUE_API_VERSION;
responseObject.statusCode = 200;
responseObject.clientRequestId = options.requestId;
return response;
}
}
exports.default = MessagesHandler;
//# sourceMappingURL=MessagesHandler.js.map
;