/ cloudformation-templates / node_modules / aws-cdk / node_modules / aws-sdk / lib / param_validator.js
param_validator.js
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 if (paramValue !== undefined && paramValue !== null) { 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 var validTypes = ['string']; 151 if (shape.isJsonValue) { 152 validTypes = validTypes.concat(['number', 'object', 'boolean']); 153 } 154 if (value !== null && this.validateType(value, context, validTypes)) { 155 this.validateEnum(shape, value, context); 156 this.validateRange(shape, value.length, context, 'string length'); 157 this.validatePattern(shape, value, context); 158 this.validateUri(shape, value, context); 159 } 160 }, 161 162 validateUri: function validateUri(shape, value, context) { 163 if (shape['location'] === 'uri') { 164 if (value.length === 0) { 165 this.fail('UriParameterError', 'Expected uri parameter to have length >= 1,' 166 + ' but found "' + value +'" for ' + context); 167 } 168 } 169 }, 170 171 validatePattern: function validatePattern(shape, value, context) { 172 if (this.validation['pattern'] && shape['pattern'] !== undefined) { 173 if (!(new RegExp(shape['pattern'])).test(value)) { 174 this.fail('PatternMatchError', 'Provided value "' + value + '" ' 175 + 'does not match regex pattern /' + shape['pattern'] + '/ for ' 176 + context); 177 } 178 } 179 }, 180 181 validateRange: function validateRange(shape, value, context, descriptor) { 182 if (this.validation['min']) { 183 if (shape['min'] !== undefined && value < shape['min']) { 184 this.fail('MinRangeError', 'Expected ' + descriptor + ' >= ' 185 + shape['min'] + ', but found ' + value + ' for ' + context); 186 } 187 } 188 if (this.validation['max']) { 189 if (shape['max'] !== undefined && value > shape['max']) { 190 this.fail('MaxRangeError', 'Expected ' + descriptor + ' <= ' 191 + shape['max'] + ', but found ' + value + ' for ' + context); 192 } 193 } 194 }, 195 196 validateEnum: function validateRange(shape, value, context) { 197 if (this.validation['enum'] && shape['enum'] !== undefined) { 198 // Fail if the string value is not present in the enum list 199 if (shape['enum'].indexOf(value) === -1) { 200 this.fail('EnumError', 'Found string value of ' + value + ', but ' 201 + 'expected ' + shape['enum'].join('|') + ' for ' + context); 202 } 203 } 204 }, 205 206 validateType: function validateType(value, context, acceptedTypes, type) { 207 // We will not log an error for null or undefined, but we will return 208 // false so that callers know that the expected type was not strictly met. 209 if (value === null || value === undefined) return false; 210 211 var foundInvalidType = false; 212 for (var i = 0; i < acceptedTypes.length; i++) { 213 if (typeof acceptedTypes[i] === 'string') { 214 if (typeof value === acceptedTypes[i]) return true; 215 } else if (acceptedTypes[i] instanceof RegExp) { 216 if ((value || '').toString().match(acceptedTypes[i])) return true; 217 } else { 218 if (value instanceof acceptedTypes[i]) return true; 219 if (AWS.util.isType(value, acceptedTypes[i])) return true; 220 if (!type && !foundInvalidType) acceptedTypes = acceptedTypes.slice(); 221 acceptedTypes[i] = AWS.util.typeName(acceptedTypes[i]); 222 } 223 foundInvalidType = true; 224 } 225 226 var acceptedType = type; 227 if (!acceptedType) { 228 acceptedType = acceptedTypes.join(', ').replace(/,([^,]+)$/, ', or$1'); 229 } 230 231 var vowel = acceptedType.match(/^[aeiou]/i) ? 'n' : ''; 232 this.fail('InvalidParameterType', 'Expected ' + context + ' to be a' + 233 vowel + ' ' + acceptedType); 234 return false; 235 }, 236 237 validateNumber: function validateNumber(shape, value, context) { 238 if (value === null || value === undefined) return; 239 if (typeof value === 'string') { 240 var castedValue = parseFloat(value); 241 if (castedValue.toString() === value) value = castedValue; 242 } 243 if (this.validateType(value, context, ['number'])) { 244 this.validateRange(shape, value, context, 'numeric value'); 245 } 246 }, 247 248 validatePayload: function validatePayload(value, context) { 249 if (value === null || value === undefined) return; 250 if (typeof value === 'string') return; 251 if (value && typeof value.byteLength === 'number') return; // typed arrays 252 if (AWS.util.isNode()) { // special check for buffer/stream in Node.js 253 var Stream = AWS.util.stream.Stream; 254 if (AWS.util.Buffer.isBuffer(value) || value instanceof Stream) return; 255 } else { 256 if (typeof Blob !== void 0 && value instanceof Blob) return; 257 } 258 259 var types = ['Buffer', 'Stream', 'File', 'Blob', 'ArrayBuffer', 'DataView']; 260 if (value) { 261 for (var i = 0; i < types.length; i++) { 262 if (AWS.util.isType(value, types[i])) return; 263 if (AWS.util.typeName(value.constructor) === types[i]) return; 264 } 265 } 266 267 this.fail('InvalidParameterType', 'Expected ' + context + ' to be a ' + 268 'string, Buffer, Stream, Blob, or typed array object'); 269 } 270 });