smartystreets-javascript-sdk 1.13.6 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Makefile CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/make -f
2
2
 
3
- VERSION := $(shell tagit -p --dry-run)
3
+ VERSION := $(shell tagit -M --dry-run)
4
4
  VERSION_FILE1 := package.json
5
5
  VERSION_FILE2 := package-lock.json
6
6
 
@@ -11,7 +11,7 @@ node_modules:
11
11
  npm install
12
12
 
13
13
  publish: test version upload unversion
14
- tagit -p
14
+ tagit -M
15
15
  git push origin --tags
16
16
 
17
17
  upload:
@@ -2,8 +2,8 @@ const SmartySDK = require("smartystreets-javascript-sdk");
2
2
  const SmartyCore = SmartySDK.core;
3
3
  const Lookup = SmartySDK.internationalAddressAutocomplete.Lookup;
4
4
 
5
- // US Autocomplete Pro only supports using Website Keys
6
- let key = process.env.SMARTY_WEBSITE_KEY;
5
+ // US Autocomplete Pro only supports using Embedded Keys
6
+ let key = process.env.SMARTY_EMBEDDED_KEY;
7
7
  const credentials = new SmartyCore.SharedCredentials(key);
8
8
 
9
9
  // The appropriate license values to be used for your subscriptions
@@ -8,7 +8,7 @@ const Lookup = SmartySDK.internationalStreet.Lookup;
8
8
  // const credentials = new SmartyCore.StaticCredentials(authId, authToken);
9
9
 
10
10
  // for client-side requests (browser/mobile), use this code:
11
- let key = process.env.SMARTY_WEBSITE_KEY;
11
+ let key = process.env.SMARTY_EMBEDDED_KEY;
12
12
  const credentials = new SmartyCore.SharedCredentials(key);
13
13
 
14
14
  // The appropriate license values to be used for your subscriptions
@@ -2,8 +2,8 @@ const SmartySDK = require("smartystreets-javascript-sdk");
2
2
  const SmartyCore = SmartySDK.core;
3
3
  const Lookup = SmartySDK.usAutocompletePro.Lookup;
4
4
 
5
- // US Autocomplete Pro only supports using Website Keys
6
- let key = process.env.SMARTY_WEBSITE_KEY;
5
+ // US Autocomplete Pro only supports using Embedded Keys
6
+ let key = process.env.SMARTY_EMBEDDED_KEY;
7
7
  const credentials = new SmartyCore.SharedCredentials(key);
8
8
 
9
9
  // The appropriate license values to be used for your subscriptions
@@ -8,7 +8,7 @@ const Lookup = SmartySDK.usExtract.Lookup;
8
8
  // const credentials = new SmartyCore.StaticCredentials(authId, authToken);
9
9
 
10
10
  // for client-side requests (browser/mobile), use this code:
11
- let key = process.env.SMARTY_WEBSITE_KEY;
11
+ let key = process.env.SMARTY_EMBEDDED_KEY;
12
12
  const credentials = new SmartyCore.SharedCredentials(key);
13
13
 
14
14
  let clientBuilder = new SmartyCore.ClientBuilder(credentials);
@@ -8,7 +8,7 @@ const Lookup = SmartySDK.usReverseGeo.Lookup;
8
8
  // const credentials = new SmartyCore.StaticCredentials(authId, authToken);
9
9
 
10
10
  // for client-side requests (browser/mobile), use this code:
11
- let key = process.env.SMARTY_WEBSITE_KEY;
11
+ let key = process.env.SMARTY_EMBEDDED_KEY;
12
12
  const credentials = new SmartyCore.SharedCredentials(key);
13
13
 
14
14
  // The appropriate license values to be used for your subscriptions
@@ -8,7 +8,7 @@ const Lookup = SmartySDK.usStreet.Lookup;
8
8
  // const credentials = new SmartyCore.StaticCredentials(authId, authToken);
9
9
 
10
10
  // for client-side requests (browser/mobile), use this code:
11
- let key = process.env.SMARTY_WEBSITE_KEY;
11
+ let key = process.env.SMARTY_EMBEDDED_KEY;
12
12
  const credentials = new SmartyCore.SharedCredentials(key);
13
13
 
14
14
  // The appropriate license values to be used for your subscriptions
@@ -8,7 +8,7 @@ const Lookup = SmartySDK.usZipcode.Lookup;
8
8
  // const credentials = new SmartyCore.StaticCredentials(authId, authToken);
9
9
 
10
10
  // for client-side requests (browser/mobile), use this code:
11
- let key = process.env.SMARTY_WEBSITE_KEY;
11
+ let key = process.env.SMARTY_EMBEDDED_KEY;
12
12
  const credentials = new SmartyCore.SharedCredentials(key);
13
13
 
14
14
  let clientBuilder = new SmartyCore.ClientBuilder(credentials);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smartystreets-javascript-sdk",
3
- "version": "1.13.6",
3
+ "version": "2.0.0",
4
4
  "description": "Quick and easy Smarty address validation.",
5
5
  "keywords": [
6
6
  "smarty",
@@ -32,8 +32,8 @@
32
32
  "url": "github:smartystreets/smartystreets-javascript-sdk"
33
33
  },
34
34
  "devDependencies": {
35
- "chai": "^4.2.0",
36
- "mocha": "^9.2.1"
35
+ "chai": "^4.3.6",
36
+ "mocha": "^9.2.2"
37
37
  },
38
38
  "dependencies": {
39
39
  "axios": "^0.26.1",
@@ -8,6 +8,8 @@ const CustomHeaderSender = require("./CustomHeaderSender");
8
8
  const StatusCodeSender = require("./StatusCodeSender");
9
9
  const LicenseSender = require("./LicenseSender");
10
10
  const BadCredentialsError = require("./Errors").BadCredentialsError;
11
+ const RetrySender = require("./RetrySender.js");
12
+ const Sleeper = require("./util/Sleeper.js");
11
13
 
12
14
  //TODO: refactor this to work more cleanly with a bundler.
13
15
  const UsStreetClient = require("./us_street/Client");
@@ -151,10 +153,14 @@ class ClientBuilder {
151
153
  buildSender() {
152
154
  if (this.httpSender) return this.httpSender;
153
155
 
154
- const httpSender = new HttpSender(this.maxTimeout, this.maxRetries, this.proxy, this.debug);
156
+ const httpSender = new HttpSender(this.maxTimeout, this.proxy, this.debug);
155
157
  const statusCodeSender = new StatusCodeSender(httpSender);
156
158
  const signingSender = new SigningSender(statusCodeSender, this.signer);
157
- const agentSender = new AgentSender(signingSender);
159
+ let agentSender = new AgentSender(signingSender);
160
+ if (this.maxRetries > 0) {
161
+ const retrySender = new RetrySender(this.maxRetries, signingSender, new Sleeper());
162
+ agentSender = new AgentSender(retrySender);
163
+ }
158
164
  const customHeaderSender = new CustomHeaderSender(agentSender, this.customHeaders);
159
165
  const baseUrlSender = new BaseUrlSender(customHeaderSender, this.baseUrl);
160
166
  const licenseSender = new LicenseSender(baseUrlSender, this.licenses);
package/src/Errors.js CHANGED
@@ -54,7 +54,7 @@ class UnprocessableEntityError extends SmartyError {
54
54
 
55
55
  class TooManyRequestsError extends SmartyError {
56
56
  constructor() {
57
- super("When using the public 'website key' authentication, we restrict the number of requests coming from a given source over too short of a time.");
57
+ super("When using the public 'embedded key' authentication, we restrict the number of requests coming from a given source over too short of a time.");
58
58
  }
59
59
  }
60
60
 
package/src/HttpSender.js CHANGED
@@ -1,12 +1,9 @@
1
1
  const Response = require("./Response");
2
2
  const Axios = require("axios");
3
- const axiosRetry = require("axios-retry");
3
+ const {buildSmartyResponse} = require("../src/util/buildSmartyResponse");
4
4
 
5
5
  class HttpSender {
6
- constructor(timeout = 10000, retries = 5, proxyConfig, debug = false) {
7
- axiosRetry(Axios, {
8
- retries: retries,
9
- });
6
+ constructor(timeout = 10000, proxyConfig, debug = false) {
10
7
  this.timeout = timeout;
11
8
  this.proxyConfig = proxyConfig;
12
9
  if (debug) this.enableDebug();
@@ -33,24 +30,19 @@ class HttpSender {
33
30
  return config;
34
31
  }
35
32
 
36
- buildSmartyResponse(response, error) {
37
- if (response) return new Response(response.status, response.data);
38
- return new Response(undefined, undefined, error)
39
- }
40
-
41
33
  send(request) {
42
34
  return new Promise((resolve, reject) => {
43
35
  let requestConfig = this.buildRequestConfig(request);
44
36
 
45
37
  Axios(requestConfig)
46
38
  .then(response => {
47
- let smartyResponse = this.buildSmartyResponse(response);
39
+ let smartyResponse = buildSmartyResponse(response);
48
40
 
49
41
  if (smartyResponse.statusCode >= 400) reject(smartyResponse);
50
42
 
51
43
  resolve(smartyResponse);
52
44
  })
53
- .catch(error => reject(this.buildSmartyResponse(undefined, error)));
45
+ .catch(error => reject(buildSmartyResponse(undefined, error)));
54
46
  });
55
47
  }
56
48
 
package/src/Response.js CHANGED
@@ -1,8 +1,9 @@
1
1
  class Response {
2
- constructor (statusCode, payload, error = undefined) {
2
+ constructor (statusCode, payload, error, headers) {
3
3
  this.statusCode = statusCode;
4
4
  this.payload = payload;
5
5
  this.error = error;
6
+ this.headers = headers;
6
7
  }
7
8
  }
8
9
 
@@ -0,0 +1,50 @@
1
+ class RetrySender {
2
+ constructor(maxRetires = 5, inner, sleeper) {
3
+ this.maxRetries = maxRetires;
4
+ this.statusToRetry = [408, 429, 500, 502, 503, 504];
5
+ this.statusTooManyRequests = 429;
6
+ this.maxBackoffDuration = 10;
7
+ this.inner = inner;
8
+ this.sleeper = sleeper;
9
+ }
10
+
11
+ async send(request) {
12
+ let response = await this.inner.send(request);
13
+
14
+ for (let i = 0; i < this.maxRetries; i++) {
15
+
16
+ if (!this.statusToRetry.includes(parseInt(response.statusCode))) {
17
+ break;
18
+ }
19
+
20
+ if (parseInt(response.statusCode) === this.statusTooManyRequests) {
21
+ let secondsToBackoff = 10;
22
+ if (response.headers) {
23
+ const retryAfterHeader = response.headers["Retry-After"];
24
+ if (Number.isInteger(retryAfterHeader)) {
25
+ secondsToBackoff = retryAfterHeader;
26
+ }
27
+ }
28
+ await this.rateLimitBackOff(secondsToBackoff);
29
+ } else {
30
+ await this.backoff(i);
31
+ }
32
+ response = await this.inner.send(request);
33
+ }
34
+
35
+ return response;
36
+ };
37
+
38
+ async backoff(attempt) {
39
+ const backoffDuration = Math.min(attempt, this.maxBackoffDuration);
40
+ console.log(`There was an error processing the request. Retrying in ${backoffDuration} seconds...`);
41
+ await this.sleeper.sleep(backoffDuration);
42
+ };
43
+
44
+ async rateLimitBackOff(backoffDuration) {
45
+ console.log(`Rate limit reached. Retrying in ${backoffDuration} seconds...`);
46
+ await this.sleeper.sleep(backoffDuration);
47
+ };
48
+ }
49
+
50
+ module.exports = RetrySender;
@@ -0,0 +1,8 @@
1
+ class Sleeper {
2
+ constructor () {}
3
+ sleep(seconds) {
4
+ return new Promise(resolve => setTimeout(resolve, seconds*1000));
5
+ }
6
+ }
7
+
8
+ module.exports = Sleeper;
@@ -0,0 +1,10 @@
1
+ const Response = require("../Response.js");
2
+
3
+ function buildSmartyResponse(response, error) {
4
+ if (response) return new Response(response.status, response.data, response.error, response.headers);
5
+ return new Response(undefined, undefined, error)
6
+ }
7
+
8
+ module.exports = {
9
+ buildSmartyResponse
10
+ };
@@ -0,0 +1,10 @@
1
+ class MockSleeper {
2
+ constructor() {
3
+ this.sleepDurations = [];
4
+ }
5
+ sleep(ms) {
6
+ this.sleepDurations.push(ms);
7
+ }
8
+ }
9
+
10
+ module.exports = MockSleeper;
@@ -1,3 +1,4 @@
1
+ const {buildSmartyResponse} = require("../../src/util/buildSmartyResponse.js");
1
2
  const Response = require("../../src/Response");
2
3
 
3
4
  module.exports = {
@@ -20,4 +21,21 @@ module.exports = {
20
21
  });
21
22
  }
22
23
  },
23
- };
24
+ MockSenderWithStatusCodesAndHeaders: function (statusCodes, headers = undefined, error = undefined) {
25
+ this.statusCodes = statusCodes;
26
+ this.headers = headers;
27
+ this.error = error;
28
+ this.currentStatusCodeIndex = 0;
29
+
30
+ this.send = function (request) {
31
+ let mockResponse = {
32
+ status: this.statusCodes[this.currentStatusCodeIndex],
33
+ headers: this.headers,
34
+ error: this.error,
35
+ };
36
+ const response = buildSmartyResponse(mockResponse);
37
+ this.currentStatusCodeIndex += 1;
38
+ return response;
39
+ }
40
+ }
41
+ };
@@ -2,6 +2,7 @@ const chai = require("chai");
2
2
  const expect = chai.expect;
3
3
  const Request = require("../src/Request");
4
4
  const HttpSender = require("../src/HttpSender");
5
+ const {buildSmartyResponse} = require("../src/util/buildSmartyResponse");
5
6
 
6
7
  describe ("An Axios implementation of a HTTP sender", function () {
7
8
  it("adds a data payload to the HTTP request config.", function () {
@@ -71,7 +72,7 @@ describe ("An Axios implementation of a HTTP sender", function () {
71
72
  let mockResponse = {
72
73
  status: 200
73
74
  };
74
- let smartyResponse = sender.buildSmartyResponse(mockResponse);
75
+ let smartyResponse = buildSmartyResponse(mockResponse);
75
76
 
76
77
  expect(smartyResponse.hasOwnProperty("statusCode")).to.equal(true);
77
78
  expect(smartyResponse.statusCode).to.equal(200);
@@ -84,7 +85,7 @@ describe ("An Axios implementation of a HTTP sender", function () {
84
85
  status: 200,
85
86
  data: mockData
86
87
  };
87
- let smartyResponse = sender.buildSmartyResponse(mockResponse);
88
+ let smartyResponse = buildSmartyResponse(mockResponse);
88
89
 
89
90
  expect(smartyResponse.hasOwnProperty("payload")).to.equal(true);
90
91
  expect(smartyResponse.payload).to.equal(mockData);
@@ -0,0 +1,98 @@
1
+ const chai = require("chai");
2
+ const expect = chai.expect;
3
+ const RetrySender = require("../src/RetrySender");
4
+ const {MockSenderWithStatusCodesAndHeaders} = require("./fixtures/mock_senders");
5
+ const Request = require("../src/Request.js");
6
+ const MockSleeper = require("./fixtures/MockSleeper.js");
7
+
8
+ async function sendWithRetry(retries, inner, sleeper) {
9
+ const request = new Request();
10
+ const sender = new RetrySender(retries, inner, sleeper);
11
+ return await sender.send(request);
12
+ }
13
+
14
+ describe ("Retry Sender tests", function () {
15
+ it("test success does not retry", async function () {
16
+ let inner = new MockSenderWithStatusCodesAndHeaders(["200"]);
17
+ await sendWithRetry(5, inner, new MockSleeper());
18
+
19
+ expect(inner.currentStatusCodeIndex).to.equal(1);
20
+ });
21
+
22
+ it("test client error does not retry", async function () {
23
+ let inner = new MockSenderWithStatusCodesAndHeaders(["422"]);
24
+ await sendWithRetry(5, inner, new MockSleeper());
25
+
26
+ expect(inner.currentStatusCodeIndex).to.equal(1);
27
+ });
28
+
29
+ it("test will retry until success", async function () {
30
+ let inner = new MockSenderWithStatusCodesAndHeaders(["500", "500", "500", "200", "500"]);
31
+ await sendWithRetry(10, inner, new MockSleeper());
32
+
33
+ expect(inner.currentStatusCodeIndex).to.equal(4);
34
+ });
35
+
36
+ it("test return response if retry limit exceeded", async function () {
37
+ let inner = new MockSenderWithStatusCodesAndHeaders(["500", "500", "500", "500", "500"]);
38
+ const sleeper = new MockSleeper();
39
+ const response = await sendWithRetry(4, inner, sleeper);
40
+
41
+ expect(response);
42
+ expect(inner.currentStatusCodeIndex).to.equal(5);
43
+ expect(response.statusCode).to.equal("500");
44
+ expect(sleeper.sleepDurations).to.deep.equal([0, 1, 2, 3]);
45
+ });
46
+
47
+ it("test backoff does not exceed max", async function () {
48
+ let inner = new MockSenderWithStatusCodesAndHeaders(["500", "500", "500", "500", "500", "500", "500", "500", "500", "500", "500", "500", "500", "200"]);
49
+ const sleeper = new MockSleeper();
50
+
51
+ await sendWithRetry(20, inner, sleeper);
52
+
53
+ expect(sleeper.sleepDurations).to.deep.equal([0,1,2,3,4,5,6,7,8,9,10,10,10]);
54
+ });
55
+
56
+ it("test empty status does not retry", async function () {
57
+ let inner = new MockSenderWithStatusCodesAndHeaders([]);
58
+ await sendWithRetry(5, inner, new MockSleeper());
59
+
60
+ expect(inner.currentStatusCodeIndex).to.equal(1);
61
+ });
62
+
63
+ it("test sleep on rate limit", async function () {
64
+ let inner = new MockSenderWithStatusCodesAndHeaders(["429", "200"]);
65
+ const sleeper = new MockSleeper();
66
+
67
+ await sendWithRetry(5, inner, sleeper);
68
+
69
+ expect(sleeper.sleepDurations).to.deep.equal([10]);
70
+ });
71
+
72
+ it("test rate limit error return", async function () {
73
+ let inner = new MockSenderWithStatusCodesAndHeaders(["429"], {"Retry-After": 7});
74
+ const sleeper = new MockSleeper();
75
+
76
+ await sendWithRetry(10, inner, sleeper);
77
+
78
+ expect(sleeper.sleepDurations).to.deep.equal([7]);
79
+ });
80
+
81
+ it("test retry after invalid value", async function () {
82
+ let inner = new MockSenderWithStatusCodesAndHeaders(["429"], {"Retry-After": "a"});
83
+ const sleeper = new MockSleeper();
84
+
85
+ await sendWithRetry(10, inner, sleeper);
86
+
87
+ expect(sleeper.sleepDurations).to.deep.equal([10]);
88
+ });
89
+
90
+ it("test retry error", async function () {
91
+ let inner = new MockSenderWithStatusCodesAndHeaders(["429"], undefined, "Big Bad");
92
+ const sleeper = new MockSleeper();
93
+
94
+ const response = await sendWithRetry(10, inner, sleeper);
95
+
96
+ expect(response.error).to.equal("Big Bad");
97
+ });
98
+ });