git-off

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

param_validator.js (9429B)


      1 var AWS = require('./core');
      2 
      3 /**
      4  * @api private
      5  */
      6 AWS.ParamValidator = AWS.util.inherit({
      7   /**
      8    * Create a new validator object.
      9    *
     10    * @param validation [Boolean|map] whether input parameters should be
     11    *     validated against the operation description before sending the
     12    *     request. Pass a map to enable any of the following specific
     13    *     validation features:
     14    *
     15    *     * **min** [Boolean] — Validates that a value meets the min
     16    *       constraint. This is enabled by default when paramValidation is set
     17    *       to `true`.
     18    *     * **max** [Boolean] — Validates that a value meets the max
     19    *       constraint.
     20    *     * **pattern** [Boolean] — Validates that a string value matches a
     21    *       regular expression.
     22    *     * **enum** [Boolean] — Validates that a string value matches one
     23    *       of the allowable enum values.
     24    */
     25   constructor: function ParamValidator(validation) {
     26     if (validation === true || validation === undefined) {
     27       validation = {'min': true};
     28     }
     29     this.validation = validation;
     30   },
     31 
     32   validate: function validate(shape, params, context) {
     33     this.errors = [];
     34     this.validateMember(shape, params || {}, context || 'params');
     35 
     36     if (this.errors.length > 1) {
     37       var msg = this.errors.join('\n* ');
     38       msg = 'There were ' + this.errors.length +
     39         ' validation errors:\n* ' + msg;
     40       throw AWS.util.error(new Error(msg),
     41         {code: 'MultipleValidationErrors', errors: this.errors});
     42     } else if (this.errors.length === 1) {
     43       throw this.errors[0];
     44     } else {
     45       return true;
     46     }
     47   },
     48 
     49   fail: function fail(code, message) {
     50     this.errors.push(AWS.util.error(new Error(message), {code: code}));
     51   },
     52 
     53   validateStructure: function validateStructure(shape, params, context) {
     54     this.validateType(params, context, ['object'], 'structure');
     55 
     56     var paramName;
     57     for (var i = 0; shape.required && i < shape.required.length; i++) {
     58       paramName = shape.required[i];
     59       var value = params[paramName];
     60       if (value === undefined || value === null) {
     61         this.fail('MissingRequiredParameter',
     62           'Missing required key \'' + paramName + '\' in ' + context);
     63       }
     64     }
     65 
     66     // validate hash members
     67     for (paramName in params) {
     68       if (!Object.prototype.hasOwnProperty.call(params, paramName)) continue;
     69 
     70       var paramValue = params[paramName],
     71           memberShape = shape.members[paramName];
     72 
     73       if (memberShape !== undefined) {
     74         var memberContext = [context, paramName].join('.');
     75         this.validateMember(memberShape, paramValue, memberContext);
     76       } else {
     77         this.fail('UnexpectedParameter',
     78           'Unexpected key \'' + paramName + '\' found in ' + context);
     79       }
     80     }
     81 
     82     return true;
     83   },
     84 
     85   validateMember: function validateMember(shape, param, context) {
     86     switch (shape.type) {
     87       case 'structure':
     88         return this.validateStructure(shape, param, context);
     89       case 'list':
     90         return this.validateList(shape, param, context);
     91       case 'map':
     92         return this.validateMap(shape, param, context);
     93       default:
     94         return this.validateScalar(shape, param, context);
     95     }
     96   },
     97 
     98   validateList: function validateList(shape, params, context) {
     99     if (this.validateType(params, context, [Array])) {
    100       this.validateRange(shape, params.length, context, 'list member count');
    101       // validate array members
    102       for (var i = 0; i < params.length; i++) {
    103         this.validateMember(shape.member, params[i], context + '[' + i + ']');
    104       }
    105     }
    106   },
    107 
    108   validateMap: function validateMap(shape, params, context) {
    109     if (this.validateType(params, context, ['object'], 'map')) {
    110       // Build up a count of map members to validate range traits.
    111       var mapCount = 0;
    112       for (var param in params) {
    113         if (!Object.prototype.hasOwnProperty.call(params, param)) continue;
    114         // Validate any map key trait constraints
    115         this.validateMember(shape.key, param,
    116                             context + '[key=\'' + param + '\']')
    117         this.validateMember(shape.value, params[param],
    118                             context + '[\'' + param + '\']');
    119         mapCount++;
    120       }
    121       this.validateRange(shape, mapCount, context, 'map member count');
    122     }
    123   },
    124 
    125   validateScalar: function validateScalar(shape, value, context) {
    126     switch (shape.type) {
    127       case null:
    128       case undefined:
    129       case 'string':
    130         return this.validateString(shape, value, context);
    131       case 'base64':
    132       case 'binary':
    133         return this.validatePayload(value, context);
    134       case 'integer':
    135       case 'float':
    136         return this.validateNumber(shape, value, context);
    137       case 'boolean':
    138         return this.validateType(value, context, ['boolean']);
    139       case 'timestamp':
    140         return this.validateType(value, context, [Date,
    141           /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z$/, 'number'],
    142           'Date object, ISO-8601 string, or a UNIX timestamp');
    143       default:
    144         return this.fail('UnkownType', 'Unhandled type ' +
    145                          shape.type + ' for ' + context);
    146     }
    147   },
    148 
    149   validateString: function validateString(shape, value, context) {
    150     if (this.validateType(value, context, ['string'])) {
    151       this.validateEnum(shape, value, context);
    152       this.validateRange(shape, value.length, context, 'string length');
    153       this.validatePattern(shape, value, context);
    154     }
    155   },
    156 
    157   validatePattern: function validatePattern(shape, value, context) {
    158     if (this.validation['pattern'] && shape['pattern'] !== undefined) {
    159       if (!(new RegExp(shape['pattern'])).test(value)) {
    160         this.fail('PatternMatchError', 'Provided value "' + value + '" '
    161           + 'does not match regex pattern /' + shape['pattern'] + '/ for '
    162           + context);
    163       }
    164     }
    165   },
    166 
    167   validateRange: function validateRange(shape, value, context, descriptor) {
    168     if (this.validation['min']) {
    169       if (shape['min'] !== undefined && value < shape['min']) {
    170         this.fail('MinRangeError', 'Expected ' + descriptor + ' >= '
    171           + shape['min'] + ', but found ' + value + ' for ' + context);
    172       }
    173     }
    174     if (this.validation['max']) {
    175       if (shape['max'] !== undefined && value > shape['max']) {
    176         this.fail('MaxRangeError', 'Expected ' + descriptor + ' <= '
    177           + shape['max'] + ', but found ' + value + ' for ' + context);
    178       }
    179     }
    180   },
    181 
    182   validateEnum: function validateRange(shape, value, context) {
    183     if (this.validation['enum'] && shape['enum'] !== undefined) {
    184       // Fail if the string value is not present in the enum list
    185       if (shape['enum'].indexOf(value) === -1) {
    186         this.fail('EnumError', 'Found string value of ' + value + ', but '
    187           + 'expected ' + shape['enum'].join('|') + ' for ' + context);
    188       }
    189     }
    190   },
    191 
    192   validateType: function validateType(value, context, acceptedTypes, type) {
    193     // We will not log an error for null or undefined, but we will return
    194     // false so that callers know that the expected type was not strictly met.
    195     if (value === null || value === undefined) return false;
    196 
    197     var foundInvalidType = false;
    198     for (var i = 0; i < acceptedTypes.length; i++) {
    199       if (typeof acceptedTypes[i] === 'string') {
    200         if (typeof value === acceptedTypes[i]) return true;
    201       } else if (acceptedTypes[i] instanceof RegExp) {
    202         if ((value || '').toString().match(acceptedTypes[i])) return true;
    203       } else {
    204         if (value instanceof acceptedTypes[i]) return true;
    205         if (AWS.util.isType(value, acceptedTypes[i])) return true;
    206         if (!type && !foundInvalidType) acceptedTypes = acceptedTypes.slice();
    207         acceptedTypes[i] = AWS.util.typeName(acceptedTypes[i]);
    208       }
    209       foundInvalidType = true;
    210     }
    211 
    212     var acceptedType = type;
    213     if (!acceptedType) {
    214       acceptedType = acceptedTypes.join(', ').replace(/,([^,]+)$/, ', or$1');
    215     }
    216 
    217     var vowel = acceptedType.match(/^[aeiou]/i) ? 'n' : '';
    218     this.fail('InvalidParameterType', 'Expected ' + context + ' to be a' +
    219               vowel + ' ' + acceptedType);
    220     return false;
    221   },
    222 
    223   validateNumber: function validateNumber(shape, value, context) {
    224     if (value === null || value === undefined) return;
    225     if (typeof value === 'string') {
    226       var castedValue = parseFloat(value);
    227       if (castedValue.toString() === value) value = castedValue;
    228     }
    229     if (this.validateType(value, context, ['number'])) {
    230       this.validateRange(shape, value, context, 'numeric value');
    231     }
    232   },
    233 
    234   validatePayload: function validatePayload(value, context) {
    235     if (value === null || value === undefined) return;
    236     if (typeof value === 'string') return;
    237     if (value && typeof value.byteLength === 'number') return; // typed arrays
    238     if (AWS.util.isNode()) { // special check for buffer/stream in Node.js
    239       var Stream = AWS.util.stream.Stream;
    240       if (AWS.util.Buffer.isBuffer(value) || value instanceof Stream) return;
    241     }
    242 
    243     var types = ['Buffer', 'Stream', 'File', 'Blob', 'ArrayBuffer', 'DataView'];
    244     if (value) {
    245       for (var i = 0; i < types.length; i++) {
    246         if (AWS.util.isType(value, types[i])) return;
    247         if (AWS.util.typeName(value.constructor) === types[i]) return;
    248       }
    249     }
    250 
    251     this.fail('InvalidParameterType', 'Expected ' + context + ' to be a ' +
    252       'string, Buffer, Stream, Blob, or typed array object');
    253   }
    254 });