service.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854
  1. var AWS = require('./core');
  2. var Api = require('./model/api');
  3. var regionConfig = require('./region_config');
  4. var inherit = AWS.util.inherit;
  5. var clientCount = 0;
  6. var region_utils = require('./region/utils');
  7. /**
  8. * The service class representing an AWS service.
  9. *
  10. * @class_abstract This class is an abstract class.
  11. *
  12. * @!attribute apiVersions
  13. * @return [Array<String>] the list of API versions supported by this service.
  14. * @readonly
  15. */
  16. AWS.Service = inherit({
  17. /**
  18. * Create a new service object with a configuration object
  19. *
  20. * @param config [map] a map of configuration options
  21. */
  22. constructor: function Service(config) {
  23. if (!this.loadServiceClass) {
  24. throw AWS.util.error(new Error(),
  25. 'Service must be constructed with `new\' operator');
  26. }
  27. if (config) {
  28. if (config.region) {
  29. var region = config.region;
  30. if (region_utils.isFipsRegion(region)) {
  31. config.region = region_utils.getRealRegion(region);
  32. config.useFipsEndpoint = true;
  33. }
  34. if (region_utils.isGlobalRegion(region)) {
  35. config.region = region_utils.getRealRegion(region);
  36. }
  37. }
  38. if (typeof config.useDualstack === 'boolean'
  39. && typeof config.useDualstackEndpoint !== 'boolean') {
  40. config.useDualstackEndpoint = config.useDualstack;
  41. }
  42. }
  43. var ServiceClass = this.loadServiceClass(config || {});
  44. if (ServiceClass) {
  45. var originalConfig = AWS.util.copy(config);
  46. var svc = new ServiceClass(config);
  47. Object.defineProperty(svc, '_originalConfig', {
  48. get: function() { return originalConfig; },
  49. enumerable: false,
  50. configurable: true
  51. });
  52. svc._clientId = ++clientCount;
  53. return svc;
  54. }
  55. this.initialize(config);
  56. },
  57. /**
  58. * @api private
  59. */
  60. initialize: function initialize(config) {
  61. var svcConfig = AWS.config[this.serviceIdentifier];
  62. this.config = new AWS.Config(AWS.config);
  63. if (svcConfig) this.config.update(svcConfig, true);
  64. if (config) this.config.update(config, true);
  65. this.validateService();
  66. if (!this.config.endpoint) regionConfig.configureEndpoint(this);
  67. this.config.endpoint = this.endpointFromTemplate(this.config.endpoint);
  68. this.setEndpoint(this.config.endpoint);
  69. //enable attaching listeners to service client
  70. AWS.SequentialExecutor.call(this);
  71. AWS.Service.addDefaultMonitoringListeners(this);
  72. if ((this.config.clientSideMonitoring || AWS.Service._clientSideMonitoring) && this.publisher) {
  73. var publisher = this.publisher;
  74. this.addNamedListener('PUBLISH_API_CALL', 'apiCall', function PUBLISH_API_CALL(event) {
  75. process.nextTick(function() {publisher.eventHandler(event);});
  76. });
  77. this.addNamedListener('PUBLISH_API_ATTEMPT', 'apiCallAttempt', function PUBLISH_API_ATTEMPT(event) {
  78. process.nextTick(function() {publisher.eventHandler(event);});
  79. });
  80. }
  81. },
  82. /**
  83. * @api private
  84. */
  85. validateService: function validateService() {
  86. },
  87. /**
  88. * @api private
  89. */
  90. loadServiceClass: function loadServiceClass(serviceConfig) {
  91. var config = serviceConfig;
  92. if (!AWS.util.isEmpty(this.api)) {
  93. return null;
  94. } else if (config.apiConfig) {
  95. return AWS.Service.defineServiceApi(this.constructor, config.apiConfig);
  96. } else if (!this.constructor.services) {
  97. return null;
  98. } else {
  99. config = new AWS.Config(AWS.config);
  100. config.update(serviceConfig, true);
  101. var version = config.apiVersions[this.constructor.serviceIdentifier];
  102. version = version || config.apiVersion;
  103. return this.getLatestServiceClass(version);
  104. }
  105. },
  106. /**
  107. * @api private
  108. */
  109. getLatestServiceClass: function getLatestServiceClass(version) {
  110. version = this.getLatestServiceVersion(version);
  111. if (this.constructor.services[version] === null) {
  112. AWS.Service.defineServiceApi(this.constructor, version);
  113. }
  114. return this.constructor.services[version];
  115. },
  116. /**
  117. * @api private
  118. */
  119. getLatestServiceVersion: function getLatestServiceVersion(version) {
  120. if (!this.constructor.services || this.constructor.services.length === 0) {
  121. throw new Error('No services defined on ' +
  122. this.constructor.serviceIdentifier);
  123. }
  124. if (!version) {
  125. version = 'latest';
  126. } else if (AWS.util.isType(version, Date)) {
  127. version = AWS.util.date.iso8601(version).split('T')[0];
  128. }
  129. if (Object.hasOwnProperty(this.constructor.services, version)) {
  130. return version;
  131. }
  132. var keys = Object.keys(this.constructor.services).sort();
  133. var selectedVersion = null;
  134. for (var i = keys.length - 1; i >= 0; i--) {
  135. // versions that end in "*" are not available on disk and can be
  136. // skipped, so do not choose these as selectedVersions
  137. if (keys[i][keys[i].length - 1] !== '*') {
  138. selectedVersion = keys[i];
  139. }
  140. if (keys[i].substr(0, 10) <= version) {
  141. return selectedVersion;
  142. }
  143. }
  144. throw new Error('Could not find ' + this.constructor.serviceIdentifier +
  145. ' API to satisfy version constraint `' + version + '\'');
  146. },
  147. /**
  148. * @api private
  149. */
  150. api: {},
  151. /**
  152. * @api private
  153. */
  154. defaultRetryCount: 3,
  155. /**
  156. * @api private
  157. */
  158. customizeRequests: function customizeRequests(callback) {
  159. if (!callback) {
  160. this.customRequestHandler = null;
  161. } else if (typeof callback === 'function') {
  162. this.customRequestHandler = callback;
  163. } else {
  164. throw new Error('Invalid callback type \'' + typeof callback + '\' provided in customizeRequests');
  165. }
  166. },
  167. /**
  168. * Calls an operation on a service with the given input parameters.
  169. *
  170. * @param operation [String] the name of the operation to call on the service.
  171. * @param params [map] a map of input options for the operation
  172. * @callback callback function(err, data)
  173. * If a callback is supplied, it is called when a response is returned
  174. * from the service.
  175. * @param err [Error] the error object returned from the request.
  176. * Set to `null` if the request is successful.
  177. * @param data [Object] the de-serialized data returned from
  178. * the request. Set to `null` if a request error occurs.
  179. */
  180. makeRequest: function makeRequest(operation, params, callback) {
  181. if (typeof params === 'function') {
  182. callback = params;
  183. params = null;
  184. }
  185. params = params || {};
  186. if (this.config.params) { // copy only toplevel bound params
  187. var rules = this.api.operations[operation];
  188. if (rules) {
  189. params = AWS.util.copy(params);
  190. AWS.util.each(this.config.params, function(key, value) {
  191. if (rules.input.members[key]) {
  192. if (params[key] === undefined || params[key] === null) {
  193. params[key] = value;
  194. }
  195. }
  196. });
  197. }
  198. }
  199. var request = new AWS.Request(this, operation, params);
  200. this.addAllRequestListeners(request);
  201. this.attachMonitoringEmitter(request);
  202. if (callback) request.send(callback);
  203. return request;
  204. },
  205. /**
  206. * Calls an operation on a service with the given input parameters, without
  207. * any authentication data. This method is useful for "public" API operations.
  208. *
  209. * @param operation [String] the name of the operation to call on the service.
  210. * @param params [map] a map of input options for the operation
  211. * @callback callback function(err, data)
  212. * If a callback is supplied, it is called when a response is returned
  213. * from the service.
  214. * @param err [Error] the error object returned from the request.
  215. * Set to `null` if the request is successful.
  216. * @param data [Object] the de-serialized data returned from
  217. * the request. Set to `null` if a request error occurs.
  218. */
  219. makeUnauthenticatedRequest: function makeUnauthenticatedRequest(operation, params, callback) {
  220. if (typeof params === 'function') {
  221. callback = params;
  222. params = {};
  223. }
  224. var request = this.makeRequest(operation, params).toUnauthenticated();
  225. return callback ? request.send(callback) : request;
  226. },
  227. /**
  228. * Waits for a given state
  229. *
  230. * @param state [String] the state on the service to wait for
  231. * @param params [map] a map of parameters to pass with each request
  232. * @option params $waiter [map] a map of configuration options for the waiter
  233. * @option params $waiter.delay [Number] The number of seconds to wait between
  234. * requests
  235. * @option params $waiter.maxAttempts [Number] The maximum number of requests
  236. * to send while waiting
  237. * @callback callback function(err, data)
  238. * If a callback is supplied, it is called when a response is returned
  239. * from the service.
  240. * @param err [Error] the error object returned from the request.
  241. * Set to `null` if the request is successful.
  242. * @param data [Object] the de-serialized data returned from
  243. * the request. Set to `null` if a request error occurs.
  244. */
  245. waitFor: function waitFor(state, params, callback) {
  246. var waiter = new AWS.ResourceWaiter(this, state);
  247. return waiter.wait(params, callback);
  248. },
  249. /**
  250. * @api private
  251. */
  252. addAllRequestListeners: function addAllRequestListeners(request) {
  253. var list = [AWS.events, AWS.EventListeners.Core, this.serviceInterface(),
  254. AWS.EventListeners.CorePost];
  255. for (var i = 0; i < list.length; i++) {
  256. if (list[i]) request.addListeners(list[i]);
  257. }
  258. // disable parameter validation
  259. if (!this.config.paramValidation) {
  260. request.removeListener('validate',
  261. AWS.EventListeners.Core.VALIDATE_PARAMETERS);
  262. }
  263. if (this.config.logger) { // add logging events
  264. request.addListeners(AWS.EventListeners.Logger);
  265. }
  266. this.setupRequestListeners(request);
  267. // call prototype's customRequestHandler
  268. if (typeof this.constructor.prototype.customRequestHandler === 'function') {
  269. this.constructor.prototype.customRequestHandler(request);
  270. }
  271. // call instance's customRequestHandler
  272. if (Object.prototype.hasOwnProperty.call(this, 'customRequestHandler') && typeof this.customRequestHandler === 'function') {
  273. this.customRequestHandler(request);
  274. }
  275. },
  276. /**
  277. * Event recording metrics for a whole API call.
  278. * @returns {object} a subset of api call metrics
  279. * @api private
  280. */
  281. apiCallEvent: function apiCallEvent(request) {
  282. var api = request.service.api.operations[request.operation];
  283. var monitoringEvent = {
  284. Type: 'ApiCall',
  285. Api: api ? api.name : request.operation,
  286. Version: 1,
  287. Service: request.service.api.serviceId || request.service.api.endpointPrefix,
  288. Region: request.httpRequest.region,
  289. MaxRetriesExceeded: 0,
  290. UserAgent: request.httpRequest.getUserAgent(),
  291. };
  292. var response = request.response;
  293. if (response.httpResponse.statusCode) {
  294. monitoringEvent.FinalHttpStatusCode = response.httpResponse.statusCode;
  295. }
  296. if (response.error) {
  297. var error = response.error;
  298. var statusCode = response.httpResponse.statusCode;
  299. if (statusCode > 299) {
  300. if (error.code) monitoringEvent.FinalAwsException = error.code;
  301. if (error.message) monitoringEvent.FinalAwsExceptionMessage = error.message;
  302. } else {
  303. if (error.code || error.name) monitoringEvent.FinalSdkException = error.code || error.name;
  304. if (error.message) monitoringEvent.FinalSdkExceptionMessage = error.message;
  305. }
  306. }
  307. return monitoringEvent;
  308. },
  309. /**
  310. * Event recording metrics for an API call attempt.
  311. * @returns {object} a subset of api call attempt metrics
  312. * @api private
  313. */
  314. apiAttemptEvent: function apiAttemptEvent(request) {
  315. var api = request.service.api.operations[request.operation];
  316. var monitoringEvent = {
  317. Type: 'ApiCallAttempt',
  318. Api: api ? api.name : request.operation,
  319. Version: 1,
  320. Service: request.service.api.serviceId || request.service.api.endpointPrefix,
  321. Fqdn: request.httpRequest.endpoint.hostname,
  322. UserAgent: request.httpRequest.getUserAgent(),
  323. };
  324. var response = request.response;
  325. if (response.httpResponse.statusCode) {
  326. monitoringEvent.HttpStatusCode = response.httpResponse.statusCode;
  327. }
  328. if (
  329. !request._unAuthenticated &&
  330. request.service.config.credentials &&
  331. request.service.config.credentials.accessKeyId
  332. ) {
  333. monitoringEvent.AccessKey = request.service.config.credentials.accessKeyId;
  334. }
  335. if (!response.httpResponse.headers) return monitoringEvent;
  336. if (request.httpRequest.headers['x-amz-security-token']) {
  337. monitoringEvent.SessionToken = request.httpRequest.headers['x-amz-security-token'];
  338. }
  339. if (response.httpResponse.headers['x-amzn-requestid']) {
  340. monitoringEvent.XAmznRequestId = response.httpResponse.headers['x-amzn-requestid'];
  341. }
  342. if (response.httpResponse.headers['x-amz-request-id']) {
  343. monitoringEvent.XAmzRequestId = response.httpResponse.headers['x-amz-request-id'];
  344. }
  345. if (response.httpResponse.headers['x-amz-id-2']) {
  346. monitoringEvent.XAmzId2 = response.httpResponse.headers['x-amz-id-2'];
  347. }
  348. return monitoringEvent;
  349. },
  350. /**
  351. * Add metrics of failed request.
  352. * @api private
  353. */
  354. attemptFailEvent: function attemptFailEvent(request) {
  355. var monitoringEvent = this.apiAttemptEvent(request);
  356. var response = request.response;
  357. var error = response.error;
  358. if (response.httpResponse.statusCode > 299 ) {
  359. if (error.code) monitoringEvent.AwsException = error.code;
  360. if (error.message) monitoringEvent.AwsExceptionMessage = error.message;
  361. } else {
  362. if (error.code || error.name) monitoringEvent.SdkException = error.code || error.name;
  363. if (error.message) monitoringEvent.SdkExceptionMessage = error.message;
  364. }
  365. return monitoringEvent;
  366. },
  367. /**
  368. * Attach listeners to request object to fetch metrics of each request
  369. * and emit data object through \'ApiCall\' and \'ApiCallAttempt\' events.
  370. * @api private
  371. */
  372. attachMonitoringEmitter: function attachMonitoringEmitter(request) {
  373. var attemptTimestamp; //timestamp marking the beginning of a request attempt
  374. var attemptStartRealTime; //Start time of request attempt. Used to calculating attemptLatency
  375. var attemptLatency; //latency from request sent out to http response reaching SDK
  376. var callStartRealTime; //Start time of API call. Used to calculating API call latency
  377. var attemptCount = 0; //request.retryCount is not reliable here
  378. var region; //region cache region for each attempt since it can be updated in plase (e.g. s3)
  379. var callTimestamp; //timestamp when the request is created
  380. var self = this;
  381. var addToHead = true;
  382. request.on('validate', function () {
  383. callStartRealTime = AWS.util.realClock.now();
  384. callTimestamp = Date.now();
  385. }, addToHead);
  386. request.on('sign', function () {
  387. attemptStartRealTime = AWS.util.realClock.now();
  388. attemptTimestamp = Date.now();
  389. region = request.httpRequest.region;
  390. attemptCount++;
  391. }, addToHead);
  392. request.on('validateResponse', function() {
  393. attemptLatency = Math.round(AWS.util.realClock.now() - attemptStartRealTime);
  394. });
  395. request.addNamedListener('API_CALL_ATTEMPT', 'success', function API_CALL_ATTEMPT() {
  396. var apiAttemptEvent = self.apiAttemptEvent(request);
  397. apiAttemptEvent.Timestamp = attemptTimestamp;
  398. apiAttemptEvent.AttemptLatency = attemptLatency >= 0 ? attemptLatency : 0;
  399. apiAttemptEvent.Region = region;
  400. self.emit('apiCallAttempt', [apiAttemptEvent]);
  401. });
  402. request.addNamedListener('API_CALL_ATTEMPT_RETRY', 'retry', function API_CALL_ATTEMPT_RETRY() {
  403. var apiAttemptEvent = self.attemptFailEvent(request);
  404. apiAttemptEvent.Timestamp = attemptTimestamp;
  405. //attemptLatency may not be available if fail before response
  406. attemptLatency = attemptLatency ||
  407. Math.round(AWS.util.realClock.now() - attemptStartRealTime);
  408. apiAttemptEvent.AttemptLatency = attemptLatency >= 0 ? attemptLatency : 0;
  409. apiAttemptEvent.Region = region;
  410. self.emit('apiCallAttempt', [apiAttemptEvent]);
  411. });
  412. request.addNamedListener('API_CALL', 'complete', function API_CALL() {
  413. var apiCallEvent = self.apiCallEvent(request);
  414. apiCallEvent.AttemptCount = attemptCount;
  415. if (apiCallEvent.AttemptCount <= 0) return;
  416. apiCallEvent.Timestamp = callTimestamp;
  417. var latency = Math.round(AWS.util.realClock.now() - callStartRealTime);
  418. apiCallEvent.Latency = latency >= 0 ? latency : 0;
  419. var response = request.response;
  420. if (
  421. response.error &&
  422. response.error.retryable &&
  423. typeof response.retryCount === 'number' &&
  424. typeof response.maxRetries === 'number' &&
  425. (response.retryCount >= response.maxRetries)
  426. ) {
  427. apiCallEvent.MaxRetriesExceeded = 1;
  428. }
  429. self.emit('apiCall', [apiCallEvent]);
  430. });
  431. },
  432. /**
  433. * Override this method to setup any custom request listeners for each
  434. * new request to the service.
  435. *
  436. * @method_abstract This is an abstract method.
  437. */
  438. setupRequestListeners: function setupRequestListeners(request) {
  439. },
  440. /**
  441. * Gets the signing name for a given request
  442. * @api private
  443. */
  444. getSigningName: function getSigningName() {
  445. return this.api.signingName || this.api.endpointPrefix;
  446. },
  447. /**
  448. * Gets the signer class for a given request
  449. * @api private
  450. */
  451. getSignerClass: function getSignerClass(request) {
  452. var version;
  453. // get operation authtype if present
  454. var operation = null;
  455. var authtype = '';
  456. if (request) {
  457. var operations = request.service.api.operations || {};
  458. operation = operations[request.operation] || null;
  459. authtype = operation ? operation.authtype : '';
  460. }
  461. if (this.config.signatureVersion) {
  462. version = this.config.signatureVersion;
  463. } else if (authtype === 'v4' || authtype === 'v4-unsigned-body') {
  464. version = 'v4';
  465. } else if (authtype === 'bearer') {
  466. version = 'bearer';
  467. } else {
  468. version = this.api.signatureVersion;
  469. }
  470. return AWS.Signers.RequestSigner.getVersion(version);
  471. },
  472. /**
  473. * @api private
  474. */
  475. serviceInterface: function serviceInterface() {
  476. switch (this.api.protocol) {
  477. case 'ec2': return AWS.EventListeners.Query;
  478. case 'query': return AWS.EventListeners.Query;
  479. case 'json': return AWS.EventListeners.Json;
  480. case 'rest-json': return AWS.EventListeners.RestJson;
  481. case 'rest-xml': return AWS.EventListeners.RestXml;
  482. }
  483. if (this.api.protocol) {
  484. throw new Error('Invalid service `protocol\' ' +
  485. this.api.protocol + ' in API config');
  486. }
  487. },
  488. /**
  489. * @api private
  490. */
  491. successfulResponse: function successfulResponse(resp) {
  492. return resp.httpResponse.statusCode < 300;
  493. },
  494. /**
  495. * How many times a failed request should be retried before giving up.
  496. * the defaultRetryCount can be overriden by service classes.
  497. *
  498. * @api private
  499. */
  500. numRetries: function numRetries() {
  501. if (this.config.maxRetries !== undefined) {
  502. return this.config.maxRetries;
  503. } else {
  504. return this.defaultRetryCount;
  505. }
  506. },
  507. /**
  508. * @api private
  509. */
  510. retryDelays: function retryDelays(retryCount, err) {
  511. return AWS.util.calculateRetryDelay(retryCount, this.config.retryDelayOptions, err);
  512. },
  513. /**
  514. * @api private
  515. */
  516. retryableError: function retryableError(error) {
  517. if (this.timeoutError(error)) return true;
  518. if (this.networkingError(error)) return true;
  519. if (this.expiredCredentialsError(error)) return true;
  520. if (this.throttledError(error)) return true;
  521. if (error.statusCode >= 500) return true;
  522. return false;
  523. },
  524. /**
  525. * @api private
  526. */
  527. networkingError: function networkingError(error) {
  528. return error.code === 'NetworkingError';
  529. },
  530. /**
  531. * @api private
  532. */
  533. timeoutError: function timeoutError(error) {
  534. return error.code === 'TimeoutError';
  535. },
  536. /**
  537. * @api private
  538. */
  539. expiredCredentialsError: function expiredCredentialsError(error) {
  540. // TODO : this only handles *one* of the expired credential codes
  541. return (error.code === 'ExpiredTokenException');
  542. },
  543. /**
  544. * @api private
  545. */
  546. clockSkewError: function clockSkewError(error) {
  547. switch (error.code) {
  548. case 'RequestTimeTooSkewed':
  549. case 'RequestExpired':
  550. case 'InvalidSignatureException':
  551. case 'SignatureDoesNotMatch':
  552. case 'AuthFailure':
  553. case 'RequestInTheFuture':
  554. return true;
  555. default: return false;
  556. }
  557. },
  558. /**
  559. * @api private
  560. */
  561. getSkewCorrectedDate: function getSkewCorrectedDate() {
  562. return new Date(Date.now() + this.config.systemClockOffset);
  563. },
  564. /**
  565. * @api private
  566. */
  567. applyClockOffset: function applyClockOffset(newServerTime) {
  568. if (newServerTime) {
  569. this.config.systemClockOffset = newServerTime - Date.now();
  570. }
  571. },
  572. /**
  573. * @api private
  574. */
  575. isClockSkewed: function isClockSkewed(newServerTime) {
  576. if (newServerTime) {
  577. return Math.abs(this.getSkewCorrectedDate().getTime() - newServerTime) >= 300000;
  578. }
  579. },
  580. /**
  581. * @api private
  582. */
  583. throttledError: function throttledError(error) {
  584. // this logic varies between services
  585. if (error.statusCode === 429) return true;
  586. switch (error.code) {
  587. case 'ProvisionedThroughputExceededException':
  588. case 'Throttling':
  589. case 'ThrottlingException':
  590. case 'RequestLimitExceeded':
  591. case 'RequestThrottled':
  592. case 'RequestThrottledException':
  593. case 'TooManyRequestsException':
  594. case 'TransactionInProgressException': //dynamodb
  595. case 'EC2ThrottledException':
  596. return true;
  597. default:
  598. return false;
  599. }
  600. },
  601. /**
  602. * @api private
  603. */
  604. endpointFromTemplate: function endpointFromTemplate(endpoint) {
  605. if (typeof endpoint !== 'string') return endpoint;
  606. var e = endpoint;
  607. e = e.replace(/\{service\}/g, this.api.endpointPrefix);
  608. e = e.replace(/\{region\}/g, this.config.region);
  609. e = e.replace(/\{scheme\}/g, this.config.sslEnabled ? 'https' : 'http');
  610. return e;
  611. },
  612. /**
  613. * @api private
  614. */
  615. setEndpoint: function setEndpoint(endpoint) {
  616. this.endpoint = new AWS.Endpoint(endpoint, this.config);
  617. },
  618. /**
  619. * @api private
  620. */
  621. paginationConfig: function paginationConfig(operation, throwException) {
  622. var paginator = this.api.operations[operation].paginator;
  623. if (!paginator) {
  624. if (throwException) {
  625. var e = new Error();
  626. throw AWS.util.error(e, 'No pagination configuration for ' + operation);
  627. }
  628. return null;
  629. }
  630. return paginator;
  631. }
  632. });
  633. AWS.util.update(AWS.Service, {
  634. /**
  635. * Adds one method for each operation described in the api configuration
  636. *
  637. * @api private
  638. */
  639. defineMethods: function defineMethods(svc) {
  640. AWS.util.each(svc.prototype.api.operations, function iterator(method) {
  641. if (svc.prototype[method]) return;
  642. var operation = svc.prototype.api.operations[method];
  643. if (operation.authtype === 'none') {
  644. svc.prototype[method] = function (params, callback) {
  645. return this.makeUnauthenticatedRequest(method, params, callback);
  646. };
  647. } else {
  648. svc.prototype[method] = function (params, callback) {
  649. return this.makeRequest(method, params, callback);
  650. };
  651. }
  652. });
  653. },
  654. /**
  655. * Defines a new Service class using a service identifier and list of versions
  656. * including an optional set of features (functions) to apply to the class
  657. * prototype.
  658. *
  659. * @param serviceIdentifier [String] the identifier for the service
  660. * @param versions [Array<String>] a list of versions that work with this
  661. * service
  662. * @param features [Object] an object to attach to the prototype
  663. * @return [Class<Service>] the service class defined by this function.
  664. */
  665. defineService: function defineService(serviceIdentifier, versions, features) {
  666. AWS.Service._serviceMap[serviceIdentifier] = true;
  667. if (!Array.isArray(versions)) {
  668. features = versions;
  669. versions = [];
  670. }
  671. var svc = inherit(AWS.Service, features || {});
  672. if (typeof serviceIdentifier === 'string') {
  673. AWS.Service.addVersions(svc, versions);
  674. var identifier = svc.serviceIdentifier || serviceIdentifier;
  675. svc.serviceIdentifier = identifier;
  676. } else { // defineService called with an API
  677. svc.prototype.api = serviceIdentifier;
  678. AWS.Service.defineMethods(svc);
  679. }
  680. AWS.SequentialExecutor.call(this.prototype);
  681. //util.clientSideMonitoring is only available in node
  682. if (!this.prototype.publisher && AWS.util.clientSideMonitoring) {
  683. var Publisher = AWS.util.clientSideMonitoring.Publisher;
  684. var configProvider = AWS.util.clientSideMonitoring.configProvider;
  685. var publisherConfig = configProvider();
  686. this.prototype.publisher = new Publisher(publisherConfig);
  687. if (publisherConfig.enabled) {
  688. //if csm is enabled in environment, SDK should send all metrics
  689. AWS.Service._clientSideMonitoring = true;
  690. }
  691. }
  692. AWS.SequentialExecutor.call(svc.prototype);
  693. AWS.Service.addDefaultMonitoringListeners(svc.prototype);
  694. return svc;
  695. },
  696. /**
  697. * @api private
  698. */
  699. addVersions: function addVersions(svc, versions) {
  700. if (!Array.isArray(versions)) versions = [versions];
  701. svc.services = svc.services || {};
  702. for (var i = 0; i < versions.length; i++) {
  703. if (svc.services[versions[i]] === undefined) {
  704. svc.services[versions[i]] = null;
  705. }
  706. }
  707. svc.apiVersions = Object.keys(svc.services).sort();
  708. },
  709. /**
  710. * @api private
  711. */
  712. defineServiceApi: function defineServiceApi(superclass, version, apiConfig) {
  713. var svc = inherit(superclass, {
  714. serviceIdentifier: superclass.serviceIdentifier
  715. });
  716. function setApi(api) {
  717. if (api.isApi) {
  718. svc.prototype.api = api;
  719. } else {
  720. svc.prototype.api = new Api(api, {
  721. serviceIdentifier: superclass.serviceIdentifier
  722. });
  723. }
  724. }
  725. if (typeof version === 'string') {
  726. if (apiConfig) {
  727. setApi(apiConfig);
  728. } else {
  729. try {
  730. setApi(AWS.apiLoader(superclass.serviceIdentifier, version));
  731. } catch (err) {
  732. throw AWS.util.error(err, {
  733. message: 'Could not find API configuration ' +
  734. superclass.serviceIdentifier + '-' + version
  735. });
  736. }
  737. }
  738. if (!Object.prototype.hasOwnProperty.call(superclass.services, version)) {
  739. superclass.apiVersions = superclass.apiVersions.concat(version).sort();
  740. }
  741. superclass.services[version] = svc;
  742. } else {
  743. setApi(version);
  744. }
  745. AWS.Service.defineMethods(svc);
  746. return svc;
  747. },
  748. /**
  749. * @api private
  750. */
  751. hasService: function(identifier) {
  752. return Object.prototype.hasOwnProperty.call(AWS.Service._serviceMap, identifier);
  753. },
  754. /**
  755. * @param attachOn attach default monitoring listeners to object
  756. *
  757. * Each monitoring event should be emitted from service client to service constructor prototype and then
  758. * to global service prototype like bubbling up. These default monitoring events listener will transfer
  759. * the monitoring events to the upper layer.
  760. * @api private
  761. */
  762. addDefaultMonitoringListeners: function addDefaultMonitoringListeners(attachOn) {
  763. attachOn.addNamedListener('MONITOR_EVENTS_BUBBLE', 'apiCallAttempt', function EVENTS_BUBBLE(event) {
  764. var baseClass = Object.getPrototypeOf(attachOn);
  765. if (baseClass._events) baseClass.emit('apiCallAttempt', [event]);
  766. });
  767. attachOn.addNamedListener('CALL_EVENTS_BUBBLE', 'apiCall', function CALL_EVENTS_BUBBLE(event) {
  768. var baseClass = Object.getPrototypeOf(attachOn);
  769. if (baseClass._events) baseClass.emit('apiCall', [event]);
  770. });
  771. },
  772. /**
  773. * @api private
  774. */
  775. _serviceMap: {}
  776. });
  777. AWS.util.mixin(AWS.Service, AWS.SequentialExecutor);
  778. /**
  779. * @api private
  780. */
  781. module.exports = AWS.Service;