UNPKG

azurite

Version:

An open source Azure Storage API compatible server

251 lines 12.4 kB
"use strict"; 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