diff --git a/examples/international_address_autocomplete.mjs b/examples/international_address_autocomplete.mjs index e021ccb..1737567 100644 --- a/examples/international_address_autocomplete.mjs +++ b/examples/international_address_autocomplete.mjs @@ -1,4 +1,4 @@ -import SmartySDK from "smartystreets-javascript-sdk"; +import SmartySDK, { InternationalAutocompleteLanguageMode } from "smartystreets-javascript-sdk"; const SmartyCore = SmartySDK.core; const Lookup = SmartySDK.internationalAddressAutocomplete.Lookup; @@ -29,6 +29,7 @@ const summaryLookup = new Lookup({ country, maxGroupResults: 50, geolocation: true, + language: InternationalAutocompleteLanguageMode.Native, }); await handleRequest(summaryLookup, "Response of summary results"); diff --git a/examples/international_address_autocomplete.ts b/examples/international_address_autocomplete.ts index 9fb80c9..caba895 100644 --- a/examples/international_address_autocomplete.ts +++ b/examples/international_address_autocomplete.ts @@ -2,6 +2,7 @@ import { ClientBuilder, BasicAuthCredentials, LookupInternationalAddressAutocomplete, + InternationalAutocompleteLanguageMode, } from "smartystreets-javascript-sdk"; // for client-side requests (browser/mobile), use this code: @@ -63,6 +64,7 @@ async function main(): Promise { country, maxGroupResults: 50, geolocation: true, + language: InternationalAutocompleteLanguageMode.Native, }); await handleRequest(summaryLookup, "Response of summary results"); diff --git a/index.ts b/index.ts index aa02bfc..9d909c2 100644 --- a/index.ts +++ b/index.ts @@ -61,6 +61,7 @@ export type { AutocompleteSource } from "./src/us_autocomplete/Lookup.js"; export type { ReverseGeoSource } from "./src/us_reverse_geo/Lookup.js"; export type { Geocode } from "./src/international_street/Lookup.js"; export { LanguageMode } from "./src/international_street/Lookup.js"; +export { LanguageMode as InternationalAutocompleteLanguageMode } from "./src/international_address_autocomplete/LanguageMode.js"; export { Batch, diff --git a/src/international_address_autocomplete/Client.ts b/src/international_address_autocomplete/Client.ts index 957eb2e..278252e 100644 --- a/src/international_address_autocomplete/Client.ts +++ b/src/international_address_autocomplete/Client.ts @@ -5,6 +5,7 @@ import buildInputData from "../util/buildInputData.js"; import apiToSDKKeyMap from "../util/apiToSDKKeyMap.js"; import { Sender } from "../types.js"; import Lookup from "./Lookup.js"; +import { resolveLanguageMode } from "./LanguageMode.js"; const keyTranslationFormat = apiToSDKKeyMap.internationalAddressAutocomplete; @@ -21,6 +22,9 @@ export default class Client { const request = new Request(); request.parameters = buildInputData(lookup, keyTranslationFormat); + const resolvedLanguage = resolveLanguageMode(lookup.language); + if (resolvedLanguage !== undefined) request.parameters["language"] = resolvedLanguage; + if (lookup.geolocation) { request.parameters["geolocation"] = "on"; } else { diff --git a/src/international_address_autocomplete/LanguageMode.ts b/src/international_address_autocomplete/LanguageMode.ts new file mode 100644 index 0000000..14b3b9c --- /dev/null +++ b/src/international_address_autocomplete/LanguageMode.ts @@ -0,0 +1,27 @@ +import { UnprocessableEntityError } from "../Errors.js"; + +export enum LanguageMode { + Native = "native", + Latin = "latin", +} + +const messages = { + invalidLanguage: + "Invalid input: language can only be set to 'latin' or 'native'. When not set, the output language will match the default for the country.", +}; + +// Resolves a LanguageMode member or a raw value (eg. from untyped JS callers) into a LanguageMode, +// matching "native"/"latin" regardless of case. Returns undefined for unset/empty input. +export function resolveLanguageMode( + value: LanguageMode | string | undefined, +): LanguageMode | undefined { + if (value === undefined || String(value).trim().length < 1) return undefined; + + const match = Object.values(LanguageMode).find( + (mode) => mode.toLowerCase() === String(value).toLowerCase(), + ); + + if (!match) throw new UnprocessableEntityError(messages.invalidLanguage); + + return match; +} diff --git a/src/international_address_autocomplete/Lookup.ts b/src/international_address_autocomplete/Lookup.ts index 075e541..2db7e3d 100644 --- a/src/international_address_autocomplete/Lookup.ts +++ b/src/international_address_autocomplete/Lookup.ts @@ -1,4 +1,5 @@ import Suggestion from "./Suggestion.js"; +import { LanguageMode } from "./LanguageMode.js"; export default class Lookup { result: Suggestion[]; @@ -10,6 +11,7 @@ export default class Lookup { includeOnlyPostalCode: string | undefined; maxGroupResults: number; geolocation: boolean; + language: LanguageMode | undefined; customParameters: Record; constructor({ @@ -21,6 +23,7 @@ export default class Lookup { includeOnlyPostalCode, maxGroupResults = 100, geolocation = false, + language, }: { search?: string; addressId?: string; @@ -30,6 +33,7 @@ export default class Lookup { includeOnlyPostalCode?: string; maxGroupResults?: number; geolocation?: boolean; + language?: LanguageMode; } = {}) { this.result = []; @@ -41,6 +45,7 @@ export default class Lookup { this.includeOnlyPostalCode = includeOnlyPostalCode; this.maxGroupResults = maxGroupResults; this.geolocation = geolocation; + this.language = language; this.customParameters = {}; } diff --git a/tests/international_address_autocomplete/test_Client.ts b/tests/international_address_autocomplete/test_Client.ts index 5844c61..73d0595 100644 --- a/tests/international_address_autocomplete/test_Client.ts +++ b/tests/international_address_autocomplete/test_Client.ts @@ -1,6 +1,7 @@ import { expect } from "chai"; import Client from "../../src/international_address_autocomplete/Client.js"; import Lookup from "../../src/international_address_autocomplete/Lookup.js"; +import { LanguageMode } from "../../src/international_address_autocomplete/LanguageMode.js"; import Suggestion from "../../src/international_address_autocomplete/Suggestion.js"; import errors from "../../src/Errors.js"; import { MockSender, MockSenderWithResponse } from "../fixtures/mock_senders.js"; @@ -87,6 +88,86 @@ describe("An International Address Autocomplete Client", function () { expect(mockSender.request.parameters).to.deep.equal(expectedParameters); }); + it("builds parameters without a language when it is not set", function () { + let mockSender = new MockSender(); + let client = new Client(mockSender); + let lookup = new Lookup({ search: "(" }); + + client.send(lookup); + + expect(mockSender.request.parameters).to.not.have.property("language"); + }); + + it("builds parameters with a native language", function () { + let mockSender = new MockSender(); + let client = new Client(mockSender); + let search = "("; + let lookup = new Lookup({ search, language: LanguageMode.Native }); + let expectedParameters = { + max_results: 5, + max_group_results: 100, + search: search, + language: "native", + }; + + client.send(lookup); + expect(mockSender.request.parameters).to.deep.equal(expectedParameters); + }); + + it("builds parameters with a latin language", function () { + let mockSender = new MockSender(); + let client = new Client(mockSender); + let search = "("; + let lookup = new Lookup({ search, language: LanguageMode.Latin }); + let expectedParameters = { + max_results: 5, + max_group_results: 100, + search: search, + language: "latin", + }; + + client.send(lookup); + expect(mockSender.request.parameters).to.deep.equal(expectedParameters); + }); + + it("normalizes a mixed-case language value before sending.", function () { + let mockSender = new MockSender(); + let client = new Client(mockSender); + let lookup = new Lookup({ search: "(", language: "Latin" as LanguageMode }); + + client.send(lookup); + + expect(mockSender.request.parameters["language"]).to.equal("latin"); + }); + + it("does not mutate the original language value when normalizing.", function () { + let mockSender = new MockSender(); + let client = new Client(mockSender); + let lookup = new Lookup({ search: "(", language: "Latin" as LanguageMode }); + + client.send(lookup); + + expect(lookup.language).to.equal("Latin"); + }); + + it("omits the language parameter when it is set to a blank value", function () { + let mockSender = new MockSender(); + let client = new Client(mockSender); + let lookup = new Lookup({ search: "(", language: " " as LanguageMode }); + + client.send(lookup); + + expect(mockSender.request.parameters).to.not.have.property("language"); + }); + + it("throws an error if sending a lookup with an invalid language.", function () { + let mockSender = new MockSender(); + let client = new Client(mockSender); + let lookup = new Lookup({ search: "(", language: "Rubberduckian" as LanguageMode }); + + expect(() => client.send(lookup)).to.throw(errors.UnprocessableEntityError); + }); + it("throws an error if sending without a lookup.", function () { let mockSender = new MockSender(); let client = new Client(mockSender); diff --git a/tests/international_address_autocomplete/test_Lookup.ts b/tests/international_address_autocomplete/test_Lookup.ts index 5bb07c8..55e6a12 100644 --- a/tests/international_address_autocomplete/test_Lookup.ts +++ b/tests/international_address_autocomplete/test_Lookup.ts @@ -1,5 +1,6 @@ import { expect } from "chai"; import Lookup from "../../src/international_address_autocomplete/Lookup.js"; +import { LanguageMode } from "../../src/international_street/Lookup.js"; describe("An International Address Autocomplete lookup", function () { it("Can be newed up with a prefix", function () { @@ -50,6 +51,12 @@ describe("An International Address Autocomplete lookup", function () { expect(lookup.geolocation).to.equal(geolocation); }); + it("Set language", function () { + const language = LanguageMode.Native; + let lookup = new Lookup({ language }); + expect(lookup.language).to.equal(language); + }); + it("Checking defaults of params on instantiation ", function () { const defaultLookup = { result: [], @@ -61,6 +68,7 @@ describe("An International Address Autocomplete lookup", function () { includeOnlyPostalCode: undefined, maxGroupResults: 100, geolocation: false, + language: undefined, customParameters: {}, }; let lookup = new Lookup();