diff --git a/.jshintrc b/.jshintrc index 80e742d3b1..46c92c1d51 100644 --- a/.jshintrc +++ b/.jshintrc @@ -22,5 +22,6 @@ "maxparams": false, "maxdepth": false, "maxstatements": 40, - "maxcomplexity": 5 + "maxcomplexity": 5, + "sub": true } diff --git a/doc-src/dynamodb.docs.js b/doc-src/dynamodb.docs.js index c139d204d7..4598217716 100644 --- a/doc-src/dynamodb.docs.js +++ b/doc-src/dynamodb.docs.js @@ -98,7 +98,7 @@ AWS.DynamoDB = inherit({}) * * +ConsistentRead+ - (Boolean) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -156,7 +156,7 @@ AWS.DynamoDB = inherit({}) * binary attributes. * * +AttributesToGet+ - (Array) * * +ConsistentRead+ - (Boolean) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method batchWriteItem(params, callback) @@ -228,7 +228,7 @@ AWS.DynamoDB = inherit({}) * binary attributes. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -302,7 +302,7 @@ AWS.DynamoDB = inherit({}) * * +NS+ - (Array) A set of numbers. * * +BS+ - (Array) A set of * binary attributes. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method createTable(params, callback) @@ -343,7 +343,7 @@ AWS.DynamoDB = inherit({}) * WriteCapacityUnits. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -383,7 +383,7 @@ AWS.DynamoDB = inherit({}) * * +WriteCapacityUnits+ - (Integer) * * +TableSizeBytes+ - (Integer) * * +ItemCount+ - (Integer) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method deleteItem(params, callback) @@ -450,7 +450,7 @@ AWS.DynamoDB = inherit({}) * * +ReturnValues+ - (String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -475,7 +475,7 @@ AWS.DynamoDB = inherit({}) * * +BS+ - (Array) A set of binary * attributes. * * +ConsumedCapacityUnits+ - (Float) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method deleteTable(params, callback) @@ -486,7 +486,7 @@ AWS.DynamoDB = inherit({}) * _ (underscore), - (hyphen) and . (period). * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -526,7 +526,7 @@ AWS.DynamoDB = inherit({}) * * +WriteCapacityUnits+ - (Integer) * * +TableSizeBytes+ - (Integer) * * +ItemCount+ - (Integer) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method describeTable(params, callback) @@ -538,7 +538,7 @@ AWS.DynamoDB = inherit({}) * 0-9, _ (underscore), - (hyphen) and . (period). * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -578,7 +578,7 @@ AWS.DynamoDB = inherit({}) * * +WriteCapacityUnits+ - (Integer) * * +TableSizeBytes+ - (Integer) * * +ItemCount+ - (Integer) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method getItem(params, callback) @@ -628,7 +628,7 @@ AWS.DynamoDB = inherit({}) * * +ConsistentRead+ - (Boolean) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -651,7 +651,7 @@ AWS.DynamoDB = inherit({}) * * +BS+ - (Array) A set of binary * attributes. * * +ConsumedCapacityUnits+ - (Float) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method listTables(params, callback) @@ -665,7 +665,7 @@ AWS.DynamoDB = inherit({}) * * +Limit+ - (Integer) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -678,7 +678,7 @@ AWS.DynamoDB = inherit({}) * ExclusiveStartTableName in a new request to continue the list * until all the table names are returned. If this value is null, * all table names have been returned. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method putItem(params, callback) @@ -724,7 +724,7 @@ AWS.DynamoDB = inherit({}) * * +ReturnValues+ - (String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -748,7 +748,7 @@ AWS.DynamoDB = inherit({}) * * +BS+ - (Array) A set of binary * attributes. * * +ConsumedCapacityUnits+ - (Float) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method query(params, callback) @@ -853,7 +853,7 @@ AWS.DynamoDB = inherit({}) * attributes. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -904,7 +904,7 @@ AWS.DynamoDB = inherit({}) * * +BS+ - (Array) A set of binary * attributes. * * +ConsumedCapacityUnits+ - (Float) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method scan(params, callback) @@ -989,7 +989,7 @@ AWS.DynamoDB = inherit({}) * attributes. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -1043,7 +1043,7 @@ AWS.DynamoDB = inherit({}) * * +BS+ - (Array) A set of binary * attributes. * * +ConsumedCapacityUnits+ - (Float) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method updateItem(params, callback) @@ -1126,7 +1126,7 @@ AWS.DynamoDB = inherit({}) * * +ReturnValues+ - (String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -1150,7 +1150,7 @@ AWS.DynamoDB = inherit({}) * * +BS+ - (Array) A set of binary * attributes. * * +ConsumedCapacityUnits+ - (Float) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method updateTable(params, callback) @@ -1171,7 +1171,7 @@ AWS.DynamoDB = inherit({}) * WriteCapacityUnits. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -1211,7 +1211,7 @@ AWS.DynamoDB = inherit({}) * * +WriteCapacityUnits+ - (Integer) * * +TableSizeBytes+ - (Integer) * * +ItemCount+ - (Integer) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * diff --git a/doc-src/guide/MakingRequests.md b/doc-src/guide/MakingRequests.md index 47e35687e3..a531c3aa26 100644 --- a/doc-src/guide/MakingRequests.md +++ b/doc-src/guide/MakingRequests.md @@ -27,11 +27,11 @@ The `error` and `data` parameters are described in the "Response Object" section below. Note that if you do not specify a callback, the operation will -return an `AWS.AWSRequest` object that must be manually sent using +return an `AWS.Request` object that must be manually sent using the `send()` method: ```js -// create the AWS.AWSRequest object +// create the AWS.Request object var request = new AWS.EC2().client.describeInstances(); // register a callback to report on the data @@ -43,10 +43,10 @@ request.done(function(resp) { request.send(); ``` -### The Response Object (`AWS.AWSResponse`) +### The Response Object (`AWS.Response`) The response object is passed into each callback function so -that you can access response data. The `AWS.AWSResponse` object that +that you can access response data. The `AWS.Response` object that is passed in contains two important properties to get at this data: When using the standard callback mechanism, the two properties will @@ -88,7 +88,7 @@ before attempting to access the `response.data` property. Currently, you can register callbacks for various events by either using the simplified callback syntax, or by using the callback -methods on the returned `AWS.AWSRequest` object. +methods on the returned `AWS.Request` object. #### Simplified Callback Method @@ -101,9 +101,9 @@ For example: ```js s3.client.listBuckets(function(error, data) { if (err) { - console.log(error); // error is AWSResponse.error + console.log(error); // error is Response.error } else { - console.log(data); // data is AWSResponse.data + console.log(data); // data is Response.data } }); ``` @@ -119,7 +119,7 @@ Prints (assuming the request succeeded): ``` The error and data parameters accepted are equivalent to the `error` and -`data` properties discussed in the `AWS.AWSResponse` response object section +`data` properties discussed in the `AWS.Response` response object section above. If you are passing parameters to the operation, the callback should be placed @@ -131,10 +131,10 @@ s3.client.getObject({Bucket: 'bucket', Key: 'key'}, function(err, data) { }); ``` -#### AWS.AWSRequest Callbacks +#### AWS.Request Callbacks You can alternatively register callbacks on events provided by the -`AWS.AWSRequest` object returned by each low-level client operation method. +`AWS.Request` object returned by each low-level client operation method. This request object exposes the `done`, `fail`, `data`, and `always` events, each taking a callback that accepts the response object. @@ -221,7 +221,7 @@ request.always(function(response) { ## Binding Custom Context Data on a Callback By default, the `this` context of a callback function registered on an -event will be the `AWS.AWSResponse` object returned from the service. +event will be the `AWS.Response` object returned from the service. In some cases, it may be necessary to pass extra custom context to these functions; in these cases, you can bind a custom value to be used as the `this` context object when the callbacks are executed. To do so, pass diff --git a/doc-src/s3.docs.js b/doc-src/s3.docs.js index ec7201e09c..9835f572fe 100644 --- a/doc-src/s3.docs.js +++ b/doc-src/s3.docs.js @@ -58,13 +58,13 @@ AWS.S3 = inherit({}) * * +UploadId+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method completeMultipartUpload(params, callback) @@ -82,7 +82,7 @@ AWS.S3 = inherit({}) * was uploaded. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -99,7 +99,7 @@ AWS.S3 = inherit({}) * * +ServerSideEncryption+ - (String) The Server-side * encryption algorithm used when storing this object in S3. * * +VersionId+ - (String) Version of the object. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method copyObject(params, callback) @@ -154,7 +154,7 @@ AWS.S3 = inherit({}) * store with the object in S3. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -169,7 +169,7 @@ AWS.S3 = inherit({}) * * +CopySourceVersionId+ - (String) * * +ServerSideEncryption+ - (String) The Server-side * encryption algorithm used when storing this object in S3. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method createBucket(params, callback) @@ -192,7 +192,7 @@ AWS.S3 = inherit({}) * write, read ACP, and write ACP permissions on the bucket. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -200,7 +200,7 @@ AWS.S3 = inherit({}) * the request. Set to +null+ if a request error occurs. * The +data+ object has the following properties: * * +Location+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method createMultipartUpload(params, callback) @@ -241,7 +241,7 @@ AWS.S3 = inherit({}) * store with the object in S3. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -256,7 +256,7 @@ AWS.S3 = inherit({}) * upload. * * +ServerSideEncryption+ - (String) The Server-side * encryption algorithm used when storing this object in S3. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method deleteBucket(params, callback) @@ -267,13 +267,13 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method deleteBucketCors(params, callback) @@ -282,13 +282,13 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method deleteBucketLifecycle(params, callback) @@ -297,13 +297,13 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method deleteBucketPolicy(params, callback) @@ -312,13 +312,13 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method deleteBucketTagging(params, callback) @@ -327,13 +327,13 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method deleteBucketWebsite(params, callback) @@ -342,13 +342,13 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method deleteObject(params, callback) @@ -360,7 +360,7 @@ AWS.S3 = inherit({}) * * +Key+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -372,7 +372,7 @@ AWS.S3 = inherit({}) * not (false) a delete marker. * * +VersionId+ - (String) Returns the version ID of the * delete marker created as a result of the DELETE operation. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method deleteObjects(params, callback) @@ -394,7 +394,7 @@ AWS.S3 = inherit({}) * that is displayed on your authentication device. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -411,7 +411,7 @@ AWS.S3 = inherit({}) * * +VersionId+ - (String) * * +Code+ - (String) * * +Message+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method getBucketAcl(params, callback) @@ -420,7 +420,7 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -442,7 +442,7 @@ AWS.S3 = inherit({}) * * +URI+ - (String) URI of the grantee group. * * +Permission+ - (String) Specifies the permission * given to the grantee. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method getBucketCors(params, callback) @@ -451,7 +451,7 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -472,7 +472,7 @@ AWS.S3 = inherit({}) * in the response that you want customers to be able to access * from their applications (for example, from a JavaScript * XMLHttpRequest object). - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method getBucketLifecycle(params, callback) @@ -481,7 +481,7 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -510,7 +510,7 @@ AWS.S3 = inherit({}) * be a non-zero positive integer. * * +Date+ - (Date) Indicates at what date the object * is to be moved or deleted. Should be in GMT ISO 8601 Format. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method getBucketLocation(params, callback) @@ -519,7 +519,7 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -527,7 +527,7 @@ AWS.S3 = inherit({}) * the request. Set to +null+ if a request error occurs. * The +data+ object has the following properties: * * +LocationConstraint+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method getBucketLogging(params, callback) @@ -538,7 +538,7 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -568,7 +568,7 @@ AWS.S3 = inherit({}) * grantee. * * +URI+ - (String) URI of the grantee group. * * +Permission+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method getBucketNotification(params, callback) @@ -577,7 +577,7 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -590,7 +590,7 @@ AWS.S3 = inherit({}) * the bucket. * * +Event+ - (String) Bucket event for which to send * notifications. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method getBucketPolicy(params, callback) @@ -599,7 +599,7 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -608,7 +608,7 @@ AWS.S3 = inherit({}) * The +data+ object has the following properties: * * +Policy+ - (String) The bucket policy as a JSON * document. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method getBucketRequestPayment(params, callback) @@ -617,7 +617,7 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -626,7 +626,7 @@ AWS.S3 = inherit({}) * The +data+ object has the following properties: * * +Payer+ - (String) Specifies who pays for the download * and request fees. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method getBucketTagging(params, callback) @@ -635,7 +635,7 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -645,7 +645,7 @@ AWS.S3 = inherit({}) * * +TagSet+ - (Array) * * +Key+ - (String) Name of the tag. * * +Value+ - (String) Value of the tag. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method getBucketVersioning(params, callback) @@ -654,7 +654,7 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -667,7 +667,7 @@ AWS.S3 = inherit({}) * only returned if the bucket has been configured with MFA delete. * If the bucket has never been so configured, this element is not * returned. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method getBucketWebsite(params, callback) @@ -676,7 +676,7 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -693,7 +693,7 @@ AWS.S3 = inherit({}) * * +ErrorDocument+ - (Object) * * +Key+ - (String) The object key name to use when a * 4XX class error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method getObject(params, callback) @@ -733,7 +733,7 @@ AWS.S3 = inherit({}) * return a 304 (not modified). * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -772,7 +772,7 @@ AWS.S3 = inherit({}) * * +VersionId+ - (String) Version of the object. * * +Metadata+ - (Object) A map of metadata to * store with the object in S3. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method getObjectAcl(params, callback) @@ -784,7 +784,7 @@ AWS.S3 = inherit({}) * specific version of the object. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -806,7 +806,7 @@ AWS.S3 = inherit({}) * * +URI+ - (String) URI of the grantee group. * * +Permission+ - (String) Specifies the permission * given to the grantee. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method getObjectTorrent(params, callback) @@ -816,7 +816,7 @@ AWS.S3 = inherit({}) * * +Key+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -824,7 +824,7 @@ AWS.S3 = inherit({}) * the request. Set to +null+ if a request error occurs. * The +data+ object has the following properties: * * +Body+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method headBucket(params, callback) @@ -834,13 +834,13 @@ AWS.S3 = inherit({}) * * +Bucket+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method headObject(params, callback) @@ -871,7 +871,7 @@ AWS.S3 = inherit({}) * return a 304 (not modified). * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -909,7 +909,7 @@ AWS.S3 = inherit({}) * * +VersionId+ - (String) Version of the object. * * +Metadata+ - (Object) A map of metadata to * store with the object in S3. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method listBuckets(params, callback) @@ -918,7 +918,7 @@ AWS.S3 = inherit({}) * @param params [Object] * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -931,7 +931,7 @@ AWS.S3 = inherit({}) * * +Owner+ - (Object) * * +ID+ - (String) * * +DisplayName+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method listMultipartUploads(params, callback) @@ -954,7 +954,7 @@ AWS.S3 = inherit({}) * is ignored. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -999,7 +999,7 @@ AWS.S3 = inherit({}) * it provides the Canonical User ID. If the principal is an * IAM User, it provides a user ARN value. * * +DisplayName+ - (String) Name of the Principal. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method listObjectVersions(params, callback) @@ -1019,7 +1019,7 @@ AWS.S3 = inherit({}) * begin with the specified prefix. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -1069,7 +1069,7 @@ AWS.S3 = inherit({}) * results. * * +CommonPrefixes+ - (Array) * * +Prefix+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method listObjects(params, callback) @@ -1089,7 +1089,7 @@ AWS.S3 = inherit({}) * begin with the specified prefix. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -1119,7 +1119,7 @@ AWS.S3 = inherit({}) * results. * * +CommonPrefixes+ - (Array) * * +Prefix+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method listParts(params, callback) @@ -1137,7 +1137,7 @@ AWS.S3 = inherit({}) * will be listed. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -1179,7 +1179,7 @@ AWS.S3 = inherit({}) * * +DisplayName+ - (String) * * +StorageClass+ - (String) The class of storage used to * store the object. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method putBucketAcl(params, callback) @@ -1215,13 +1215,13 @@ AWS.S3 = inherit({}) * write, read ACP, and write ACP permissions on the bucket. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method putBucketCors(params, callback) @@ -1245,13 +1245,13 @@ AWS.S3 = inherit({}) * JavaScript XMLHttpRequest object). * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method putBucketLifecycle(params, callback) @@ -1286,13 +1286,13 @@ AWS.S3 = inherit({}) * Format. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method putBucketLogging(params, callback) @@ -1327,13 +1327,13 @@ AWS.S3 = inherit({}) * * +Permission+ - (String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method putBucketNotification(params, callback) @@ -1349,13 +1349,13 @@ AWS.S3 = inherit({}) * notifications. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method putBucketPolicy(params, callback) @@ -1367,13 +1367,13 @@ AWS.S3 = inherit({}) * JSON document. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method putBucketRequestPayment(params, callback) @@ -1388,13 +1388,13 @@ AWS.S3 = inherit({}) * the download and request fees. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method putBucketTagging(params, callback) @@ -1407,13 +1407,13 @@ AWS.S3 = inherit({}) * * +Value+ - (*required*, String) Value of the tag. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method putBucketVersioning(params, callback) @@ -1434,13 +1434,13 @@ AWS.S3 = inherit({}) * displayed on your authentication device. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method putBucketWebsite(params, callback) @@ -1461,13 +1461,13 @@ AWS.S3 = inherit({}) * use when a 4XX class error occurs. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method putObject(params, callback) @@ -1509,7 +1509,7 @@ AWS.S3 = inherit({}) * store with the object in S3. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -1523,7 +1523,7 @@ AWS.S3 = inherit({}) * encryption algorithm used when storing this object in S3. * * +ETag+ - (String) Entity tag for the uploaded object. * * +VersionId+ - (String) Version of the object. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method putObjectAcl(params, callback) @@ -1561,13 +1561,13 @@ AWS.S3 = inherit({}) * write, read ACP, and write ACP permissions on the bucket. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method restoreObject(params, callback) @@ -1580,13 +1580,13 @@ AWS.S3 = inherit({}) * copy in days * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method uploadPart(params, callback) @@ -1601,7 +1601,7 @@ AWS.S3 = inherit({}) * * +Body+ - (String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -1611,7 +1611,7 @@ AWS.S3 = inherit({}) * * +ServerSideEncryption+ - (String) The Server-side * encryption algorithm used when storing this object in S3. * * +ETag+ - (String) Entity tag for the uploaded object. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method uploadPartCopy(params, callback) @@ -1643,7 +1643,7 @@ AWS.S3 = inherit({}) * if it has been modified since the specified time. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -1659,7 +1659,7 @@ AWS.S3 = inherit({}) * object was uploaded. * * +ServerSideEncryption+ - (String) The Server-side * encryption algorithm used when storing this object in S3. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * diff --git a/doc-src/simpleworkflow.docs.js b/doc-src/simpleworkflow.docs.js index aff73aca8b..5dd7368733 100644 --- a/doc-src/simpleworkflow.docs.js +++ b/doc-src/simpleworkflow.docs.js @@ -84,7 +84,7 @@ AWS.SimpleWorkflow = inherit({}) * * +status+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -93,7 +93,7 @@ AWS.SimpleWorkflow = inherit({}) * The +data+ object has the following properties: * * +count+ - (Integer) * * +truncated+ - (Boolean) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method countOpenWorkflowExecutions(params, callback) @@ -120,7 +120,7 @@ AWS.SimpleWorkflow = inherit({}) * * +workflowId+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -129,7 +129,7 @@ AWS.SimpleWorkflow = inherit({}) * The +data+ object has the following properties: * * +count+ - (Integer) * * +truncated+ - (Boolean) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method countPendingActivityTasks(params, callback) @@ -145,7 +145,7 @@ AWS.SimpleWorkflow = inherit({}) * * +name+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -154,7 +154,7 @@ AWS.SimpleWorkflow = inherit({}) * The +data+ object has the following properties: * * +count+ - (Integer) * * +truncated+ - (Boolean) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method countPendingDecisionTasks(params, callback) @@ -170,7 +170,7 @@ AWS.SimpleWorkflow = inherit({}) * * +name+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -179,7 +179,7 @@ AWS.SimpleWorkflow = inherit({}) * The +data+ object has the following properties: * * +count+ - (Integer) * * +truncated+ - (Boolean) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method deprecateActivityType(params, callback) @@ -196,13 +196,13 @@ AWS.SimpleWorkflow = inherit({}) * * +version+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method deprecateDomain(params, callback) @@ -217,13 +217,13 @@ AWS.SimpleWorkflow = inherit({}) * deprecate. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method deprecateWorkflowType(params, callback) @@ -241,13 +241,13 @@ AWS.SimpleWorkflow = inherit({}) * * +version+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method describeActivityType(params, callback) @@ -263,7 +263,7 @@ AWS.SimpleWorkflow = inherit({}) * * +version+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -285,7 +285,7 @@ AWS.SimpleWorkflow = inherit({}) * * +name+ - (String) * * +defaultTaskScheduleToStartTimeout+ - (String) * * +defaultTaskScheduleToCloseTimeout+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method describeDomain(params, callback) @@ -296,7 +296,7 @@ AWS.SimpleWorkflow = inherit({}) * describe. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -310,7 +310,7 @@ AWS.SimpleWorkflow = inherit({}) * * +configuration+ - (Object) * * +workflowExecutionRetentionPeriodInDays+ - (*required*, * String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method describeWorkflowExecution(params, callback) @@ -325,7 +325,7 @@ AWS.SimpleWorkflow = inherit({}) * * +runId+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -361,7 +361,7 @@ AWS.SimpleWorkflow = inherit({}) * * +openChildWorkflowExecutions+ - (Integer) * * +latestActivityTaskTimestamp+ - (Date) * * +latestExecutionContext+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method describeWorkflowType(params, callback) @@ -377,7 +377,7 @@ AWS.SimpleWorkflow = inherit({}) * * +version+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -398,7 +398,7 @@ AWS.SimpleWorkflow = inherit({}) * * +defaultTaskList+ - (Object) * * +name+ - (String) * * +defaultChildPolicy+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method getWorkflowExecutionHistory(params, callback) @@ -427,7 +427,7 @@ AWS.SimpleWorkflow = inherit({}) * in ascending order of the eventTimeStamp of the events. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -766,7 +766,7 @@ AWS.SimpleWorkflow = inherit({}) * Integer) * * +control+ - (String) * * +nextPageToken+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method listActivityTypes(params, callback) @@ -798,7 +798,7 @@ AWS.SimpleWorkflow = inherit({}) * activity types. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -814,7 +814,7 @@ AWS.SimpleWorkflow = inherit({}) * * +creationDate+ - (Date) * * +deprecationDate+ - (Date) * * +nextPageToken+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method listClosedWorkflowExecutions(params, callback) @@ -869,7 +869,7 @@ AWS.SimpleWorkflow = inherit({}) * the executions. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -893,7 +893,7 @@ AWS.SimpleWorkflow = inherit({}) * * +tagList+ - (Array) * * +cancelRequested+ - (Boolean) * * +nextPageToken+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method listDomains(params, callback) @@ -917,7 +917,7 @@ AWS.SimpleWorkflow = inherit({}) * domains. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -929,7 +929,7 @@ AWS.SimpleWorkflow = inherit({}) * * +status+ - (String) * * +description+ - (String) * * +nextPageToken+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method listOpenWorkflowExecutions(params, callback) @@ -970,7 +970,7 @@ AWS.SimpleWorkflow = inherit({}) * * +workflowId+ - (*required*, String) * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -994,7 +994,7 @@ AWS.SimpleWorkflow = inherit({}) * * +tagList+ - (Array) * * +cancelRequested+ - (Boolean) * * +nextPageToken+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method listWorkflowTypes(params, callback) @@ -1022,7 +1022,7 @@ AWS.SimpleWorkflow = inherit({}) * workflow types. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -1038,7 +1038,7 @@ AWS.SimpleWorkflow = inherit({}) * * +creationDate+ - (Date) * * +deprecationDate+ - (Date) * * +nextPageToken+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method pollForActivityTask(params, callback) @@ -1063,7 +1063,7 @@ AWS.SimpleWorkflow = inherit({}) * problems arise. The form of this identity is user defined. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -1080,7 +1080,7 @@ AWS.SimpleWorkflow = inherit({}) * * +name+ - (String) * * +version+ - (String) * * +input+ - (String) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method pollForDecisionTask(params, callback) @@ -1113,7 +1113,7 @@ AWS.SimpleWorkflow = inherit({}) * in ascending order of the eventTimestamp of the events. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -1461,7 +1461,7 @@ AWS.SimpleWorkflow = inherit({}) * * +control+ - (String) * * +nextPageToken+ - (String) * * +previousStartedEventId+ - (Integer) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method recordActivityTaskHeartbeat(params, callback) @@ -1480,7 +1480,7 @@ AWS.SimpleWorkflow = inherit({}) * about the progress of the task. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -1488,7 +1488,7 @@ AWS.SimpleWorkflow = inherit({}) * the request. Set to +null+ if a request error occurs. * The +data+ object has the following properties: * * +cancelRequested+ - (Boolean) - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method registerActivityType(params, callback) @@ -1536,13 +1536,13 @@ AWS.SimpleWorkflow = inherit({}) * activity task using the ScheduleActivityTask Decision. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method registerDomain(params, callback) @@ -1562,13 +1562,13 @@ AWS.SimpleWorkflow = inherit({}) * retained at all. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method registerWorkflowType(params, callback) @@ -1610,13 +1610,13 @@ AWS.SimpleWorkflow = inherit({}) * policies are: * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method requestCancelWorkflowExecution(params, callback) @@ -1635,13 +1635,13 @@ AWS.SimpleWorkflow = inherit({}) * to cancel. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method respondActivityTaskCanceled(params, callback) @@ -1655,13 +1655,13 @@ AWS.SimpleWorkflow = inherit({}) * cancellation. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method respondActivityTaskCompleted(params, callback) @@ -1674,13 +1674,13 @@ AWS.SimpleWorkflow = inherit({}) * is a free form string that is implementation specific. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method respondActivityTaskFailed(params, callback) @@ -1695,13 +1695,13 @@ AWS.SimpleWorkflow = inherit({}) * about the failure. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method respondDecisionTaskCompleted(params, callback) @@ -1791,13 +1791,13 @@ AWS.SimpleWorkflow = inherit({}) * add to workflow execution. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method signalWorkflowExecution(params, callback) @@ -1820,13 +1820,13 @@ AWS.SimpleWorkflow = inherit({}) * execution's history. * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method startWorkflowExecution(params, callback) @@ -1879,7 +1879,7 @@ AWS.SimpleWorkflow = inherit({}) * RegisterWorkflowType. The supported child policies are: * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. @@ -1889,7 +1889,7 @@ AWS.SimpleWorkflow = inherit({}) * * +runId+ - (String) The runId of a workflow execution. * This Id is generated by the service and can be used to uniquely * identify the workflow execution within a domain. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * @!method terminateWorkflowExecution(params, callback) @@ -1917,13 +1917,13 @@ AWS.SimpleWorkflow = inherit({}) * are: * @callback callback function(err, data) * Called when a response from the service is returned. If a - * callback is not supplied, you must call {AWS.AWSRequest.send} + * callback is not supplied, you must call {AWS.Request.send} * on the returned request object to initiate the request. * @param err [Object] the error object returned from the request. * Set to +null+ if the request is successful. * @param data [Object] the de-serialized data returned from * the request. Set to +null+ if a request error occurs. - * @return [AWS.AWSRequest] a handle to the operation request for + * @return [AWS.Request] a handle to the operation request for * subsequent event callback registration. * * diff --git a/features/support/helpers.js b/features/support/helpers.js index ad186aa1a2..566d93312f 100644 --- a/features/support/helpers.js +++ b/features/support/helpers.js @@ -71,7 +71,7 @@ module.exports = { * finish execution before moving onto the next step in the scenario. */ request: function request(svc, operation, params, next) { - this[svc][operation](params).always(function (resp) { + this[svc][operation](params).on('complete', function (resp) { if (resp.error) { this.unexpectedError(resp, next); } else { diff --git a/lib/client.js b/lib/client.js index d57bec2984..fb08257c80 100644 --- a/lib/client.js +++ b/lib/client.js @@ -14,6 +14,7 @@ */ var AWS = require('./core'); +require('./event_listeners'); var inherit = AWS.util.inherit; /** @@ -39,65 +40,37 @@ AWS.Client = inherit({ callback = params; params = {}; } - var request = new AWS.AWSRequest(this, operation, params); + + var request = new AWS.Request(this, operation, params); + this.addAllRequestListeners(request); + if (callback) { - request.always(function (resp) { + request.on('complete', function (resp) { callback.call(resp, resp.error, resp.data); }); request.send(); } - return request; - }, - parseResponse: function parseResponse(httpResponse, method, callback) { - var error = null, data = null; - try { - if (this.successfulResponse(httpResponse, method)) { - data = this.extractData(httpResponse, method); - } else { - error = this.extractError(httpResponse, method); - error.statusCode = httpResponse.statusCode; - error.retryable = this.retryableError(error, method); - } - } catch (err) { - // unrecoverable error trying to parse the response data/error - error = err; - } - callback.call(this, error, data); + return request; }, - successfulResponse: function successfulResponse(httpResponse) { - return httpResponse.statusCode < 300; + addAllRequestListeners: function addAllRequestListeners(request) { + request.addListeners(AWS.EventListeners.Core); + request.addListeners(AWS.EventListeners.Http); + this.setupRequestListeners(request); }, - newHttpRequest: function newHttpRequest() { - - var serviceName = this.serviceName; - var credentials = AWS.util.copy(this.config.credentials); - var signatureVersion = this.signatureVersion; - - var httpRequest = new AWS.HttpRequest(); - httpRequest.endpoint = AWS.util.copy(this.endpoint); - httpRequest.region = this.config.region; - httpRequest.sign = function sign() { - /*jshint newcap:false*/ - var signer = new signatureVersion(httpRequest, serviceName); - signer.addAuthorization(credentials); - }; - - return httpRequest; - + setupRequestListeners: function setupRequestListeners() { }, - retryableError: function retryableError(error) { - if (this.expiredCredentialsError(error)) return true; - if (this.throttledError(error)) return true; - if (error.statusCode >= 500) return true; - return false; + successfulResponse: function successfulResponse(resp) { + return resp.httpResponse.statusCode < 300; }, - // How many times a failed request should be retried before giving up. - // the defaultRetryCount can be overriden by client classes. + /** + * How many times a failed request should be retried before giving up. + * the defaultRetryCount can be overriden by client classes. + */ numRetries: function numRetries() { if (this.config.maxRetries !== undefined) { return this.config.maxRetries; @@ -115,6 +88,18 @@ AWS.Client = inherit({ return delays; }, + retryableError: function retryableError(error) { + if (this.networkingError(error)) return true; + if (this.expiredCredentialsError(error)) return true; + if (this.throttledError(error)) return true; + if (error.statusCode >= 500) return true; + return false; + }, + + networkingError: function networkingError(error) { + return error.code == 'NetworkingError'; + }, + expiredCredentialsError: function expiredCredentialsError(error) { // TODO : this only handles *one* of the expired credential codes return (error.code === 'ExpiredTokenException'); diff --git a/lib/core.js b/lib/core.js index ba4af8c21d..2d650ad1fb 100644 --- a/lib/core.js +++ b/lib/core.js @@ -36,6 +36,11 @@ AWS.util.update(AWS, { } }), + /** + * @api private + */ + ServiceInterface: {}, + /** * @api private */ @@ -45,6 +50,8 @@ AWS.util.update(AWS, { require('./config'); require('./http'); -require('./promise'); +require('./event_emitter'); +require('./event_listeners'); +require('./request'); require('./client'); require('./service'); diff --git a/lib/event_emitter.js b/lib/event_emitter.js new file mode 100644 index 0000000000..492a43136a --- /dev/null +++ b/lib/event_emitter.js @@ -0,0 +1,51 @@ +/** + * Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You + * may not use this file except in compliance with the License. A copy of + * the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ + +var AWS = require('./core'); +var EventEmitter = require('events').EventEmitter; + +AWS.EventEmitter = AWS.util.inherit(EventEmitter, { + constructor: function AWSEventEmitter() { + EventEmitter.call(this); + }, + + addListeners: function addListeners(listeners) { + var self = this; + + // extract listeners if parameter is an EventEmitter object + if (listeners._events) listeners = listeners._events; + + AWS.util.each(listeners, function(event, callbacks) { + if (callbacks instanceof Function) callbacks = [callbacks]; + AWS.util.arrayEach(callbacks, function(callback) { + self.on(event, callback); + }); + }); + }, + + addNamedListener: function addNamedListener(name, eventName, callback) { + this[name] = callback; + this.addListener(eventName, callback); + return this; + }, + + addNamedListeners: function addNamedListeners(callback) { + var self = this; + callback(function() { + self.addNamedListener.apply(self, arguments); + }); + return this; + } +}); diff --git a/lib/event_listeners.js b/lib/event_listeners.js new file mode 100644 index 0000000000..6d7c3c8f88 --- /dev/null +++ b/lib/event_listeners.js @@ -0,0 +1,179 @@ +/** + * Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You + * may not use this file except in compliance with the License. A copy of + * the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ + +var AWS = require('./core'); +require('./event_emitter'); +require('./service_interface/json'); +require('./service_interface/query'); +require('./service_interface/rest'); +require('./service_interface/rest_xml'); + +/** + * @api private + */ +AWS.EventListeners = { + Core: new AWS.EventEmitter().addNamedListeners(function(add) { + add('VALIDATE_CREDENTIALS', 'validate', function VALIDATE_CREDENTIALS() { + if (!this.client.config.credentials.accessKeyId || + !this.client.config.credentials.secretAccessKey) { + throw AWS.util.error(new Error(), + {code: 'SigningError', message: 'Missing credentials in config'}); + } + }); + + add('VALIDATE_REGION', 'validate', function VALIDATE_REGION() { + if (!this.client.config.region) { + throw AWS.util.error(new Error(), + {code: 'SigningError', message: 'Missing region in config'}); + } + }); + + add('SIGN', 'sign', function SIGN() { + var date = AWS.util.date.getDate(); + var credentials = AWS.util.copy(this.client.config.credentials); + var SignerClass = this.client.signatureVersion; + var signer = new SignerClass(this.httpRequest, this.client.serviceName); + + // clear old authorization headers + delete this.httpRequest.headers['Authorization']; + delete this.httpRequest.headers['Date']; + delete this.httpRequest.headers['X-Amz-Date']; + + // add new authorization + signer.addAuthorization(credentials, date); + }); + + add('SETUP_ERROR', 'extractError', function SETUP_ERROR(resp) { + if (this.client.successfulResponse(resp, this)) { + // throwing null will stop the error extraction chain + // but will not set an error for data extraction + throw null; + } + + resp.error = AWS.util.error(new Error(), + {code: 'UnknownError', message: 'An unknown error occurred.'}); + resp.data = null; + }); + + add('SETUP_DATA', 'extractData', function SETUP_DATA(resp) { + resp.data = {}; + resp.error = null; + }); + }), + + Http: new AWS.EventEmitter().addNamedListeners(function(add) { + add('SEND', 'send', function SEND(resp) { + AWS.HttpClient.getInstance().handleRequest(this, resp); + }); + + add('HTTP_HEADERS', 'httpHeaders', + function HTTP_HEADERS(statusCode, headers, resp) { + resp.httpResponse.statusCode = statusCode; + resp.httpResponse.headers = headers; + resp.httpResponse.body = ''; + }); + + add('HTTP_DATA', 'httpData', function HTTP_DONE(chunk, resp) { + resp.httpResponse.body += chunk; + }); + + add('HTTP_DONE', 'httpDone', function HTTP_DONE(resp) { + this.completeRequest(resp); + }); + + add('HTTP_ERROR', 'httpError', function HTTP_ERROR(error, resp) { + resp.error = error; + this.completeRequest(resp); + }); + + add('FINALIZE_ERROR', 'retry', function FINALIZE_ERROR(resp) { + resp.error.statusCode = resp.httpResponse.statusCode; + if (resp.error.retryable === undefined) { + resp.error.retryable = this.client.retryableError(resp.error, this); + } + }); + + add('REDIRECT', 'retry', function REDIRECT(resp) { + if (resp.error && resp.error.statusCode == 307) { + this.httpRequest.endpoint = + new AWS.Endpoint(resp.httpResponse.headers['location']); + resp.error.retryable = true; + } + }); + + add('RETRY_CHECK', 'retry', function RETRY_CHECK(resp) { + if (resp.error) { + if (!resp.error.retryable) + throw resp.error; + if (resp.retryCount >= this.client.numRetries()) + throw resp.error; + + resp.retryCount++; + } + }); + + add('RETRY_SIGN', 'retry', function RETRY_SIGN(resp) { + this.emitEvents(resp, 'sign'); + }); + + add('RETRY_DELAY_SEND', 'retry', function RETRY_DELAY_SEND(resp) { + var delay = 0; + if (resp.error) { + delay = this.client.retryDelays()[resp.retryCount-1] || 0; + } + + resp.error = null; + resp.data = null; + + setTimeout(function() { resp.request.emitEvents(resp, 'send'); }, delay); + }); + + // Allow streaming by disabling HTTP_DATA callback if + // any httpData event is added + add('DISABLE_HTTP_DATA', 'newListener', function DISABLE_HTTP_DATA(event) { + if (event === 'httpData') { + this.removeListener('httpData', AWS.EventListeners.Http.HTTP_DATA); + } + }); + }), + + Json: new AWS.EventEmitter().addNamedListeners(function(add) { + var svc = AWS.ServiceInterface.Json; + add('BUILD', 'build', svc.buildRequest); + add('EXTRACT_DATA', 'extractData', svc.extractData); + add('EXTRACT_ERROR', 'extractError', svc.extractError); + }), + + Rest: new AWS.EventEmitter().addNamedListeners(function(add) { + var svc = AWS.ServiceInterface.Rest; + add('BUILD', 'build', svc.buildRequest); + add('EXTRACT_DATA', 'extractData', svc.extractData); + add('EXTRACT_ERROR', 'extractError', svc.extractError); + }), + + RestXml: new AWS.EventEmitter().addNamedListeners(function(add) { + var svc = AWS.ServiceInterface.RestXml; + add('BUILD', 'build', svc.buildRequest); + add('EXTRACT_DATA', 'extractData', svc.extractData); + add('EXTRACT_ERROR', 'extractError', svc.extractError); + }), + + Query: new AWS.EventEmitter().addNamedListeners(function(add) { + var svc = AWS.ServiceInterface.Query; + add('BUILD', 'build', svc.buildRequest); + add('EXTRACT_DATA', 'extractData', svc.extractData); + add('EXTRACT_ERROR', 'extractError', svc.extractError); + }) +}; diff --git a/lib/http.js b/lib/http.js index 3b598a4942..d9634d7284 100644 --- a/lib/http.js +++ b/lib/http.js @@ -97,14 +97,14 @@ AWS.HttpRequest = inherit({ /** * @api private */ - constructor: function HttpRequest() { + constructor: function HttpRequest(endpoint, region) { this.method = 'POST'; this.path = '/'; this.headers = {}; this.headers['User-Agent'] = AWS.util.userAgent(); - this.body = undefined; - this.endpoint = undefined; - this.region = undefined; + this.body = ''; + this.endpoint = AWS.util.copy(endpoint); + this.region = region; }, /** @@ -142,140 +142,9 @@ AWS.HttpResponse = inherit({ * @api private */ constructor: function HttpResponse() { - this.statusCode = null; + this.statusCode = undefined; this.headers = {}; - this.body = null; - } -}); - -/** - * @api private - */ -AWS.RequestHandler = inherit({ - - constructor: function RequestHandler(awsRequest) { - this.awsRequest = awsRequest; - this.awsResponse = awsRequest.awsResponse; - this.client = awsRequest.client; - this.operation = awsRequest.operation; - this.params = awsRequest.params; - }, - - makeRequest: function makeRequest() { - if (!this.client.config.credentials.accessKeyId || - !this.client.config.credentials.secretAccessKey) { - return this.handleError(AWS.util.error(new Error(), { - code: 'SigningError', message: 'Missing credentials in config' - })); - } - else if (!this.client.config.region) { - return this.handleError(AWS.util.error(new Error(), { - code: 'SigningError', message: 'Missing region in config' - })); - } - - var httpRequest = this.client.buildRequest(this.operation, this.params); - httpRequest.sign(); - this.sendRequest(httpRequest); - }, - - sendRequest: function sendRequest(httpRequest) { - - var httpResponse = new AWS.HttpResponse(); - - this.awsResponse.httpResponse = httpResponse; - this.awsResponse.httpRequest = httpRequest; - - var self = this; - AWS.HttpClient.getInstance().handleRequest(httpRequest, { - - onHeaders: function onHeaders(statusCode, headers) { - httpResponse.statusCode = statusCode; - httpResponse.headers = headers; - }, - - onData: function onData(data) { - if (httpResponse.body === null) { - httpResponse.body = data; - } else { - httpResponse.body += data; - } - self.handleHttpData(httpResponse); - }, - - onEnd: function onEnd() { - - if (httpResponse.body) { - httpResponse.body = httpResponse.body.toString(); - } - - if (httpResponse.statusCode == 307) { - self.redirect(httpRequest, httpResponse); - } else { - self.handleHttpResponse(httpResponse); - } - - }, - - onError: function onError(error) { - self.retryRequest(error); - } - - }); - - }, - - handleHttpData: function handleHttpData(httpResponse) { - if (this.awsRequest.callbacks.data.length === 0) return; - this.awsRequest.notifyData(httpResponse.body); - httpResponse.body = null; - }, - - // Called when the http client returns a response. Successfull http requests - // may still contain an error (e.g. 400, 500, etc) - redirect: function redirect(httpRequest, httpResponse) { - /*jshint sub:true */ - httpRequest.endpoint = new AWS.Endpoint(httpResponse.headers['location']); - this.sendRequest(httpRequest); - }, - - handleHttpResponse: function handleHttpResponse(httpResponse) { - var self = this; - this.client.parseResponse(httpResponse, this.operation, function (error, data) { - if (error) - self.handleError(error); - else - self.awsRequest.notifyDone(data); - }); - }, - - handleError: function handleError(error) { - if (error.retryable) { - this.retryRequest(error); - } else { - this.awsRequest.notifyFail(error); - } - }, - - retryRequest: function retryRequest(error) { - - var delays = this.client.retryDelays(); - var delay = delays[this.awsResponse.retryCount]; - - this.awsResponse.retryCount += 1; - - if (delay !== undefined) { - - var self = this; - setTimeout(function delayRetry() { - self.makeRequest(); - }, delay); - - } else { - - this.awsRequest.notifyFail(error); // retried too many times - - } + this.body = undefined; } }); @@ -286,15 +155,15 @@ AWS.NodeHttpClient = inherit({ constructor: function NodeHttpClient() {}, - handleRequest: function handleRequest(request, callbacks) { + handleRequest: function handleRequest(request, response) { var options = { - host: request.endpoint.hostname, - port: request.endpoint.port, - method: request.method, - headers: request.headers, - path: request.path + host: request.httpRequest.endpoint.hostname, + port: request.httpRequest.endpoint.port, + method: request.httpRequest.method, + headers: request.httpRequest.headers, + path: request.httpRequest.path }; - var useSSL = request.endpoint.protocol === 'https:'; + var useSSL = request.httpRequest.endpoint.protocol === 'https:'; var client = useSSL ? require('https') : require('http'); if (useSSL) { @@ -303,21 +172,34 @@ AWS.NodeHttpClient = inherit({ options.agent = new client.Agent(options); } - var req = client.request(options, function onResponse(resp) { - callbacks.onHeaders(resp.statusCode, resp.headers); - resp.on('data', callbacks.onData); - resp.on('end', callbacks.onEnd); - }); + var req = this.setupEvents(client, options, request, response); + if (request.httpRequest.body) { + req.write(request.httpRequest.body); + } + req.end(); + }, - if (request.body) req.write(request.body); + setupEvents: function setupEvents(client, options, request, response) { + var req = client.request(options, function onResponse(httpResponse) { + request.emit('httpHeaders', httpResponse.statusCode, + httpResponse.headers, response, request); - req.end(); - req.on('error', function (e) { - callbacks.onError(AWS.util.error( - new Error(e.message), {code: 'NetworkingError'})); + httpResponse.on('data', function onData(data) { + request.emit('httpData', data, response, request); + }); + + httpResponse.on('end', function onEnd() { + request.emitEvents(response, 'httpDone'); + }); }); - } + req.on('error', function (err) { + request.emit('httpError', AWS.util.error(err, + {code: 'NetworkingError', retryable: true})); + }); + + return req; + } }); /** diff --git a/lib/promise.js b/lib/promise.js deleted file mode 100644 index 9c9b5e43c6..0000000000 --- a/lib/promise.js +++ /dev/null @@ -1,422 +0,0 @@ -/** - * Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You - * may not use this file except in compliance with the License. A copy of - * the License is located at - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is - * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF - * ANY KIND, either express or implied. See the License for the specific - * language governing permissions and limitations under the License. - */ - -var AWS = require('./core'); -var inherit = AWS.util.inherit; - -/** - * == Asynchronous Requests - * - * All requests made through the SDK are asynchronous and use a - * callback interface. Each service method that kicks off a request - * returns an +AWS.AWSRequest+ object that you can use to register - * callbacks. - * - * For example, the following service method returns the request - * object as "request", which can be used to register callbacks: - * - * // request is an AWS.AWSRequest object - * var request = ec2.client.describeInstances(); - * - * // register callbacks on request to retrieve response data - * request.done(function(response) { - * console.log(response.data); - * }); - * - * When a request is ready to be sent, the {send} method should - * be called: - * - * request.send(); - * - * == Multiple Callbacks and Chaining - * - * You can register multiple callbacks on any request object. The - * callbacks can be registered for different events, or all for the - * same event. In addition, you can chain callback registration, for - * example: - * - * request. - * done(function(response) { - * console.log("Success!"); - * }). - * fail(function(response) { - * console.log("Error!"); - * }). - * always(function(response) { - * console.log("Always!"); - * }). - * send(); - * - * The above example will print either "Success! Always!", or "Error! Always!", - * depending on whether the request succeeded or not. - * - * == Binding Custom Context Data on a Callback - * - * By default, the +this+ context of a callback function registered on an - * event will be the {AWS.AWSResponse} object returned from the service. - * In some cases, it may be necessary to pass extra custom context to these - * functions; in these cases, you can bind a custom value to be used as the - * +this+ context object when the callbacks are executed. To do so, pass - * the +bind+ option to the asynchronous registration method: - * - * var myContext = new Object(); - * request.always(function(response) { - * console.log(this === myContext); - * }, {bind: myContext}).send(); - * - * The above callback will print +true+ when the callback function is executed. - * - * @see AWS.AWSResponse - */ -AWS.AWSRequest = inherit({ - - /** - * @api private - */ - constructor: function AWSRequest(client, operation, params) { - this.client = client; - this.operation = operation; - this.params = params; - this.awsResponse = new AWS.AWSResponse(this); - this.state = null; - this.callbacks = { data: [], done: [], fail: [], always: [] }; - }, - - /** - * @!group Sending a Request - */ - - /** - * Initiates sending of the given request object. - * - * @example Sending a request - * request = s3.client.putObject({Bucket: 'bucket', Key: 'key'}); - * request.done(function(resp) { ... }); // register a callback - * request.send(); - */ - send: function send() { - new AWS.RequestHandler(this).makeRequest(); - }, - - /** - * @!group Registering Callbacks - */ - - /** - * This event is used to stream response data from the - * service packet-by-packet. This event is mostly used for large responses, - * when it is inefficient (or impossible) to load the entire response into - * memory. - * - * === Example: Registering a +data+ callback - * - * s3.client.getObject({Bucket: b, Key: k}).data(function(resp) { - * console.log(resp.data); - * }).send(); - * - * Prints: - * - * - * - * ... - * - * @note If you register a +data+ callback, - * +response.data+ will not contain serialized output - * for the entire request. Instead, it will be your responsibility - * to stream the output and de-serialize the result on your own. - * @callback callback function(response) - * @param response [AWS.AWSResponse] the response object containing the - * current packet in the +data+ property. - * @option options bind [Object] (response object) an object to bind - * the callback function to. Defaults to the response object. - * @return [AWS.AWSRequest] The same object, for chaining. - */ - data: function data(callback, options) { - if (this.awsResponse.data !== null) { - this.call(callback, options); - } - this.callbacks.data.push([callback, options]); - return this; - }, - - /** - * This event registers a callback to be called when a successful response - * from the server is returned. The response contains a - * {AWS.AWSResponse.data data} field with the serialized response data - * from the service. - * - * === Example: Registering a +done+ callback - * - * s3.client.listBuckets().done(function(response) { - * console.log(response.data); - * }).send(); - * - * Prints: - * - * { Owner: { ID: '...', DisplayName: '...' }, - * Buckets: - * [ { Name: 'someBucketName', CreationDate: someCreationDate }, - * { Name: 'otherBucketName', CreationDate: otherCreationDate } ], - * RequestId: '...' } - * - * @option options bind [Object] (response object) an object to bind - * the callback function to. Defaults to the response object. - * @callback callback function(response) - * @param response [AWS.AWSResponse] the response object containing the - * de-serialized response contents in the +data+ property. - * @note The +response.error+ property will be +null+ when this callback - * is called. - * @return [AWS.AWSRequest] The same object, for chaining. - */ - done: function done(callback, options) { - if (this.state === 'done') { - this.call(callback, options); - } else if (this.state === null) { - this.callbacks.done.push([callback, options]); - } - return this; - }, - - /** - * This event registers a callback to be called when a failure response - * from the server is returned, or if a networking error occurs. - * The response contains a {AWS.AWSResponse.error data} field with the - * error data from the service. - * - * === Example: Registering a +fail+ callback - * - * s3.config.credentials.accessKeyId = 'invalid'; - * s3.client.listBuckets().fail(function(response) { - * console.log(response.error); - * }).send(); - * - * Prints: - * - * { code: 'Forbidden', message: null } - * - * @option options bind [Object] (response object) an object to bind - * the callback function to. Defaults to the response object. - * @callback callback function(response) - * @param response [AWS.AWSResponse] the response object containing the - * service error details in the +error+ property. - * @note The +response.data+ property will be +null+ when this callback - * is called. - * @return [AWS.AWSRequest] The same object, for chaining. - */ - fail: function fail(callback, options) { - if (this.state === 'fail') { - this.call(callback, options); - } else if (this.state === null) { - this.callbacks.fail.push([callback, options]); - } - return this; - }, - - /** - * This event registers a callback to be called in any final state of a - * request, i.e., both {done} and {fail}. Use this callback to handle any - * request cleanup that must be executed regardless of the success state. - * - * Note that if you do intend to use response data inside of this callback, - * you must check for the presence of +response.data+ or +response.error+ - * before attempting to access either property. For example: - * - * request.always(function(response) { - * if (response.error) { - * // an error occurred, handle it - * } else { - * // we can use response.data here - * } - * }).send(); - * - * @option options bind [Object] (response object) an object to bind - * the callback function to. Defaults to the response object. - * @callback callback function(response) - * @param response [AWS.AWSResponse] the response object containing the - * de-serialized response contents in the +data+ property or the - * error information in the +error+ property, depending on the result. - * @return [AWS.AWSRequest] The same object, for chaining. - */ - always: function always(callback, options) { - if (this.state) { - this.call(callback, options); - } else { - this.callbacks.always.push([callback, options]); - } - return this; - }, - - /** - * @api private - */ - notifyData: function notifyData(data) { - this.awsResponse.data = data; - this.notify('data', this.callbacks.data); - }, - - /** - * @api private - */ - notifyDone: function notifyDone(data) { - this.awsResponse.data = data; - this.notify('done', this.callbacks.done.concat(this.callbacks.always)); - }, - - /** - * @api private - */ - notifyFail: function notifyFail(error) { - this.awsResponse.error = error; - this.notify('fail', this.callbacks.fail.concat(this.callbacks.always)); - }, - - /** - * @api private - */ - notify: function notify(state, callbacks) { - AWS.util.arrayEach.call(this, callbacks, function iterator(cb) { - this.call(cb[0], cb[1]); - }); - this.state = state; - }, - - /** - * @api private - */ - call: function call(callback, options) { - var binding = this.awsResponse; - if (options && options.bind) { - binding = options.bind; - } - callback.call(binding, this.awsResponse); - } - -}); - -/** - * This class encapsulates the the response information - * from a service request operation sent through {AWS.AWSRequest}. - * The response object has two main properties for getting information - * back from a request: - * - * == The +data+ property - * - * The +response.data+ property contains the serialized object data - * retrieved from the service request. For instance, for an - * Amazon DynamoDB +listTables+ method call, the response data might - * look like: - * - * > resp.data - * { TableNames: - * [ 'table1', 'table2', ... ] } - * - * The +data+ property can be null if an error occurs (see below). - * - * == The +error+ property - * - * In the event of a service error (or transfer error), the - * +response.error+ property will be filled with the given - * error data in the form: - * - * { code: 'SHORT_UNIQUE_ERROR_CODE', - * message: 'Some human readable error message' } - * - * In the case of an error, the +data+ property will be +null+. - * Note that if you handle events that can be in a failure state, - * you should always check whether +response.error+ is set - * before attempting to access the +response.data+ property. - * - * @!attribute data - * @readonly - * @group Data Properties - * @note Inside of a {AWS.AWSRequest.data} event, this - * property contains a single raw packet instead of the - * full de-serialized service response. - * @return [Object] the de-serialized response data - * from the service. - * - * @!attribute error - * An structure containing information about a service - * or networking error. - * @readonly - * @group Data Properties - * @note This attribute is only filled if a service or - * networking error occurs. - * @return [Object] - * * code [String] a unique short code representing the - * error that was emitted. - * * message [String] a longer human readable error message - * * retryable [Boolean] whether the error message is - * retryable. - * - * @!attribute client - * @readonly - * @group Operation Properties - * @return [AWS.Client] The low-level service client object - * that initiated the request. - * - * @!attribute operation - * @readonly - * @group Operation Properties - * @return [String] the name of the operation executed on - * the service. - * - * @!attribute params - * @readonly - * @group Operation Properties - * @return [Object] the parameters sent in the request to - * the service. - * - * @!attribute retryCount - * @readonly - * @group Operation Properties - * @return [Integer] the number of retries that have were - * attempted before the request was completed. - * - * @!attribute httpRequest - * @readonly - * @group HTTP Properties - * @return [AWS.HttpRequest] the raw HTTP request object - * containing request headers and body information - * sent by the client. - * - * @!attribute httpResponse - * @readonly - * @group HTTP Properties - * @return [AWS.HttpResponse] the raw HTTP response object - * containing the response headers and body information - * from the server. - * - * @see AWS.AWSRequest - */ -AWS.AWSResponse = inherit({ - - /** - * @api private - */ - constructor: function AWSResponse(request) { - - this.request = request; - - this.data = null; - this.error = null; - this.retryCount = 0; - - this.httpRequest = null; - this.httpResponse = null; - - } - -}); diff --git a/lib/request.js b/lib/request.js new file mode 100644 index 0000000000..fa7f00d1e9 --- /dev/null +++ b/lib/request.js @@ -0,0 +1,291 @@ +/** + * Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You + * may not use this file except in compliance with the License. A copy of + * the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ + +var AWS = require('./core'); +var inherit = AWS.util.inherit; + +/** + * == Asynchronous Requests + * + * All requests made through the SDK are asynchronous and use a + * callback interface. Each service method that kicks off a request + * returns an +AWS.Request+ object that you can use to register + * callbacks. + * + * For example, the following service method returns the request + * object as "request", which can be used to register callbacks: + * + * // request is an AWS.Request object + * var request = ec2.client.describeInstances(); + * + * // register callbacks on request to retrieve response data + * request.on('success', function(response) { + * console.log(response.data); + * }); + * + * When a request is ready to be sent, the {send} method should + * be called: + * + * request.send(); + * + * == Multiple Callbacks and Chaining + * + * You can register multiple callbacks on any request object. The + * callbacks can be registered for different events, or all for the + * same event. In addition, you can chain callback registration, for + * example: + * + * request. + * on('success', function(response) { + * console.log("Success!"); + * }). + * on('error', function(response) { + * console.log("Error!"); + * }). + * on('complete', function(response) { + * console.log("Always!"); + * }). + * send(); + * + * The above example will print either "Success! Always!", or "Error! Always!", + * depending on whether the request succeeded or not. + * + * == Binding Custom Context Data on a Callback + * + * By default, the +this+ context of a callback function registered on an + * event will be the {AWS.Response} object returned from the service. + * In some cases, it may be necessary to pass extra custom context to these + * functions; in these cases, you can bind a custom value to be used as the + * +this+ context object when the callbacks are executed. To do so, pass + * the +bind+ option to the asynchronous registration method: + * + * var myContext = new Object(); + * request.always(function(response) { + * console.log(this === myContext); + * }, {bind: myContext}).send(); + * + * The above callback will print +true+ when the callback function is executed. + * + * @see AWS.Response + */ +AWS.Request = inherit({ + + /** + * @api private + */ + constructor: function Request(client, operation, params) { + var endpoint = client.endpoint; + var region = client.config.region; + + this.client = client; + this.operation = operation; + this.params = params || {}; + this.httpRequest = new AWS.HttpRequest(endpoint, region); + }, + + /** + * @!group Sending a Request + */ + + /** + * @overload send() + * Initiates sending of the given request object. + * + * @example Sending a request + * request = s3.client.putObject({Bucket: 'bucket', Key: 'key'}); + * request.on('complete', function(req, resp) { ... }); // register a callback + * request.send(); + */ + send: function send(response) { + response = response || new AWS.Response(this); + this.emitEvents(response, 'validate', 'build', 'sign', 'send'); + if (response.error) this.completeRequest(response); + return response; + }, + + /** + * @api private + */ + completeRequest: function completeRequest(response) { + this.emitEvents(response, 'extractError', 'extractData'); + + if (response.error) { + this.emitEventsAlways(response, 'retry'); + if (!response.error) return; + this.emitEventsAlways(response, 'error'); + } else { + this.emitEvents(response, 'success'); + } + this.emitEventsAlways(response, 'complete'); + }, + + /** + * @api private + */ + emitEventsAlways: function emitEventsAlways() { + var response = arguments[0]; + for (var i = 1; i < arguments.length; i++) { + var eventName = arguments[i]; + if (this.listeners(eventName).length > 0) { + this.emitEvent(eventName, response); + } + } + }, + + /** + * @api private + */ + emitEvents: function emitEvents() { + var response = arguments[0]; + if (response.error) return; + for (var i = 1; i < arguments.length; i++) { + var eventName = arguments[i]; + if (response.error) return AWS.util.abort; + if (this.listeners(eventName).length > 0) { + this.emitEvent(eventName, response); + } + } + }, + + /** + * @api private + */ + emitEvent: function emitEvent(eventName, response) { + try { + this.emit(eventName, eventName == 'build' ? this : response); + } catch (err) { + response.error = err; + } + } +}); + +/*jshint forin:false*/ +for (var prop in AWS.EventEmitter.prototype) { + var fn = AWS.EventEmitter.prototype[prop]; + if (fn instanceof Function && prop != 'constructor') { + AWS.Request.prototype[prop] = fn; + } +} + +/** + * This class encapsulates the the response information + * from a service request operation sent through {AWS.Request}. + * The response object has two main properties for getting information + * back from a request: + * + * == The +data+ property + * + * The +response.data+ property contains the serialized object data + * retrieved from the service request. For instance, for an + * Amazon DynamoDB +listTables+ method call, the response data might + * look like: + * + * > resp.data + * { TableNames: + * [ 'table1', 'table2', ... ] } + * + * The +data+ property can be null if an error occurs (see below). + * + * == The +error+ property + * + * In the event of a service error (or transfer error), the + * +response.error+ property will be filled with the given + * error data in the form: + * + * { code: 'SHORT_UNIQUE_ERROR_CODE', + * message: 'Some human readable error message' } + * + * In the case of an error, the +data+ property will be +null+. + * Note that if you handle events that can be in a failure state, + * you should always check whether +response.error+ is set + * before attempting to access the +response.data+ property. + * + * @!attribute data + * @readonly + * @group Data Properties + * @note Inside of a {AWS.Request.data} event, this + * property contains a single raw packet instead of the + * full de-serialized service response. + * @return [Object] the de-serialized response data + * from the service. + * + * @!attribute error + * An structure containing information about a service + * or networking error. + * @readonly + * @group Data Properties + * @note This attribute is only filled if a service or + * networking error occurs. + * @return [Object] + * * code [String] a unique short code representing the + * error that was emitted. + * * message [String] a longer human readable error message + * * retryable [Boolean] whether the error message is + * retryable. + * + * @!attribute client + * @readonly + * @group Operation Properties + * @return [AWS.Client] The low-level service client object + * that initiated the request. + * + * @!attribute operation + * @readonly + * @group Operation Properties + * @return [String] the name of the operation executed on + * the service. + * + * @!attribute params + * @readonly + * @group Operation Properties + * @return [Object] the parameters sent in the request to + * the service. + * + * @!attribute retryCount + * @readonly + * @group Operation Properties + * @return [Integer] the number of retries that have were + * attempted before the request was completed. + * + * @!attribute httpRequest + * @readonly + * @group HTTP Properties + * @return [AWS.HttpRequest] the raw HTTP request object + * containing request headers and body information + * sent by the client. + * + * @!attribute httpResponse + * @readonly + * @group HTTP Properties + * @return [AWS.HttpResponse] the raw HTTP response object + * containing the response headers and body information + * from the server. + * + * @see AWS.Request + */ +AWS.Response = inherit({ + + /** + * @api private + */ + constructor: function Response(request) { + this.request = request; + this.data = null; + this.error = null; + this.retryCount = 0; + this.httpResponse = new AWS.HttpResponse(); + } + +}); diff --git a/lib/rest_xml_client.js b/lib/rest_xml_client.js deleted file mode 100644 index 0387e5a0cd..0000000000 --- a/lib/rest_xml_client.js +++ /dev/null @@ -1,111 +0,0 @@ -/** - * Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"). You - * may not use this file except in compliance with the License. A copy of - * the License is located at - * - * http://aws.amazon.com/apache2.0/ - * - * or in the "license" file accompanying this file. This file is - * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF - * ANY KIND, either express or implied. See the License for the specific - * language governing permissions and limitations under the License. - */ - -var AWS = require('./core'); -require('./rest_client'); -require('./xml/builder'); -require('./xml/parser'); -var inherit = AWS.util.inherit; - -/** - * @api private - */ -AWS.RESTXMLClient = inherit(AWS.RESTClient, { - - constructor: function RESTXMLClient(config) { - AWS.RESTClient.call(this, config); - }, - - buildRequest: function buildRequest(method, params) { - - var _super = AWS.RESTClient.prototype.buildRequest; - var req = _super.call(this, method, params); - this.populateBody(req, this.api.operations[method], params || {}); - return req; - - }, - - populateBody: function populateBody(req, operation, params) { - - var input = operation.i || {}; - var xmlWrapper = input.n; - var rules = input.m || {}; - var body = null; - - if (xmlWrapper) { - - // filter the params to only those that belong in the body - var xmlParams = {}; - AWS.util.each(rules, function (name, rule) { - if (!rule.l && params[name] !== undefined) { - xmlParams[name] = params[name]; - } - }); - - if (!AWS.util.isEmpty(xmlParams)) { - var builder = new AWS.XML.Builder(xmlWrapper, rules, this.api); - body = builder.toXML(xmlParams); - } - - } else { - AWS.util.each.call(this, rules, function (name, rule) { - if (rule.l == 'body') - body = params[name]; - }); - } - - req.body = body; - req.headers['Content-Length'] = body ? body.length : 0; - }, - - extractData: function extractData(httpResponse, method) { - - /*jshint sub:true */ - - var _super = AWS.RESTClient.prototype.extractData; - var data = _super.call(this, httpResponse, method); - - var rules = this.api.operations[method].o || {}; - - if (rules['Body'] && rules['Body']['t'] === 'bl') { - data['Body'] = httpResponse.body || ''; - } else if (httpResponse.body) { - var parser = new AWS.XML.Parser(rules); - AWS.util.update(data, parser.parse(httpResponse.body)); - } - - // extract request id - data['RequestId'] = httpResponse.headers['x-amz-request-id']; - - return data; - - }, - - extractError: function extractError(httpResponse) { - var data = new AWS.XML.Parser({}).parse(httpResponse.body); - if (data.Code) { - return AWS.util.error(new Error(), { - code: data.Code, - message: data.Message - }); - } else { - return AWS.util.error(new Error(), { - code: httpResponse.statusCode, - message: null - }); - } - } - -}); diff --git a/lib/json_client.js b/lib/service_interface/json.js similarity index 64% rename from lib/json_client.js rename to lib/service_interface/json.js index fa49e47a41..ec7d385328 100644 --- a/lib/json_client.js +++ b/lib/service_interface/json.js @@ -13,39 +13,24 @@ * language governing permissions and limitations under the License. */ -var AWS = require('./core'); -var inherit = AWS.util.inherit; +var AWS = require('../core'); -/** - * @api private - */ -AWS.JSONClient = inherit(AWS.Client, { - - constructor: function JSONClient(config) { - AWS.Client.call(this, config); - }, +AWS.ServiceInterface.Json = { + buildRequest: function buildRequest(req) { + var httpRequest = req.httpRequest; + var api = req.client.api; + var target = api.targetPrefix + api.operations[req.operation].n; - buildRequest: function buildRequest(operation, params) { - - var httpRequest = this.newHttpRequest(); - - httpRequest.body = JSON.stringify(params || {}); - - var target = this.api.targetPrefix + this.api.operations[operation].n; + httpRequest.body = JSON.stringify(req.params || {}); httpRequest.headers['Content-Type'] = 'application/x-amz-json-1.0'; httpRequest.headers['Content-Length'] = httpRequest.body.length; httpRequest.headers['X-Amz-Target'] = target; - - return httpRequest; - - }, - - extractData: function extractData(httpResponse) { - return JSON.parse(httpResponse.body || '{}'); }, - extractError: function extractError(httpResponse) { + extractError: function extractError(resp) { var error = {}; + var httpResponse = resp.httpResponse; + if (httpResponse.body) { var e = JSON.parse(httpResponse.body); error.code = e.__type.split('#').pop(); @@ -58,7 +43,11 @@ AWS.JSONClient = inherit(AWS.Client, { error.code = httpResponse.statusCode; error.message = null; } - return AWS.util.error(new Error(), error); - } -}); + resp.error = AWS.util.error(new Error(), error); + }, + + extractData: function extractData(resp) { + resp.data = JSON.parse(resp.httpResponse.body || '{}'); + } +}; diff --git a/lib/query_client.js b/lib/service_interface/query.js similarity index 79% rename from lib/query_client.js rename to lib/service_interface/query.js index fb2a506f5c..a7b0ed1934 100644 --- a/lib/query_client.js +++ b/lib/service_interface/query.js @@ -13,65 +13,53 @@ * language governing permissions and limitations under the License. */ -var AWS = require('./core'); -require('./xml/parser.js'); +var AWS = require('../core'); var inherit = AWS.util.inherit; -/** - * @api private - */ -AWS.QueryClient = inherit(AWS.Client, { - - constructor: function QueryClient(config) { - AWS.Client.call(this, config); - }, - - buildRequest: function buildRequest(method, requestParams) { - - var httpRequest = this.newHttpRequest(); +require('../xml/parser'); +AWS.ServiceInterface.Query = { + buildRequest: function buildRequest(req) { + var operation = req.client.api.operations[req.operation]; + var httpRequest = req.httpRequest; httpRequest.headers['Content-Type'] = 'application/x-www-form-urlencoded; charset=utf-8'; - httpRequest.params = new AWS.QueryParamList(); - httpRequest.params.add('Version', this.api.apiVersion); - httpRequest.params.add('Action', this.api.operations[method].n); - + httpRequest.params.add('Version', req.client.api.apiVersion); + httpRequest.params.add('Action', operation.n); // convert the request parameters into a list of query params, // e.g. Deeply.NestedParam.0.Name=value - var rules = this.api.operations[method].i; + var rules = operation.i; if (rules) rules = rules.m; var builder = new AWS.QueryParamSerializer(rules); - builder.serialize(requestParams, function (name, value) { + builder.serialize(req.params, function(name, value) { httpRequest.params.add(name, value); }); - - return httpRequest; - - }, - - extractData: function extractData(httpResponse, method) { - var parser = new AWS.XML.Parser(this.api.operations[method].o); - return parser.parse(httpResponse.body); }, - extractError: function extractError(httpResponse) { - var data = new AWS.XML.Parser({}).parse(httpResponse.body); + extractError: function extractError(resp) { + var data = new AWS.XML.Parser({}).parse(resp.httpResponse.body); if (data.Code) { - return AWS.util.error(new Error(), { + resp.error = AWS.util.error(new Error(), { code: data.Code, message: data.Message }); } else { - return AWS.util.error(new Error(), { - code: httpResponse.statusCode, + resp.error = AWS.util.error(new Error(), { + code: resp.httpResponse.statusCode, message: null }); } - } + }, -}); + extractData: function extractData(resp) { + var req = resp.request; + var operation = req.client.api.operations[req.operation]; + var parser = new AWS.XML.Parser(operation.o); + resp.data = parser.parse(resp.httpResponse.body); + } +}; /** * @api private diff --git a/lib/rest_client.js b/lib/service_interface/rest.js similarity index 63% rename from lib/rest_client.js rename to lib/service_interface/rest.js index 782d516a9d..4777b2703d 100644 --- a/lib/rest_client.js +++ b/lib/service_interface/rest.js @@ -13,60 +13,72 @@ * language governing permissions and limitations under the License. */ -var AWS = require('./core'); -var inherit = AWS.util.inherit; +var AWS = require('../core'); -/** - * @api private - */ -AWS.RESTClient = inherit(AWS.Client, { - - constructor: function RESTClient(config) { - AWS.Client.call(this, config); +AWS.ServiceInterface.Rest = { + buildRequest: function buildRequest(req) { + AWS.ServiceInterface.Rest.populateMethod(req); + AWS.ServiceInterface.Rest.populateURI(req); + AWS.ServiceInterface.Rest.populateHeaders(req); }, - buildRequest: function buildRequest(method, params) { - - if (!params) params = {}; - - var operation = this.api.operations[method]; + extractError: function extractError() { + }, - var req = this.newHttpRequest(); + extractData: function extractData(resp) { + var req = resp.request; + var data = {}; + var r = resp.httpResponse; + var operation = req.client.api.operations[req.operation]; + var rules = operation.o || {}; - this.populateMethod(req, operation); - this.populateURI(req, operation, params); - this.populateHeaders(req, operation, params); + // normalize headers names to lower-cased keys for matching + var headers = {}; + AWS.util.each(r.headers, function (k, v) { + headers[k.toLowerCase()] = v; + }); - return req; + AWS.util.each(rules, function (name, rule) { + if (rule.l === 'header') { + var header = (rule.n || name).toLowerCase(); + if (headers[header] !== undefined) { + data[name] = headers[header]; + } + } + if (rule.l === 'status') { + data[name] = parseInt(r.statusCode, 10); + } + }); + resp.data = data; }, - populateMethod: function populateMethod(req, operation) { - req.method = operation.m; + populateMethod: function populateMethod(req) { + req.httpRequest.method = req.client.api.operations[req.operation].m; }, - populateURI: function populateURI(req, operation, params) { - + populateURI: function populateURI(req) { + var operation = req.client.api.operations[req.operation]; var uri = operation.u; var pathPattern = uri.split(/\?/)[0]; - var rules = (operation.i || {}).m || {}; - AWS.util.each.call(this, rules, function (name, rule) { - - if (rule.l == 'uri' && params[name]) { + var escapePathParam = req.client.escapePathParam || + AWS.ServiceInterface.Rest.escapePathParam; + var escapeQuerystringParam = req.client.escapeQuerystringParam || + AWS.ServiceInterface.Rest.escapeQuerystringParam; + AWS.util.each.call(this, rules, function (name, rule) { + if (rule.l == 'uri' && req.params[name]) { // if the value is being inserted into the path portion of the // URI, then we need to use a different (potentially) escaping // pattern, this is especially true for S3 path params like Key. var value = pathPattern.match('{' + name + '}') ? - this.escapePathParam(params[name]) : - this.escapeQuerystringParam(params[name]); + escapePathParam(req.params[name]) : + escapeQuerystringParam(req.params[name]); uri = uri.replace('{' + name + '}', value); - } - }); var path = uri.split('?')[0]; @@ -82,8 +94,7 @@ AWS.RESTClient = inherit(AWS.Client, { uri = path; } - req.path = uri; - + req.httpRequest.path = uri; }, escapePathParam: function escapePathParam(value) { @@ -94,52 +105,21 @@ AWS.RESTClient = inherit(AWS.Client, { return AWS.util.uriEscape(String(value)); }, - populateHeaders: function populateHeaders(req, operation, params) { - + populateHeaders: function populateHeaders(req) { + var operation = req.client.api.operations[req.operation]; var rules = (operation.i || {}).m || {}; AWS.util.each.call(this, rules, function (name, rule) { - if (rule.l === 'header' && params[name]) { + if (rule.l === 'header' && req.params[name]) { if (rule.t === 'm') { // header map - AWS.util.each(params[name], function (key, value) { - req.headers[rule.n + key] = value; + AWS.util.each(req.params[name], function (key, value) { + req.httpRequest.headers[rule.n + key] = value; }); } else { - req.headers[rule.n] = params[name]; + req.httpRequest.headers[rule.n] = req.params[name]; } } }); - }, - - extractData: function extractData(httpResponse, method) { - - var data = {}; - - var r = httpResponse; - - var rules = this.api.operations[method].o || {}; - - // normalize headers names to lower-cased keys for matching - var headers = {}; - AWS.util.each(r.headers, function (k, v) { - headers[k.toLowerCase()] = v; - }); - - AWS.util.each(rules, function (name, rule) { - if (rule.l === 'header') { - var header = (rule.n || name).toLowerCase(); - if (headers[header] !== undefined) { - data[name] = headers[header]; - } - } - if (rule.l === 'status') { - data[name] = parseInt(r.statusCode, 10); - } - }); - - return data; - } - -}); +}; diff --git a/lib/service_interface/rest_xml.js b/lib/service_interface/rest_xml.js new file mode 100644 index 0000000000..8c558a0558 --- /dev/null +++ b/lib/service_interface/rest_xml.js @@ -0,0 +1,93 @@ +/** + * Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You + * may not use this file except in compliance with the License. A copy of + * the License is located at + * + * http://aws.amazon.com/apache2.0/ + * + * or in the "license" file accompanying this file. This file is + * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF + * ANY KIND, either express or implied. See the License for the specific + * language governing permissions and limitations under the License. + */ + +var AWS = require('../core'); +require('../xml/builder'); +require('./rest'); + +AWS.ServiceInterface.RestXml = { + buildRequest: function buildRequest(req) { + AWS.ServiceInterface.Rest.buildRequest(req); + AWS.ServiceInterface.RestXml.populateBody(req); + }, + + extractError: function extractError(resp) { + AWS.ServiceInterface.Rest.extractError(resp); + + var data = new AWS.XML.Parser({}).parse(resp.httpResponse.body); + if (data.Code) { + resp.error = AWS.util.error(new Error(), { + code: data.Code, + message: data.Message + }); + } else { + resp.error = AWS.util.error(new Error(), { + code: resp.httpResponse.statusCode, + message: null + }); + } + }, + + extractData: function extractData(resp) { + AWS.ServiceInterface.Rest.extractData(resp); + + var req = resp.request; + var httpResponse = resp.httpResponse; + var operation = req.client.api.operations[req.operation]; + var rules = operation.o || {}; + + if (rules['Body'] && rules['Body']['t'] === 'bl') { + resp.data['Body'] = httpResponse.body; + } else if (httpResponse.body !== '') { + var parser = new AWS.XML.Parser(rules); + AWS.util.update(resp.data, parser.parse(httpResponse.body)); + } + + // extract request id + resp.data['RequestId'] = httpResponse.headers['x-amz-request-id']; + }, + + populateBody: function populateBody(req) { + var operation = req.client.api.operations[req.operation]; + var input = operation.i || {}; + var xmlWrapper = input.n; + var rules = input.m || {}; + var body = null; + + if (xmlWrapper) { + // filter the params to only those that belong in the body + var xmlParams = {}; + AWS.util.each(rules, function(name, rule) { + if (!rule.l && req.params[name] !== undefined) { + xmlParams[name] = req.params[name]; + } + }); + + if (!AWS.util.isEmpty(xmlParams)) { + var builder = new AWS.XML.Builder(xmlWrapper, rules, req.client.api); + body = builder.toXML(xmlParams); + } + + } else { + AWS.util.each.call(this, rules, function(name, rule) { + if (rule.l == 'body') + body = req.params[name]; + }); + } + + req.httpRequest.body = body; + req.httpRequest.headers['Content-Length'] = body ? body.length : 0; + } +}; diff --git a/lib/services/dynamodb.js b/lib/services/dynamodb.js index 85add55a0b..ca1e12801e 100644 --- a/lib/services/dynamodb.js +++ b/lib/services/dynamodb.js @@ -16,7 +16,6 @@ var AWS = require('../core'); var inherit = AWS.util.inherit; -require('../json_client'); require('../sigv4'); AWS.DynamoDB = inherit({ @@ -27,14 +26,18 @@ AWS.DynamoDB = inherit({ }); -AWS.DynamoDB.Client = inherit(AWS.JSONClient, { +AWS.DynamoDB.Client = inherit(AWS.Client, { /** * @api private */ constructor: function DynamoDBClient(options) { this.serviceName = 'dynamodb'; - AWS.JSONClient.call(this, options); + AWS.Client.call(this, options); + }, + + setupRequestListeners: function setupRequestListeners(request) { + request.addListeners(AWS.EventListeners.Json); }, /** diff --git a/lib/services/ec2.js b/lib/services/ec2.js index 98e5f190c1..dd01423de5 100644 --- a/lib/services/ec2.js +++ b/lib/services/ec2.js @@ -16,7 +16,6 @@ var AWS = require('../core'); var inherit = AWS.util.inherit; -require('../query_client'); require('../sigv2'); AWS.EC2 = inherit({ @@ -27,11 +26,17 @@ AWS.EC2 = inherit({ }); -AWS.EC2.Client = inherit(AWS.QueryClient, { +AWS.EC2.Client = inherit(AWS.Client, { constructor: function EC2Client(options) { this.serviceName = 'ec2'; - AWS.QueryClient.call(this, options); + AWS.Client.call(this, options); + }, + + setupRequestListeners: function setupRequestListeners(request) { + request.addListeners(AWS.EventListeners.Query); + request.removeListener('extractError', AWS.EventListeners.Query.EXTRACT_ERROR); + request.addListener('extractError', this.extractError); }, /** @@ -42,16 +47,17 @@ AWS.EC2.Client = inherit(AWS.QueryClient, { /** * @api private */ - extractError: function extractError(httpResponse) { + extractError: function extractError(resp) { // EC2 nests the error code and message deeper than other AWS Query services. - var data = new AWS.XML.Parser({}).parse(httpResponse.body); + var httpResponse = resp.httpResponse; + var data = new AWS.XML.Parser({}).parse(httpResponse.body || ''); if (data.Errors) - return AWS.util.error(new Error(), { + resp.error = AWS.util.error(new Error(), { code: data.Errors.Error.Code, message: data.Errors.Error.Message }); else - return AWS.util.error(new Error(), { + resp.error = AWS.util.error(new Error(), { code: httpResponse.statusCode, message: null }); diff --git a/lib/services/s3.js b/lib/services/s3.js index b1a5fb7781..fdd9950d68 100644 --- a/lib/services/s3.js +++ b/lib/services/s3.js @@ -16,7 +16,6 @@ var AWS = require('../core'); var inherit = AWS.util.inherit; -require('../rest_xml_client'); require('../sigvs3'); AWS.S3 = inherit({ @@ -27,14 +26,14 @@ AWS.S3 = inherit({ }); -AWS.S3.Client = inherit(AWS.RESTXMLClient, { +AWS.S3.Client = inherit(AWS.Client, { /** * @api private */ constructor: function S3Client(options) { this.serviceName = 's3'; - AWS.RESTXMLClient.call(this, options); + AWS.Client.call(this, options); this.setEndpoint((options || {}).endpoint, options); }, @@ -43,31 +42,39 @@ AWS.S3.Client = inherit(AWS.RESTXMLClient, { */ signatureVersion: AWS.SigVS3, + setupRequestListeners: function setupRequestListeners(request) { + request.addListeners(AWS.EventListeners.RestXml); + request.addListener('build', this.populateURI); + request.removeListener('validate', + AWS.EventListeners.Core.VALIDATE_REGION); + request.addListener('extractError', this.extractError); + request.addListener('extractData', this.extractData); + }, + /** * S3 prefers dns-compatible bucket names to be moved from the uri path * to the hostname as a sub-domain. This is not possible, even for dns-compat * buckets when using SSL and the bucket name contains a dot ('.'). The - * ssl wildcart certificate is only 1-level deep. + * ssl wildcard certificate is only 1-level deep. * * @api private */ - populateURI: function populateURI(req, operation, params) { - - AWS.RESTClient.prototype.populateURI.call(this, req, operation, params); + populateURI: function populateURI(req) { + var httpRequest = req.httpRequest; + var b = req.params.Bucket; - var b = params.Bucket; if (b) { - if (!this.pathStyleBucketName(b)) { - - req.virtualHostedBucket = b; // needed for signing the request - - req.path = req.path.replace(new RegExp('^/' + b), ''); - if (req.path[0] !== '/') req.path = '/' + req.path; - req.endpoint.hostname = b + '.' + req.endpoint.hostname; - + if (!req.client.pathStyleBucketName(b)) { + httpRequest.endpoint.hostname = b + '.' + + httpRequest.endpoint.hostname; + + httpRequest.virtualHostedBucket = b; // needed for signing the request + httpRequest.path = httpRequest.path.replace(new RegExp('^/' + b), ''); + if (httpRequest.path[0] !== '/') { + httpRequest.path = '/' + httpRequest.path; + } } } - }, /** @@ -78,7 +85,6 @@ AWS.S3.Client = inherit(AWS.RESTXMLClient, { * @api private */ pathStyleBucketName: function pathStyleBucketName(bucketName) { - // user can force path style requests via the configuration if (this.config.s3ForcePathStyle) return true; @@ -87,7 +93,6 @@ AWS.S3.Client = inherit(AWS.RESTXMLClient, { } else { return true; // not dns compatible names must always use path style } - }, /** @@ -117,8 +122,11 @@ AWS.S3.Client = inherit(AWS.RESTXMLClient, { * @return [Boolean] whether response contains an error * @api private */ - successfulResponse: function successfulResponse(httpResponse, method) { - if (method === 'completeMultipartUpload' && httpResponse.body && httpResponse.body.match('')) + successfulResponse: function successfulResponse(resp) { + var req = resp.request; + var httpResponse = resp.httpResponse; + if (req.operation === 'completeMultipartUpload' && + httpResponse.body.match('')) return false; else return httpResponse.statusCode < 300; @@ -128,12 +136,13 @@ AWS.S3.Client = inherit(AWS.RESTXMLClient, { * @return [Boolean] whether the error can be retried * @api private */ - retryableError: function retryableError(error, method) { - if (method == 'completeMultipartUpload' && error.statusCode === 200) { + retryableError: function retryableError(error, request) { + if (request.operation == 'completeMultipartUpload' && + error.statusCode === 200) { return true; } else { var _super = AWS.Client.prototype.retryableError; - return _super.call(this, error, method); + return _super.call(this, error, request); } }, @@ -143,24 +152,16 @@ AWS.S3.Client = inherit(AWS.RESTXMLClient, { * * @api private */ - extractData: function extractData(httpResponse, method) { - - var _super = AWS.RESTXMLClient.prototype.extractData; - var data = _super.call(this, httpResponse, method); - - if (method === 'getBucketLocation') { - + extractData: function extractData(resp) { + var req = resp.request; + if (req.operation === 'getBucketLocation') { /*jshint regexp:false*/ - var match = httpResponse.body.match(/>(.+)<\/Location/); + var match = resp.httpResponse.body.match(/>(.+)<\/Location/); if (match) { - /*jshint sub:true */ - delete data['_']; - data.LocationConstraint = match[1]; + delete resp.data['_']; + resp.data.LocationConstraint = match[1]; } - } - - return data; }, /** @@ -168,30 +169,25 @@ AWS.S3.Client = inherit(AWS.RESTXMLClient, { * * @api private */ - extractError: function extractError(httpResponse) { - + extractError: function extractError(resp) { var codes = { 304: 'NotModified', 403: 'Forbidden', 404: 'NotFound' }; - if (codes[httpResponse.statusCode]) { - - return AWS.util.error(new Error(), { - code: codes[httpResponse.statusCode], + if (codes[resp.httpResponse.statusCode]) { + resp.error = AWS.util.error(new Error(), { + code: codes[resp.httpResponse.statusCode], message: null }); - } else { + var data = new AWS.XML.Parser({}).parse(resp.httpResponse.body); - var data = new AWS.XML.Parser({}).parse(httpResponse.body || ''); - - return AWS.util.error(new Error(), { - code: data.Code || httpResponse.statusCode, + resp.error = AWS.util.error(new Error(), { + code: data.Code || resp.httpResponse.statusCode, message: data.Message || null }); - } }, diff --git a/lib/services/simpleworkflow.js b/lib/services/simpleworkflow.js index 011ca6de7a..aa7fda6129 100644 --- a/lib/services/simpleworkflow.js +++ b/lib/services/simpleworkflow.js @@ -16,7 +16,6 @@ var AWS = require('../core'); var inherit = AWS.util.inherit; -require('../json_client'); require('../sigv3'); AWS.SimpleWorkflow = inherit({ @@ -27,14 +26,18 @@ AWS.SimpleWorkflow = inherit({ }); -AWS.SimpleWorkflow.Client = inherit(AWS.JSONClient, { +AWS.SimpleWorkflow.Client = inherit(AWS.Client, { /** * @api private */ constructor: function SimpleWorkflowClient(options) { this.serviceName = 'swf'; - AWS.JSONClient.call(this, options); + AWS.Client.call(this, options); + }, + + setupRequestListeners: function setupRequestListeners(request) { + request.addListeners(AWS.EventListeners.Json); }, /** diff --git a/lib/sigv3.js b/lib/sigv3.js index 70f859f350..ff6e795aa2 100644 --- a/lib/sigv3.js +++ b/lib/sigv3.js @@ -29,7 +29,6 @@ AWS.SigV3 = inherit(AWS.RequestSigner, { var datetime = AWS.util.date.rfc822(date); - /*jshint sub:true */ this.request.headers['Date'] = datetime; this.request.headers['X-Amz-Date'] = datetime; this.request.headers['Host'] = this.request.endpoint.hostname; diff --git a/lib/sigv4.js b/lib/sigv4.js index 9897f87974..729ee52b55 100644 --- a/lib/sigv4.js +++ b/lib/sigv4.js @@ -29,13 +29,11 @@ AWS.SigV4 = inherit(AWS.RequestSigner, { addAuthorization: function addAuthorization(credentials, date) { var datetime = AWS.util.date.iso8601(date).replace(/[:\-]|\.\d{3}/g, ''); this.addHeaders(credentials, datetime); - /*jshint sub:true */ this.request.headers['Authorization'] = this.authorization(credentials, datetime); }, addHeaders: function addHeaders(credentials, datetime) { - /*jshint sub:true */ this.request.headers['Host'] = this.request.endpoint.hostname; this.request.headers['Date'] = datetime; this.request.headers['X-Amz-Date'] = datetime; @@ -79,7 +77,7 @@ AWS.SigV4 = inherit(AWS.RequestSigner, { parts.push(this.request.search()); parts.push(this.canonicalHeaders() + '\n'); parts.push(this.signedHeaders()); - parts.push(this.hexEncodedHash(this.request.body || '')); + parts.push(this.hexEncodedHash(this.request.body)); return parts.join('\n'); }, diff --git a/lib/sigvs3.js b/lib/sigvs3.js index 9af9117714..8fa86842d9 100644 --- a/lib/sigvs3.js +++ b/lib/sigvs3.js @@ -58,8 +58,7 @@ AWS.SigVS3 = inherit(AWS.RequestSigner, { }, addAuthorization: function addAuthorization(credentials, date) { - - this.setDate(date); + this.request.headers['Date'] = AWS.util.date.rfc822(date); if (credentials.sessionToken) this.request.headers['X-Amz-Security-Token'] = credentials.sessionToken; @@ -67,14 +66,10 @@ AWS.SigVS3 = inherit(AWS.RequestSigner, { var signature = this.sign(credentials.secretAccessKey, this.stringToSign()); var auth = 'AWS ' + credentials.accessKeyId + ':' + signature; - /*jshint sub:true */ this.request.headers['Authorization'] = auth; - }, stringToSign: function stringToSign() { - /*jshint sub:true */ - var r = this.request; var parts = []; @@ -172,18 +167,7 @@ AWS.SigVS3 = inherit(AWS.RequestSigner, { sign: function sign(secret, string) { return AWS.util.crypto.hmac(secret, string, 'base64', 'sha1'); - }, - - setDate: function setDate(date) { - /*jshint sub:true */ - var r = this.request; - if (date) { - r.headers['Date'] = date; - } else if (!r.headers['X-Amz-Date'] && !r.headers['Date']) { - r.headers['Date'] = AWS.util.date.rfc822(); - } } - }); module.exports = AWS.SigVS3; diff --git a/lib/util.js b/lib/util.js index f9e874618c..afd8be89a2 100644 --- a/lib/util.js +++ b/lib/util.js @@ -165,6 +165,7 @@ AWS.util = { }, copy: function copy(object) { + if (object === null || object === undefined) return object; var dupe = {}; /*jshint forin:false */ for (var key in object) { @@ -184,7 +185,6 @@ AWS.util = { error: function error(err, options) { err.message = err.message || null; - err.retryable = err.retryable || false; if (typeof options === 'string') { err.message = options; @@ -203,6 +203,7 @@ AWS.util = { var newObject = null; if (features === undefined) { features = klass; + klass = Object; newObject = {}; } else { /*jshint newcap:false camelcase:false */ @@ -210,6 +211,14 @@ AWS.util = { ctor.prototype = klass.prototype; newObject = new ctor(); } + + // constructor not supplied, create pass-through ctor + if (features.constructor === Object) { + features.constructor = function() { + klass.apply(this, arguments); + }; + } + features.constructor.prototype = newObject; AWS.util.update(features.constructor.prototype, features); features.constructor.__super__ = klass; diff --git a/test/helpers.coffee b/test/helpers.coffee index e5c8f0b4b1..2fb0c91373 100644 --- a/test/helpers.coffee +++ b/test/helpers.coffee @@ -12,12 +12,21 @@ # language governing permissions and limitations under the License. AWS = require('../lib/aws') +fs = require('fs') +configFile = __dirname + '/../configuration' + +if fs.existsSync(configFile) + AWS.config.loadFromPath(configFile) +else + AWS.config.update credentials: + accessKeyId: 'akid' + secretAccessKey: 'secret' integration = (reqBuilder, respCallback) -> req = reqBuilder() resp = null runs -> - req.always (respObject) -> resp = respObject + req.on('complete', (respObject) -> resp = respObject) req.send() waitsFor -> resp != null runs -> respCallback(resp) @@ -37,14 +46,13 @@ MockClient = AWS.util.inherit AWS.Client, AWS.Client.call(this, config) @config.credentials = accessKeyId: 'akid', secretAccessKey: 'secret' @config.region = 'mock-region' - buildRequest: -> - req = this.newHttpRequest() - req.sign = -> - req - extractData: (httpResponse) -> - return httpResponse.body - extractError: (httpResponse) -> - return { code: httpResponse.statusCode, message: null, retryable: false } + setupRequestListeners: (request) -> + request.on 'extractData', (resp) -> + resp.data = resp.httpResponse.body + request.on 'extractError', (resp) -> + resp.error = + code: resp.httpResponse.statusCode + message: null serviceName: 'mockservice' signatureVersion: require('../lib/sigv4') @@ -55,15 +63,15 @@ MockService.Client = MockClient mockHttpResponse = (status, headers, data) -> spyOn(AWS.HttpClient, 'getInstance') - if typeof status == 'number' - AWS.HttpClient.getInstance.andReturn handleRequest: (req, cb) -> - cb.onHeaders(status, headers) + AWS.HttpClient.getInstance.andReturn handleRequest: (req, resp) -> + if typeof status == 'number' + req.emit('httpHeaders', status, headers, resp) + str = str instanceof Array ? str : [str] AWS.util.arrayEach data, (str) -> - cb.onData(str) - cb.onEnd() - else - AWS.HttpClient.getInstance.andReturn handleRequest: (req, cb) -> - cb.onError(status) + req.emit('httpData', str, resp) + req.emit('httpDone', resp) + else + req.emit('httpError', status, resp) module.exports = AWS: AWS diff --git a/test/integration/dynamodb.spec.coffee b/test/integration/dynamodb.spec.coffee index 2e43f6ef21..ffaec4908b 100644 --- a/test/integration/dynamodb.spec.coffee +++ b/test/integration/dynamodb.spec.coffee @@ -11,7 +11,7 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -helpers = require('./helpers'); AWS = helpers.AWS +helpers = require('../helpers'); AWS = helpers.AWS describe 'AWS.DynamoDB', -> client = new AWS.DynamoDB.Client() @@ -21,7 +21,7 @@ describe 'AWS.DynamoDB', -> helpers.integration (-> client.listTables(Limit: 3)), (resp) -> expect(resp.error).toEqual(null) expect(JSON.stringify(resp.data)).toMatch(/\{.*"TableNames":.*\}/) - expect(resp.httpRequest.body).toEqual('{"Limit":3}') + expect(resp.request.httpRequest.body).toEqual('{"Limit":3}') describe 'deleteItem', -> it 'should fail if TableName not provided', -> diff --git a/test/integration/ec2.spec.coffee b/test/integration/ec2.spec.coffee index 9d29531703..4adb9c19a3 100644 --- a/test/integration/ec2.spec.coffee +++ b/test/integration/ec2.spec.coffee @@ -11,7 +11,7 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -helpers = require('./helpers'); AWS = helpers.AWS +helpers = require('../helpers'); AWS = helpers.AWS describe 'AWS.EC2', -> diff --git a/test/integration/helpers.coffee b/test/integration/helpers.coffee deleted file mode 100644 index 9cda9cac71..0000000000 --- a/test/integration/helpers.coffee +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"). You -# may not use this file except in compliance with the License. A copy of -# the License is located at -# -# http://aws.amazon.com/apache2.0/ -# -# or in the "license" file accompanying this file. This file is -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF -# ANY KIND, either express or implied. See the License for the specific -# language governing permissions and limitations under the License. - -helpers = require('../helpers'); - -AWS = helpers.AWS -AWS.config.loadFromPath(__dirname + '/../../configuration'); - -module.exports = helpers diff --git a/test/integration/s3.spec.coffee b/test/integration/s3.spec.coffee index 0fbcb004c9..a3ab27b757 100644 --- a/test/integration/s3.spec.coffee +++ b/test/integration/s3.spec.coffee @@ -11,7 +11,7 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -helpers = require('./helpers'); AWS = helpers.AWS +helpers = require('../helpers'); AWS = helpers.AWS describe 'AWS.S3', -> diff --git a/test/integration/simpleworkflow.spec.coffee b/test/integration/simpleworkflow.spec.coffee index 9b3bdab65a..c97ba43911 100644 --- a/test/integration/simpleworkflow.spec.coffee +++ b/test/integration/simpleworkflow.spec.coffee @@ -11,7 +11,7 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -helpers = require('./helpers'); AWS = helpers.AWS +helpers = require('../helpers'); AWS = helpers.AWS describe 'AWS.SimpleWorkflow', -> client = new AWS.SimpleWorkflow.Client() @@ -21,7 +21,7 @@ describe 'AWS.SimpleWorkflow', -> helpers.integration (-> client.listDomains(registrationStatus: "REGISTERED")), (resp) -> expect(resp.error).toEqual(null) expect(JSON.stringify(resp.data)).toMatch(/\{"domainInfos":.*\}/) - expect(resp.httpRequest.body).toEqual('{"registrationStatus":"REGISTERED"}') + expect(resp.request.httpRequest.body).toEqual('{"registrationStatus":"REGISTERED"}') describe 'getWorkflowExecutionHistory', -> it 'should fail if TableName not provided', -> diff --git a/test/unit/aws_request.spec.coffee b/test/unit/aws_request.spec.coffee deleted file mode 100644 index 47e57c8cf0..0000000000 --- a/test/unit/aws_request.spec.coffee +++ /dev/null @@ -1,83 +0,0 @@ -# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"). You -# may not use this file except in compliance with the License. A copy of -# the License is located at -# -# http://aws.amazon.com/apache2.0/ -# -# or in the "license" file accompanying this file. This file is -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF -# ANY KIND, either express or implied. See the License for the specific -# language governing permissions and limitations under the License. - -helpers = require('../helpers') -AWS = helpers.AWS - -describe 'AWS.AWSRequest', -> - request = null - response = null - beforeEach -> - request = new AWS.AWSRequest(null, 'operation', {}) - response = request.awsResponse - - sharedBehaviour = (cbMethod, notifyMethod, data) -> - - it 'can register callback', -> - spy = jasmine.createSpy(cbMethod + '_register') - request[cbMethod](spy) - request[notifyMethod](data) - expect(spy).toHaveBeenCalled() - - it 'will trigger even if registered after notification', -> - spy = jasmine.createSpy(cbMethod + '_trigger_after_notification') - request[notifyMethod](data) - request[cbMethod](spy) - expect(spy).toHaveBeenCalled() - - it 'can register multiple callbacks', -> - spies = [jasmine.createSpy(cbMethod + '_multiple_cb'), - jasmine.createSpy(cbMethod + '_multiple_cb')] - request[notifyMethod](data) - for index of spies - request[cbMethod](spies[index]) - expect(spies[index]).toHaveBeenCalled() - - it 'can chain callbacks', -> - spy1 = jasmine.createSpy(cbMethod + '_chain') - spy2 = jasmine.createSpy(cbMethod + '_chain') - retVal = request[cbMethod](spy1)[cbMethod](spy2) - request[notifyMethod](data) - expect(retVal).toBe(request) - expect(spy1).toHaveBeenCalled() - expect(spy2).toHaveBeenCalled() - - it 'should be triggered in default binding of response object', -> - request[cbMethod] -> - expect(this).toBe(response) - request[notifyMethod](data) - - it 'should be triggered with response object as param', -> - request[cbMethod] (context) -> - expect(context).toBe(response) - request[notifyMethod](data) - - it 'should allow overriding of binding', -> - request[cbMethod]((-> expect(this).toEqual('foo')), bind: 'foo') - request[notifyMethod](data) - - describe 'data', -> - sharedBehaviour('data', 'notifyData', 'FOO') - - describe 'done', -> - sharedBehaviour('done', 'notifyDone') - - describe 'fail', -> - sharedBehaviour('fail', 'notifyFail') - - describe 'always', -> - describe 'with notifyDone', -> - sharedBehaviour('always', 'notifyDone') - - describe 'with notifyFail', -> - sharedBehaviour('always', 'notifyFail') diff --git a/test/unit/client.spec.coffee b/test/unit/client.spec.coffee index 11308030df..865e999ba5 100644 --- a/test/unit/client.spec.coffee +++ b/test/unit/client.spec.coffee @@ -49,44 +49,30 @@ describe 'AWS.Client', -> it 'it treats params as an optinal parameter', -> helpers.mockHttpResponse(200, {}, ['FOO', 'BAR']) client = new MockClient() - client.buildRequest = (operation, params) -> - expect(operation).toEqual('operationName') - expect(params).toEqual({}) - req = this.newHttpRequest() - req.sign = -> - req client.makeRequest 'operationName', (err, data) -> + expect(data).toEqual('FOOBAR') it 'yields data to the callback', -> helpers.mockHttpResponse(200, {}, ['FOO', 'BAR']) - err = null; data = null client = new MockClient() - req = client.makeRequest 'operation', {}, (e, d) -> - err = e - data = d - expect(err).toEqual(null) - expect(data).toEqual('FOOBAR') + req = client.makeRequest 'operation', (err, data) -> + expect(err).toEqual(null) + expect(data).toEqual('FOOBAR') it 'yields service errors to the callback', -> helpers.mockHttpResponse(500, {}, ['service error']) - err = null; data = null client = new MockClient(maxRetries: 0) - req = client.makeRequest 'operation', {}, (e, d) -> - err = e - data = d - expect(err).toEqual({code:500, message:null, retryable:true, statusCode:500}) - expect(data).toEqual(null) + req = client.makeRequest 'operation', {}, (err, data) -> + expect(err).toEqual({code:500, message:null, retryable:true, statusCode:500}) + expect(data).toEqual(null) it 'yields network errors to the callback', -> error = { code: 'NetworkingError' } helpers.mockHttpResponse(error) - err = null; data = null client = new MockClient(maxRetries: 0) - req = client.makeRequest 'operation', {}, (e, d) -> - err = e - data = d - expect(err).toEqual(error) - expect(data).toEqual(null) + req = client.makeRequest 'operation', {}, (err, data) -> + expect(err).toEqual(error) + expect(data).toEqual(null) it 'does not send the request if a callback function is omitted', -> httpClient = AWS.HttpClient.getInstance() diff --git a/test/unit/event_emitter.spec.coffee b/test/unit/event_emitter.spec.coffee new file mode 100644 index 0000000000..a9a80e173a --- /dev/null +++ b/test/unit/event_emitter.spec.coffee @@ -0,0 +1,86 @@ +# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +AWS = require('../../lib/core') + +describe 'AWS.EventEmitter', -> + beforeEach -> @emitter = new AWS.EventEmitter() + + describe 'addListeners', -> + it 'accepts a hash of events and functions', -> + triggers = [0, 0, 0] + listeners = + eventName: + ConstantName1: -> triggers[0] = 1 + ConstantName2: -> triggers[1] = 1 + otherEventName: + ConstantName3: -> triggers[2] = 1 + + @emitter.addListeners(listeners) + expect(triggers).toEqual([0, 0, 0]) + + @emitter.emit('eventName') + expect(triggers).toEqual([1, 1, 0]) + + @emitter.emit('otherEventName') + expect(triggers).toEqual([1, 1, 1]) + + it 'accepts an EventEmitter object', -> + triggers = [0, 0, 0] + listeners = new AWS.EventEmitter() + listeners.on 'eventName', -> triggers[0] = 1 + listeners.on 'eventName', -> triggers[1] = 1 + listeners.on 'otherEventName', -> triggers[2] = 1 + + @emitter.addListeners(listeners) + expect(triggers).toEqual([0, 0, 0]) + + @emitter.emit('eventName') + expect(triggers).toEqual([1, 1, 0]) + + @emitter.emit('otherEventName') + expect(triggers).toEqual([1, 1, 1]) + + describe 'addNamedListener', -> + it 'defines a constant with the callback', -> + spy = createSpy() + @emitter.addNamedListener('CONSTNAME', 'eventName', spy) + expect(@emitter.CONSTNAME).toBe(spy) + + # also verify that event is hooked up like normal + @emitter.emit('eventName', 'argument') + expect(spy).toHaveBeenCalledWith('argument') + + it 'is chainable', -> + r = @emitter.addNamedListener('CONSTNAME', 'eventName', ->) + expect(r).toBe(@emitter) + + describe 'addNamedListeners', -> + it 'is chainable', -> + r = @emitter.addNamedListeners(->) + expect(r).toBe(@emitter) + + it 'provides an add function in callback to call addNamedListener', -> + spy1 = createSpy(); spy2 = createSpy() + @emitter.addNamedListeners (add) -> + add('CONST1', 'event1', spy1) + add('CONST2', 'event2', spy2) + + expect(@emitter.CONST1).toBe(spy1) + expect(@emitter.CONST2).toBe(spy2) + + @emitter.emit('event1', 'arg1') + @emitter.emit('event2', 'arg2') + + expect(spy1).toHaveBeenCalledWith('arg1') + expect(spy2).toHaveBeenCalledWith('arg2') diff --git a/test/unit/event_listeners.spec.coffee b/test/unit/event_listeners.spec.coffee new file mode 100644 index 0000000000..35f54acca6 --- /dev/null +++ b/test/unit/event_listeners.spec.coffee @@ -0,0 +1,205 @@ +# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +helpers = require('../helpers') +AWS = helpers.AWS +MockClient = helpers.MockClient + +describe 'AWS.EventListeners', -> + + oldSetTimeout = setTimeout + config = null; client = null; totalWaited = null; delays = [] + successHandler = null; errorHandler = null; completeHandler = null + retryHandler = null + + beforeEach -> + # Mock the timer manually (jasmine.Clock does not work in node) + `setTimeout = jasmine.createSpy('setTimeout');` + setTimeout.andCallFake (callback, delay) -> + totalWaited += delay + delays.push(delay) + callback() + + totalWaited = 0 + delays = [] + client = new MockClient(maxRetries: 3) + client.config.credentials = AWS.util.copy(client.config.credentials) + + # Helpful handlers + successHandler = createSpy('success') + errorHandler = createSpy('error') + completeHandler = createSpy('complete') + retryHandler = createSpy('retry') + + # Safely tear down setTimeout hack + afterEach -> `setTimeout = oldSetTimeout` + + makeRequest = (callback) -> + request = client.makeRequest('mockMethod', foo: 'bar') + request.on('retry', retryHandler) + request.on('error', errorHandler) + request.on('success', successHandler) + request.on('complete', completeHandler) + if callback + request.on 'complete', (resp) -> + callback.call(resp, resp.error, resp.data) + request.send() + else + request + + describe 'validate', -> + it 'sends error event if credentials are not set', -> + errorHandler = createSpy() + request = makeRequest() + request.on('error', errorHandler) + + client.config.credentials.accessKeyId = null + request.send() + + client.config.credentials.accessKeyId = 'akid' + client.config.credentials.secretAccessKey = null + request.send() + + expect(errorHandler).toHaveBeenCalled() + AWS.util.arrayEach errorHandler.calls, (call) -> + expect(call.args[0].error instanceof Error).toBeTruthy() + expect(call.args[0].error.code).toEqual('SigningError') + expect(call.args[0].error.message).toMatch(/Missing credentials in config/) + + it 'sends error event if region is not set', -> + client.config.region = null + request = makeRequest(->) + + call = errorHandler.calls[0] + expect(errorHandler).toHaveBeenCalled() + expect(call.args[0].error instanceof Error).toBeTruthy() + expect(call.args[0].error.code).toEqual('SigningError') + expect(call.args[0].error.message).toMatch(/Missing region in config/) + + describe 'httpData', -> + beforeEach -> + helpers.mockHttpResponse 200, {}, ['FOO', 'BAR', 'BAZ', 'QUX'] + + it 'emits httpData event on each chunk', -> + calls = [] + + # register httpData event + request = makeRequest() + request.on('httpData', (chunk) -> calls.push(chunk)) + request.send() + + expect(calls).toEqual(['FOO', 'BAR', 'BAZ', 'QUX']) + + it 'clears default httpData event if another is added (allow streaming)', -> + request = makeRequest() + request.on('httpData', ->) + response = request.send() + + expect(response.httpResponse.body).toEqual('') + + describe 'retry', -> + it 'retries a request with a set maximum retries', -> + sendHandler = createSpy('send') + client.config.maxRetries = 10 + + # fail every request with a fake networking error + helpers.mockHttpResponse + code: 'NetworkingError', message: 'Cannot connect' + + request = makeRequest() + request.on('send', sendHandler) + response = request.send() + + expect(retryHandler).toHaveBeenCalled() + expect(errorHandler).toHaveBeenCalled() + expect(completeHandler).toHaveBeenCalled() + expect(successHandler).not.toHaveBeenCalled() + expect(response.retryCount).toEqual(client.config.maxRetries); + expect(sendHandler.calls.length).toEqual(client.config.maxRetries + 1) + + it 'retries with falloff', -> + helpers.mockHttpResponse + code: 'NetworkingError', message: 'Cannot connect' + makeRequest(->) + expect(delays).toEqual([30, 60, 120]) + + it 'retries if status code is >= 500', -> + helpers.mockHttpResponse 500, {}, '' + + makeRequest (err) -> + expect(err).toEqual + code: 500, + message: null, + statusCode: 500 + retryable: true + expect(@retryCount). + toEqual(client.config.maxRetries) + + it 'should not emit error if retried fewer than maxRetries', -> + spyOn(AWS.HttpClient, 'getInstance').andReturn handleRequest: (req, resp) -> + if resp.retryCount < 2 + req.emit('httpError', {code: 'NetworkingError', message: "FAIL!"}, resp) + else + req.emit('httpHeaders', resp.retryCount < 2 ? 500 : 200, {}, resp) + req.emit('httpData', 'foo', resp) + req.emit('httpDone', resp) + + response = makeRequest(->) + + expect(totalWaited).toEqual(90) + expect(response.retryCount).toBeLessThan(client.config.maxRetries) + expect(response.data).toEqual('foo') + expect(errorHandler).not.toHaveBeenCalled() + + describe 'success', -> + it 'emits success on a successful response', -> + # fail every request with a fake networking error + helpers.mockHttpResponse 200, {}, 'Success!' + + response = makeRequest(->) + + expect(retryHandler).not.toHaveBeenCalled() + expect(errorHandler).not.toHaveBeenCalled() + expect(completeHandler).toHaveBeenCalled() + expect(successHandler).toHaveBeenCalled() + expect(response.retryCount).toEqual(0); + + describe 'error', -> + it 'emits error if error found and should not be retrying', -> + # fail every request with a fake networking error + helpers.mockHttpResponse 400, {}, '' + + response = makeRequest(->) + + expect(retryHandler).not.toHaveBeenCalled() + expect(errorHandler).toHaveBeenCalled() + expect(completeHandler).toHaveBeenCalled() + expect(successHandler).not.toHaveBeenCalled() + expect(response.retryCount).toEqual(0); + + it 'emits error if an error is set in extractError', -> + error = code: 'ParseError', message: 'error message' + extractDataHandler = createSpy('extractData') + + helpers.mockHttpResponse 400, {}, '' + + request = makeRequest() + request.on('extractData', extractDataHandler) + request.on('extractError', (resp) -> resp.error = error) + response = request.send() + + expect(response.error).toBe(error) + expect(extractDataHandler).not.toHaveBeenCalled() + expect(retryHandler).not.toHaveBeenCalled() + expect(errorHandler).toHaveBeenCalled() + expect(completeHandler).toHaveBeenCalled() diff --git a/test/unit/http_request.spec.coffee b/test/unit/http_request.spec.coffee index d377de33e0..846d2eedb6 100644 --- a/test/unit/http_request.spec.coffee +++ b/test/unit/http_request.spec.coffee @@ -30,8 +30,8 @@ describe 'AWS.HttpRequest', -> it 'provides headers with a default user agent', -> expect(request.headers).toEqual({ 'User-Agent': AWS.util.userAgent() }) - it 'defaults body to undefined', -> - expect(request.body).toEqual(undefined) + it 'defaults body to empty string', -> + expect(request.body).toEqual('') it 'defaults endpoint to undefined', -> expect(request.endpoint).toEqual(undefined) diff --git a/test/unit/json_client.spec.coffee b/test/unit/json_client.spec.coffee deleted file mode 100644 index 5d8281eb35..0000000000 --- a/test/unit/json_client.spec.coffee +++ /dev/null @@ -1,152 +0,0 @@ -# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"). You -# may not use this file except in compliance with the License. A copy of -# the License is located at -# -# http://aws.amazon.com/apache2.0/ -# -# or in the "license" file accompanying this file. This file is -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF -# ANY KIND, either express or implied. See the License for the specific -# language governing permissions and limitations under the License. - -AWS = require('../../lib/core') -require('../../lib/json_client') - -describe 'AWS.JSONClient', -> - - MockJSONClient = AWS.util.inherit AWS.JSONClient, - constructor: (config) -> - this.serviceName = 'mockservice' - AWS.JSONClient.call(this, config) - - MockJSONClient.prototype.api = - targetPrefix: 'prefix-' - operations: - operationName: - n: 'OperationName' - - AWS.Client.defineMethods(MockJSONClient) - - svc = new MockJSONClient() - - it 'defines a method for each api operation', -> - expect(typeof svc.operationName).toEqual('function') - - describe 'buildRequest', -> - - req = svc.buildRequest('operationName', {}) - - it 'should use POST method requests', -> - expect(req.method).toEqual('POST') - - it 'should perform all operations on root (/)', -> - expect(req.path).toEqual('/') - - it 'should set Content-Type header', -> - expect(req.headers['Content-Type']).toEqual('application/x-amz-json-1.0') - - it 'should set X-Amz-Target header', -> - expect(req.headers['X-Amz-Target']).toEqual('prefix-OperationName') - - it 'should set Content-Length to body length', -> - expect(req.body).toEqual('{}') - expect(req.headers['Content-Length']).toEqual(2) - - it 'should set the body to JSON serialized params', -> - req = svc.buildRequest('operationName', { foo: 'bar' }) - expect(req.body).toEqual('{"foo":"bar"}') - - it 'should preserve numeric types', -> - req = svc.buildRequest('operationName', { count: 3 }) - expect(req.body).toEqual('{"count":3}') - - describe 'parseResponse', -> - - parse = (callback) -> - svc.parseResponse resp, 'operationName', (error,data) -> - callback.call(this, error, data) - - resp = new AWS.HttpResponse() - resp.headers = {} - - describe 'with data', -> - - beforeEach -> - resp.statusCode = 200 - - it 'JSON parses http response bodies', -> - resp.body = '{"a":1, "b":"xyz"}' - parse (error, data) -> - expect(error).toEqual(null) - expect(data).toEqual({a:1, b:'xyz'}) - - it 'returns an empty object when the body is an empty string', -> - resp.body = '' - parse (error, data) -> - expect(error).toEqual(null) - expect(data).toEqual({}) - - it 'returns an empty object when the body is null', -> - resp.body = null - parse (error, data) -> - expect(error).toEqual(null) - expect(data).toEqual({}) - - describe 'with error', -> - - beforeEach -> - resp.statusCode = 500 - - it 'removes prefixes from the error code', -> - resp.body = '{"__type":"com.amazon.coral.service#ErrorCode" }' - parse (error, data) -> - expect(error instanceof Error).toBeTruthy() - expect(error.code).toEqual('ErrorCode') - expect(data).toEqual(null) - - it 'returns the full code when a # is not present', -> - resp.body = '{"__type":"ErrorCode" }' - parse (error, data) -> - expect(error instanceof Error).toBeTruthy() - expect(error.code).toEqual('ErrorCode') - expect(data).toEqual(null) - - it 'returns the status code when the body is blank', -> - resp.body = null - parse (error, data) -> - expect(error instanceof Error).toBeTruthy() - expect(error.code).toEqual(500) - expect(error.message).toEqual(null) - expect(data).toEqual(null) - - it 'returns null for the message when not present', -> - resp.body = '{"__type":"ErrorCode" }' - parse (error, data) -> - expect(error instanceof Error).toBeTruthy() - expect(error.message).toEqual(null) - expect(data).toEqual(null) - - it 'returns the message when present', -> - resp.body = '{"__type":"ErrorCode", "message":"Error Message" }' - parse (error, data) -> - expect(error instanceof Error).toBeTruthy() - expect(error.message).toEqual('Error Message') - expect(data).toEqual(null) - - # DynamoDB and SWF return error message properties with different case - it 'returns the message when the message property is upper-cased', -> - resp.body = '{"__type":"ErrorCode", "Message":"Error Message" }' - parse (error, data) -> - expect(error instanceof Error).toBeTruthy() - expect(error.message).toEqual('Error Message') - expect(data).toEqual(null) - - it 'returns a special message for RequestEntityToLarge errors', -> - resp.body = '{"__type":"RequestEntityTooLarge" }' - parse (error, data) -> - expect(error instanceof Error).toBeTruthy() - expect(error.message).toEqual('Request body must be less than 1 MB') - expect(data).toEqual(null) - diff --git a/test/unit/query_client.spec.coffee b/test/unit/query_client.spec.coffee deleted file mode 100644 index 14fb98b7b1..0000000000 --- a/test/unit/query_client.spec.coffee +++ /dev/null @@ -1,115 +0,0 @@ -# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"). You -# may not use this file except in compliance with the License. A copy of -# the License is located at -# -# http://aws.amazon.com/apache2.0/ -# -# or in the "license" file accompanying this file. This file is -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF -# ANY KIND, either express or implied. See the License for the specific -# language governing permissions and limitations under the License. - -AWS = require('../../lib/core') -require('../../lib/query_client') - -describe 'AWS.QueryClient', -> - - MockQueryClient = AWS.util.inherit AWS.QueryClient, - constructor: (config) -> - this.serviceName = 'mockservice' - AWS.QueryClient.call(this, config) - - MockQueryClient.prototype.api = - apiVersion: '2012-01-01' - operations: - operationName: - n: 'OperationName' - i: {m:{Input:{m:{}}}} - o: {Data:{t:'o',m:{Name:{t:'s'},Count:{t:'i'}}}} - - AWS.Client.defineMethods(MockQueryClient) - - svc = new MockQueryClient() - - it 'defines a method for each api operation', -> - expect(typeof svc.operationName).toEqual('function') - - describe 'buildRequest', -> - - req = svc.buildRequest('operationName', { Input:'foo+bar: yuck/baz=~' }) - - it 'should use POST method requests', -> - expect(req.method).toEqual('POST') - - it 'should perform all operations on root (/)', -> - expect(req.path).toEqual('/') - - it 'should set Content-Type header', -> - expect(req.headers['Content-Type']). - toEqual('application/x-www-form-urlencoded; charset=utf-8') - - it 'should add the api version param', -> - expect(req.params.toString()).toMatch(/Version=2012-01-01/) - - it 'should add the operation name as Action', -> - expect(req.params.toString()).toMatch(/Action=OperationName/) - - it 'should uri encode params properly', -> - expect(req.params.toString()).toMatch(/foo%2Bbar%3A%20yuck%2Fbaz%3D~/); - - describe 'parseResponse', -> - - resp = new AWS.HttpResponse() - - parse = (callback) -> - svc.parseResponse resp, 'operationName', (error,data) -> - callback.call(this, error, data) - - describe 'with data', -> - - beforeEach -> - resp.statusCode = 200 - resp.body = """ - - - abc - 123 - - - """ - - it 'parses the response using the operation output rules', -> - parse (error, data) -> - expect(error).toEqual(null) - expect(data).toEqual({Data:{Name:'abc',Count:123}}) - - describe 'with error', -> - - beforeEach -> - resp.statusCode = 400 - resp.body = """ - - InvalidArgument - Provided param is bad - - """ - - it 'extracts the error code', -> - parse (error, data) -> - expect(error.code).toEqual('InvalidArgument') - expect(data).toEqual(null) - - it 'extracts the error message', -> - parse (error, data) -> - expect(error.message).toEqual('Provided param is bad') - expect(data).toEqual(null) - - it 'returns an empty error when the body is blank', -> - resp.body = '' - parse (error, data) -> - expect(error.code).toEqual(400) - expect(error.message).toEqual(null) - expect(data).toEqual(null) - diff --git a/test/unit/request_handler.spec.coffee b/test/unit/request_handler.spec.coffee deleted file mode 100644 index 4cafbc18d8..0000000000 --- a/test/unit/request_handler.spec.coffee +++ /dev/null @@ -1,203 +0,0 @@ -# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"). You -# may not use this file except in compliance with the License. A copy of -# the License is located at -# -# http://aws.amazon.com/apache2.0/ -# -# or in the "license" file accompanying this file. This file is -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF -# ANY KIND, either express or implied. See the License for the specific -# language governing permissions and limitations under the License. - -helpers = require('../helpers') -AWS = helpers.AWS -MockClient = helpers.MockClient - -describe 'AWS.RequestHandler', -> - - oldSetTimeout = setTimeout - config = null; client = null; totalWaited = null; delays = [] - response = null; request = null; handler = null - - beforeEach -> - # Mock the timer manually (jasmine.Clock does not work in node) - `setTimeout = jasmine.createSpy('setTimeout');` - setTimeout.andCallFake (callback, delay) -> - totalWaited += delay - delays.push(delay) - callback() - - totalWaited = 0 - delays = [] - client = new MockClient(maxRetries: 3) - client.config.credentials = AWS.util.copy(client.config.credentials) - request = new AWS.AWSRequest(client, 'mockMethod', {foo:'bar'}) - response = request.awsResponse - handler = new AWS.RequestHandler(request) - - # Useful spies - spyOn(request, 'notifyFail') - spyOn(request, 'notifyDone') - spyOn(AWS.HttpClient, 'getInstance') - - # Safely tear down setTimeout hack - afterEach -> `setTimeout = oldSetTimeout` - - describe 'makeRequest', -> - it 'sends fail event if credentials are not set', -> - client.config.credentials.accessKeyId = null - handler.makeRequest() - - client.config.credentials.accessKeyId = 'akid' - client.config.credentials.secretAccessKey = null - handler.makeRequest() - - expect(request.notifyFail).toHaveBeenCalled() - AWS.util.arrayEach request.notifyFail.calls, (call) -> - expect(call.args[0] instanceof Error).toBeTruthy() - expect(call.args[0].code).toEqual('SigningError') - expect(call.args[0].message).toMatch(/Missing credentials in config/) - - it 'sends fail event if region is not set', -> - handler.client.config.region = null - handler.makeRequest() - - call = request.notifyFail.calls[0] - expect(request.notifyFail).toHaveBeenCalled() - expect(call.args[0] instanceof Error).toBeTruthy() - expect(call.args[0].code).toEqual('SigningError') - expect(call.args[0].message).toMatch(/Missing region in config/) - - describe 'handleHttpData', -> - - beforeEach -> - AWS.HttpClient.getInstance.andReturn handleRequest: (req, cb) -> - cb.onHeaders(200, {}) - cb.onData("FOO") - cb.onData("BAR") - cb.onData("BAZ") - cb.onData("QUX") - cb.onEnd() - - it 'notifies data if there are promise callbacks', -> - - calls = [] - - # Add a promise callback - request.data((resp) -> calls.push(resp.data)) - - handler.makeRequest() - - expect(request.awsResponse.httpResponse.body).toEqual(null) - expect(calls).toEqual(['FOO', 'BAR', 'BAZ', 'QUX']) - - it 'does not notify if there are no callbacks registered', -> - - spyOn(request, 'notifyData') - handler.makeRequest() - expect(request.notifyData).not.toHaveBeenCalled() - - describe 'handleHttpResponse', -> - - it 'should retry a request with a set maximum retries', -> - - client.config.maxRetries = 10 - - # fail every request with a fake networking error - AWS.HttpClient.getInstance.andReturn handleRequest: (req, cb) -> - cb.onError(code: 'NetworkingError', message: "FAIL!") - - handler.makeRequest() - - expect(response.retryCount).toEqual(client.config.maxRetries + 1); - expect(request.notifyFail).toHaveBeenCalled() - expect(request.notifyDone).not.toHaveBeenCalled() - - it 'should retry with falloff', -> - AWS.HttpClient.getInstance.andReturn handleRequest: (req, cb) -> - cb.onError(code: 'NetworkingError', message: "FAIL!") - - handler.makeRequest() - - expect(delays).toEqual([30, 60, 120]) - - it 'should retry if status code is >= 500', -> - - AWS.HttpClient.getInstance.andReturn handleRequest: (req, cb) -> - cb.onHeaders(500, {}) - cb.onEnd() - - handler.makeRequest() - - expect(request.notifyFail).toHaveBeenCalledWith( - code: 500, - message: null, - statusCode: 500 - retryable: true) - - expect(request.notifyDone).not.toHaveBeenCalled() - expect(response.retryCount).toEqual(client.config.maxRetries + 1); - - it 'should not call notifyFail if retried fewer than maxRetries', -> - - AWS.HttpClient.getInstance.andReturn handleRequest: (req, cb) -> - if response.retryCount < 2 - cb.onError(code: 'NetworkingError', message: "FAIL!") - else - cb.onHeaders(response.retryCount < 2 ? 500 : 200, {}) - cb.onData('{"data":"BAR"}') - cb.onEnd() - - handler.makeRequest() - - expect(totalWaited).toEqual(90) - expect(response.retryCount).toBeLessThan(client.config.maxRetries); - - it 'notifies done on a successful response', -> - - spyOn(handler, 'retryRequest') - - AWS.HttpClient.getInstance.andReturn handleRequest: (req, cb) -> - cb.onHeaders(200, {}) - cb.onData("Success!") - cb.onEnd() - - handler.makeRequest() - - expect(handler.retryRequest).not.toHaveBeenCalled() - expect(request.notifyFail).not.toHaveBeenCalled() - expect(request.notifyDone).toHaveBeenCalledWith("Success!") - - it 'should notifyFail if error found and should not be retrying', -> - - AWS.HttpClient.getInstance.andReturn handleRequest: (req, cb) -> - cb.onHeaders(400, {}) - cb.onEnd() - - handler.makeRequest() - - expect(request.notifyFail).toHaveBeenCalled() - expect(request.notifyDone).not.toHaveBeenCalled() - - it 'notifies fail if an error is thrown', -> - - # throw by the client while parsing a response - error = { error: 'ParseError', message: 'error message' } - - AWS.HttpClient.getInstance.andReturn handleRequest: (req, cb) -> - cb.onHeaders(200, {}) - cb.onData("Success!") - cb.onEnd() - - spyOn(client, 'parseResponse').andThrow(error) - spyOn(handler, 'retryRequest') - - try - handler.makeRequest({}) - catch e - expect(e).toBe(error) - - expect(handler.retryRequest).not.toHaveBeenCalled() - expect(request.notifyDone).not.toHaveBeenCalled() diff --git a/test/unit/rest_client.spec.coffee b/test/unit/rest_client.spec.coffee deleted file mode 100644 index 003b89e17f..0000000000 --- a/test/unit/rest_client.spec.coffee +++ /dev/null @@ -1,156 +0,0 @@ -# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"). You -# may not use this file except in compliance with the License. A copy of -# the License is located at -# -# http://aws.amazon.com/apache2.0/ -# -# or in the "license" file accompanying this file. This file is -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF -# ANY KIND, either express or implied. See the License for the specific -# language governing permissions and limitations under the License. - -AWS = require('../../lib/core') -require('../../lib/rest_xml_client') - -describe 'AWS.RESTClient', -> - - operation = null - - MockRESTClient = AWS.util.inherit AWS.RESTClient, - constructor: (config) -> - this.serviceName = 'mockservice' - AWS.RESTClient.call(this, config) - - beforeEach -> - - MockRESTClient.prototype.api = - operations: - sampleOperation: - m: 'POST' # http method - u: '/' # uri - i: null # no params - o: null # no ouputs - - AWS.Client.defineMethods(MockRESTClient) - - operation = MockRESTClient.prototype.api.operations.sampleOperation - - svc = new MockRESTClient() - - it 'defines a method for each api operation', -> - expect(typeof svc.sampleOperation).toEqual('function') - - describe 'buildRequest', -> - - buildRequest = (params) -> - svc.buildRequest('sampleOperation', params) - - it 'returns an http request', -> - req = svc.buildRequest('sampleOperation', {}) - expect(req.constructor).toBe(AWS.HttpRequest) - - describe 'method', -> - - it 'populates method from the operation', -> - operation.m = 'GET' - expect(buildRequest().method).toEqual('GET') - - describe 'uri', -> - - it 'populates uri from the operation', -> - operation.u = '/path' - expect(buildRequest().path).toEqual('/path') - - it 'replaces param placeholders', -> - operation.u = '/Owner/{Id}' - operation.i = {m:{Id:{l:'uri'}}} - expect(buildRequest({'Id': 'abc'}).path).toEqual('/Owner/abc') - - it 'can replace multiple path placeholders', -> - operation.u = '/{Id}/{Count}' - operation.i = {m:{Id:{l:'uri'},Count:{t:'i',l:'uri'}}} - expect(buildRequest({Id:'abc',Count:123}).path).toEqual('/abc/123') - - it 'performs querystring param replacements', -> - operation.u = '/path?id-param={Id}' - operation.i = {m:{Id:{l:'uri'}}} - expect(buildRequest({Id:'abc'}).path).toEqual('/path?id-param=abc') - - it 'omits querystring when param is not provided', -> - operation.u = '/path?id-param={Id}' - operation.i = {m:{Id:{l:'uri'}}} - expect(buildRequest().path).toEqual('/path') - - it 'accpets multiple query params with uri params', -> - operation.u = '/{Abc}/{Xyz}?foo={Foo}&bar={Bar}' - operation.i = {m:{Abc:{l:'uri'},Xyz:{l:'uri'},Foo:{l:'uri'},Bar:{l:'uri'}}} - params = { Abc:'abc', Xyz:'xyz', Bar:'bar' } # omitted Foo - expect(buildRequest(params).path).toEqual('/abc/xyz?bar=bar') - - it 'uri escapes params in both path and querystring', -> - operation.u = '/{Path}?query={Query}' - operation.i = {m:{Path:{l:'uri'},Query:{l:'uri'}}} - params = { Path:'a b', Query:'a/b' } - expect(buildRequest(params).path).toEqual('/a%20b?query=a%2Fb') - - describe 'headers', -> - - it 'populates default headers', -> - req = new AWS.HttpRequest() - expect(buildRequest().headers).toEqual(req.headers) - - it 'populates the headers with present params', -> - operation.i = {m:{ACL:{l:'header',n:'x-amz-acl'}}} - headers = buildRequest(ACL:'public-read').headers - expect(headers['x-amz-acl']).toEqual('public-read') - - it 'works with map types', -> - operation.i = {m:{Metadata:{t:'m',l:'header',n:'x-amz-meta-'}}} - params = - Metadata: - foo: 'bar' - abc: 'xyz' - headers = buildRequest(params).headers - expect(headers['x-amz-meta-foo']).toEqual('bar') - expect(headers['x-amz-meta-abc']).toEqual('xyz') - - describe 'extractData', -> - - extractData = (resp) -> - svc.extractData(resp, 'sampleOperation') - - describe 'headers', -> - - it 'extracts header values', -> - operation.o = {ContentType:{t:'s',l:'header',n:'content-type'}} - resp = new AWS.HttpResponse() - resp.headers['content-type'] = 'text/plain' - expect(extractData(resp).ContentType).toEqual('text/plain') - - it 'extracts headers when the rule name is camel-cased', -> - operation.o = {ContentType:{t:'s',l:'header',n:'Content-Type'}} - resp = new AWS.HttpResponse() - resp.headers['content-type'] = 'text/plain' - expect(extractData(resp).ContentType).toEqual('text/plain') - - it 'extracts headers when the header name is camel-cased', -> - operation.o = {ContentType:{t:'s',l:'header',n:'content-type'}} - resp = new AWS.HttpResponse() - resp.headers['Content-Type'] = 'text/plain' - expect(extractData(resp).ContentType).toEqual('text/plain') - - describe 'status code', -> - - it 'extracts the http status when instructed to', -> - operation.o = {Result:{t:'i',l:'status'}} - resp = new AWS.HttpResponse() - resp.statusCode = 200 - expect(extractData(resp).Result).toEqual(200) - - it 'casts string status codes to integers', -> - operation.o = {Result:{t:'i',l:'status'}} - resp = new AWS.HttpResponse() - resp.statusCode = '202' - expect(extractData(resp).Result).toEqual(202) diff --git a/test/unit/rest_xml_client.spec.coffee b/test/unit/rest_xml_client.spec.coffee deleted file mode 100644 index ad47b36578..0000000000 --- a/test/unit/rest_xml_client.spec.coffee +++ /dev/null @@ -1,246 +0,0 @@ -# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. -# -# Licensed under the Apache License, Version 2.0 (the "License"). You -# may not use this file except in compliance with the License. A copy of -# the License is located at -# -# http://aws.amazon.com/apache2.0/ -# -# or in the "license" file accompanying this file. This file is -# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF -# ANY KIND, either express or implied. See the License for the specific -# language governing permissions and limitations under the License. - -helpers = require('../helpers'); AWS = helpers.AWS -require('../../lib/rest_xml_client') - -describe 'AWS.RESTXMLClient', -> - - xmlns = 'http://mockservice.com/xmlns' - - operation = null - - MockRESTXMLClient = AWS.util.inherit AWS.RESTXMLClient, - constructor: (config) -> - this.serviceName = 'mockservice' - AWS.RESTXMLClient.call(this, config) - - beforeEach -> - - MockRESTXMLClient.prototype.api = - xmlNamespace: xmlns - operations: - sampleOperation: - m: 'POST' # http method - u: '/' # uri - i: null # no params - o: null # no ouputs - - AWS.Client.defineMethods(MockRESTXMLClient) - - operation = MockRESTXMLClient.prototype.api.operations.sampleOperation - - svc = new MockRESTXMLClient() - - it 'defines a method for each api operation', -> - expect(typeof svc.sampleOperation).toEqual('function') - - describe 'buildRequest', -> - - buildRequest = (params) -> - svc.buildRequest('sampleOperation', params) - - describe 'empty bodies', -> - - it 'defaults body to null when there are no inputs', -> - operation.i = null - expect(buildRequest().body).toEqual(null) - - it 'defaults body to null when all inputs are uri or header values', -> - operation.u = '/{Bucket}' - operation.i = {m:{Bucket:{l:'uri',r:1},ACL:{n:'x-amz-acl',l:'header'}}} - params = { Bucket:'abc', ACL:'canned-acl' } - req = buildRequest(params) - expect(req.body).toEqual(null) - expect(req.path).toEqual('/abc') - expect(req.headers['x-amz-acl']).toEqual('canned-acl') - - it 'includes Content-Length header if body is empty', -> - operation.i = null - req = buildRequest() - expect(req.body).toEqual(null) - expect(req.headers['Content-Length']).toEqual(0) - - describe 'string bodies', -> - - it 'populates the body with string types directly', -> - operation.u = '/{Bucket}' - operation.i = {m:{Bucket:{l:'uri',r:1},Data:{t:'s',l:'body'}}} - params = { Bucket: 'bucket-name', Data: 'abc' } - expect(buildRequest(params).body).toEqual('abc') - - describe 'xml bodies', -> - - it 'populates the body with XML from the params w/out a location', -> - operation.u = '/{Bucket}?next-marker={Marker}&limit={Limit}' - operation.i = - n: 'ComplexRequest', # the root xml element name - m: - Bucket: # uri path param - t: 's' - l: 'uri' - r: 1 - Marker: # uri querystring param - t: 's' - l: 'uri' - Limit: # uri querystring integer param - t: 'i' - l: 'uri' - ACL: # header string param - t: 's' - l: 'header' - n: 'x-amz-acl' - Metadata: # header map param - t: 'm' - l: 'header' - n: 'x-amz-meta-' - Config: # structure of mixed tpyes - t: 'o' - r: 1 - m: - Abc: {} # string - Locations: # array of strings - t: 'a' - m: - t: 's' - n: 'Location' - Data: # array of structures - t: 'a' - m: - t: 'o' - m: - Foo: {} - Bar: {} - Enabled: # boolean - t: 'b' - - params = { - Enabled: true - ACL: 'canned-acl' - Config: - Abc: 'abc' - Locations: ['a', 'b', 'c'] - Data: [ - { Foo:'foo1', Bar:'bar1' }, - { Foo:'foo2', Bar:'bar2' }, - ] - Bucket: 'bucket-name' - Marker: 'marker' - Limit: 123 - Metadata: - abc: 'xyz' - mno: 'hjk' - } - - xml = """ - - - abc - - a - b - c - - - - foo1 - bar1 - - - foo2 - bar2 - - - - true - - """ - - req = buildRequest(params) - expect(req.method).toEqual('POST') - expect(req.path).toEqual('/bucket-name?next-marker=marker&limit=123') - expect(req.headers['x-amz-acl']).toEqual('canned-acl') - expect(req.headers['x-amz-meta-abc']).toEqual('xyz') - expect(req.headers['x-amz-meta-mno']).toEqual('hjk') - helpers.matchXML(req.body, xml) - - it 'omits the body xml when body params are not present', -> - operation.u = '/{Bucket}' - operation.i = {n:'CreateBucketConfig', m:{Bucket:{l:'uri',r:1},Config:{}}} - params = { Bucket:'abc' } # omitting Config purposefully - req = buildRequest(params) - expect(req.body).toEqual(null) - expect(req.path).toEqual('/abc') - - describe 'parseResponse', -> - - resp = null - - beforeEach -> - resp = new AWS.HttpResponse() - - parse = (callback) -> - svc.parseResponse resp, 'sampleOperation', (error,data) -> - callback.call(this, error, data) - - describe 'with data', -> - - extractData = (resp) -> - svc.extractData(resp, 'sampleOperation') - - it 'parses the xml body', -> - operation.o = {Foo:{},Bar:{t:'a',m:{n:'Item'}}} - resp = new AWS.HttpResponse() - resp.status = 200 - resp.body = """ - - foo - - a - b - c - - - """ - expect(extractData(resp)).toEqual({Foo:'foo', Bar:['a', 'b', 'c']}) - - describe 'with error', -> - - beforeEach -> - resp.statusCode = 400 - resp.body = """ - - InvalidArgument - Provided param is bad - - """ - - it 'extracts the error code', -> - parse (error, data) -> - expect(error instanceof Error).toBeTruthy() - expect(error.code).toEqual('InvalidArgument') - expect(data).toEqual(null) - - it 'extracts the error message', -> - parse (error, data) -> - expect(error instanceof Error).toBeTruthy() - expect(error.message).toEqual('Provided param is bad') - expect(data).toEqual(null) - - it 'returns an empty error when the body is blank', -> - resp.body = '' - parse (error, data) -> - expect(error instanceof Error).toBeTruthy() - expect(error.code).toEqual(400) - expect(error.message).toEqual(null) - expect(data).toEqual(null) diff --git a/test/unit/service_interface/json.spec.coffee b/test/unit/service_interface/json.spec.coffee new file mode 100644 index 0000000000..6f373bde3f --- /dev/null +++ b/test/unit/service_interface/json.spec.coffee @@ -0,0 +1,144 @@ +# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +AWS = require('../../../lib/core') +require('../../../lib/service_interface/json') + +describe 'AWS.ServiceInterface.Json', -> + + MockJSONClient = AWS.util.inherit AWS.Client, + serviceName: 'mockservice' + api: + targetPrefix: 'prefix-' + operations: + operationName: + n: 'OperationName' + + AWS.Client.defineMethods(MockJSONClient) + + request = null + response = null + svc = eval(@description) + + beforeEach -> + client = new MockJSONClient(region: 'region') + request = new AWS.Request(client, 'operationName') + response = new AWS.Response(request) + + describe 'buildRequest', -> + buildRequest = -> + svc.buildRequest(request) + + it 'should use POST method requests', -> + buildRequest() + expect(request.httpRequest.method).toEqual('POST') + + it 'should perform all operations on root (/)', -> + buildRequest() + expect(request.httpRequest.path).toEqual('/') + + it 'should set Content-Type header', -> + buildRequest() + expect(request.httpRequest.headers['Content-Type']). + toEqual('application/x-amz-json-1.0') + + it 'should set X-Amz-Target header', -> + buildRequest() + expect(request.httpRequest.headers['X-Amz-Target']). + toEqual('prefix-OperationName') + + it 'should set Content-Length to body length', -> + buildRequest() + expect(request.httpRequest.body).toEqual('{}') + expect(request.httpRequest.headers['Content-Length']).toEqual(2) + + it 'should set the body to JSON serialized params', -> + request.params = foo: 'bar' + buildRequest() + expect(request.httpRequest.body).toEqual('{"foo":"bar"}') + + it 'should preserve numeric types', -> + request.params = count: 3 + buildRequest() + expect(request.httpRequest.body).toEqual('{"count":3}') + + describe 'extractError', -> + extractError = (body) -> + response.httpResponse.statusCode = 500 + response.httpResponse.body = body + svc.extractError(response) + + it 'removes prefixes from the error code', -> + extractError '{"__type":"com.amazon.coral.service#ErrorCode" }' + expect(response.error instanceof Error).toBeTruthy() + expect(response.error.code).toEqual('ErrorCode') + expect(response.data).toEqual(null) + + it 'returns the full code when a # is not present', -> + extractError '{"__type":"ErrorCode" }' + expect(response.error instanceof Error).toBeTruthy() + expect(response.error.code).toEqual('ErrorCode') + expect(response.data).toEqual(null) + + it 'returns the status code when the body is blank', -> + extractError null + expect(response.error instanceof Error).toBeTruthy() + expect(response.error.code).toEqual(500) + expect(response.error.message).toEqual(null) + expect(response.data).toEqual(null) + + it 'returns null for the message when not present', -> + extractError '{"__type":"ErrorCode" }' + expect(response.error instanceof Error).toBeTruthy() + expect(response.error.message).toEqual(null) + expect(response.data).toEqual(null) + + it 'returns the message when present', -> + extractError '{"__type":"ErrorCode", "message":"Error Message" }' + expect(response.error instanceof Error).toBeTruthy() + expect(response.error.message).toEqual('Error Message') + expect(response.data).toEqual(null) + + # DynamoDB and SWF return error message properties with different case + it 'returns the message when the message property is upper-cased', -> + extractError '{"__type":"ErrorCode", "Message":"Error Message" }' + expect(response.error instanceof Error).toBeTruthy() + expect(response.error.message).toEqual('Error Message') + expect(response.data).toEqual(null) + + it 'returns a special message for RequestEntityToLarge errors', -> + extractError '{"__type":"RequestEntityTooLarge" }' + expect(response.error instanceof Error).toBeTruthy() + expect(response.error.message).toEqual('Request body must be less than 1 MB') + expect(response.data).toEqual(null) + + describe 'extractData', -> + extractData = (body) -> + response.httpResponse.statusCode = 200 + response.httpResponse.body = body + svc.extractData(response) + + it 'JSON parses http response bodies', -> + extractData '{"a":1, "b":"xyz"}' + expect(response.error).toEqual(null) + expect(response.data).toEqual({a:1, b:'xyz'}) + + it 'returns an empty object when the body is an empty string', -> + extractData '' + expect(response.error).toEqual(null) + expect(response.data).toEqual({}) + + it 'returns an empty object when the body is null', -> + extractData null + expect(response.error).toEqual(null) + expect(response.data).toEqual({}) diff --git a/test/unit/service_interface/query.spec.coffee b/test/unit/service_interface/query.spec.coffee new file mode 100644 index 0000000000..b3784b1165 --- /dev/null +++ b/test/unit/service_interface/query.spec.coffee @@ -0,0 +1,115 @@ +# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +AWS = require('../../../lib/core') +require('../../../lib/service_interface/query') + +describe 'AWS.ServiceInterface.Query', -> + + MockQueryClient = AWS.util.inherit AWS.Client, + serviceName: 'mockservice' + api: + apiVersion: '2012-01-01' + operations: + operationName: + n: 'OperationName' + i: {m:{Input:{m:{}}}} + o: {Data:{t:'o',m:{Name:{t:'s'},Count:{t:'i'}}}} + + AWS.Client.defineMethods(MockQueryClient) + + request = null + response = null + svc = eval(@description) + + beforeEach -> + client = new MockQueryClient({region:'region'}) + request = new AWS.Request(client, 'operationName') + response = new AWS.Response(request) + + describe 'buildRequest', -> + buildRequest = -> + request.params = Input: 'foo+bar: yuck/baz=~' + svc.buildRequest(request) + + it 'should use POST method requests', -> + buildRequest() + expect(request.httpRequest.method).toEqual('POST') + + it 'should perform all operations on root (/)', -> + buildRequest() + expect(request.httpRequest.path).toEqual('/') + + it 'should set Content-Type header', -> + buildRequest() + expect(request.httpRequest.headers['Content-Type']). + toEqual('application/x-www-form-urlencoded; charset=utf-8') + + it 'should add the api version param', -> + buildRequest() + expect(request.httpRequest.params.toString()). + toMatch(/Version=2012-01-01/) + + it 'should add the operation name as Action', -> + buildRequest() + expect(request.httpRequest.params.toString()). + toMatch(/Action=OperationName/) + + it 'should uri encode params properly', -> + buildRequest() + expect(request.httpRequest.params.toString()). + toMatch(/foo%2Bbar%3A%20yuck%2Fbaz%3D~/); + + describe 'extractError', -> + extractError = (body) -> + if body == undefined + body = """ + + InvalidArgument + Provided param is bad + + """ + response.httpResponse.statusCode = 400 + response.httpResponse.body = body + svc.extractError(response) + + it 'extracts the error code and message', -> + extractError() + expect(response.error instanceof Error).toBeTruthy() + expect(response.error.code).toEqual('InvalidArgument') + expect(response.error.message).toEqual('Provided param is bad') + expect(response.data).toEqual(null) + + it 'returns an empty error when the body is blank', -> + extractError('') + expect(response.error.code).toEqual(400) + expect(response.error.message).toEqual(null) + expect(response.data).toEqual(null) + + describe 'extractData', -> + extractData = (body) -> + response.httpResponse.statusCode = 200 + response.httpResponse.body = body + svc.extractData(response) + + it 'parses the response using the operation output rules', -> + extractData """ + + + abc + 123 + + + """ + expect(response.error).toEqual(null) + expect(response.data).toEqual({Data:{Name:'abc',Count:123}}) diff --git a/test/unit/query_param_serializer.spec.coffee b/test/unit/service_interface/query_param_serializer.spec.coffee similarity index 98% rename from test/unit/query_param_serializer.spec.coffee rename to test/unit/service_interface/query_param_serializer.spec.coffee index babfc39389..47238e4ae3 100644 --- a/test/unit/query_param_serializer.spec.coffee +++ b/test/unit/service_interface/query_param_serializer.spec.coffee @@ -11,8 +11,8 @@ # ANY KIND, either express or implied. See the License for the specific # language governing permissions and limitations under the License. -AWS = require('../../lib/core') -require('../../lib/query_client') +AWS = require('../../../lib/core') +require('../../../lib/service_interface/query') describe 'AWS.QueryParamSerializer', -> diff --git a/test/unit/service_interface/rest.spec.coffee b/test/unit/service_interface/rest.spec.coffee new file mode 100644 index 0000000000..8cae30a335 --- /dev/null +++ b/test/unit/service_interface/rest.spec.coffee @@ -0,0 +1,155 @@ +# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +AWS = require('../../../lib/core') +require('../../../lib/service_interface/rest') + +describe 'AWS.ServiceInterface.Rest', -> + + MockRESTClient = AWS.util.inherit AWS.Client, + serviceName: 'mockservice' + + operation = null + request = null + response = null + svc = eval(@description) + + beforeEach -> + MockRESTClient.prototype.api = + operations: + sampleOperation: + m: 'POST' # http method + u: '/' # uri + i: null # no params + o: null # no ouputs + + AWS.Client.defineMethods(MockRESTClient) + + operation = MockRESTClient.prototype.api.operations.sampleOperation + client = new MockRESTClient(region: 'region') + request = new AWS.Request(client, 'sampleOperation') + response = new AWS.Response(request) + + describe 'buildRequest', -> + buildRequest = (callback) -> + if callback + callback() + svc.buildRequest(request) + + describe 'method', -> + it 'populates method from the operation', -> + buildRequest -> + operation.m = 'GET' + expect(request.httpRequest.method).toEqual('GET') + + describe 'uri', -> + it 'populates uri from the operation', -> + buildRequest -> + operation.u = '/path' + expect(request.httpRequest.path).toEqual('/path') + + it 'replaces param placeholders', -> + buildRequest -> + operation.u = '/Owner/{Id}' + operation.i = {m:{Id:{l:'uri'}}} + request.params = Id: 'abc' + expect(request.httpRequest.path).toEqual('/Owner/abc') + + it 'can replace multiple path placeholders', -> + buildRequest -> + operation.u = '/{Id}/{Count}' + operation.i = {m:{Id:{l:'uri'},Count:{t:'i',l:'uri'}}} + request.params = Id: 'abc', Count: 123 + expect(request.httpRequest.path).toEqual('/abc/123') + + it 'performs querystring param replacements', -> + buildRequest -> + operation.u = '/path?id-param={Id}' + operation.i = {m:{Id:{l:'uri'}}} + request.params = Id: 'abc' + expect(request.httpRequest.path).toEqual('/path?id-param=abc') + + it 'omits querystring when param is not provided', -> + buildRequest -> + operation.u = '/path?id-param={Id}' + operation.i = {m:{Id:{l:'uri'}}} + expect(request.httpRequest.path).toEqual('/path') + + it 'accpets multiple query params with uri params', -> + buildRequest -> + operation.u = '/{Abc}/{Xyz}?foo={Foo}&bar={Bar}' + operation.i = {m:{Abc:{l:'uri'},Xyz:{l:'uri'},Foo:{l:'uri'},Bar:{l:'uri'}}} + request.params = { Abc:'abc', Xyz:'xyz', Bar:'bar' } # omitted Foo + expect(request.httpRequest.path).toEqual('/abc/xyz?bar=bar') + + it 'uri escapes params in both path and querystring', -> + buildRequest -> + operation.u = '/{Path}?query={Query}' + operation.i = {m:{Path:{l:'uri'},Query:{l:'uri'}}} + request.params = { Path:'a b', Query:'a/b' } + expect(request.httpRequest.path).toEqual('/a%20b?query=a%2Fb') + + describe 'headers', -> + it 'populates the headers with present params', -> + buildRequest -> + operation.i = {m:{ACL:{l:'header',n:'x-amz-acl'}}} + request.params = ACL: 'public-read' + expect(request.httpRequest.headers['x-amz-acl']).toEqual('public-read') + + it 'works with map types', -> + buildRequest -> + operation.i = {m:{Metadata:{t:'m',l:'header',n:'x-amz-meta-'}}} + request.params = + Metadata: + foo: 'bar' + abc: 'xyz' + expect(request.httpRequest.headers['x-amz-meta-foo']).toEqual('bar') + expect(request.httpRequest.headers['x-amz-meta-abc']).toEqual('xyz') + + describe 'extractData', -> + extractData = (callback) -> + if callback + callback() + svc.extractData(response) + + describe 'headers', -> + it 'extracts header values', -> + extractData -> + operation.o = {ContentType:{t:'s',l:'header',n:'content-type'}} + response.httpResponse.headers['content-type'] = 'text/plain' + expect(response.data.ContentType).toEqual('text/plain') + + it 'extracts headers when the rule name is camel-cased', -> + extractData -> + operation.o = {ContentType:{t:'s',l:'header',n:'Content-Type'}} + response.httpResponse.headers['content-type'] = 'text/plain' + expect(response.data.ContentType).toEqual('text/plain') + + it 'extracts headers when the header name is camel-cased', -> + extractData -> + operation.o = {ContentType:{t:'s',l:'header',n:'content-type'}} + response.httpResponse.headers['Content-Type'] = 'text/plain' + expect(response.data.ContentType).toEqual('text/plain') + + describe 'status code', -> + it 'extracts the http status when instructed to', -> + extractData -> + operation.o = {Result:{t:'i',l:'status'}} + response.httpResponse.statusCode = 200 + expect(response.data.Result).toEqual(200) + + it 'casts string status codes to integers', -> + extractData -> + operation.o = {Result:{t:'i',l:'status'}} + response.httpResponse.statusCode = '202' + expect(response.data.Result).toEqual(202) diff --git a/test/unit/service_interface/rest_xml.spec.coffee b/test/unit/service_interface/rest_xml.spec.coffee new file mode 100644 index 0000000000..dbd31ae275 --- /dev/null +++ b/test/unit/service_interface/rest_xml.spec.coffee @@ -0,0 +1,226 @@ +# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + +helpers = require('../../helpers'); AWS = helpers.AWS +require('../../../lib/service_interface/rest_xml') + +describe 'AWS.ServiceInterface.RestXml', -> + + MockRESTXMLClient = AWS.util.inherit AWS.Client, + serviceName: 'mockservice' + + xmlns = 'http://mockservice.com/xmlns' + operation = null + request = null + response = null + svc = eval(@description) + + beforeEach -> + MockRESTXMLClient.prototype.api = + xmlNamespace: xmlns + operations: + sampleOperation: + m: 'POST' # http method + u: '/' # uri + i: null # no params + o: null # no ouputs + + AWS.Client.defineMethods(MockRESTXMLClient) + operation = MockRESTXMLClient.prototype.api.operations.sampleOperation + client = new MockRESTXMLClient(region: 'region') + request = new AWS.Request(client, 'sampleOperation') + response = new AWS.Response(request) + + describe 'buildRequest', -> + buildRequest = (callback) -> + if callback + callback() + svc.buildRequest(request) + + describe 'empty bodies', -> + it 'defaults body to null when there are no inputs', -> + buildRequest -> + operation.i = null + expect(request.httpRequest.body).toEqual(null) + + it 'defaults body to null when all inputs are uri or header values', -> + buildRequest -> + operation.u = '/{Bucket}' + operation.i = {m:{Bucket:{l:'uri',r:1},ACL:{n:'x-amz-acl',l:'header'}}} + request.params = Bucket: 'abc', ACL: 'canned-acl' + expect(request.httpRequest.body).toEqual(null) + expect(request.httpRequest.path).toEqual('/abc') + expect(request.httpRequest.headers['x-amz-acl']).toEqual('canned-acl') + + it 'includes Content-Length header if body is empty', -> + buildRequest -> + operation.i = null + expect(request.httpRequest.body).toEqual(null) + expect(request.httpRequest.headers['Content-Length']).toEqual(0) + + describe 'string bodies', -> + it 'populates the body with string types directly', -> + buildRequest -> + operation.u = '/{Bucket}' + operation.i = {m:{Bucket:{l:'uri',r:1},Data:{t:'s',l:'body'}}} + request.params = Bucket: 'bucket-name', Data: 'abc' + expect(request.httpRequest.body).toEqual('abc') + + describe 'xml bodies', -> + it 'populates the body with XML from the params w/out a location', -> + buildRequest -> + operation.u = '/{Bucket}?next-marker={Marker}&limit={Limit}' + operation.i = + n: 'ComplexRequest', # the root xml element name + m: + Bucket: # uri path param + t: 's' + l: 'uri' + r: 1 + Marker: # uri querystring param + t: 's' + l: 'uri' + Limit: # uri querystring integer param + t: 'i' + l: 'uri' + ACL: # header string param + t: 's' + l: 'header' + n: 'x-amz-acl' + Metadata: # header map param + t: 'm' + l: 'header' + n: 'x-amz-meta-' + Config: # structure of mixed tpyes + t: 'o' + r: 1 + m: + Abc: {} # string + Locations: # array of strings + t: 'a' + m: + t: 's' + n: 'Location' + Data: # array of structures + t: 'a' + m: + t: 'o' + m: + Foo: {} + Bar: {} + Enabled: # boolean + t: 'b' + + request.params = + Enabled: true + ACL: 'canned-acl' + Config: + Abc: 'abc' + Locations: ['a', 'b', 'c'] + Data: [ + { Foo:'foo1', Bar:'bar1' }, + { Foo:'foo2', Bar:'bar2' }, + ] + Bucket: 'bucket-name' + Marker: 'marker' + Limit: 123 + Metadata: + abc: 'xyz' + mno: 'hjk' + + xml = """ + + + abc + + a + b + c + + + + foo1 + bar1 + + + foo2 + bar2 + + + + true + + """ + + expect(request.httpRequest.method).toEqual('POST') + expect(request.httpRequest.path). + toEqual('/bucket-name?next-marker=marker&limit=123') + expect(request.httpRequest.headers['x-amz-acl']).toEqual('canned-acl') + expect(request.httpRequest.headers['x-amz-meta-abc']).toEqual('xyz') + expect(request.httpRequest.headers['x-amz-meta-mno']).toEqual('hjk') + helpers.matchXML(request.httpRequest.body, xml) + + it 'omits the body xml when body params are not present', -> + buildRequest -> + operation.u = '/{Bucket}' + operation.i = {n:'CreateBucketConfig', m:{Bucket:{l:'uri',r:1},Config:{}}} + request.params = Bucket:'abc' # omitting Config purposefully + expect(request.httpRequest.body).toEqual(null) + expect(request.httpRequest.path).toEqual('/abc') + + describe 'extractError', -> + extractError = (body) -> + if body == undefined + body = """ + + InvalidArgument + Provided param is bad + + """ + response.httpResponse.statusCode = 400 + response.httpResponse.body = body + svc.extractError(response) + + it 'extracts the error code and message', -> + extractError() + expect(response.error instanceof Error).toBeTruthy() + expect(response.error.code).toEqual('InvalidArgument') + expect(response.error.message).toEqual('Provided param is bad') + expect(response.data).toEqual(null) + + it 'returns an empty error when the body is blank', -> + extractError '' + expect(response.error instanceof Error).toBeTruthy() + expect(response.error.code).toEqual(400) + expect(response.error.message).toEqual(null) + expect(response.data).toEqual(null) + + describe 'extractData', -> + extractData = (body) -> + response.httpResponse.statusCode = 200 + response.httpResponse.body = body + svc.extractData(response) + + it 'parses the xml body', -> + operation.o = {Foo:{},Bar:{t:'a',m:{n:'Item'}}} + extractData """ + + foo + + a + b + c + + + """ + expect(response.data).toEqual({Foo:'foo', Bar:['a', 'b', 'c']}) diff --git a/test/unit/services/ec2.spec.coffee b/test/unit/services/ec2.spec.coffee index 36b63b229e..eeef361786 100644 --- a/test/unit/services/ec2.spec.coffee +++ b/test/unit/services/ec2.spec.coffee @@ -12,38 +12,34 @@ # language governing permissions and limitations under the License. AWS = require('../../../lib/core') +helpers = require('../../helpers') require('../../../lib/services/ec2') describe 'AWS.EC2.Client', -> - ec2 = new AWS.EC2.Client() + ec2 = new AWS.EC2.Client({region: 'us-east-1'}) describe 'parseResponse', -> - - resp = null - - beforeEach -> - resp = new AWS.HttpResponse() - + body = '' parse = (callback) -> - ec2.parseResponse resp, 'operationName', (error,data) -> + helpers.mockHttpResponse 400, {}, body + ec2.makeRequest 'describeInstances', (error, data) -> callback.call(this, error, data) describe 'with error', -> - beforeEach -> - resp.statusCode = 400 - resp.body = """ - - - - InvalidInstanceID.Malformed - Invalid id: "i-12345678" - - - ab123mno-6432-dceb-asdf-123mno543123 - - """ + body = + """ + + + + InvalidInstanceID.Malformed + Invalid id: "i-12345678" + + + ab123mno-6432-dceb-asdf-123mno543123 + + """ it 'extracts the error code', -> parse (error, data) -> @@ -56,7 +52,7 @@ describe 'AWS.EC2.Client', -> expect(data).toEqual(null) it 'returns an empty error when the body is blank', -> - resp.body = '' + body = '' parse (error, data) -> expect(error.code).toEqual(400) expect(error.message).toEqual(null) diff --git a/test/unit/services/s3.spec.coffee b/test/unit/services/s3.spec.coffee index 1f900357b9..6d995ff5ca 100644 --- a/test/unit/services/s3.spec.coffee +++ b/test/unit/services/s3.spec.coffee @@ -12,11 +12,17 @@ # language governing permissions and limitations under the License. AWS = require('../../../lib/core') +helpers = require('../../helpers') + require('../../../lib/services/s3') describe 'AWS.S3.Client', -> s3 = null + request = (operation, params) -> + req = new AWS.Request(s3, operation, params || {}) + req.client.addAllRequestListeners(req) + req beforeEach -> s3 = new AWS.S3.Client() @@ -70,42 +76,41 @@ describe 'AWS.S3.Client', -> s3 = new AWS.S3.Client({ region: 'us-west-1' }) expect(s3.endpoint.hostname).toEqual('s3-us-west-1.amazonaws.com') - describe 'buildRequest', -> - - it 'returns a http request object', -> - req = s3.buildRequest('listBuckets') - expect(req.constructor).toEqual(AWS.HttpRequest) + describe 'building a request', -> + build = (operation, params) -> + req = request(operation, params) + resp = new AWS.Response(req) + req.emitEvents(resp, 'build') + return req.httpRequest it 'obeys the configuration for s3ForcePathStyle', -> config = new AWS.Config({s3ForcePathStyle: true }) s3 = new AWS.S3.Client(config) expect(s3.config.s3ForcePathStyle).toEqual(true) - req = s3.buildRequest('headObject', {Bucket:'bucket', Key:'key'}) + req = build('headObject', {Bucket:'bucket', Key:'key'}) expect(req.endpoint.hostname).toEqual('s3.amazonaws.com') expect(req.path).toEqual('/bucket/key') describe 'uri escaped params', -> - it 'uri-escapes path and querystring params', -> # bucket param ends up as part of the hostname params = { Bucket: 'bucket', Key: 'a b c', VersionId: 'a&b' } - req = s3.buildRequest('headObject', params) + req = build('headObject', params) expect(req.path).toEqual('/a%20b%20c?versionId=a%26b') it 'does not uri-escape forward slashes in the path', -> params = { Bucket: 'bucket', Key: 'k e/y' } - req = s3.buildRequest('headObject', params) + req = build('headObject', params) expect(req.path).toEqual('/k%20e/y') it 'ensures a single forward slash exists', -> - - req = s3.buildRequest('listObjects', { Bucket: 'bucket' }) + req = build('listObjects', { Bucket: 'bucket' }) expect(req.path).toEqual('/') - req = s3.buildRequest('listObjects', { Bucket: 'bucket', MaxKeys:123 }) + req = build('listObjects', { Bucket: 'bucket', MaxKeys:123 }) expect(req.path).toEqual('/?max-keys=123') - it 'ensures a single forward slash exists when querystring is present', -> + it 'ensures a single forward slash exists when querystring is present' describe 'vitual-hosted vs path-style bucket requests', -> @@ -115,29 +120,29 @@ describe 'AWS.S3.Client', -> s3 = new AWS.S3.Client({ sslEnabled: true, region: 'us-east-1' }) it 'puts dns-compat bucket names in the hostname', -> - req = s3.buildRequest('headObject', {Bucket:'bucket-name',Key:'abc'}) + req = build('headObject', {Bucket:'bucket-name',Key:'abc'}) expect(req.method).toEqual('HEAD') expect(req.endpoint.hostname).toEqual('bucket-name.s3.amazonaws.com') expect(req.path).toEqual('/abc') it 'ensures the path contains / at a minimum when moving bucket', -> - req = s3.buildRequest('listObjects', {Bucket:'bucket-name'}) + req = build('listObjects', {Bucket:'bucket-name'}) expect(req.endpoint.hostname).toEqual('bucket-name.s3.amazonaws.com') expect(req.path).toEqual('/') it 'puts dns-compat bucket names in path if they contain a dot', -> - req = s3.buildRequest('listObjects', {Bucket:'bucket.name'}) + req = build('listObjects', {Bucket:'bucket.name'}) expect(req.endpoint.hostname).toEqual('s3.amazonaws.com') expect(req.path).toEqual('/bucket.name') it 'puts dns-compat bucket names in path if configured to do so', -> s3 = new AWS.S3.Client({ sslEnabled: true, s3ForcePathStyle: true }) - req = s3.buildRequest('listObjects', {Bucket:'bucket-name'}) + req = build('listObjects', {Bucket:'bucket-name'}) expect(req.endpoint.hostname).toEqual('s3.amazonaws.com') expect(req.path).toEqual('/bucket-name') it 'puts dns-incompat bucket names in path', -> - req = s3.buildRequest('listObjects', {Bucket:'bucket_name'}) + req = build('listObjects', {Bucket:'bucket_name'}) expect(req.endpoint.hostname).toEqual('s3.amazonaws.com') expect(req.path).toEqual('/bucket_name') @@ -147,17 +152,17 @@ describe 'AWS.S3.Client', -> s3 = new AWS.S3.Client({ sslEnabled: false, region: 'us-east-1' }) it 'puts dns-compat bucket names in the hostname', -> - req = s3.buildRequest('listObjects', {Bucket:'bucket-name'}) + req = build('listObjects', {Bucket:'bucket-name'}) expect(req.endpoint.hostname).toEqual('bucket-name.s3.amazonaws.com') expect(req.path).toEqual('/') it 'puts dns-compat bucket names in the hostname if they contain a dot', -> - req = s3.buildRequest('listObjects', {Bucket:'bucket.name'}) + req = build('listObjects', {Bucket:'bucket.name'}) expect(req.endpoint.hostname).toEqual('bucket.name.s3.amazonaws.com') expect(req.path).toEqual('/') it 'puts dns-incompat bucket names in path', -> - req = s3.buildRequest('listObjects', {Bucket:'bucket_name'}) + req = build('listObjects', {Bucket:'bucket_name'}) expect(req.endpoint.hostname).toEqual('s3.amazonaws.com') expect(req.path).toEqual('/bucket_name') @@ -165,34 +170,32 @@ describe 'AWS.S3.Client', -> # http spec) these tests ensure we give meaningful codes/messages for these. describe 'errors with no XML body', -> - extractError = (resp) -> - s3.extractError(resp) + extractError = (statusCode) -> + req = request('operation') + resp = new AWS.Response(req) + resp.httpResponse.body = '' + resp.httpResponse.statusCode = statusCode + req.emit('foo') + req.emit('extractError', resp, req) + resp.error it 'handles 304 errors', -> - resp = new AWS.HttpResponse() - resp.statusCode = 304 - error = extractError(resp) + error = extractError(304) expect(error.code).toEqual('NotModified') expect(error.message).toEqual(null) it 'handles 403 errors', -> - resp = new AWS.HttpResponse() - resp.statusCode = 403 - error = extractError(resp) + error = extractError(403) expect(error.code).toEqual('Forbidden') expect(error.message).toEqual(null) it 'handles 404 errors', -> - resp = new AWS.HttpResponse() - resp.statusCode = 404 - error = extractError(resp) + error = extractError(404) expect(error.code).toEqual('NotFound') expect(error.message).toEqual(null) it 'misc errors not known to return an empty body', -> - resp = new AWS.HttpResponse() - resp.statusCode = 412 # made up - error = extractError(resp) + error = extractError(412) # made up expect(error.code).toEqual(412) expect(error.message).toEqual(null) @@ -201,12 +204,11 @@ describe 'AWS.S3.Client', -> describe 'completeMultipartUpload', -> it 'returns data when the resp is 200 with valid response', -> - resp = - statusCode: 200 - headers: - 'x-amz-id-2': 'Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg==' - 'x-amz-request-id': '656c76696e6727732072657175657374' - body: """ + headers = + 'x-amz-id-2': 'Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg==' + 'x-amz-request-id': '656c76696e6727732072657175657374' + body = + """ http://Example-Bucket.s3.amazonaws.com/Example-Object @@ -215,7 +217,9 @@ describe 'AWS.S3.Client', -> "3858f62230ac3c915f300c664312c11f-9" """ - s3.parseResponse resp, 'completeMultipartUpload', (error, data) -> + + helpers.mockHttpResponse 200, headers, body + s3.completeMultipartUpload (error, data) -> expect(error).toBe(null) expect(data).toEqual({ Location: 'http://Example-Bucket.s3.amazonaws.com/Example-Object' @@ -226,23 +230,19 @@ describe 'AWS.S3.Client', -> }) it 'returns an error when the resp is 200 with an error xml document', -> - resp = - statusCode: 200 - headers: {} - body: """ - - - - - - - InternalError - We encountered an internal error. Please try again. - 656c76696e6727732072657175657374 - Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== - - """ - s3.parseResponse resp, 'completeMultipartUpload', (error, data) -> + body = + """ + + + InternalError + We encountered an internal error. Please try again. + 656c76696e6727732072657175657374 + Uuag1LuByRx9e6j5Onimru9pO4ZVKnJ2Qz7/C1NPcfTWAtRPfTaOFg== + + """ + + helpers.mockHttpResponse 200, {}, body + s3.completeMultipartUpload (error, data) -> expect(error instanceof Error).toBeTruthy() expect(error.code).toEqual('InternalError') expect(error.message).toEqual('We encountered an internal error. Please try again.') @@ -253,20 +253,17 @@ describe 'AWS.S3.Client', -> describe 'getBucketLocation', -> it 'returns null for the location constraint when not present', -> - resp = - statusCode: 200 - headers: {} - body: '\n' - s3.parseResponse resp, 'getBucketLocation', (error, data) -> + body = '\n' + helpers.mockHttpResponse 200, {}, body + s3.getBucketLocation (error, data) -> expect(error).toBe(null) expect(data).toEqual({}) it 'parses the location constraint from the root xml', -> - resp = - statusCode: 200 - headers: { 'x-amz-request-id': 'abcxyz' } - body: '\nEU' - s3.parseResponse resp, 'getBucketLocation', (error, data) -> + headers = { 'x-amz-request-id': 'abcxyz' } + body = '\nEU' + helpers.mockHttpResponse 200, headers, body + s3.getBucketLocation (error, data) -> expect(error).toBe(null) expect(data).toEqual({ LocationConstraint: 'EU', diff --git a/test/unit/sigv2.spec.coffee b/test/unit/sigv2.spec.coffee index e4efc15bb5..779d351dce 100644 --- a/test/unit/sigv2.spec.coffee +++ b/test/unit/sigv2.spec.coffee @@ -12,7 +12,7 @@ # language governing permissions and limitations under the License. AWS = require('../../lib/core') -require('../../lib/query_client') +require('../../lib/service_interface/query') require('../../lib/sigv2') describe 'AWS.SigV2', -> @@ -23,9 +23,7 @@ describe 'AWS.SigV2', -> signer = null buildRequest = -> - request = new AWS.HttpRequest() - request.endpoint = new AWS.Endpoint('localhost') - request.endpoint.hostname = 'locahost' + request = new AWS.HttpRequest(new AWS.Endpoint('localhost')) request.params = new AWS.QueryParamList() request diff --git a/test/unit/sigv4.spec.coffee b/test/unit/sigv4.spec.coffee index 3d8e4fd834..b644f726c9 100644 --- a/test/unit/sigv4.spec.coffee +++ b/test/unit/sigv4.spec.coffee @@ -12,16 +12,18 @@ # language governing permissions and limitations under the License. AWS = require('../../lib/core') +helpers = require('../helpers') require('../../lib/services/dynamodb') beforeEach -> spyOn(AWS.util, 'userAgent').andReturn('aws-sdk-js/0.1') buildRequest = -> - ddb = new AWS.DynamoDB.Client({ region:'region' }) - req = ddb.buildRequest('listTables', { foo: 'bar' }) - req.endpoint.hostname = 'localhost' - return req + ddb = new AWS.DynamoDB.Client({region: 'region', endpoint: 'localhost'}) + req = ddb.makeRequest('listTables', {foo: 'bar'}) + resp = new AWS.Response(req) + req.emitEvents(resp, 'validate', 'build') + return req.httpRequest buildSigner = (request) -> return new AWS.SigV4(request || buildRequest(), 'dynamodb') diff --git a/test/unit/sigvs3.spec.coffee b/test/unit/sigvs3.spec.coffee index 17581439ec..6d2f5d8d88 100644 --- a/test/unit/sigvs3.spec.coffee +++ b/test/unit/sigvs3.spec.coffee @@ -32,7 +32,7 @@ describe 'AWS.SigVS3', -> method = 'POST' path = '/' virtualHostedBucket = null - date = undefined + date = new Date(0) headers = {} body = null accessKeyId = 'akid' @@ -65,33 +65,16 @@ describe 'AWS.SigVS3', -> signer.stringToSign() describe 'addAuthorization', -> - - beforeEach -> - date = AWS.util.date.rfc822(new Date(0)) - it 'sets the date header when not present', -> - req = buildRequest() + addAuth(req) + expect(req.headers['Date']).toEqual(AWS.util.date.rfc822(date)) - # stub date.rfc822 to return a fixed date string - spyOn(AWS.util.date, 'rfc822') - AWS.util.date.rfc822.andReturn('date-string') - - signer = new AWS.SigVS3(req) - signer.addAuthorization(credentials()) - - expect(req.headers['Date']).toEqual('date-string') - - it 'does not set the date header if x-amz-date is present', -> - + it 'overwrites Date if present', -> req = buildRequest() - req.headers['X-Amz-Date'] = 'date-string' - - signer = new AWS.SigVS3(buildRequest()) - signer.addAuthorization(credentials()) - - expect(req.headers['Date']).toEqual(null) - expect(req.headers['X-Amz-Date']).toEqual('date-string') + req.headers['Date'] = 'date-string' + addAuth(req) + expect(req.headers['Date']).toEqual(AWS.util.date.rfc822(date)) it 'omits the security token header when session token is blank', -> sessionToken = null diff --git a/test/unit/util.spec.coffee b/test/unit/util.spec.coffee index 1ae7435605..79af6c97f0 100644 --- a/test/unit/util.spec.coffee +++ b/test/unit/util.spec.coffee @@ -181,6 +181,10 @@ describe 'AWS.util.arrayEach', -> expect(total).toEqual(1) describe 'AWS.util.copy', -> + it 'does not copy null or undefined', -> + expect(AWS.util.copy(null)).toEqual(null) + expect(AWS.util.copy(undefined)).toEqual(undefined) + it 'should perform a shallow copy of an object', -> obj = a: 1, b: 2, c: 3 copied = AWS.util.copy(obj) @@ -236,6 +240,17 @@ describe 'AWS.util.inherit', -> expect(derived.defaultValue).toEqual(10) expect(derived.foo()).toEqual('bar') + it 'should create pass-through constructor if not defined', -> + Base = AWS.util.inherit + constructor: createSpy() + + Derived = AWS.util.inherit Base, + other: true + + derived = new Derived(1, 2, 'three') + expect(derived.other).toEqual(true) + expect(Base.prototype.constructor).toHaveBeenCalledWith(1, 2, 'three') + describe 'AWS.util.isEmpty', -> it 'returns true when passed an empty object literal', ->