diff --git a/README.md b/README.md index 57f2b49..a629d59 100644 --- a/README.md +++ b/README.md @@ -53,13 +53,14 @@ If you wish to use the built-in transports you will also need to install the pee ### JavaScript ```javascript -const what3words, { fetchTransport } = require("@what3words/api"); +const what3words, + { fetchTransport } = require('@what3words/api'); const apiKey = ''; const config = { host: 'https://api.what3words.com', apiVersion: 'v3', -} +}; const transport = fetchTransport(); // or you can import 'axiosTransport' instead const w3wService = what3words(apiKey, config, { transport }); @@ -71,12 +72,17 @@ const w3wService = what3words(apiKey, config, { transport }); ### Typescript ```typescript -import what3words, { ApiVersion, Transport, What3wordsService, axiosTransport } from '@what3words/api'; +import what3words, { + ApiVersion, + Transport, + What3wordsService, + axiosTransport, +} from '@what3words/api'; -const apiKey: string = ''; +const apiKey = ''; const config: { - host: string, - apiVersion: ApiVersion, + host: string; + apiVersion: ApiVersion; } = { host: 'https://api.what3words.com', apiVersion: ApiVersion.Version3, @@ -179,95 +185,180 @@ import what3words, { ClientRequest, TransportResponse } from '@what3words/api'; import superagent from 'superagent'; const API_KEY = ''; -const config = {} // This will ensure we do not override the defaults - -async function customTransport(request: ClientRequest): Promise { - const { method, host, url, query = {}, headers = {}, body = {}, format } = request; - return superagent[method](`${host}${url}`) - .query({ ...query, format }) - .send(body) - .set(headers) - .end((err, res) => { - if (err) throw err; - const reponse: TransportResponse = { - status: res.status, - statusText: res. - headers: res.headers, - body: res.body, - }; - return response; - }) +const config = {}; // This will ensure we do not override the defaults + +function customTransport( + request: ClientRequest +): Promise> { + const { + method, + host, + url, + query = {}, + headers = {}, + body = {}, + format, + } = request; + return new Promise(resolve => + superagent[method](`${host}${url}`) + .query({ ...query, format }) + .send(body || {}) + .set(headers) + .end((err, res) => { + if (err || !res) + return resolve({ + status: err.status || 500, + statusText: err.response.text || 'Internal Server Error', + headers: err.headers || {}, + body: err.response.text || null, + }); + const response: TransportResponse = { + status: res.status, + statusText: res.text, + headers: res.headers, + body: res.body, + }; + resolve(response); + }) + ); } const service = what3words(API_KEY, config, { transport: customTransport }); -service.availableLanguages() +service + .availableLanguages() .then(({ languages }) => console.log('Available languages', languages)); ``` ### Autosuggest ```typescript -import { ApiVersion, AutosuggestClient, AutosuggestOptions, AutosuggestResponse } from '@what3words/api'; +import { + AutosuggestClient, + AutosuggestOptions, + AutosuggestResponse, +} from '@what3words/api'; -const API_KEY: string = ''; +const API_KEY = ''; const client: AutosuggestClient = AutosuggestClient.init(API_KEY); const options: AutosuggestOptions = { input: 'filled.count.s', }; -client.run(options) +client + .run(options) .then((res: AutosuggestResponse) => - console.log(`suggestions for "${autosuggestOptions.input}"`, res) + console.log(`suggestions for "${options.input}"`, res) ); ``` ### Convert to Coordinates ```typescript -import { ConvertToCoordinatesClient, ConvertToCoordinatesOptions, ConvertToCoordinatesResponse } from '@what3words/api'; +import { + ConvertToCoordinatesClient, + ConvertToCoordinatesOptions, + FeatureCollectionResponse, + LocationGeoJsonResponse, + LocationJsonResponse, +} from '@what3words/api'; const API_KEY = ''; -const client: ConvertToCoordinatesClient = ConvertToCoordinatesClient.init(API_KEY) +const client: ConvertToCoordinatesClient = + ConvertToCoordinatesClient.init(API_KEY); const options: ConvertToCoordinatesOptions = { words: 'filled.count.soap' }; -client.run(options) - .then((res: ConvertToCoordinatesResponse) => console.log('Convert to coordinates', res)); + +// If you want to retrieve the JSON response from our API +client + .run({ ...options, format: 'json' }) // { format: 'json' } is the default response + .then((res: LocationJsonResponse) => + console.log('Convert to coordinates', res) + ); + +// If you want to retrieve the GeoJsonResponse from our API +client + .run({ ...options, format: 'geojson' }) + .then((res: FeatureCollectionResponse) => + console.log('Convert to coordinates', res) + ); ``` ### Convert to Three Word Address ```typescript -import { ConvertTo3waClient, ConvertTo3waOptions, ConvertTo3waResponse } from '@what3words/api'; +import { + ConvertTo3waClient, + ConvertTo3waOptions, + FeatureCollectionResponse, + LocationGeoJsonResponse, + LocationJsonResponse, +} from '@what3words/api'; const API_KEY = ''; -const client: ConvertTo3waClient = ConvertTo3waClient.init(API_KEY) -const options: ConvertTo3waOptions = { coordinates: { lat: 51.520847, lng: -0.195521 } }; -client.run(options) - .then((res: ConvertTo3waResponse) => console.log('Convert to 3wa', res)); +const client: ConvertTo3waClient = ConvertTo3waClient.init(API_KEY); +const options: ConvertTo3waOptions = { + coordinates: { lat: 51.520847, lng: -0.195521 }, +}; + +// If you want to retrieve the JSON response from our API +client + .run({ ...options, format: 'json' }) // { format: 'json' } is the default response + .then((res: LocationJsonResponse) => console.log('Convert to 3wa', res)); + +// If you want to retrieve the GeoJsonResponse from our API +client + .run({ ...options, format: 'geojson' }) + .then((res: FeatureCollectionResponse) => + console.log('Convert to 3wa', res) + ); ``` ### Available Languages ```typescript -import { AvailableLanguagesClient, AvailableLanguagesResponse } from '@what3words/api'; +import { + AvailableLanguagesClient, + AvailableLanguagesResponse, +} from '@what3words/api'; const API_KEY = ''; const client: AvailableLanguagesClient = AvailableLanguagesClient.init(API_KEY); -client.run() - .then((res: AvailableLanguagesResponse) => console.log('Available Languages', res)); +client + .run() + .then((res: AvailableLanguagesResponse) => + console.log('Available Languages', res) + ); ``` ### Grid Section ```typescript -import { GridSectionClient, GridSectionOptions, GridSectionResponse } from '@what3words/api'; +import { + GridSectionClient, + GridSectionOptions, + FeatureCollectionResponse, + GridSectionGeoJsonResponse, + GridSectionJsonResponse, +} from '../src'; const API_KEY = ''; -const client: AvailableLanguagesClient = AvailableLanguagesClient.init(API_KEY); +const client: GridSectionClient = GridSectionClient.init(API_KEY); const options: GridSectionOptions = { - southwest: { lat: 52.208867, lng: 0.117540 }, - northeast: { lat: 52.207988, lng: 0.116126 } + boundingBox: { + southwest: { lat: 52.208867, lng: 0.11754 }, + northeast: { lat: 52.207988, lng: 0.116126 }, + }, }; -client.run(options) - .then((res: GridSectionResponse) => console.log('Grid Section', res)); + +// If you want to retrieve the JSON response from our API +client + .run({ ...options, format: 'json' }) // { format: 'json' } is the default response + .then((res: GridSectionJsonResponse) => console.log('Grid Section', res)); + +// If you want to retrieve the JSON response from our API +client + .run({ ...options, format: 'geojson' }) // { format: 'json' } is the default response + .then((res: FeatureCollectionResponse) => + console.log('Grid Section', res) + ); ``` > __The requested box must not exceed 4km from corner to corner, or a BadBoundingBoxTooBig error will be returned. Latitudes must be >= -90 and <= 90, but longitudes are allowed to wrap around 180. To specify a bounding-box that crosses the anti-meridian, use longitude greater than 180.__ diff --git a/package-lock.json b/package-lock.json index b0d146c..2758d0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@what3words/api", - "version": "4.0.6-tt-7263", + "version": "4.0.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@what3words/api", - "version": "4.0.6-tt-7263", + "version": "4.0.7", "devDependencies": { "@istanbuljs/nyc-config-typescript": "^1.0.1", "@testing-library/react": "^12.1.3", @@ -17,6 +17,7 @@ "@types/node": "^14.11.2", "@types/react": "^17.0.39", "@types/sinon": "^10.0.6", + "@types/superagent": "^4.1.16", "@typescript-eslint/eslint-plugin": "^5.3.1", "chance": "^1.1.8", "eslint-plugin-prettier": "^4.0.0", @@ -29,6 +30,7 @@ "react-dom": "^17.0.2", "should": "^13.2.3", "sinon": "^12.0.1", + "superagent": "^8.0.8", "ts-node": "^10.4.0", "typescript": "^4.0.3" }, @@ -781,6 +783,12 @@ "integrity": "sha512-X6c6ghhe4/sQh4XzcZWSFaTAUOda38GQHmq9BUanYkOE/EO7ZrkazwKmtsj3xzTjkLWmwULE++23g3d3CCWaWw==", "dev": true }, + "node_modules/@types/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "dev": true + }, "node_modules/@types/jsdom": { "version": "16.2.14", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.14.tgz", @@ -888,6 +896,16 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@types/superagent": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.16.tgz", + "integrity": "sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ==", + "dev": true, + "dependencies": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, "node_modules/@types/tough-cookie": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz", @@ -1595,6 +1613,12 @@ "node": ">=0.10.0" } }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1892,6 +1916,19 @@ "node": ">=8" } }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2093,6 +2130,12 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, + "node_modules/component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2131,6 +2174,12 @@ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true + }, "node_modules/create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -2322,6 +2371,16 @@ "node": ">=0.4.0" } }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -3167,6 +3226,12 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, "node_modules/fastq": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", @@ -3306,6 +3371,21 @@ "node": ">= 6" } }, + "node_modules/formidable": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "dev": true, + "dependencies": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, "node_modules/fromentries": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", @@ -3376,6 +3456,20 @@ "node": "6.* || 8.* || >= 10.*" } }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -4323,6 +4417,18 @@ "node": ">=4" } }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/has-yarn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", @@ -4357,6 +4463,15 @@ "he": "bin/he" } }, + "node_modules/hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -5332,6 +5447,15 @@ "node": ">= 8" } }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/micromatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", @@ -5345,6 +5469,18 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/mime-db": { "version": "1.51.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", @@ -5949,6 +6085,15 @@ "node": ">=0.10.0" } }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -6323,6 +6468,21 @@ "node": ">=8" } }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -6818,6 +6978,20 @@ "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", "dev": true }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -7008,6 +7182,59 @@ "node": ">=0.10.0" } }, + "node_modules/superagent": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.8.tgz", + "integrity": "sha512-OpxPrqqWKOjmuomLq5pCm4LWCSFdgAQ11XVkMl7t4ie13WxWuLkdJ83ZgG2jOQeLXKwMR2p9k30hLrKGAzkPaA==", + "dev": true, + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.1.2", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" + }, + "engines": { + "node": ">=6.4.0 <13 || >=14" + } + }, + "node_modules/superagent/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/superagent/node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", @@ -8429,6 +8656,12 @@ "integrity": "sha512-X6c6ghhe4/sQh4XzcZWSFaTAUOda38GQHmq9BUanYkOE/EO7ZrkazwKmtsj3xzTjkLWmwULE++23g3d3CCWaWw==", "dev": true }, + "@types/cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-t73xJJrvdTjXrn4jLS9VSGRbz0nUY3cl2DMGDU48lKl+HR9dbbjW2A9r3g40VA++mQpy6uuHg33gy7du2BKpog==", + "dev": true + }, "@types/jsdom": { "version": "16.2.14", "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.14.tgz", @@ -8537,6 +8770,16 @@ } } }, + "@types/superagent": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-4.1.16.tgz", + "integrity": "sha512-tLfnlJf6A5mB6ddqF159GqcDizfzbMUB1/DeT59/wBNqzRTNNKsaw79A/1TZ84X+f/EwWH8FeuSkjlCLyqS/zQ==", + "dev": true, + "requires": { + "@types/cookiejar": "*", + "@types/node": "*" + } + }, "@types/tough-cookie": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.1.tgz", @@ -9007,6 +9250,12 @@ "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -9232,6 +9481,16 @@ "write-file-atomic": "^3.0.0" } }, + "call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + } + }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -9393,6 +9652,12 @@ "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "dev": true }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -9430,6 +9695,12 @@ } } }, + "cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true + }, "create-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", @@ -9584,6 +9855,16 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", "dev": true }, + "dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, "diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", @@ -10211,6 +10492,12 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true + }, "fastq": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", @@ -10309,6 +10596,18 @@ "mime-types": "^2.1.12" } }, + "formidable": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "dev": true, + "requires": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" + } + }, "fromentries": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fromentries/-/fromentries-1.3.2.tgz", @@ -10352,6 +10651,17 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, + "get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -11017,6 +11327,12 @@ "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, "has-yarn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", @@ -11039,6 +11355,12 @@ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true }, + "hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "dev": true + }, "hosted-git-info": { "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", @@ -11782,6 +12104,12 @@ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true + }, "micromatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", @@ -11792,6 +12120,12 @@ "picomatch": "^2.2.3" } }, + "mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true + }, "mime-db": { "version": "1.51.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.51.0.tgz", @@ -12261,6 +12595,12 @@ "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true }, + "object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -12538,6 +12878,15 @@ "escape-goat": "^2.0.0" } }, + "qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, "queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -12907,6 +13256,17 @@ "integrity": "sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g==", "dev": true }, + "side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "requires": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + } + }, "signal-exit": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.3.tgz", @@ -13064,6 +13424,44 @@ "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true }, + "superagent": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.0.8.tgz", + "integrity": "sha512-OpxPrqqWKOjmuomLq5pCm4LWCSFdgAQ11XVkMl7t4ie13WxWuLkdJ83ZgG2jOQeLXKwMR2p9k30hLrKGAzkPaA==", + "dev": true, + "requires": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^2.1.2", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0", + "semver": "^7.3.8" + }, + "dependencies": { + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, "supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", diff --git a/package.json b/package.json index 8ff5880..f2b56eb 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "private": false, "name": "@what3words/api", - "version": "4.0.7", + "version": "4.0.8", "description": "what3words JavaScript API", "homepage": "https://github.com/what3words/w3w-node-wrapper#readme", "main": "dist/index.js", @@ -45,6 +45,7 @@ "@types/node": "^14.11.2", "@types/react": "^17.0.39", "@types/sinon": "^10.0.6", + "@types/superagent": "^4.1.16", "@typescript-eslint/eslint-plugin": "^5.3.1", "chance": "^1.1.8", "eslint-plugin-prettier": "^4.0.0", @@ -57,6 +58,7 @@ "react-dom": "^17.0.2", "should": "^13.2.3", "sinon": "^12.0.1", + "superagent": "^8.0.8", "ts-node": "^10.4.0", "typescript": "^4.0.3" }, diff --git a/src/lib/client/abstract.ts b/src/lib/client/abstract.ts index 0b0ba51..774203b 100644 --- a/src/lib/client/abstract.ts +++ b/src/lib/client/abstract.ts @@ -48,12 +48,11 @@ export abstract class ApiClient< return this._config; } - public async run(options?: Params): Promise; public async run( - options?: Params & { format?: 'json' } + options?: Params | (Params & { format?: 'json' }) ): Promise; public async run( - options?: Params & { format: 'geojson' } + options: Params & { format: 'geojson' } ): Promise; public async run(options?: Params): Promise { const validation = await this.validate(options); diff --git a/src/lib/transport/axios.ts b/src/lib/transport/axios.ts index 8443262..e594177 100644 --- a/src/lib/transport/axios.ts +++ b/src/lib/transport/axios.ts @@ -26,6 +26,7 @@ export function axiosTransport(): Transport { }); return response; }) + // eslint-disable-next-line @typescript-eslint/no-explicit-any .catch((err: any) => { if (err.isAxiosError) errorHandler({ diff --git a/src/lib/transport/model.ts b/src/lib/transport/model.ts index ec022b9..b29f180 100644 --- a/src/lib/transport/model.ts +++ b/src/lib/transport/model.ts @@ -1,6 +1,25 @@ +import { + AutosuggestResponse, + AvailableLanguagesResponse, + FeatureCollectionResponse, + GridSectionGeoJsonResponse, + GridSectionJsonResponse, + LocationGeoJsonResponse, + LocationJsonResponse, +} from 'client'; import { ClientRequest } from '../client'; -export type Transport = ( +export type Transport = < + T = + | AutosuggestResponse + | AvailableLanguagesResponse + | LocationJsonResponse + | GridSectionJsonResponse + | FeatureCollectionResponse + | FeatureCollectionResponse + | string + | null +>( req: ClientRequest ) => Promise>; export interface TransportResponse { diff --git a/test/lib/transport/custom.spec.ts b/test/lib/transport/custom.spec.ts new file mode 100644 index 0000000..e955d89 --- /dev/null +++ b/test/lib/transport/custom.spec.ts @@ -0,0 +1,191 @@ +import 'should'; +import nock from 'nock'; +import Chance from 'chance'; +import what3words, { + ClientRequest, + TransportResponse, + HEADERS, + What3wordsService, + ApiVersion, +} from '../../../src'; +import superagent from 'superagent'; + +function customTransport( + request: ClientRequest +): Promise> { + const { + method, + host, + url, + query = {}, + headers = {}, + body = {}, + format, + } = request; + return new Promise(resolve => + superagent[method](`${host}${url}`) + .query({ ...query, format }) + .send(body || {}) + .set(headers) + .end((err, res) => { + if (err || !res) + return resolve({ + status: err.status || 500, + statusText: err.response.text || 'Internal Server Error', + headers: err.headers || {}, + body: err.response.text || null, + }); + const response: TransportResponse = { + status: res.status, + statusText: res.text, + headers: res.headers, + body: res.body, + }; + resolve(response); + }) + ); +} + +const CHANCE = new Chance(); +const MOCK_RESPONSE = { foo: 'bar' }; +const MOCK_ERROR_RESPONSE = 'My custom error response message'; + +describe('Custom Transport', () => { + const query = { + example: 'params', + random: 'value', + }; + let host: string; + let url: string; + let method: 'get' | 'post'; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let request: any; + let service: What3wordsService; + let api_key: string; + + beforeEach(() => { + method = CHANCE.pickone(['get', 'post']); + host = `http://${CHANCE.domain({})}`; + url = '/foo/bar'; + request = { + method, + host, + url, + query, + headers: { + 'X-Custom-Header': 'my-random-header-value', + ...HEADERS, + }, + body: null, + }; + api_key = CHANCE.string({ length: 8, symbols: false }); + service = what3words(api_key, { host }, { transport: customTransport }); + }); + + afterEach(() => { + nock.cleanAll(); + }); + + it('should make request using custom transport and return 200', async () => { + nock(host, { allowUnmocked: false }) + [method](url) + .query(request.query) + .reply(200, MOCK_RESPONSE, { + 'Content-Type': 'application/json;charset=utf-8', + }); + + (await customTransport(request)).should.be.eql({ + status: 200, + statusText: JSON.stringify(MOCK_RESPONSE), + headers: { 'content-type': 'application/json;charset=utf-8' }, + body: MOCK_RESPONSE, + }); + }); + + it('should make request using custom transport and return 500', async () => { + nock(host, { allowUnmocked: false }) + [method](url) + .query(request.query) + .reply(500, MOCK_ERROR_RESPONSE); + + (await customTransport(request)).should.be.eql({ + status: 500, + statusText: MOCK_ERROR_RESPONSE, + headers: {}, + body: MOCK_ERROR_RESPONSE, + }); + }); + + it('should call autosuggest and return results', async () => { + const input = `${CHANCE.word()}.${CHANCE.word()}.${CHANCE.letter()}`; + const options = { input }; + const mock_response = { suggestions: [] }; + + nock(`${host}`, { + reqheaders: { + 'X-Api-Key': api_key, + ...HEADERS, + }, + allowUnmocked: false, + }) + .get(`/${ApiVersion.Version3}/autosuggest`) + .query({ ...options, key: api_key }) + .reply(200, mock_response, { + 'Content-Type': 'application/json;charset=utf-8', + }); + + const result = await service.autosuggest({ input }); + result.should.be.eql(mock_response); + }); + + it('should call available-languages and return results', async () => { + const mock_response = { languages: [] }; + + nock(`${host}`, { + reqheaders: { + 'X-Api-Key': api_key, + ...HEADERS, + }, + allowUnmocked: false, + }) + .get(`/${ApiVersion.Version3}/available-languages`) + .query({ key: api_key }) + .reply(200, mock_response, { + 'Content-Type': 'application/json;charset=utf-8', + }); + + const result = await service.availableLanguages(); + result.should.be.eql(mock_response); + }); + + it('should call convert-to-3wa and return json results', async () => { + const mock_response = { languages: [] }; + const options = { + coordinates: { + lat: parseFloat(CHANCE.coordinates().split(', ')[0]), + lng: parseFloat(CHANCE.coordinates().split(', ')[1]), + }, + language: CHANCE.locale(), + }; + + nock(`${host}`, { + reqheaders: { + 'X-Api-Key': api_key, + ...HEADERS, + }, + allowUnmocked: false, + }) + .get(`/${ApiVersion.Version3}/convert-to-3wa`) + .query({ + coordinates: `${options.coordinates.lat},${options.coordinates.lng}`, + language: options.language, + key: api_key, + }) + .reply(200, mock_response, { + 'Content-Type': 'application/json;charset=utf-8', + }); + + const result = await service.convertTo3wa(options); + result.should.be.eql(mock_response); + }); +}); diff --git a/test/service.spec.ts b/test/service.spec.ts index fdbbd59..0119e46 100644 --- a/test/service.spec.ts +++ b/test/service.spec.ts @@ -1,13 +1,11 @@ import 'should'; import nock from 'nock'; import { Chance } from 'chance'; -import { SinonSpy, spy } from 'sinon'; import what3words, { ApiClientConfiguration, ApiVersion, searchParams, axiosTransport, - Transport, } from '../src'; import { What3wordsService } from '../src/service'; @@ -24,8 +22,6 @@ describe('what3words', () => { let apiVersion: ApiVersion; let apiKey: string; let config: ApiClientConfiguration; - let transportSpy: SinonSpy; - let transport: Transport; beforeEach(() => { apiKey = CHANCE.string({ length: 8 }); @@ -39,15 +35,6 @@ describe('what3words', () => { apiVersion, headers: {}, }; - transportSpy = spy(); - transport = async (...args) => { - transportSpy(...args); - return { - status: 200, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - body: {} as any, - }; - }; }); describe('Service', () => {