Skip to content

Commit 1bb04c5

Browse files
Merge pull request #115 from smarty/andrew/postal-code-api
adds international postal code lookups
2 parents 5dc5f57 + ab1520b commit 1bb04c5

File tree

12 files changed

+345
-10
lines changed

12 files changed

+345
-10
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import SmartySDK from "smartystreets-javascript-sdk";
2+
3+
const SmartyCore = SmartySDK.core;
4+
const Lookup = SmartySDK.internationalPostalCode.Lookup;
5+
6+
// for Server-to-server requests, use this code:
7+
// let authId = process.env.SMARTY_AUTH_ID;
8+
// let authToken = process.env.SMARTY_AUTH_TOKEN;
9+
// const credentials = new SmartyCore.StaticCredentials(authId, authToken);
10+
11+
// for client-side requests (browser/mobile), use this code:
12+
let key = process.env.SMARTY_EMBEDDED_KEY;
13+
const credentials = new SmartyCore.SharedCredentials(key);
14+
15+
// The appropriate license values to be used for your subscriptions
16+
// can be found on the Subscription page of the account dashboard.
17+
// https://www.smarty.com/docs/cloud/licensing
18+
let clientBuilder = new SmartyCore.ClientBuilder(credentials);
19+
// .withBaseUrl("YOUR URL") // withBaseUrl() should be used if you are self-hosting the Smarty API
20+
let client = clientBuilder.buildInternationalPostalCodeClient();
21+
22+
// Documentation for input fields can be found at:
23+
// https://www.smarty.com/docs/cloud/international-postal-code-api#input-fields
24+
25+
// Lookup by postal code and country
26+
let lookup1 = new Lookup("Australia", "2776");
27+
// uncomment the following line to add a custom parameter
28+
// lookup1.addCustomParameter("input_id", 1234);
29+
30+
// Lookup by locality, administrative area, and country
31+
let lookup2 = new Lookup("Brazil", null, "SP", "Sao Paulo", "ID-8675309");
32+
33+
await handleResponse(lookup1, "Postal code lookup");
34+
await handleResponse(lookup2, "Locality and administrative area lookup");
35+
36+
function displayResult(lookup, message) {
37+
console.log("*** " + message + " ***");
38+
if (lookup.result && lookup.result.length > 0) {
39+
lookup.result.forEach((result) => {
40+
console.log("Input ID:", result.inputId);
41+
console.log("Administrative Area:", result.administrativeArea);
42+
console.log("Super Administrative Area:", result.superAdministrativeArea);
43+
console.log("Sub Administrative Area:", result.subAdministrativeArea);
44+
console.log("Locality:", result.locality);
45+
console.log("Dependent Locality:", result.dependentLocality);
46+
console.log("Dependent Locality Name:", result.dependentLocalityName);
47+
console.log("Double Dependent Locality:", result.doubleDependentLocality);
48+
console.log("Postal Code:", result.postalCode);
49+
console.log("Postal Code Extra:", result.postalCodeExtra);
50+
console.log("Country ISO3:", result.countryIso3);
51+
console.log("---");
52+
});
53+
} else {
54+
console.log("No results found");
55+
}
56+
console.log("\n");
57+
}
58+
59+
function handleError(error) {
60+
console.log("ERROR:", error);
61+
}
62+
63+
async function handleResponse(lookup, lookupType) {
64+
try {
65+
const result = await client.send(lookup);
66+
displayResult(result, lookupType);
67+
} catch (err) {
68+
handleError(err);
69+
}
70+
}

examples/us_zipcode.mjs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ let key = process.env.SMARTY_EMBEDDED_KEY;
1313
const credentials = new SmartyCore.SharedCredentials(key);
1414

1515
let clientBuilder = new SmartyCore.ClientBuilder(credentials);
16-
// .withBaseUrl("YOUR URL") // withBaseUrl() should be used if you are self-hosting the Smarty API
16+
// .withBaseUrl("YOUR URL") // withBaseUrl() should be used if you are self-hosting the Smarty API
1717

1818
let client = clientBuilder.buildUsZipcodeClient();
1919

@@ -45,17 +45,19 @@ batch.add(lookup3);
4545
await handleResponse(batch);
4646

4747
function viewResults(response) {
48-
response.lookups.map(lookup => lookup.result.map(candidate => {
49-
candidate.cities.map(city => console.log(city.city));
50-
// candidate.zipcodes.map(zipcode => console.log(zipcode.zipcode));
51-
}));
48+
response.lookups.map((lookup) =>
49+
lookup.result.map((candidate) => {
50+
candidate.cities.map((city) => console.log(city.city));
51+
// candidate.zipcodes.map(zipcode => console.log(zipcode.zipcode));
52+
}),
53+
);
5254
}
5355

5456
async function handleResponse(lookup) {
5557
try {
5658
const result = await client.send(lookup);
5759
viewResults(result);
58-
} catch(err) {
60+
} catch (err) {
5961
console.log(err);
6062
}
61-
}
63+
}

index.mjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ import SuggestionInternationalAddressAutocomplete from "./src/international_addr
3232
import LookupUSEnrichment from "./src/us_enrichment/Lookup.js";
3333
import ResponseUSEnrichment from "./src/us_enrichment/Response.js";
3434

35+
import LookupInternationalPostalCode from "./src/international_postal_code/Lookup.js";
36+
import ResultInternationalPostalCode from "./src/international_postal_code/Result.js";
37+
3538
export const core = {
3639
Batch,
3740
ClientBuilder,
@@ -80,6 +83,11 @@ export const usEnrichment = {
8083
Response: ResponseUSEnrichment,
8184
};
8285

86+
export const internationalPostalCode = {
87+
Lookup: LookupInternationalPostalCode,
88+
Result: ResultInternationalPostalCode,
89+
};
90+
8391
export default {
8492
core,
8593
usStreet,
@@ -90,4 +98,5 @@ export default {
9098
usReverseGeo,
9199
internationalAddressAutocomplete,
92100
usEnrichment,
101+
internationalPostalCode,
93102
};

src/ClientBuilder.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ const InternationalStreetClient = require("./international_street/Client");
2020
const UsReverseGeoClient = require("./us_reverse_geo/Client");
2121
const InternationalAddressAutocompleteClient = require("./international_address_autocomplete/Client");
2222
const UsEnrichmentClient = require("./us_enrichment/Client");
23+
const InternationalPostalCodeClient = require("./international_postal_code/Client");
2324

2425
const INTERNATIONAL_STREET_API_URI = "https://international-street.api.smarty.com/verify";
2526
const US_AUTOCOMPLETE_PRO_API_URL = "https://us-autocomplete-pro.api.smarty.com/lookup";
@@ -30,7 +31,7 @@ const US_REVERSE_GEO_API_URL = "https://us-reverse-geo.api.smarty.com/lookup";
3031
const INTERNATIONAL_ADDRESS_AUTOCOMPLETE_API_URL =
3132
"https://international-autocomplete.api.smarty.com/v2/lookup";
3233
const US_ENRICHMENT_API_URL = "https://us-enrichment.api.smarty.com/lookup";
33-
34+
const INTERNATIONAL_POSTAL_CODE_API_URL = "https://international-postal-code.api.smarty.com/lookup";
3435
/**
3536
* The ClientBuilder class helps you build a client object for one of the supported Smarty APIs.<br>
3637
* You can use ClientBuilder's methods to customize settings like maximum retries or timeout duration. These methods<br>
@@ -51,7 +52,7 @@ class ClientBuilder {
5152
this.licenses = [];
5253

5354
function noCredentialsProvided() {
54-
return !signer instanceof StaticCredentials || !signer instanceof SharedCredentials;
55+
return (!signer) instanceof StaticCredentials || (!signer) instanceof SharedCredentials;
5556
}
5657
}
5758

@@ -185,6 +186,10 @@ class ClientBuilder {
185186
return this.buildClient(US_ZIP_CODE_API_URL, UsZipcodeClient);
186187
}
187188

189+
buildInternationalPostalCodeClient() {
190+
return this.buildClient(INTERNATIONAL_POSTAL_CODE_API_URL, InternationalPostalCodeClient);
191+
}
192+
188193
buildUsAutocompleteProClient() {
189194
return this.buildClient(US_AUTOCOMPLETE_PRO_API_URL, UsAutocompleteProClient);
190195
}
@@ -213,4 +218,4 @@ class ClientBuilder {
213218
}
214219
}
215220

216-
module.exports = ClientBuilder;
221+
module.exports = ClientBuilder;
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
const Request = require("../Request");
2+
const Result = require("./Result");
3+
const buildInputData = require("../util/buildInputData");
4+
const keyTranslationFormat = require("../util/apiToSDKKeyMap").internationalPostalCode;
5+
const { UndefinedLookupError } = require("../Errors");
6+
7+
/**
8+
* This client sends lookups to the Smarty International Postal Code API, <br>
9+
* and attaches the results to the appropriate Lookup objects.
10+
*/
11+
class Client {
12+
constructor(sender) {
13+
this.sender = sender;
14+
}
15+
16+
/**
17+
* Sends a single lookup for validation.
18+
* @param data A Lookup object
19+
* @throws SmartyException
20+
*/
21+
send(lookup) {
22+
if (typeof lookup === "undefined") throw new UndefinedLookupError();
23+
24+
let request = new Request();
25+
request.parameters = buildInputData(lookup, keyTranslationFormat);
26+
27+
return new Promise((resolve, reject) => {
28+
this.sender
29+
.send(request)
30+
.then((response) => {
31+
if (response.error) reject(response.error);
32+
33+
resolve(attachLookupResults(response, lookup));
34+
})
35+
.catch(reject);
36+
});
37+
38+
function attachLookupResults(response, lookup) {
39+
if (response.payload && Array.isArray(response.payload)) {
40+
lookup.result = response.payload.map((r) => new Result(r));
41+
} else {
42+
lookup.result = [];
43+
}
44+
return lookup;
45+
}
46+
}
47+
}
48+
49+
module.exports = Client;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* In addition to holding all of the input data for this lookup, this class also<br>
3+
* will contain the result of the lookup after it comes back from the API.
4+
* @see "https://www.smarty.com/docs/cloud/international-postal-code-api#http-request-input-fields"
5+
*/
6+
class Lookup {
7+
constructor(country, postalCode, administrativeArea, locality, inputId) {
8+
this.inputId = inputId;
9+
this.country = country;
10+
this.postalCode = postalCode;
11+
this.administrativeArea = administrativeArea;
12+
this.locality = locality;
13+
this.result = [];
14+
this.customParameters = {};
15+
}
16+
17+
addCustomParameter(key, value) {
18+
this.customParameters[key] = value;
19+
}
20+
}
21+
22+
module.exports = Lookup;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @see "https://www.smarty.com/docs/cloud/international-postal-code-api#output-fields"
3+
*/
4+
class Result {
5+
constructor(responseData) {
6+
this.inputId = responseData.input_id;
7+
this.administrativeArea = responseData.administrative_area;
8+
this.superAdministrativeArea = responseData.super_administrative_area;
9+
this.subAdministrativeArea = responseData.sub_administrative_area;
10+
this.locality = responseData.locality;
11+
this.dependentLocality = responseData.dependent_locality;
12+
this.dependentLocalityName = responseData.dependent_locality_name;
13+
this.doubleDependentLocality = responseData.double_dependent_locality;
14+
this.postalCode = responseData.postal_code;
15+
this.postalCodeExtra = responseData.postal_code_extra;
16+
this.countryIso3 = responseData.country_iso_3;
17+
}
18+
}
19+
20+
module.exports = Result;

src/util/apiToSDKKeyMap.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,5 +72,12 @@ module.exports = {
7272
exclude: "exclude",
7373
dataset: "dataset",
7474
data_subset: "dataSubset",
75+
},
76+
internationalPostalCode: {
77+
"input_id": "inputId",
78+
"country": "country",
79+
"locality": "locality",
80+
"administrative_area": "administrativeArea",
81+
"postal_code": "postalCode",
7582
}
7683
};

src/util/buildClients.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ function buildUsEnrichmentApiClient(credentials) {
3636
return instantiateClientBuilder(credentials).buildUsEnrichmentClient();
3737
}
3838

39+
function buildInternationalPostalCodeApiClient(credentials) {
40+
return instantiateClientBuilder(credentials).buildInternationalPostalCodeClient();
41+
}
42+
3943
module.exports = {
4044
usStreet: buildUsStreetApiClient,
4145
usAutocompletePro: buildUsAutocompleteProApiClient,
@@ -45,4 +49,5 @@ module.exports = {
4549
usReverseGeo: buildUsReverseGeoApiClient,
4650
internationalAddressAutocomplete: buildInternationalAddressAutocompleteApiClient,
4751
usEnrichment: buildUsEnrichmentApiClient,
52+
internationalPostalCode: buildInternationalPostalCodeApiClient,
4853
};
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
const chai = require("chai");
2+
const expect = chai.expect;
3+
const Client = require("../../src/international_postal_code/Client");
4+
const Lookup = require("../../src/international_postal_code/Lookup");
5+
const MockSender = require("../fixtures/mock_senders").MockSender;
6+
const MockSenderWithResponse = require("../fixtures/mock_senders").MockSenderWithResponse;
7+
8+
describe("An International Postal Code client", function () {
9+
it("has an inner sender.", function () {
10+
let mockSender = new MockSender();
11+
let client = new Client(mockSender);
12+
13+
expect(client.sender).to.deep.equal(mockSender);
14+
});
15+
16+
it("throws an error if sending without a lookup.", function () {
17+
let mockSender = new MockSender();
18+
let client = new Client(mockSender);
19+
20+
expect(() => client.send()).to.throw();
21+
});
22+
23+
it("attaches a result from a response to a lookup.", function () {
24+
const expectedMockPayload = [
25+
{
26+
input_id: "1234",
27+
administrative_area: "SP",
28+
super_administrative_area: undefined,
29+
sub_administrative_area: undefined,
30+
locality: "São Paulo",
31+
dependent_locality: "Casa Verde",
32+
dependent_locality_name: undefined,
33+
double_dependent_locality: undefined,
34+
postal_code: "02516-040",
35+
postal_code_extra: undefined,
36+
country_iso_3: "BRA",
37+
},
38+
];
39+
let mockSender = new MockSenderWithResponse(expectedMockPayload);
40+
const client = new Client(mockSender);
41+
let lookup = new Lookup("Brazil", "02516-040");
42+
43+
return client.send(lookup).then(() => {
44+
expect(lookup.result).to.be.an("array");
45+
expect(lookup.result.length).to.equal(1);
46+
expect(lookup.result[0].inputId).to.equal("1234");
47+
expect(lookup.result[0].administrativeArea).to.equal("SP");
48+
expect(lookup.result[0].locality).to.equal("São Paulo");
49+
expect(lookup.result[0].dependentLocality).to.equal("Casa Verde");
50+
expect(lookup.result[0].postalCode).to.equal("02516-040");
51+
expect(lookup.result[0].countryIso3).to.equal("BRA");
52+
});
53+
});
54+
55+
it("handles empty results array.", function () {
56+
const expectedMockPayload = {
57+
results: [],
58+
};
59+
let mockSender = new MockSenderWithResponse(expectedMockPayload);
60+
const client = new Client(mockSender);
61+
let lookup = new Lookup("Brazil", "99999-999");
62+
63+
return client.send(lookup).then(() => {
64+
expect(lookup.result).to.be.an("array");
65+
expect(lookup.result.length).to.equal(0);
66+
});
67+
});
68+
});

0 commit comments

Comments
 (0)