Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multiple validator #25

Merged
merged 7 commits into from
Dec 7, 2023
60 changes: 45 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,35 @@ const usps = new USPS({
});
```

### verifyMultiple(object[])

Verify takes one parameter: object[]

object[]: [{Address1, Address2, City, State, Zip}]

**Example**

```js
usps
.verifyMultiple([{
Address1: "322 3rd st.",
Address2: "Apt 2",
City: "San Francisco",
State: "CA",
Zip5: "94103",
}, {
Address1: "322 3rd st.",
Address2: "Apt 2",
City: "San Francisco",
State: "CA",
Zip5: "94103",
}
])
.then((address) => {
console.log(address);
});
```

### verify(object)

Verify takes one parameter: object
Expand Down Expand Up @@ -88,19 +117,20 @@ console.log(result);
### Coverage

```
-------------------------|---------|----------|---------|---------|----------------------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------------|---------|----------|---------|---------|----------------------------------
All files | 91.78 | 59.61 | 87.5 | 91.78 |
src | 99.32 | 47.36 | 100 | 99.32 |
address-validate.ts | 98.97 | 33.33 | 100 | 98.97 | 94
usps.ts | 100 | 100 | 100 | 100 |
src/lookups | 85.55 | 60 | 66.66 | 85.55 |
city-state-lookup.ts | 97.95 | 80 | 100 | 97.95 | 45
pricing-rate-lookup.ts | 75.16 | 100 | 0 | 75.16 | 113-149
zip-code-lookup.ts | 98.61 | 50 | 100 | 98.61 | 68
src/utils | 94.58 | 72.22 | 100 | 94.58 |
proper-case.ts | 97.95 | 100 | 100 | 97.95 | 36
request.ts | 93.5 | 61.53 | 100 | 93.5 | 48-49,55,115-116,120-122,153-154
-------------------------|---------|----------|---------|---------|----------------------------------
-------------------------------|---------|----------|---------|---------|------------------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------------------------|---------|----------|---------|---------|------------------------------
All files | 91.64 | 62.65 | 93.75 | 91.64 |
src | 95.85 | 55.55 | 100 | 95.85 |
address-validate.ts | 96 | 37.5 | 100 | 96 | 81,84,87,93
multiple-address-validate.ts | 92.77 | 54.54 | 100 | 92.77 | 62,65,68,80-82
usps.ts | 100 | 100 | 100 | 100 |
src/lookups | 84.81 | 66.66 | 83.33 | 84.81 |
city-state-lookup.ts | 100 | 83.33 | 100 | 100 | 45
pricing-rate-lookup.ts | 74.49 | 100 | 50 | 74.49 | 112-149
zip-code-lookup.ts | 95.83 | 54.54 | 100 | 95.83 | 57,60,63
src/utils | 95.65 | 75 | 100 | 95.65 |
proper-case.ts | 100 | 100 | 100 | 100 |
request.ts | 94.23 | 64.28 | 100 | 94.23 | 54-55,61,112,122-123,127-129
-------------------------------|---------|----------|---------|---------|------------------------------
```
86 changes: 86 additions & 0 deletions src/multiple-address-validate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import type { Address, MultipleAddress } from "./usps.js";
import type USPSClass from "./usps.js";
import type { AddressValidateResponse } from "./address-validate.js";
import properCase from "./utils/proper-case.js";
import callUSPS from "./utils/request.js";

// See page 4, "AddressValidateRequest / Address /" section of: https://www.usps.com/business/web-tools-apis/address-information-api.pdf

export interface MultipleAddressValidateRequest {
Address: MultipleAddress[];
Revision: number;
}

// eslint-disable-next-line sonarjs/cognitive-complexity, func-names
export default async function (
this: USPSClass,
addresses: Address[],
): Promise<MultipleAddress[]> {
if (addresses.length > 5) {
throw new Error("Maximum of 5 addresses allowed per request.")
}

if (Array.isArray(addresses) === false) {
throw new TypeError("Must pass an array of addresses. For single address use 'verify' method.");
}

const Addresses: MultipleAddress[] = addresses.map((address: Address, index: number) => ({
'@ID': index.toString(),
Address1: address.Address2 ?? "",
Address2: address.Address1 ?? "",
City: address.City ?? "",
State: address.State ?? "",
Urbanization: address.Urbanization ?? "",
Zip5: address.Zip5 ?? "",
// USPS expects Zip4 after Zip5
// eslint-disable-next-line sort-keys
Zip4: address.Zip4 ?? "",
}));

const parameters: MultipleAddressValidateRequest = {
Revision: 1,
// USPS expects Address to come after Revision
// eslint-disable-next-line sort-keys
Address: Addresses,
};

let response: AddressValidateResponse[];
try {
response = (await callUSPS(
"Verify",
"AddressValidate",
"Address",
this.config,
parameters,
)) as AddressValidateResponse[];
if (response) {
return response.map((addr) => {
const fAddr = { ...addr };

const switchAddresses = fAddr.Address1;
fAddr.Address1 = fAddr.Address2;
fAddr.Address2 = switchAddresses;
if (this.config.properCase) {
fAddr.Address1 = fAddr.Address1
? properCase(fAddr.Address1)
: undefined;
fAddr.Address2 = fAddr.Address2
? properCase(fAddr.Address2)
: undefined;
fAddr.City = fAddr.City ? properCase(fAddr.City) : undefined;
fAddr.FirmName = fAddr.FirmName
? properCase(fAddr.FirmName)
: undefined;
}
fAddr.Zip4 =
typeof fAddr.Zip4 === "object"
? undefined
: fAddr.Zip4?.toString();
return fAddr as MultipleAddress;
});
}
throw new Error("Can't find results");
} catch (error) {
throw new Error(error as string);
}
}
10 changes: 10 additions & 0 deletions src/usps.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import verify from "./address-validate.js";
import verifyMultiple from "./multiple-address-validate.js"
import cityStateLookup from "./lookups/city-state-lookup.js";
import pricingRateLookup from "./lookups/pricing-rate-lookup.js";
import zipCodeLookup from "./lookups/zip-code-lookup.js";
Expand Down Expand Up @@ -28,6 +29,13 @@ export interface Address {
Zip5?: string;
}

// Each address needs an ID when sending multiple in a single request
// Error can be returned for each address
export interface MultipleAddress extends Address {
"@ID": string;
Error?: ErrorResponse;
}

export default class {
public cityStateLookup = cityStateLookup;

Expand All @@ -46,5 +54,7 @@ export default class {

public verify = verify;

public verifyMultiple = verifyMultiple;

public zipCodeLookup = zipCodeLookup;
}
4 changes: 4 additions & 0 deletions src/utils/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import type {
AddressValidateRequest,
AddressValidateResponse,
} from "../address-validate.js";
import type {
MultipleAddressValidateRequest,
} from "../multiple-address-validate.js";
import type {
CityStateLookupRequest,
CityStateLookupResponse,
Expand Down Expand Up @@ -72,6 +75,7 @@ export default async (
config: Config,
parameters:
| AddressValidateRequest
| MultipleAddressValidateRequest
| ZipCodeLookupRequest
| CityStateLookupRequest
| RateV4Request,
Expand Down
115 changes: 115 additions & 0 deletions test/multiple-validator-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/* eslint-disable sonarjs/no-duplicate-string */
import test from "ava";

import USPS from "../src/usps.js";

const usps = new USPS({
userId: process.env["USPS_ID"]!,
});

const fourAddresses = [
{
Address1: "11205 SE 233RD PL.",
Address2: "Apartment 2",
City: "Kent",
State: "WA",
Zip5: "98031",
},
{
Address1: "11205 SE 233RD PL.",
Address2: "UNIT 2",
City: "Kent",
State: "WA",
Zip5: "98031",
},
{
Address1: "11205 southeast 233Road PLace.",
Address2: "Building 2",
City: "Kent",
State: "WA",
Zip5: "98031",
},
{
Address1: "11205 SE 233RD PL.",
Address2: "Floor 2",
City: "Kent",
State: "WA",
Zip5: "98031",
},
];

const moreAddresses = [
{
Address1: "11205 SE 233RD PL.",
Address2: "Apartment 4",
City: "Kent",
State: "WA",
Zip5: "98031",
},
{
Address1: "11205 southeast 233Road PLace.",
Address2: "Building 3",
City: "Kent",
State: "WA",
Zip5: "98031",
},
]

const invalidAddress = {
Address1: "1212 s kingsway rd",
City: "seffner",
State: "fl",
Zip5: "33584",
}

test("Multiple address verify should return the same number of addresses.", async (t) => {
const addresses = await usps.verifyMultiple(fourAddresses);
t.is(addresses.length, 4);
});

test("Multiple address verify should only accept addesses in an array.", async (t) => {
const error = await t.throwsAsync(async () => {
// @ts-expect-error Testing invalid input
await usps.verifyMultiple(fourAddresses[0]);
});
t.is(
error?.message,
"Must pass an array of addresses. For single address use 'verify' method.",
);
});

test("Multiple address verify should throw an error if more than 5 addresses are passed.", async (t) => {
const error = await t.throwsAsync(async () => {
await usps.verifyMultiple([...fourAddresses, ...moreAddresses]);
});
t.is(
error?.message,
"Maximum of 5 addresses allowed per request.",
);
});

test("Multiple address verify should validate each address in the same way as a single verify would.", async (t) => {
const addresses = await usps.verifyMultiple(fourAddresses);
t.is(addresses[0]?.Address2, "APT 2");
t.is(addresses[1]?.Address2, "UNIT 2");
t.is(addresses[2]?.Address2, "BLDG 2");
t.is(addresses[3]?.Address2, "FL 2");
});

test("Multiple address verify should handle proper case the same as single verify.", async (t) => {
const uspsCase = new USPS({
properCase: true,
userId: process.env["USPS_ID"]!,
});
const addresses = await uspsCase.verifyMultiple(moreAddresses);
t.is(addresses[0]?.Address1, "11205 SE 233rd Pl");
t.is(addresses[0]?.Address2, "Apt 4");
t.is(addresses[0]?.City, "Kent");
});

test("Multiple address verify should contain error message for any unverifiable addresses in a list.", async (t) => {
const addresses = await usps.verifyMultiple([...moreAddresses, invalidAddress]);
t.false("Error" in addresses[0]!);
t.false("Error" in addresses[1]!);
t.is(addresses[2]?.Error?.Description, "Multiple addresses were found for the information you entered, and no default exists.");
});
1 change: 0 additions & 1 deletion test/validator-test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
/* eslint-disable sonarjs/no-duplicate-string */

import test from "ava";

import USPS from "../src/usps.js";
Expand Down
Loading