'use strict'; var _regenerator = require('babel-runtime/regenerator'); var _regenerator2 = _interopRequireDefault(_regenerator); var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var assert = require('assert'); var debug = require('debug')('mns:client'); var httpx = require('httpx'); var kitx = require('kitx'); var _require = require('./helper'), getEndpoint = _require.getEndpoint, toXMLBuffer = _require.toXMLBuffer, parseXML = _require.parseXML, extract = _require.extract, getResponseHeaders = _require.getResponseHeaders, getCanonicalizedMNSHeaders = _require.getCanonicalizedMNSHeaders; var Client = function () { function Client(accountid, opts) { _classCallCheck(this, Client); assert(accountid, '"accountid" must be passed in'); this.accountid = accountid; assert(opts, 'must pass in "opts"'); // 兼容 var accessKeyID = opts.accessKeyId || opts.accessKeyID; assert(accessKeyID, 'must pass in "opts.accessKeyID"'); this.accessKeyID = accessKeyID; assert(opts.accessKeySecret, 'must pass in "opts.accessKeySecret"'); this.accessKeySecret = opts.accessKeySecret; assert(opts.region, 'must pass in "opts.region"'); var _getEndpoint = getEndpoint(accountid, opts), domain = _getEndpoint.domain, endpoint = _getEndpoint.endpoint; this.endpointDomain = domain; this.endpoint = endpoint; // security token this.securityToken = opts.securityToken; } _createClass(Client, [{ key: 'request', value: function () { var _ref = _asyncToGenerator( /*#__PURE__*/_regenerator2.default.mark(function _callee(method, resource, type, requestBody) { var attentions = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : []; var opts = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : {}; var url, headers, response, code, contentType, responseBody, body, responseData, e, message, requestid, hostid, err; return _regenerator2.default.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: url = `${this.endpoint}${resource}`; debug('url: %s', url); debug('method: %s', method); headers = this.buildHeaders(method, requestBody, resource, opts.headers); debug('request headers: %j', headers); debug('request body: %s', requestBody.toString()); _context.next = 8; return httpx.request(url, Object.assign(opts, { method: method, headers: headers, data: requestBody })); case 8: response = _context.sent; debug('statusCode %s', response.statusCode); debug('response headers: %j', response.headers); code = response.statusCode; contentType = response.headers['content-type'] || ''; // const contentLength = response.headers['content-length']; _context.next = 15; return httpx.read(response, 'utf8'); case 15: responseBody = _context.sent; debug('response body: %s', responseBody); if (!(responseBody && (contentType.startsWith('text/xml') || contentType.startsWith('application/xml')))) { _context.next = 31; break; } _context.next = 20; return parseXML(responseBody); case 20: responseData = _context.sent; if (!responseData.Error) { _context.next = 29; break; } e = responseData.Error; message = extract(e.Message); requestid = extract(e.RequestId); hostid = extract(e.HostId); err = new Error(`${method} ${url} failed with ${code}. ` + `requestid: ${requestid}, hostid: ${hostid}, message: ${message}`); err.name = 'MNS' + extract(e.Code) + err.name; throw err; case 29: body = {}; Object.keys(responseData[type]).forEach(function (key) { if (key !== '$') { body[key] = extract(responseData[type][key]); } }); case 31: return _context.abrupt('return', { code, headers: getResponseHeaders(response.headers, attentions), body: body }); case 32: case 'end': return _context.stop(); } } }, _callee, this); })); function request(_x3, _x4, _x5, _x6) { return _ref.apply(this, arguments); } return request; }() }, { key: 'get', value: function get(resource, type, opts) { return this.request('GET', resource, type, '', [], opts); } }, { key: 'put', value: function put(resource, type, body) { var attentions = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : []; var opts = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {}; return this.request('PUT', resource, type, body, attentions, opts); } }, { key: 'post', value: function post(resource, type, body) { return this.request('POST', resource, type, body); } }, { key: 'delete', value: function _delete(resource, type, body) { return this.request('DELETE', resource, type, body); } }, { key: 'sign', value: function sign(verb, headers, resource) { var canonicalizedMNSHeaders = getCanonicalizedMNSHeaders(headers); var md5 = headers['content-md5'] || ''; var date = headers['date']; var type = headers['content-type'] || ''; var toSignString = `${verb}\n${md5}\n${type}\n${date}\n${canonicalizedMNSHeaders}${resource}`; // Signature = base64(hmac-sha1(VERB + "\n" // + CONTENT-MD5 + "\n" // + CONTENT-TYPE + "\n" // + DATE + "\n" // + CanonicalizedMNSHeaders // + CanonicalizedResource)) var buff = Buffer.from(toSignString, 'utf8'); var degist = kitx.sha1(buff, this.accessKeySecret, 'binary'); return Buffer.from(degist, 'binary').toString('base64'); } }, { key: 'buildHeaders', value: function buildHeaders(method, body, resource, customHeaders) { var date = new Date().toGMTString(); var headers = { 'date': date, 'host': this.endpointDomain, 'x-mns-date': date, 'x-mns-version': '2015-06-06' }; if (method !== 'GET' && method !== 'HEAD') { var contentType = 'text/xml'; var digest = kitx.md5(body, 'hex'); var md5 = Buffer.from(digest, 'utf8').toString('base64'); Object.assign(headers, { 'content-length': body.length, 'content-type': contentType, 'content-md5': md5 }); } Object.assign(headers, customHeaders); var signature = this.sign(method, headers, resource); headers['authorization'] = `MNS ${this.accessKeyID}:${signature}`; if (this.securityToken) { headers['security-token'] = this.securityToken; } return headers; } // Queue }, { key: 'createQueue', value: function createQueue(name) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var body = toXMLBuffer('Queue', params); var url = `/queues/${name}`; return this.put(url, 'Queue', body, ['location']); } }, { key: 'deleteQueue', value: function deleteQueue(name) { return this.delete(`/queues/${name}`, 'Queue', ''); } }, { key: 'listQueue', value: function () { var _ref2 = _asyncToGenerator( /*#__PURE__*/_regenerator2.default.mark(function _callee2(start, limit, prefix) { var customHeaders, subType, response; return _regenerator2.default.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: customHeaders = {}; if (typeof start !== 'undefined') { customHeaders['x-mns-marker'] = start; } if (typeof limit !== 'undefined') { customHeaders['x-mns-ret-number'] = limit; } if (typeof limit !== 'undefined') { customHeaders['x-mns-prefix'] = prefix; } subType = 'Queue'; _context2.next = 7; return this.get('/queues', 'Queues', { headers: customHeaders }); case 7: response = _context2.sent; response.body = response.body[subType]; return _context2.abrupt('return', response); case 10: case 'end': return _context2.stop(); } } }, _callee2, this); })); function listQueue(_x10, _x11, _x12) { return _ref2.apply(this, arguments); } return listQueue; }() }, { key: 'getQueueAttributes', value: function getQueueAttributes(queueName) { return this.get(`/queues/${queueName}`, 'Queue'); } }, { key: 'setQueueAttributes', value: function setQueueAttributes(queueName) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var body = toXMLBuffer('Queue', params); var url = `/queues/${queueName}?metaoverride=true`; return this.put(url, 'Queue', body); } // Message }, { key: 'sendMessage', value: function sendMessage(queueName, params) { var url = `/queues/${queueName}/messages`; var body = toXMLBuffer('Message', params); return this.post(url, 'Message', body); } }, { key: 'batchSendMessage', value: function () { var _ref3 = _asyncToGenerator( /*#__PURE__*/_regenerator2.default.mark(function _callee3(queueName, params) { var url, subType, body, response; return _regenerator2.default.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: url = `/queues/${queueName}/messages`; subType = 'Message'; body = toXMLBuffer('Messages', params, subType); _context3.next = 5; return this.post(url, 'Messages', body); case 5: response = _context3.sent; response.body = response.body[subType]; return _context3.abrupt('return', response); case 8: case 'end': return _context3.stop(); } } }, _callee3, this); })); function batchSendMessage(_x14, _x15) { return _ref3.apply(this, arguments); } return batchSendMessage; }() }, { key: 'receiveMessage', value: function receiveMessage(queueName, waitSeconds) { var url = `/queues/${queueName}/messages`; if (waitSeconds) { url += `?waitseconds=${waitSeconds}`; } // 31000 31s +1s max waitSeconds is 30s return this.get(url, 'Message', { timeout: 31000 }); } }, { key: 'batchReceiveMessage', value: function () { var _ref4 = _asyncToGenerator( /*#__PURE__*/_regenerator2.default.mark(function _callee4(queueName, numOfMessages, waitSeconds) { var url, subType, response; return _regenerator2.default.wrap(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: url = `/queues/${queueName}/messages?numOfMessages=${numOfMessages}`; if (waitSeconds) { url += `&waitseconds=${waitSeconds}`; } subType = 'Message'; // 31000 31s +1s max waitSeconds is 30s _context4.next = 5; return this.get(url, 'Messages', { timeout: 31000 }); case 5: response = _context4.sent; response.body = response.body[subType]; return _context4.abrupt('return', response); case 8: case 'end': return _context4.stop(); } } }, _callee4, this); })); function batchReceiveMessage(_x16, _x17, _x18) { return _ref4.apply(this, arguments); } return batchReceiveMessage; }() }, { key: 'peekMessage', value: function peekMessage(queueName) { return this.get(`/queues/${queueName}/messages?peekonly=true`, 'Message'); } }, { key: 'batchPeekMessage', value: function () { var _ref5 = _asyncToGenerator( /*#__PURE__*/_regenerator2.default.mark(function _callee5(queueName, numOfMessages) { var url, subType, response; return _regenerator2.default.wrap(function _callee5$(_context5) { while (1) { switch (_context5.prev = _context5.next) { case 0: url = `/queues/${queueName}/messages?` + `peekonly=true&numOfMessages=${numOfMessages}`; subType = 'Message'; // 31000 31s +1s max waitSeconds is 30s _context5.next = 4; return this.get(url, 'Messages'); case 4: response = _context5.sent; response.body = response.body[subType]; return _context5.abrupt('return', response); case 7: case 'end': return _context5.stop(); } } }, _callee5, this); })); function batchPeekMessage(_x19, _x20) { return _ref5.apply(this, arguments); } return batchPeekMessage; }() }, { key: 'deleteMessage', value: function deleteMessage(queueName, receiptHandle) { var url = `/queues/${queueName}/messages?ReceiptHandle=${receiptHandle}`; return this.delete(url, 'Message', ''); } }, { key: 'batchDeleteMessage', value: function () { var _ref6 = _asyncToGenerator( /*#__PURE__*/_regenerator2.default.mark(function _callee6(queueName, receiptHandles) { var body, url, response, subType; return _regenerator2.default.wrap(function _callee6$(_context6) { while (1) { switch (_context6.prev = _context6.next) { case 0: body = toXMLBuffer('ReceiptHandles', receiptHandles, 'ReceiptHandle'); url = `/queues/${queueName}/messages`; _context6.next = 4; return this.delete(url, 'Errors', body); case 4: response = _context6.sent; // 3种情况,普通失败,部分失败,全部成功 if (response.body) { subType = 'Error'; // 部分失败 response.body = response.body[subType]; } return _context6.abrupt('return', response); case 7: case 'end': return _context6.stop(); } } }, _callee6, this); })); function batchDeleteMessage(_x21, _x22) { return _ref6.apply(this, arguments); } return batchDeleteMessage; }() }, { key: 'changeMessageVisibility', value: function changeMessageVisibility(queueName, receiptHandle, visibilityTimeout) { var url = `/queues/${queueName}/messages?` + `receiptHandle=${receiptHandle}&visibilityTimeout=${visibilityTimeout}`; return this.put(url, 'ChangeVisibility', ''); } // Topic }, { key: 'createTopic', value: function createTopic(name) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var body = toXMLBuffer('Topic', params); return this.put(`/topics/${name}`, 'Topic', body, ['location']); } }, { key: 'deleteTopic', value: function deleteTopic() {} }, { key: 'listTopic', value: function () { var _ref7 = _asyncToGenerator( /*#__PURE__*/_regenerator2.default.mark(function _callee7(start, limit, prefix) { var customHeaders, subType, response; return _regenerator2.default.wrap(function _callee7$(_context7) { while (1) { switch (_context7.prev = _context7.next) { case 0: customHeaders = {}; if (typeof start !== 'undefined') { customHeaders['x-mns-marker'] = start; } if (typeof limit !== 'undefined') { customHeaders['x-mns-ret-number'] = limit; } if (typeof limit !== 'undefined') { customHeaders['x-mns-prefix'] = prefix; } subType = 'Topic'; _context7.next = 7; return this.get('/topics', 'Topics', { headers: customHeaders }); case 7: response = _context7.sent; response.body = response.body[subType]; return _context7.abrupt('return', response); case 10: case 'end': return _context7.stop(); } } }, _callee7, this); })); function listTopic(_x24, _x25, _x26) { return _ref7.apply(this, arguments); } return listTopic; }() }, { key: 'getTopicAttributes', value: function getTopicAttributes(name) { return this.get(`/topics/${name}`, 'Topic'); } }, { key: 'setTopicAttributes', value: function setTopicAttributes(name) { var params = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var body = toXMLBuffer('Topic', params); var url = `/topics/${name}?metaoverride=true`; return this.put(url, 'Topic', body); } // Subscription }, { key: 'subscribe', value: function subscribe() {} }, { key: 'unsubscribe', value: function unsubscribe() {} }, { key: 'listSubscriptionByTopic', value: function listSubscriptionByTopic() {} }, { key: 'getSubscriptionAttributes', value: function getSubscriptionAttributes() {} }, { key: 'setSubscriptionAttributes', value: function setSubscriptionAttributes() {} // Message }, { key: 'publishMessage', value: function publishMessage(topic, params) { var url = `/topics/${topic}/messages`; var body = toXMLBuffer('Message', params); return this.post(url, 'Message', body); } // Notifications }, { key: 'httpEndpoint', value: function httpEndpoint() {} }]); return Client; }(); module.exports = Client;