Repository: aws/aws-sdk-js
Pull request #2448 (2018-12-21 01:10:15)

Update client side monitoring event interface.
/cc @cjyclaire

##### Checklis

<!-- Remove items that do not apply. For completed items, change [ ] to [x]. -->

- [x] `npm run test` passes

codecov-io: # [Codecov](https://codecov.io/gh/aws/aws-sdk-js/pull/2448?src=pr&el=h1) Report
> Merging [#2448](https://codecov.io/gh/aws/aws-sdk-js/pull/2448?src=pr&el=desc) into [master](https://codecov.io/gh/aws/aws-sdk-js/commit/2a872adac4f1c49d45feb5b368e72adf0c343cf4?src=pr&el=desc) will **increase** coverage by `<.01%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/aws/aws-sdk-js/pull/2448/graphs/tree.svg?width=650&token=qXdPGBZWAr&height=150&src=pr)](https://codecov.io/gh/aws/aws-sdk-js/pull/2448?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master    #2448      +/-   ##
==========================================
+ Coverage   96.83%   96.83%   +<.01%     
==========================================
  Files         278      278              
  Lines        8465     8478      +13     
  Branches     1608     1618      +10     
==========================================
+ Hits         8197     8210      +13     
  Misses        268      268
```


| [Impacted Files](https://codecov.io/gh/aws/aws-sdk-js/pull/2448?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [lib/publisher/index.js](https://codecov.io/gh/aws/aws-sdk-js/pull/2448/diff?src=pr&el=tree#diff-bGliL3B1Ymxpc2hlci9pbmRleC5qcw==) | `100% <ø> (ø)` | :arrow_up: |
| [lib/service.js](https://codecov.io/gh/aws/aws-sdk-js/pull/2448/diff?src=pr&el=tree#diff-bGliL3NlcnZpY2UuanM=) | `98.11% <100%> (+0.06%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/aws/aws-sdk-js/pull/2448?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/aws/aws-sdk-js/pull/2448?src=pr&el=footer). Last update [2a872ad...4af6e4e](https://codecov.io/gh/aws/aws-sdk-js/pull/2448?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/.changes/next-release/bugfix-credentials-3d679332.json b/.changes/next-release/bugfix-credentials-3d679332.json
new file mode 100644
index 000000000..1247916b3
--- /dev/null
+++ b/.changes/next-release/bugfix-credentials-3d679332.json
@@ -0,0 +1,5 @@
+{
+  "type": "bugfix",
+  "category": "credentials",
+  "description": "Make CredentialProviderChain coalesce resolvation synchronous"
+}
\ No newline at end of file
diff --git a/.changes/next-release/feature-MediaStoreData-c635fcae.json b/.changes/next-release/feature-MediaStoreData-c635fcae.json
new file mode 100644
index 000000000..62e4a8ca8
--- /dev/null
+++ b/.changes/next-release/feature-MediaStoreData-c635fcae.json
@@ -0,0 +1,5 @@
+{
+  "type": "feature",
+  "category": "MediaStoreData",
+  "description": "enable cors to make MediaStoreData available in default browser build"
+}
\ No newline at end of file
diff --git a/apis/metadata.json b/apis/metadata.json
index 18423d7b4..0335d35c4 100644
--- a/apis/metadata.json
+++ b/apis/metadata.json
@@ -486,7 +486,8 @@
   },
   "mediastoredata": {
     "prefix": "mediastore-data",
-    "name": "MediaStoreData"
+    "name": "MediaStoreData",
+    "cors": true
   },
   "appsync": {
     "name": "AppSync"
diff --git a/lib/credentials/credential_provider_chain.js b/lib/credentials/credential_provider_chain.js
index ff7d4925a..2f50ff771 100644
--- a/lib/credentials/credential_provider_chain.js
+++ b/lib/credentials/credential_provider_chain.js
@@ -108,9 +108,7 @@ AWS.CredentialProviderChain = AWS.util.inherit(AWS.Credentials, {
       function resolveNext(err, creds) {
         if ((!err && creds) || index === providers.length) {
           AWS.util.arrayEach(self.resolveCallbacks, function (callback) {
-            AWS.util.defer(function () {
-              callback(err, creds);
-            });
+            callback(err, creds);
           });
           self.resolveCallbacks.length = 0;
           return;
diff --git a/lib/publisher/index.js b/lib/publisher/index.js
index 44c4b08c2..8794d0a2e 100644
--- a/lib/publisher/index.js
+++ b/lib/publisher/index.js
@@ -30,7 +30,12 @@ Publisher.prototype.fieldsToTrim = {
     SdkException: 128,
     SdkExceptionMessage: 512,
     AwsException: 128,
-    AwsExceptionMessage: 512
+    AwsExceptionMessage: 512,
+    FinalSdkException: 128,
+    FinalSdkExceptionMessage: 512,
+    FinalAwsException: 128,
+    FinalAwsExceptionMessage: 512
+
 };
 
 /**
diff --git a/lib/service.js b/lib/service.js
index 3d34c6e39..24e2de7b3 100644
--- a/lib/service.js
+++ b/lib/service.js
@@ -291,14 +291,31 @@ AWS.Service = inherit({
    */
   apiCallEvent: function apiCallEvent(request) {
     var api = request.service.api.operations[request.operation];
-    return {
+    var monitoringEvent = {
       Type: 'ApiCall',
       Api: api ? api.name : request.operation,
       Version: 1,
       Service: request.service.api.serviceId || request.service.api.endpointPrefix,
       Region: request.httpRequest.region,
-      MaxRetriesExceeded: 0
+      MaxRetriesExceeded: 0,
+      UserAgent: request.httpRequest.getUserAgent(),
+    }
+    var response = request.response;
+    if (response.httpResponse.statusCode) {
+      monitoringEvent.FinalHttpStatusCode = response.httpResponse.statusCode;
+    }
+    if (response.error) {
+      var error = response.error;
+      var statusCode = response.httpResponse.statusCode;
+      if (statusCode > 299) {
+        if (error.code) monitoringEvent.FinalAwsException = error.code;
+        if (error.message) monitoringEvent.FinalAwsExceptionMessage = error.message;
+      } else {
+        if (error.code || error.name) monitoringEvent.FinalSdkException = error.code || error.name;
+        if (error.message) monitoringEvent.FinalSdkExceptionMessage = error.message;
+      }
     }
+    return monitoringEvent
   },
 
   /**
@@ -317,6 +334,16 @@ AWS.Service = inherit({
       UserAgent: request.httpRequest.getUserAgent(),
     };
     var response = request.response;
+    if (response.httpResponse.statusCode) {
+      monitoringEvent.HttpStatusCode = response.httpResponse.statusCode;
+    }
+    if (
+      !request._unAuthenticated &&
+      request.service.config.credentials &&
+      request.service.config.credentials.accessKeyId
+    ) {
+      monitoringEvent.AccessKey = request.service.config.credentials.accessKeyId;
+    }
     if (!response.httpResponse.headers) return monitoringEvent;
     if (request.httpRequest.headers['x-amz-security-token']) {
       monitoringEvent.SessionToken = request.httpRequest.headers['x-amz-security-token'];
@@ -330,15 +357,6 @@ AWS.Service = inherit({
     if (response.httpResponse.headers['x-amz-id-2']) {
       monitoringEvent.XAmzId2 = response.httpResponse.headers['x-amz-id-2'];
     }
-    if (response.httpResponse.statusCode) {
-      monitoringEvent.HttpStatusCode = response.httpResponse.statusCode;
-    }
-    if (
-      !request._unAuthenticated &&
-      request.service.config.credentials.accessKeyId
-    ) {
-      monitoringEvent.AccessKey = request.service.config.credentials.accessKeyId;
-    }
     return monitoringEvent
   },
 
@@ -349,12 +367,13 @@ AWS.Service = inherit({
   attemptFailEvent: function attemptFailEvent(request) {
     var monitoringEvent = this.apiAttemptEvent(request);
     var response = request.response;
+    var error = response.error;
     if (response.httpResponse.statusCode > 299 ) {
-      monitoringEvent.AwsException = response.error.code;
-      monitoringEvent.AwsExceptionMessage = response.error.message;
+      if (error.code) monitoringEvent.AwsException = error.code;
+      if (error.message) monitoringEvent.AwsExceptionMessage = error.message;
     } else {
-      monitoringEvent.SdkException = response.error.code || response.error.name;
-      monitoringEvent.SdkExceptionMessage = response.error.message;
+      if (error.code || error.name) monitoringEvent.SdkException = error.code || error.name;
+      if (error.message) monitoringEvent.SdkExceptionMessage = error.message;
     }
     return monitoringEvent;
   },


Test patch:
diff --git a/test/credential_provider_chain.spec.js b/test/credential_provider_chain.spec.js
index 53f954a38..a9ccc6211 100644
--- a/test/credential_provider_chain.spec.js
+++ b/test/credential_provider_chain.spec.js
@@ -53,6 +53,18 @@
             return expect(creds.sessionToken).to.equal('session');
           });
         });
+        it('should be able to resolve credentials synchronously', function() {
+          process.env['AMAZON_ACCESS_KEY_ID'] = 'akid';
+          process.env['AMAZON_SECRET_ACCESS_KEY'] = 'secret';
+          process.env['AMAZON_SESSION_TOKEN'] = 'session';
+          var credentials = null;
+          chain.resolve(function(err, creds) {
+            credentials = creds;
+          });
+          expect(credentials.accessKeyId).to.equal('akid');
+          expect(credentials.secretAccessKey).to.equal('secret');
+          expect(credentials.sessionToken).to.equal('session');
+        });
         it('prefers AWS credentials to AMAZON credentials', function() {
           process.env['AWS_ACCESS_KEY_ID'] = 'akid';
           process.env['AWS_SECRET_ACCESS_KEY'] = 'secret';
diff --git a/test/publisher/functional_test/cases.js b/test/publisher/functional_test/cases.js
index d2e5bf462..b9fbe014c 100644
--- a/test/publisher/functional_test/cases.js
+++ b/test/publisher/functional_test/cases.js
@@ -11,7 +11,7 @@ module.exports = {
     },
     "optionalEventFields": {
       "ApiCall": {
-        "APICallTimeout": "ANY_INT"
+        "ApiCallTimeout": "ANY_INT"
       },
       "ApiCallAttempt": {
         "DestinationIp": "ANY_STR",
@@ -27,7 +27,7 @@ module.exports = {
   },
   "cases": [
     {
-      "description": "Test a single client API call with no configuration provided",
+      "description": "Tests a single client API call with no configuration provided",
       "configuration": {
         "accessKey": "myaccesskey",
         "region": "us-west-2",
@@ -66,35 +66,37 @@ module.exports = {
       ],
       "expectedMonitoringEvents": [
         {
+          "Version": 1,
           "Type": "ApiCallAttempt",
           "Service": "CSM Test",
           "Api": "TestOperation",
           "ClientId": "",
-          "Version": 1,
           "Timestamp": "ANY_INT",
           "AttemptLatency": "ANY_INT",
           "Fqdn": "csmtest.us-west-2.amazonaws.com",
-          "UserAgent": "ANY_STR",
           "Region": "us-west-2",
+          "UserAgent": "ANY_STR",
           "AccessKey": "myaccesskey",
           "HttpStatusCode": 200
         },
         {
+          "Version": 1,
           "Type": "ApiCall",
           "Service": "CSM Test",
           "Api": "TestOperation",
           "ClientId": "",
           "Timestamp": "ANY_INT",
-          "Version": 1,
-          "AttemptCount": 1,
           "Latency": "ANY_INT",
-          "Region": "us-west-2",
+          "AttemptCount": 1,
           "MaxRetriesExceeded": 0,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalHttpStatusCode": 200
         }
       ]
     },
     {
-      "description": "Test a single client API call with bad request",
+      "description": "Tests a single client API call with bad request",
       "apiCalls": [
         {
           "serviceId": "CSM Test",
@@ -112,37 +114,41 @@ module.exports = {
       ],
       "expectedMonitoringEvents": [
         {
+          "Version": 1,
           "Type": "ApiCallAttempt",
           "Service": "CSM Test",
           "Api": "TestOperation",
           "ClientId": "",
-          "Version": 1,
           "Timestamp": "ANY_INT",
           "AttemptLatency": "ANY_INT",
           "Fqdn": "csmtest.us-west-2.amazonaws.com",
-          "UserAgent": "ANY_STR",
           "Region": "us-west-2",
+          "UserAgent": "ANY_STR",
           "AccessKey": "myaccesskey",
           "HttpStatusCode": 400,
           "AwsException": "TestOperationException",
           "AwsExceptionMessage": "There was an error"
         },
         {
+          "Version": 1,
           "Type": "ApiCall",
           "Service": "CSM Test",
           "Api": "TestOperation",
           "ClientId": "",
           "Timestamp": "ANY_INT",
-          "Version": 1,
-          "AttemptCount": 1,
           "Latency": "ANY_INT",
-          "Region": "us-west-2",
+          "AttemptCount": 1,
           "MaxRetriesExceeded": 0,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalAwsException": "TestOperationException",
+          "FinalAwsExceptionMessage": "There was an error",
+          "FinalHttpStatusCode": 400
         }
       ]
     },
     {
-      "description": "Test a single client API call with retries",
+      "description": "Tests a single client API call with retries",
       "apiCalls": [
         {
           "serviceId": "CSM Test",
@@ -164,51 +170,53 @@ module.exports = {
       ],
       "expectedMonitoringEvents": [
         {
+          "Version": 1,
           "Type": "ApiCallAttempt",
           "Service": "CSM Test",
           "Api": "TestOperation",
           "ClientId": "",
-          "Version": 1,
           "Timestamp": "ANY_INT",
           "AttemptLatency": "ANY_INT",
           "Fqdn": "csmtest.us-west-2.amazonaws.com",
-          "UserAgent": "ANY_STR",
           "Region": "us-west-2",
+          "UserAgent": "ANY_STR",
           "AccessKey": "myaccesskey",
           "HttpStatusCode": 503,
           "AwsException": "ServiceUnavailable",
           "AwsExceptionMessage": "Service is unavailable"
         },
         {
+          "Version": 1,
           "Type": "ApiCallAttempt",
           "Service": "CSM Test",
           "Api": "TestOperation",
           "ClientId": "",
-          "Version": 1,
           "Timestamp": "ANY_INT",
           "AttemptLatency": "ANY_INT",
           "Fqdn": "csmtest.us-west-2.amazonaws.com",
-          "UserAgent": "ANY_STR",
           "Region": "us-west-2",
+          "UserAgent": "ANY_STR",
           "AccessKey": "myaccesskey",
           "HttpStatusCode": 200
         },
         {
+          "Version": 1,
           "Type": "ApiCall",
           "Service": "CSM Test",
           "Api": "TestOperation",
           "ClientId": "",
           "Timestamp": "ANY_INT",
-          "Version": 1,
-          "AttemptCount": 2,
           "Latency": "ANY_INT",
-          "Region": "us-west-2",
+          "AttemptCount": 2,
           "MaxRetriesExceeded": 0,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalHttpStatusCode": 200
         }
       ]
     },
     {
-      "description": "Test a single client API call with non-retryable SDK exception",
+      "description": "Tests a single client API call with non-retryable SDK exception",
       "apiCalls": [
         {
           "serviceId": "CSM Test",
@@ -226,31 +234,34 @@ module.exports = {
       ],
       "expectedMonitoringEvents": [
         {
+          "Version": 1,
           "Type": "ApiCallAttempt",
           "Service": "CSM Test",
           "Api": "TestOperation",
           "ClientId": "",
-          "Version": 1,
           "Timestamp": "ANY_INT",
           "AttemptLatency": "ANY_INT",
           "Fqdn": "csmtest.us-west-2.amazonaws.com",
-          "UserAgent": "ANY_STR",
           "Region": "us-west-2",
+          "UserAgent": "ANY_STR",
           "AccessKey": "myaccesskey",
           "SdkException": "ANY_STR",
           "SdkExceptionMessage": "Unexpected exception was thrown"
         },
         {
+          "Version": 1,
           "Type": "ApiCall",
           "Service": "CSM Test",
           "Api": "TestOperation",
           "ClientId": "",
           "Timestamp": "ANY_INT",
-          "Version": 1,
-          "AttemptCount": 1,
           "Latency": "ANY_INT",
-          "Region": "us-west-2",
+          "AttemptCount": 1,
           "MaxRetriesExceeded": 0,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalSdkException": "ANY_STR",
+          "FinalSdkExceptionMessage": "Unexpected exception was thrown"
         }
       ]
     },
@@ -314,8 +325,10 @@ module.exports = {
           "Timestamp": "ANY_INT",
           "Latency": "ANY_INT",
           "AttemptCount": 2,
-          "Region": "us-west-2",
           "MaxRetriesExceeded": 0,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalHttpStatusCode": 200
         }
       ]
     },
@@ -366,8 +379,10 @@ module.exports = {
           "Timestamp": "ANY_INT",
           "Latency": "ANY_INT",
           "AttemptCount": 1,
-          "Region": "us-west-2",
           "MaxRetriesExceeded": 0,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalHttpStatusCode": 200
         }
       ]
     },
@@ -470,8 +485,10 @@ module.exports = {
           "Timestamp": "ANY_INT",
           "Latency": "ANY_INT",
           "AttemptCount": 1,
-          "Region": "us-west-2",
           "MaxRetriesExceeded": 0,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalHttpStatusCode": 200
         }
       ]
     },
@@ -550,8 +567,10 @@ module.exports = {
           "Timestamp": "ANY_INT",
           "Latency": "ANY_INT",
           "AttemptCount": 1,
-          "Region": "us-west-2",
           "MaxRetriesExceeded": 0,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalHttpStatusCode": 200
         }
       ]
     },
@@ -604,8 +623,10 @@ module.exports = {
           "Timestamp": "ANY_INT",
           "Latency": "ANY_INT",
           "AttemptCount": 1,
-          "Region": "us-west-2",
           "MaxRetriesExceeded": 0,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalHttpStatusCode": 200
         }
       ]
     },
@@ -659,8 +680,10 @@ module.exports = {
           "Timestamp": "ANY_INT",
           "Latency": "ANY_INT",
           "AttemptCount": 1,
-          "Region": "us-west-2",
           "MaxRetriesExceeded": 0,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalHttpStatusCode": 200
         }
       ]
     },
@@ -713,8 +736,10 @@ module.exports = {
           "Timestamp": "ANY_INT",
           "Latency": "ANY_INT",
           "AttemptCount": 1,
-          "Region": "us-west-2",
           "MaxRetriesExceeded": 0,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalHttpStatusCode": 200
         }
       ]
     },
@@ -760,8 +785,10 @@ module.exports = {
           "Timestamp": "ANY_INT",
           "Latency": "ANY_INT",
           "AttemptCount": 1,
-          "Region": "us-west-2",
           "MaxRetriesExceeded": 0,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalHttpStatusCode": 200
         }
       ]
     },
@@ -807,8 +834,10 @@ module.exports = {
           "Timestamp": "ANY_INT",
           "Latency": "ANY_INT",
           "AttemptCount": 1,
-          "Region": "us-west-2",
           "MaxRetriesExceeded": 0,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalHttpStatusCode": 200
         }
       ]
     },
@@ -854,14 +883,105 @@ module.exports = {
           "Timestamp": "ANY_INT",
           "Latency": "ANY_INT",
           "AttemptCount": 1,
-          "Region": "us-west-2",
           "MaxRetriesExceeded": 0,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalHttpStatusCode": 200
+        }
+      ]
+    },
+    {
+      "description": "Test max retries from AWS exception",
+      "configuration": {
+        "accessKey": "myaccesskey",
+        "region": "us-west-2",
+        "environmentVariables": {
+          "AWS_CSM_ENABLED": "true"
+        },
+        "sharedConfigFile": {},
+        "maxRetries": 1
+      },
+      "apiCalls": [
+        {
+          "serviceId": "CSM Test",
+          "operationName": "TestOperation",
+          "params": {},
+          "attemptResponses": [
+            {
+              "httpStatus": 503,
+              "responseHeaders": {},
+              "errorCode": "ServiceUnavailable",
+              "errorMessage": "Service is unavailable"
+            },
+            {
+              "httpStatus": 503,
+              "responseHeaders": {},
+              "errorCode": "ServiceUnavailable",
+              "errorMessage": "Service is unavailable"
+            }
+          ]
+        }
+      ],
+      "expectedMonitoringEvents": [
+        {
+          "Version": 1,
+          "Type": "ApiCallAttempt",
+          "Service": "CSM Test",
+          "Api": "TestOperation",
+          "ClientId": "",
+          "Timestamp": "ANY_INT",
+          "AttemptLatency": "ANY_INT",
+          "Fqdn": "csmtest.us-west-2.amazonaws.com",
+          "Region": "us-west-2",
+          "UserAgent": "ANY_STR",
+          "AccessKey": "myaccesskey",
+          "HttpStatusCode": 503,
+          "AwsException": "ServiceUnavailable",
+          "AwsExceptionMessage": "Service is unavailable"
+        },
+        {
+          "Version": 1,
+          "Type": "ApiCallAttempt",
+          "Service": "CSM Test",
+          "Api": "TestOperation",
+          "ClientId": "",
+          "Timestamp": "ANY_INT",
+          "AttemptLatency": "ANY_INT",
+          "Fqdn": "csmtest.us-west-2.amazonaws.com",
+          "Region": "us-west-2",
+          "UserAgent": "ANY_STR",
+          "AccessKey": "myaccesskey",
+          "HttpStatusCode": 503,
+          "AwsException": "ServiceUnavailable",
+          "AwsExceptionMessage": "Service is unavailable"
+        },
+        {
+          "Version": 1,
+          "Type": "ApiCall",
+          "Service": "CSM Test",
+          "Api": "TestOperation",
+          "ClientId": "",
+          "Timestamp": "ANY_INT",
+          "Latency": "ANY_INT",
+          "AttemptCount": 2,
+          "MaxRetriesExceeded": 1,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalAwsException": "ServiceUnavailable",
+          "FinalAwsExceptionMessage": "Service is unavailable",
+          "FinalHttpStatusCode": 503
         }
       ]
     },
     {
-      "description": "Test a single client API call with exceeded retries",
+      "description": "Test max retries from SDK exception",
       "configuration": {
+        "accessKey": "myaccesskey",
+        "region": "us-west-2",
+        "environmentVariables": {
+          "AWS_CSM_ENABLED": "true"
+        },
+        "sharedConfigFile": {},
         "maxRetries": 1
       },
       "apiCalls": [
@@ -925,10 +1045,84 @@ module.exports = {
           "Timestamp": "ANY_INT",
           "Latency": "ANY_INT",
           "AttemptCount": 2,
-          "Region": "us-west-2",
           "MaxRetriesExceeded": 1,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalSdkException": "ANY_STR",
+          "FinalSdkExceptionMessage": "Retryable exception was thrown"
+        }
+      ]
+    },
+    {
+      "description": "Test final uses the exception from final attempt",
+      "apiCalls": [
+        {
+          "serviceId": "CSM Test",
+          "operationName": "TestOperation",
+          "params": {},
+          "attemptResponses": [
+            {
+              "sdkException": {
+                "isRetryable": true,
+                "message": "First retryable exception"
+              }
+            },
+            {
+              "sdkException": {
+                "isRetryable": false,
+                "message": "Second un-retryable exception"
+              }
+            }
+          ]
+        }
+      ],
+      "expectedMonitoringEvents": [
+        {
+          "Version": 1,
+          "Type": "ApiCallAttempt",
+          "Service": "CSM Test",
+          "Api": "TestOperation",
+          "ClientId": "",
+          "Timestamp": "ANY_INT",
+          "AttemptLatency": "ANY_INT",
+          "Fqdn": "csmtest.us-west-2.amazonaws.com",
+          "Region": "us-west-2",
+          "UserAgent": "ANY_STR",
+          "AccessKey": "myaccesskey",
+          "SdkException": "ANY_STR",
+          "SdkExceptionMessage": "First retryable exception"
+        },
+        {
+          "Version": 1,
+          "Type": "ApiCallAttempt",
+          "Service": "CSM Test",
+          "Api": "TestOperation",
+          "ClientId": "",
+          "Timestamp": "ANY_INT",
+          "AttemptLatency": "ANY_INT",
+          "Fqdn": "csmtest.us-west-2.amazonaws.com",
+          "Region": "us-west-2",
+          "UserAgent": "ANY_STR",
+          "AccessKey": "myaccesskey",
+          "SdkException": "ANY_STR",
+          "SdkExceptionMessage": "Second un-retryable exception"
+        },
+        {
+          "Version": 1,
+          "Type": "ApiCall",
+          "Service": "CSM Test",
+          "Api": "TestOperation",
+          "ClientId": "",
+          "Timestamp": "ANY_INT",
+          "Latency": "ANY_INT",
+          "AttemptCount": 2,
+          "MaxRetriesExceeded": 0,
+          "UserAgent": "ANY_STR",
+          "Region": "us-west-2",
+          "FinalSdkException": "ANY_STR",
+          "FinalSdkExceptionMessage": "Second un-retryable exception"
         }
       ]
     }
   ]
-}
\ No newline at end of file
+}
diff --git a/test/publisher/functional_test/utils/validateEvents.js b/test/publisher/functional_test/utils/validateEvents.js
index 2e88fcc8f..17a6d1a73 100644
--- a/test/publisher/functional_test/utils/validateEvents.js
+++ b/test/publisher/functional_test/utils/validateEvents.js
@@ -36,9 +36,11 @@ function validateApiCallEvent(event, expected) {
   if (event.Type !== 'ApiCall' || expected.Type !== 'ApiCall') return false;
   const keysToValidate = [
     //shared monitoring event entries
-    'Service', 'Api', 'ClientId', 'Version', 'Timestamp', 'Region',
+    'Service', 'Api', 'ClientId', 'Version', 'Timestamp', 'Region', 'UserAgent',
     //api call monitoring events
-    'AttemptCount', 'Latency', 'MaxRetriesExceeded'
+    'AttemptCount', 'Latency', 'MaxRetriesExceeded', 'ApiCallTimeout', 'FinalAwsException',
+    'FinalAwsExceptionMessage', 'FinalSdkException', 'FinalSdkExceptionMessage',
+    'FinalHttpStatusCode'
   ]
   for (const keyToValidate of keysToValidate) {
     if (!validateEntry(event, expected, keyToValidate)) return false;
@@ -51,9 +53,9 @@ function validateApiCallAttemptEvent(event, expected) {
   if (event.Type !== 'ApiCallAttempt' || expected.Type !== 'ApiCallAttempt') return false;
   const keysToValidate = [
     //shared monitoring event entries
-    'Service', 'Api', 'ClientId', 'Version', 'Timestamp', 'Region',
+    'Service', 'Api', 'ClientId', 'Version', 'Timestamp', 'Region', 'UserAgent',
     //api call attempt monitoring event entries
-    'Fqdn', 'UserAgent', 'AttemptLatency', 'SessionToken', 'AccessKey', 'HttpStatusCode',
+    'Fqdn', 'AttemptLatency', 'SessionToken', 'AccessKey', 'HttpStatusCode',
     'AwsException', 'AwsExceptionMessage', 'SdkException', 'SdkExceptionMessage', 'XAmznRequestId', 
     'XAmzRequestId', 'XAmzId2'
   ]
diff --git a/test/publisher/index.spec.js b/test/publisher/index.spec.js
index 062122db5..b92c88246 100644
--- a/test/publisher/index.spec.js
+++ b/test/publisher/index.spec.js
@@ -122,6 +122,50 @@ if (AWS.util.isNode()) {
         publisher.trimFields(mockEvent);
         expect(mockEvent.AwsExceptionMessage.length).to.equal(512);
       });
+
+      it('trims FinalSdkException to 128 characters', function () {
+        var mockEvent = { FinalSdkException: mockValue };
+        var publisher = new Publisher({
+          clientId: 'Foo'
+        });
+
+        expect(mockEvent.FinalSdkException.length).to.equal(1000);
+        publisher.trimFields(mockEvent);
+        expect(mockEvent.FinalSdkException.length).to.equal(128);
+      });
+
+      it('trims FinalSdkExceptionMessage to 512 characters', function () {
+        var mockEvent = { FinalSdkExceptionMessage: mockValue };
+        var publisher = new Publisher({
+          clientId: 'Foo'
+        });
+
+        expect(mockEvent.FinalSdkExceptionMessage.length).to.equal(1000);
+        publisher.trimFields(mockEvent);
+        expect(mockEvent.FinalSdkExceptionMessage.length).to.equal(512);
+      });
+
+      it('trims AwsExceptionMessage to 128 characters', function () {
+        var mockEvent = { FinalAwsException: mockValue };
+        var publisher = new Publisher({
+          clientId: 'Foo'
+        });
+
+        expect(mockEvent.FinalAwsException.length).to.equal(1000);
+        publisher.trimFields(mockEvent);
+        expect(mockEvent.FinalAwsException.length).to.equal(128);
+      });
+
+      it('trims FinalAwsExceptionMessage to 512 characters', function () {
+        var mockEvent = { FinalAwsExceptionMessage: mockValue };
+        var publisher = new Publisher({
+          clientId: 'Foo'
+        });
+
+        expect(mockEvent.FinalAwsExceptionMessage.length).to.equal(1000);
+        publisher.trimFields(mockEvent);
+        expect(mockEvent.FinalAwsExceptionMessage.length).to.equal(512);
+      });
     });
 
     describe('eventHandler', function () {
diff --git a/test/service.spec.js b/test/service.spec.js
index a2d7a4a1f..62f014eaf 100644
--- a/test/service.spec.js
+++ b/test/service.spec.js
@@ -911,17 +911,39 @@
       })
     })
 
-    it('should emit api call events corresponding to event interface', function() {
+    it('should emit api call events when request succeeds', function(done) {
       helpers.mockHttpResponse(200, {}, ['FOO', 'BAR']); 
-      var client = new MockService();
+      var client = new MockService({});
       client.on('apiCall', function apiCallListener(event) {
         expect(event.Type).to.equal('ApiCall');
-        expect(event.Service).to.equal('MockService');
+        expect(event.Service).to.equal('mockservice');
         expect(event.Api).to.equal('operationName');
-        expect(Math.abs(event.Timestemp - Date.now()) < 100).to.equal(true);
+        expect(Math.abs(event.Timestamp - Date.now()) < 100).to.equal(true);
         expect(event.Version).to.equal(1);
-        expect(event.AttemptCount).to.equal(0);
+        expect(event.AttemptCount).to.equal(1);
         expect(typeof event.Latency).to.equal('number');
+        expect(typeof event.UserAgent).to.equal('string');
+        expect(event.FinalHttpStatusCode).to.equal(200);
+        done();
+      });
+      client.makeRequest('operationName', function(err, data) {});
+    });
+
+    it('should emit api call events when request fails for aws exception', function(done) {
+      helpers.mockHttpResponse(500, {}, ['ServiceUnavailableException']); 
+      var client = new MockService({maxRetries: 0});
+      client.on('apiCall', function apiCallListener(event) {
+        expect(event.Type).to.equal('ApiCall');
+        expect(event.Service).to.equal('mockservice');
+        expect(event.Api).to.equal('operationName');
+        expect(Math.abs(event.Timestamp - Date.now()) < 100).to.equal(true);
+        expect(event.Version).to.equal(1);
+        expect(event.AttemptCount).to.equal(1);
+        expect(typeof event.Latency).to.equal('number');
+        expect(typeof event.UserAgent).to.equal('string');
+        expect(event.FinalHttpStatusCode).to.equal(500);
+        expect(event.FinalAwsException).to.equal('ServiceUnavailableException');
+        done();
       });
       client.makeRequest('operationName', function(err, data) {});
     });