git-off

git off handles large files in git repos
git clone https://noulin.net/git/git-off.git
Log | Files | Refs | README

service.js (16328B)


      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 
      7 /**
      8  * The service class representing an AWS service.
      9  *
     10  * @abstract
     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     var ServiceClass = this.loadServiceClass(config || {});
     28     if (ServiceClass) {
     29       var originalConfig = AWS.util.copy(config);
     30       var svc = new ServiceClass(config);
     31       Object.defineProperty(svc, '_originalConfig', {
     32         get: function() { return originalConfig; },
     33         enumerable: false,
     34         configurable: true
     35       });
     36       svc._clientId = ++clientCount;
     37       return svc;
     38     }
     39     this.initialize(config);
     40   },
     41 
     42   /**
     43    * @api private
     44    */
     45   initialize: function initialize(config) {
     46     var svcConfig = AWS.config[this.serviceIdentifier];
     47 
     48     this.config = new AWS.Config(AWS.config);
     49     if (svcConfig) this.config.update(svcConfig, true);
     50     if (config) this.config.update(config, true);
     51 
     52     this.validateService();
     53     if (!this.config.endpoint) regionConfig(this);
     54 
     55     this.config.endpoint = this.endpointFromTemplate(this.config.endpoint);
     56     this.setEndpoint(this.config.endpoint);
     57   },
     58 
     59   /**
     60    * @api private
     61    */
     62   validateService: function validateService() {
     63   },
     64 
     65   /**
     66    * @api private
     67    */
     68   loadServiceClass: function loadServiceClass(serviceConfig) {
     69     var config = serviceConfig;
     70     if (!AWS.util.isEmpty(this.api)) {
     71       return null;
     72     } else if (config.apiConfig) {
     73       return AWS.Service.defineServiceApi(this.constructor, config.apiConfig);
     74     } else if (!this.constructor.services) {
     75       return null;
     76     } else {
     77       config = new AWS.Config(AWS.config);
     78       config.update(serviceConfig, true);
     79       var version = config.apiVersions[this.constructor.serviceIdentifier];
     80       version = version || config.apiVersion;
     81       return this.getLatestServiceClass(version);
     82     }
     83   },
     84 
     85   /**
     86    * @api private
     87    */
     88   getLatestServiceClass: function getLatestServiceClass(version) {
     89     version = this.getLatestServiceVersion(version);
     90     if (this.constructor.services[version] === null) {
     91       AWS.Service.defineServiceApi(this.constructor, version);
     92     }
     93 
     94     return this.constructor.services[version];
     95   },
     96 
     97   /**
     98    * @api private
     99    */
    100   getLatestServiceVersion: function getLatestServiceVersion(version) {
    101     if (!this.constructor.services || this.constructor.services.length === 0) {
    102       throw new Error('No services defined on ' +
    103                       this.constructor.serviceIdentifier);
    104     }
    105 
    106     if (!version) {
    107       version = 'latest';
    108     } else if (AWS.util.isType(version, Date)) {
    109       version = AWS.util.date.iso8601(version).split('T')[0];
    110     }
    111 
    112     if (Object.hasOwnProperty(this.constructor.services, version)) {
    113       return version;
    114     }
    115 
    116     var keys = Object.keys(this.constructor.services).sort();
    117     var selectedVersion = null;
    118     for (var i = keys.length - 1; i >= 0; i--) {
    119       // versions that end in "*" are not available on disk and can be
    120       // skipped, so do not choose these as selectedVersions
    121       if (keys[i][keys[i].length - 1] !== '*') {
    122         selectedVersion = keys[i];
    123       }
    124       if (keys[i].substr(0, 10) <= version) {
    125         return selectedVersion;
    126       }
    127     }
    128 
    129     throw new Error('Could not find ' + this.constructor.serviceIdentifier +
    130                     ' API to satisfy version constraint `' + version + '\'');
    131   },
    132 
    133   /**
    134    * @api private
    135    */
    136   api: {},
    137 
    138   /**
    139    * @api private
    140    */
    141   defaultRetryCount: 3,
    142 
    143   /**
    144    * @api private
    145    */
    146   customizeRequests: function customizeRequests(callback) {
    147     if (!callback) {
    148       this.customRequestHandler = null;
    149     } else if (typeof callback === 'function') {
    150       this.customRequestHandler = callback;
    151     } else {
    152       throw new Error('Invalid callback type \'' + typeof callback + '\' provided in customizeRequests');
    153     }
    154   },
    155 
    156   /**
    157    * Calls an operation on a service with the given input parameters.
    158    *
    159    * @param operation [String] the name of the operation to call on the service.
    160    * @param params [map] a map of input options for the operation
    161    * @callback callback function(err, data)
    162    *   If a callback is supplied, it is called when a response is returned
    163    *   from the service.
    164    *   @param err [Error] the error object returned from the request.
    165    *     Set to `null` if the request is successful.
    166    *   @param data [Object] the de-serialized data returned from
    167    *     the request. Set to `null` if a request error occurs.
    168    */
    169   makeRequest: function makeRequest(operation, params, callback) {
    170     if (typeof params === 'function') {
    171       callback = params;
    172       params = null;
    173     }
    174 
    175     params = params || {};
    176     if (this.config.params) { // copy only toplevel bound params
    177       var rules = this.api.operations[operation];
    178       if (rules) {
    179         params = AWS.util.copy(params);
    180         AWS.util.each(this.config.params, function(key, value) {
    181           if (rules.input.members[key]) {
    182             if (params[key] === undefined || params[key] === null) {
    183               params[key] = value;
    184             }
    185           }
    186         });
    187       }
    188     }
    189 
    190     var request = new AWS.Request(this, operation, params);
    191     this.addAllRequestListeners(request);
    192 
    193     if (callback) request.send(callback);
    194     return request;
    195   },
    196 
    197   /**
    198    * Calls an operation on a service with the given input parameters, without
    199    * any authentication data. This method is useful for "public" API operations.
    200    *
    201    * @param operation [String] the name of the operation to call on the service.
    202    * @param params [map] a map of input options for the operation
    203    * @callback callback function(err, data)
    204    *   If a callback is supplied, it is called when a response is returned
    205    *   from the service.
    206    *   @param err [Error] the error object returned from the request.
    207    *     Set to `null` if the request is successful.
    208    *   @param data [Object] the de-serialized data returned from
    209    *     the request. Set to `null` if a request error occurs.
    210    */
    211   makeUnauthenticatedRequest: function makeUnauthenticatedRequest(operation, params, callback) {
    212     if (typeof params === 'function') {
    213       callback = params;
    214       params = {};
    215     }
    216 
    217     var request = this.makeRequest(operation, params).toUnauthenticated();
    218     return callback ? request.send(callback) : request;
    219   },
    220 
    221   /**
    222    * Waits for a given state
    223    *
    224    * @param state [String] the state on the service to wait for
    225    * @param params [map] a map of parameters to pass with each request
    226    * @callback callback function(err, data)
    227    *   If a callback is supplied, it is called when a response is returned
    228    *   from the service.
    229    *   @param err [Error] the error object returned from the request.
    230    *     Set to `null` if the request is successful.
    231    *   @param data [Object] the de-serialized data returned from
    232    *     the request. Set to `null` if a request error occurs.
    233    */
    234   waitFor: function waitFor(state, params, callback) {
    235     var waiter = new AWS.ResourceWaiter(this, state);
    236     return waiter.wait(params, callback);
    237   },
    238 
    239   /**
    240    * @api private
    241    */
    242   addAllRequestListeners: function addAllRequestListeners(request) {
    243     var list = [AWS.events, AWS.EventListeners.Core, this.serviceInterface(),
    244                 AWS.EventListeners.CorePost];
    245     for (var i = 0; i < list.length; i++) {
    246       if (list[i]) request.addListeners(list[i]);
    247     }
    248 
    249     // disable parameter validation
    250     if (!this.config.paramValidation) {
    251       request.removeListener('validate',
    252         AWS.EventListeners.Core.VALIDATE_PARAMETERS);
    253     }
    254 
    255     if (this.config.logger) { // add logging events
    256       request.addListeners(AWS.EventListeners.Logger);
    257     }
    258 
    259     this.setupRequestListeners(request);
    260     // call prototype's customRequestHandler
    261     if (typeof this.constructor.prototype.customRequestHandler === 'function') {
    262       this.constructor.prototype.customRequestHandler(request);
    263     }
    264     // call instance's customRequestHandler
    265     if (Object.prototype.hasOwnProperty.call(this, 'customRequestHandler') && typeof this.customRequestHandler === 'function') {
    266       this.customRequestHandler(request);
    267     }
    268   },
    269 
    270   /**
    271    * Override this method to setup any custom request listeners for each
    272    * new request to the service.
    273    *
    274    * @abstract
    275    */
    276   setupRequestListeners: function setupRequestListeners() {
    277   },
    278 
    279   /**
    280    * Gets the signer class for a given request
    281    * @api private
    282    */
    283   getSignerClass: function getSignerClass() {
    284     var version;
    285     if (this.config.signatureVersion) {
    286       version = this.config.signatureVersion;
    287     } else {
    288       version = this.api.signatureVersion;
    289     }
    290     return AWS.Signers.RequestSigner.getVersion(version);
    291   },
    292 
    293   /**
    294    * @api private
    295    */
    296   serviceInterface: function serviceInterface() {
    297     switch (this.api.protocol) {
    298       case 'ec2': return AWS.EventListeners.Query;
    299       case 'query': return AWS.EventListeners.Query;
    300       case 'json': return AWS.EventListeners.Json;
    301       case 'rest-json': return AWS.EventListeners.RestJson;
    302       case 'rest-xml': return AWS.EventListeners.RestXml;
    303     }
    304     if (this.api.protocol) {
    305       throw new Error('Invalid service `protocol\' ' +
    306         this.api.protocol + ' in API config');
    307     }
    308   },
    309 
    310   /**
    311    * @api private
    312    */
    313   successfulResponse: function successfulResponse(resp) {
    314     return resp.httpResponse.statusCode < 300;
    315   },
    316 
    317   /**
    318    * How many times a failed request should be retried before giving up.
    319    * the defaultRetryCount can be overriden by service classes.
    320    *
    321    * @api private
    322    */
    323   numRetries: function numRetries() {
    324     if (this.config.maxRetries !== undefined) {
    325       return this.config.maxRetries;
    326     } else {
    327       return this.defaultRetryCount;
    328     }
    329   },
    330 
    331   /**
    332    * @api private
    333    */
    334   retryDelays: function retryDelays(retryCount) {
    335     return AWS.util.calculateRetryDelay(retryCount, this.config.retryDelayOptions);
    336   },
    337 
    338   /**
    339    * @api private
    340    */
    341   retryableError: function retryableError(error) {
    342     if (this.networkingError(error)) return true;
    343     if (this.expiredCredentialsError(error)) return true;
    344     if (this.throttledError(error)) return true;
    345     if (error.statusCode >= 500) return true;
    346     return false;
    347   },
    348 
    349   /**
    350    * @api private
    351    */
    352   networkingError: function networkingError(error) {
    353     return error.code === 'NetworkingError';
    354   },
    355 
    356   /**
    357    * @api private
    358    */
    359   expiredCredentialsError: function expiredCredentialsError(error) {
    360     // TODO : this only handles *one* of the expired credential codes
    361     return (error.code === 'ExpiredTokenException');
    362   },
    363 
    364   /**
    365    * @api private
    366    */
    367   clockSkewError: function clockSkewError(error) {
    368     switch (error.code) {
    369       case 'RequestTimeTooSkewed':
    370       case 'RequestExpired':
    371       case 'InvalidSignatureException':
    372       case 'SignatureDoesNotMatch':
    373       case 'AuthFailure':
    374       case 'RequestInTheFuture':
    375         return true;
    376       default: return false;
    377     }
    378   },
    379 
    380   /**
    381    * @api private
    382    */
    383   throttledError: function throttledError(error) {
    384     // this logic varies between services
    385     switch (error.code) {
    386       case 'ProvisionedThroughputExceededException':
    387       case 'Throttling':
    388       case 'ThrottlingException':
    389       case 'RequestLimitExceeded':
    390       case 'RequestThrottled':
    391         return true;
    392       default:
    393         return false;
    394     }
    395   },
    396 
    397   /**
    398    * @api private
    399    */
    400   endpointFromTemplate: function endpointFromTemplate(endpoint) {
    401     if (typeof endpoint !== 'string') return endpoint;
    402 
    403     var e = endpoint;
    404     e = e.replace(/\{service\}/g, this.api.endpointPrefix);
    405     e = e.replace(/\{region\}/g, this.config.region);
    406     e = e.replace(/\{scheme\}/g, this.config.sslEnabled ? 'https' : 'http');
    407     return e;
    408   },
    409 
    410   /**
    411    * @api private
    412    */
    413   setEndpoint: function setEndpoint(endpoint) {
    414     this.endpoint = new AWS.Endpoint(endpoint, this.config);
    415   },
    416 
    417   /**
    418    * @api private
    419    */
    420   paginationConfig: function paginationConfig(operation, throwException) {
    421     var paginator = this.api.operations[operation].paginator;
    422     if (!paginator) {
    423       if (throwException) {
    424         var e = new Error();
    425         throw AWS.util.error(e, 'No pagination configuration for ' + operation);
    426       }
    427       return null;
    428     }
    429 
    430     return paginator;
    431   }
    432 });
    433 
    434 AWS.util.update(AWS.Service, {
    435 
    436   /**
    437    * Adds one method for each operation described in the api configuration
    438    *
    439    * @api private
    440    */
    441   defineMethods: function defineMethods(svc) {
    442     AWS.util.each(svc.prototype.api.operations, function iterator(method) {
    443       if (svc.prototype[method]) return;
    444       var operation = svc.prototype.api.operations[method];
    445       if (operation.authtype === 'none') {
    446         svc.prototype[method] = function (params, callback) {
    447           return this.makeUnauthenticatedRequest(method, params, callback);
    448         };
    449       } else {
    450         svc.prototype[method] = function (params, callback) {
    451           return this.makeRequest(method, params, callback);
    452         };
    453       }
    454     });
    455   },
    456 
    457   /**
    458    * Defines a new Service class using a service identifier and list of versions
    459    * including an optional set of features (functions) to apply to the class
    460    * prototype.
    461    *
    462    * @param serviceIdentifier [String] the identifier for the service
    463    * @param versions [Array<String>] a list of versions that work with this
    464    *   service
    465    * @param features [Object] an object to attach to the prototype
    466    * @return [Class<Service>] the service class defined by this function.
    467    */
    468   defineService: function defineService(serviceIdentifier, versions, features) {
    469     AWS.Service._serviceMap[serviceIdentifier] = true;
    470     if (!Array.isArray(versions)) {
    471       features = versions;
    472       versions = [];
    473     }
    474 
    475     var svc = inherit(AWS.Service, features || {});
    476 
    477     if (typeof serviceIdentifier === 'string') {
    478       AWS.Service.addVersions(svc, versions);
    479 
    480       var identifier = svc.serviceIdentifier || serviceIdentifier;
    481       svc.serviceIdentifier = identifier;
    482     } else { // defineService called with an API
    483       svc.prototype.api = serviceIdentifier;
    484       AWS.Service.defineMethods(svc);
    485     }
    486 
    487     return svc;
    488   },
    489 
    490   /**
    491    * @api private
    492    */
    493   addVersions: function addVersions(svc, versions) {
    494     if (!Array.isArray(versions)) versions = [versions];
    495 
    496     svc.services = svc.services || {};
    497     for (var i = 0; i < versions.length; i++) {
    498       if (svc.services[versions[i]] === undefined) {
    499         svc.services[versions[i]] = null;
    500       }
    501     }
    502 
    503     svc.apiVersions = Object.keys(svc.services).sort();
    504   },
    505 
    506   /**
    507    * @api private
    508    */
    509   defineServiceApi: function defineServiceApi(superclass, version, apiConfig) {
    510     var svc = inherit(superclass, {
    511       serviceIdentifier: superclass.serviceIdentifier
    512     });
    513 
    514     function setApi(api) {
    515       if (api.isApi) {
    516         svc.prototype.api = api;
    517       } else {
    518         svc.prototype.api = new Api(api);
    519       }
    520     }
    521 
    522     if (typeof version === 'string') {
    523       if (apiConfig) {
    524         setApi(apiConfig);
    525       } else {
    526         try {
    527           setApi(AWS.apiLoader(superclass.serviceIdentifier, version));
    528         } catch (err) {
    529           throw AWS.util.error(err, {
    530             message: 'Could not find API configuration ' +
    531               superclass.serviceIdentifier + '-' + version
    532           });
    533         }
    534       }
    535       if (!Object.prototype.hasOwnProperty.call(superclass.services, version)) {
    536         superclass.apiVersions = superclass.apiVersions.concat(version).sort();
    537       }
    538       superclass.services[version] = svc;
    539     } else {
    540       setApi(version);
    541     }
    542 
    543     AWS.Service.defineMethods(svc);
    544     return svc;
    545   },
    546 
    547   /**
    548    * @api private
    549    */
    550   hasService: function(identifier) {
    551     return Object.prototype.hasOwnProperty.call(AWS.Service._serviceMap, identifier);
    552   },
    553 
    554   /**
    555    * @api private
    556    */
    557   _serviceMap: {}
    558 });
    559 
    560 module.exports = AWS.Service;