Skip to content

Commit

Permalink
feat(Core Interface): Upgrade to v2
Browse files Browse the repository at this point in the history
Version 2 of core-interface includes breaking changes to improve load
and running time. This is achieved by reorganising the library to be
better tree-shakeable.

Helper methods (like lookupPostcode) and resources (like addresses,
udprn) are not included with the client. Now they are exported by the
library and require the client to be injected

BREAKING CHANGE:

- Package now exports a `defaults` object
- Client.defaults has been removed
- All client config is now stored in `client.config`
- All resources have been removed from the client. Instead retrieve
these from the library and inject the client. E.g.
`client.postcodes.retrieve` becomes `postcodes.retrieve(client, ...)`
- Helper methods (like lookupPostcode, ping) have been removed from the client.
Instead retrieve these from teh library and inject the client. E.g.
`client.lookupPostcode` becomes `lookupPostcode(client, ...)`
  • Loading branch information
cblanc committed Jun 8, 2021
1 parent bf4f2f1 commit a7f958f
Show file tree
Hide file tree
Showing 9 changed files with 41 additions and 107 deletions.
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ npm install @ideal-postcodes/core-browser
#### Instantiate

```javascript
import { Client } from "@ideal-postcodes/core-browser"
import { Client } from "@ideal-postcodes/core-browser";

const client = new Client({ api_key: "iddqd" });
```
Expand All @@ -79,16 +79,17 @@ const client = new Client({ api_key: "iddqd" });
#### Use

```javascript
const addresses = await client.lookupPostcode({ postcode: "SW1A2AA" });
const addresses = await lookupPostcode({ client: client, postcode: "SW1A2AA" });
```

#### Catch Errors

```javascript
const { IdpcRequestFailedError } = Client.errors;
import { errors } from "@ideal-postcodes/core-browser";
const { IdpcRequestFailedError } = errors;

try {
await client.lookupAddress({ query: "10 downing street" });
await lookupAddress({ client, query: "10 downing street" });
} catch (error) {
if (error instanceof IdpcRequestFailedError) {
// IdpcRequestFailedError indicates a 402 response code
Expand Down Expand Up @@ -124,7 +125,7 @@ Return addresses associated with a given `postcode`
```javascript
const postcode = "id11qd";

const addresses = await client.lookupPostcode({ postcode });
const addresses = await lookupPostcode({ client, postcode });
```

[Method options](https://core-interface.ideal-postcodes.dev/interfaces/lookuppostcodeoptions.html)
Expand All @@ -136,7 +137,7 @@ Return addresses associated with a given `query`
```javascript
const query = "10 downing street sw1a";

const addresses = await client.lookupAddress({ query });
const addresses = await lookupAddress({ client, query });
```

[Method options](https://core-interface.ideal-postcodes.dev/interfaces/lookupaddressoptions.html)
Expand All @@ -150,7 +151,7 @@ Invalid UDPRN will return `null`
```javascript
const udprn = 23747771;

const address = await client.lookupUdprn({ udprn });
const address = await lookupUdprn({ client, udprn });
```

[Method options](https://core-interface.ideal-postcodes.dev/interfaces/lookupudprnoptions.html)
Expand Down
58 changes: 2 additions & 56 deletions lib/client.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,9 @@
import {
Client as CoreInterface,
TLS,
API_URL,
VERSION,
TIMEOUT,
STRICT_AUTHORISATION,
Config,
} from "@ideal-postcodes/core-interface";
import { Agent } from "./agent";

export interface Config {
/**
* Use TLS. Defaults to `true`
*/
tls?: boolean;
/**
* API Key. Used in API helper methods
*/
api_key: string;
/**
* Target API hostname. Defaults to `'api.ideal-postcodes.co.uk'`
*/
baseUrl?: string;
/**
* API version. Defaults to `'v1'`
*/
version?: string;
/**
* Force autocomplete authorisation via HTTP headers only. Defaults to `false`
*/
strictAuthorisation?: boolean;
/**
* Default time in ms before HTTP request timeout. Defaults to 10s (`10000`)
*/
timeout?: number;
/**
* String map specifying default headers
*/
header?: Record<string, string>;
}

export class Client extends CoreInterface {
/**
* Client constructor extends CoreInterface
Expand All @@ -49,25 +14,6 @@ export class Client extends CoreInterface {
*/
constructor(config: Config, fetchConfig: RequestInit = {}) {
const agent = new Agent(fetchConfig);
const tls = config.tls === undefined ? TLS : config.tls;
const baseUrl = config.baseUrl === undefined ? API_URL : config.baseUrl;
const version = config.version === undefined ? VERSION : config.version;
const strictAuthorisation =
config.strictAuthorisation === undefined
? STRICT_AUTHORISATION
: config.strictAuthorisation;
const timeout = config.timeout === undefined ? TIMEOUT : config.timeout;

const { api_key } = config;
const interfaceConfig = {
tls,
api_key,
baseUrl,
version,
strictAuthorisation,
timeout,
header: { ...config.header },
};
super({ agent, ...interfaceConfig });
super({ agent, ...config });
}
}
1 change: 1 addition & 0 deletions lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "@ideal-postcodes/core-interface";
import { Client } from "./client";
import { Agent } from "./agent";

Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
],
"license": "MIT",
"dependencies": {
"@ideal-postcodes/core-interface": "~1.8.1"
"@ideal-postcodes/core-interface": "~2.0.1"
},
"devDependencies": {
"@cablanchard/eslint-config": "~2.0.1",
Expand Down
13 changes: 7 additions & 6 deletions test/agent.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import * as sinon from "sinon";
import { assert } from "chai";
import { Agent, parseQuery, toHeader } from "../lib/agent";
import { errors } from "@ideal-postcodes/core-interface";
import { Client } from "../lib/client";

const { IdealPostcodesError } = Client.errors;
const { IdealPostcodesError } = errors;

type HttpVerb = "GET" | "POST";

Expand All @@ -26,7 +27,7 @@ const testRequestHeaders = (
request: Request,
header: Record<string, string>
): void => {
Object.keys(header).forEach(key => {
Object.keys(header).forEach((key) => {
assert.equal(request.headers[key], header[key]);
});
};
Expand All @@ -35,14 +36,14 @@ const testRequest = (
request: Request,
expectedAttributes: Record<string, any>
): void => {
Object.keys(expectedAttributes).forEach(key => {
Object.keys(expectedAttributes).forEach((key) => {
// Some browsers will drop unexpected request attributes althogether
if (request[key] !== undefined)
assert.equal(request[key], expectedAttributes[key]);
});
};

const defaultResponse = header =>
const defaultResponse = (header) =>
new Response("{}", {
status: SUCCESS,
statusText: "OK",
Expand All @@ -52,7 +53,7 @@ const defaultResponse = header =>
describe("Agent", () => {
let agent: Agent;

beforeEach(function(this: any) {
beforeEach(function (this: any) {
agent = new Agent();
this.timeout(10000);
});
Expand Down Expand Up @@ -202,7 +203,7 @@ describe("Agent", () => {
before(() => {
abortStub = sinon
.stub(window.AbortController.prototype, "abort")
.callsFake(function() {
.callsFake(function () {
abortCalled = true;
});
});
Expand Down
30 changes: 7 additions & 23 deletions test/client.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,5 @@
import { assert } from "chai";
import { Client } from "../lib/client";
import {
TLS,
API_URL,
VERSION,
TIMEOUT,
STRICT_AUTHORISATION,
} from "@ideal-postcodes/core-interface";

describe("Client", () => {
describe("instantiation", () => {
Expand All @@ -17,15 +10,6 @@ describe("Client", () => {
client = new Client({ api_key });
});

it("assigns default config values", () => {
assert.equal(client.api_key, api_key);
assert.equal(client.tls, TLS);
assert.equal(client.baseUrl, API_URL);
assert.equal(client.version, VERSION);
assert.equal(client.strictAuthorisation, STRICT_AUTHORISATION);
assert.equal(client.timeout, TIMEOUT);
});

it("allows default config values to be overwritten", () => {
const options = {
tls: false,
Expand All @@ -36,21 +20,21 @@ describe("Client", () => {
timeout: 2,
};
const customClient = new Client(options);
assert.equal(customClient.api_key, options.api_key);
assert.equal(customClient.tls, options.tls);
assert.equal(customClient.baseUrl, options.baseUrl);
assert.equal(customClient.version, options.version);
assert.equal(customClient.config.api_key, options.api_key);
assert.equal(customClient.config.tls, options.tls);
assert.equal(customClient.config.baseUrl, options.baseUrl);
assert.equal(customClient.config.version, options.version);
assert.equal(
customClient.strictAuthorisation,
customClient.config.strictAuthorisation,
options.strictAuthorisation
);
assert.equal(customClient.timeout, options.timeout);
assert.equal(customClient.config.timeout, options.timeout);
});

it("allows second argument which is passed to Fetch", () => {
const cache = "default";
const client = new Client({ api_key }, { cache });
assert.equal((client as any).agent.config.cache, cache);
assert.equal((client as any).config.agent.config.cache, cache);
});
});
});
18 changes: 9 additions & 9 deletions test/integration.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { assert } from "chai";
import { Client } from "../lib/client";
import { errors, Client, ping, lookupPostcode } from "../lib/index";

const SUCCESS = 200;

Expand All @@ -8,15 +8,15 @@ const args = __karma__.config.args;
const api_key = args[0] || "iddqd";

const client = new Client({ api_key });
const { IdpcInvalidKeyError, IdpcRequestFailedError } = Client.errors;
const { IdpcInvalidKeyError, IdpcRequestFailedError } = errors;

describe("Client integration test", () => {
describe("ping", () => {
it("pings /", async () => {
try {
const response = await client.ping();
const response = await ping(client);
assert.equal(response.httpStatus, SUCCESS);
} catch(error) {
} catch (error) {
return error;
}
});
Expand All @@ -29,22 +29,22 @@ describe("Client integration test", () => {
describe("lookupPostcode", () => {
it("retrieves postcode", async () => {
const postcode = "SW1A 2AA";
const addresses = await client.lookupPostcode({ postcode });
const addresses = await lookupPostcode({ client, postcode });

assert.isTrue(addresses.length > 0);
assert.equal(addresses[0].postcode, postcode);
});

it("returns empty array for invalid postcode", async () => {
const postcode = "Definitely bogus";
const addresses = await client.lookupPostcode({ postcode });
const addresses = await lookupPostcode({ client, postcode });
assert.deepEqual(addresses, []);
});

it("returns an error for limit breached", async () => {
const postcode = "ID1 CLIP";
try {
await client.lookupPostcode({ postcode });
await lookupPostcode({ client, postcode });
} catch (error) {
assert.isTrue(error instanceof IdpcRequestFailedError);
return;
Expand All @@ -55,7 +55,7 @@ describe("Client integration test", () => {
it("returns an error for balance depleted", async () => {
const postcode = "ID1 CHOP";
try {
await client.lookupPostcode({ postcode });
await lookupPostcode({ client, postcode });
} catch (error) {
assert.isTrue(error instanceof IdpcRequestFailedError);
return;
Expand All @@ -66,7 +66,7 @@ describe("Client integration test", () => {
it("returns an error for invalid key", async () => {
const postcode = "SW1A 2AA";
try {
await client.lookupPostcode({ postcode, api_key: "badKey" });
await lookupPostcode({ client, postcode, api_key: "badKey" });
} catch (error) {
assert.isTrue(error instanceof IdpcInvalidKeyError);
return;
Expand Down
5 changes: 3 additions & 2 deletions test/timed_fetch.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import * as sinon from "sinon";
import { assert } from "chai";
import { Client } from "../lib/client";
const { IdealPostcodesError } = Client.errors;
import { Client, errors } from "../lib/index";
import { timedFetch } from "../lib/timed_fetch";

const { IdealPostcodesError } = errors;

type HttpVerb = "GET";

describe("timedFetch", () => {
Expand Down

0 comments on commit a7f958f

Please sign in to comment.