diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000..6697471 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,5 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +ts-node ./check-version.ts +pretty-quick --staged diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..92a3b2c --- /dev/null +++ b/.prettierignore @@ -0,0 +1,7 @@ +.github/ +.nyc_output/ +dist/ + +.gitpod.yml +package.json +package-lock.json diff --git a/CHANGELOG.md b/CHANGELOG.md index d5e4389..3656583 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ # Changelog + All notable changes to this project will be documented in this file. ## [3.2.0] @@ -42,74 +43,97 @@ All notable changes to this project will be documented in this file. ## [3.0.0] - (**BREAKING**) Remove the dependency on `request` ([#62](https://github.com/customerio/customerio-node/pull/62)) + - We don't expect this to break many consumers of `customerio-node`. Unless you were using [`request`](https://github.com/request/request#requestoptions-callback) specific options, you don't need to make any changes. - (**BREAKING**) Return an `Error` instance for non-`2XX` status codes ([#62](https://github.com/customerio/customerio-node/pull/62)) + - We don't expect this to break many consumers of `customerio-node`. Unless you were using `instanceof` to check the type of error returned from track or api methods, you don't need to make any changed. `message`, `statusCode`, `response`, and `body` are still accessible as properties on the error. -- (**BREAKING**) `trackAnonymous` now requires an `anonymous_id` and cannot trigger campaigns. If you previously used anonymous events to trigger campaigns, you can still do so [directly through the API](https://customer.io/docs/api/#operation/trackAnonymous). We now refer to anonymous events that trigger campaigns as "invite events". +- (**BREAKING**) `trackAnonymous` now requires an `anonymous_id` and cannot trigger campaigns. If you previously used anonymous events to trigger campaigns, you can still do so [directly through the API](https://customer.io/docs/api/#operation/trackAnonymous). We now refer to anonymous events that trigger campaigns as "invite events". - (**BREAKING**) Restructure the package to have a single entry point, rather than three. This is more of a standard package structure, and is more future-proof. ([#63](https://github.com/customerio/customerio-node/pull/63)) - Return a readable message when the server returns an array of errors instead of `Unknown error` ([#62](https://github.com/customerio/customerio-node/pull/62)) - ## [2.1.1] ### Changed + - Fix exported typings for folks using `customerio-node` with Typescript ([#56](https://github.com/customerio/customerio-node/pull/56)) ## [2.1.0] ### Changed + - Upgrade `ini` from 1.3.5 to 1.3.8 ([#36](https://github.com/customerio/customerio-node/pull/36)) ### Added + - Convert `customerio-node` to Typescript ([#49](https://github.com/customerio/customerio-node/pull/49)) ## [2.0.0] + ### Changed + - (Breaking) Move triggerBroadcast method from Track to API class ([#46](https://github.com/customerio/customerio-node/pull/46)) ## [1.1.0] + ### Added + - Support for the EU region ## [1.0.0] + ### Added + - Support for the Transactional API ### Removed + - `addToSegment` and `removeFromSegment` methods ### Changed + - IDs in the URLs are now escaped. - Improved validations for data that's passed in. ## [0.7.0] + ### Changed + - Catch scenarios where a response body is unexpectedly `null` ([#25](https://github.com/customerio/customerio-node/pull/25)) ### Added + - Allow request defaults to be overridden ([#26](https://github.com/customerio/customerio-node/pull/26)) - New API call for supressing customers ([#27](https://github.com/customerio/customerio-node/pull/27)) ## [0.6.0] + ### Changed + - Add missing API params to `triggerBroadcast` ([#19](https://github.com/customerio/customerio-node/pull/19)) - Further improve the `triggerBroadcast` API call and catch additional params ([#20](https://github.com/customerio/customerio-node/pull/20)) - Switch from Travis CI to Circle CI ([#21](https://github.com/customerio/customerio-node/pull/21)) ## [0.5.0] + ### Added + - New API calls for manual segments (`addToSegment`, `removeFromSegment`) ([#16](https://github.com/customerio/customerio-node/pull/16)) ## [0.4.0] + ### Added + - New API call for adding and removing devices from push notifications ([#14](https://github.com/customerio/customerio-node/pull/14)) ## [0.3.0] + ### Changed + - Huge thanks to [@jescalan](https://github.com/jescalan) for his work in modernizing the Javascript to es6 along with updating dependencies. ([#13](https://github.com/customerio/customerio-node/pull/13)) - README now has the correct Travis-CI badge - README has standardized and expanded examples ([#10](https://github.com/customerio/customerio-node/issues/10)) @@ -117,6 +141,7 @@ All notable changes to this project will be documented in this file. - Cleaned up .gitignore by removing unnecessary ignore statements ### Added + - This CHANGELOG file along with historical changes to provide better transparancy to changes made to the library - New API call for API triggered broadcasts - Added a test for the new call @@ -125,14 +150,19 @@ All notable changes to this project will be documented in this file. - Travis-CI builds now use currently maintained LTS versions of Node.JS (6, 8, 9) ### Removed + - .gitkeep files no longer necessary to preserve directories ## [0.2.0] - 2015-07-22 + ### Removed + - url.resolve() removed from API calls ## [0.1.0] - 2015-07-22 + ### Added + - Initial API client library - Create Identify call - Create Track call @@ -147,8 +177,7 @@ All notable changes to this project will be documented in this file. - Create DELETE request - Test suite for middleware - -[Unreleased]: https://github.com/customerio/customerio-node/compare/v1.0.0...HEAD +[unreleased]: https://github.com/customerio/customerio-node/compare/v1.0.0...HEAD [0.3.0]: https://github.com/customerio/customerio-node/compare/d3df250...v0.3.0 [0.2.0]: https://github.com/customerio/customerio-node/compare/54e7c68...d3df250 [0.1.0]: https://github.com/customerio/customerio-node/compare/943668e...54e7c68 diff --git a/README.md b/README.md index 809b7f3..466e3f5 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ [![ci](https://github.com/customerio/customerio-node/actions/workflows/main.yml/badge.svg)](https://github.com/customerio/customerio-node/actions/workflows/main.yml) # Customer.io Node + A node client for the Customer.io [REST API](https://customer.io/docs/api/). ## React Native and alternative Node runtimes @@ -106,14 +107,16 @@ The first and third parameters represent the identifier for the primary and seco // cio.mergeCustomers("primaryType", "primaryIdentifier", "secondaryType", "secondaryIdentifier") // primaryType / secondaryType are one of "id", "email", or "cio_id" // primaryIdentifier / secondaryIdentifier are the identifier value corresponding to the type. -cio.mergeCustomers(IdentifierType.Id, "cool.person@company.com", IdentifierType.Email, "cperson@gmail.com") +cio.mergeCustomers(IdentifierType.Id, "cool.person@company.com", IdentifierType.Email, "cperson@gmail.com"); ``` + #### Options - **primaryType**: One of the ID types - "id" / "email" / "cio_id" (required) - **primaryIdentifier**: Primary profile Identifier, String or number (required) - **secondaryType**: One of the ID types - "id" / "email" / "cio_id" (required) - **secondaryIdentifier**: Secondary profile Identifier, String or number (required) + --- ### cio.track(id, data) diff --git a/check-version.ts b/check-version.ts new file mode 100644 index 0000000..f993d4c --- /dev/null +++ b/check-version.ts @@ -0,0 +1,12 @@ +import { version as hardCodedVersion } from './lib/version'; +import packageJson from './package.json'; + +if (packageJson.version !== hardCodedVersion) { + console.error(`You need to update \`./lib/version.ts\` to match the version in package.json before committing! + +package.json version: \x1B[1;32m${packageJson.version}\x1B[m +hard coded version: \x1B[1;31m${hardCodedVersion}\x1B[m`); + process.exit(1); +} + +process.exit(0); diff --git a/lib/api.ts b/lib/api.ts index b4b0fa2..4ebc858 100644 --- a/lib/api.ts +++ b/lib/api.ts @@ -53,9 +53,9 @@ export class APIClient { triggerBroadcast(id: string | number, data: RequestData, recipients: Recipients) { let payload = {}; - let customRecipientField = (Object.keys( - BROADCASTS_ALLOWED_RECIPIENT_FIELDS, - ) as BroadcastsAllowedRecipientFieldsKeys[]).find((field) => recipients[field]); + let customRecipientField = ( + Object.keys(BROADCASTS_ALLOWED_RECIPIENT_FIELDS) as BroadcastsAllowedRecipientFieldsKeys[] + ).find((field) => recipients[field]); if (customRecipientField) { payload = Object.assign({ data }, filterRecipientsDataForField(recipients, customRecipientField)); diff --git a/lib/request.ts b/lib/request.ts index aca40a8..a9e6aed 100644 --- a/lib/request.ts +++ b/lib/request.ts @@ -1,8 +1,8 @@ import { request } from 'https'; import type { RequestOptions } from 'https'; import { URL } from 'url'; -import { resolve } from 'path'; -import { CustomerIORequestError, findPackageJson } from './utils'; +import { CustomerIORequestError } from './utils'; +import { version } from './version'; export type BasicAuth = { apikey: string; @@ -24,10 +24,9 @@ export interface PushRequestData { device_id?: string; event?: 'delivered' | 'opened' | 'converted'; timestamp?: number; -}; +} const TIMEOUT = 10_000; -const PACKAGE_JSON = findPackageJson(resolve(__dirname, '..')); export default class CIORequest { apikey?: BasicAuth['apikey']; @@ -57,23 +56,11 @@ export default class CIORequest { options(uri: string, method: RequestOptions['method'], data?: RequestData): RequestHandlerOptions { const body = data ? JSON.stringify(data) : null; - let libraryVersion = 'Unknown'; - - try { - let json = JSON.parse(PACKAGE_JSON.toString()); - - libraryVersion = json.version; - } catch { - console.warn( - 'WARN: package.json contents could not be read. Activity source data in Customer.io will be incorrect.', - ); - } - const headers = { Authorization: this.auth, 'Content-Type': 'application/json', 'Content-Length': body ? Buffer.byteLength(body, 'utf8') : 0, - 'User-Agent': `Customer.io Node Client/${libraryVersion}`, + 'User-Agent': `Customer.io Node Client/${version}`, }; return { method, uri, headers, body }; diff --git a/lib/track.ts b/lib/track.ts index 0bfc2f5..c0307c9 100644 --- a/lib/track.ts +++ b/lib/track.ts @@ -4,7 +4,7 @@ import { Region, RegionUS } from './regions'; import { isEmpty } from './utils'; import { IdentifierType } from './types'; -type TrackDefaults = RequestOptions & { region: Region; url?: string; apiUrl?: string; }; +type TrackDefaults = RequestOptions & { region: Region; url?: string; apiUrl?: string }; class MissingParamError extends Error { constructor(param: string) { diff --git a/lib/utils.ts b/lib/utils.ts index 1af85b6..431ba6e 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,6 +1,4 @@ import { IncomingMessage } from 'http'; -import { resolve } from 'path'; -import fs from 'fs'; export const isEmpty = (value: unknown) => { return value === null || value === undefined || (typeof value === 'string' && value.trim() === ''); @@ -37,31 +35,3 @@ ${json.meta.errors.map((error: string) => ` - ${error}`).join('\n')}`; this.body = body; } } - -// Node.js v12 doesn't support the `throwIfNoEntry` -// Once we drop v12, let's remove this function and use `throwIfNoEntry` -const checkIfPathExists = (path: string) => { - try { - let stat = fs.statSync(path); - - return stat; - } catch { - return undefined; - } -}; - -export const findPackageJson = (dirName: string): string => { - const path = resolve(dirName, 'package.json'); - - if (checkIfPathExists(path) == null) { - const parentPath = resolve(dirName, '..'); - - if (checkIfPathExists(parentPath) != null) { - return findPackageJson(parentPath); - } - - return ''; - } - - return fs.readFileSync(path).toString(); -}; diff --git a/lib/version.ts b/lib/version.ts new file mode 100644 index 0000000..e8e3a50 --- /dev/null +++ b/lib/version.ts @@ -0,0 +1 @@ +export const version = '3.2.0'; diff --git a/package-lock.json b/package-lock.json index e384cf2..bfd6921 100644 --- a/package-lock.json +++ b/package-lock.json @@ -462,6 +462,12 @@ "integrity": "sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA==", "dev": true }, + "@types/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", + "dev": true + }, "@types/node": { "version": "15.12.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-15.12.4.tgz", @@ -606,6 +612,12 @@ "sprintf-js": "~1.0.2" } }, + "array-differ": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", + "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", + "dev": true + }, "array-find-index": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/array-find-index/-/array-find-index-1.0.2.tgz", @@ -1331,6 +1343,34 @@ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + } + } + }, "fast-diff": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz", @@ -1595,6 +1635,18 @@ "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", "dev": true }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "husky": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/husky/-/husky-7.0.4.tgz", + "integrity": "sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ==", + "dev": true + }, "ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", @@ -2148,6 +2200,12 @@ } } }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, "merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -2191,12 +2249,31 @@ "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, + "mri": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", + "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==", + "dev": true + }, "ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, + "multimatch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-4.0.0.tgz", + "integrity": "sha512-lDmx79y1z6i7RNx0ZGCPq1bzJ6ZoDDKbvh7jxr9SJcWLkShMzXrHbYVpTdnhNM5MXpDUxCQ4DgqVttVXlBgiBQ==", + "dev": true, + "requires": { + "@types/minimatch": "^3.0.3", + "array-differ": "^3.0.0", + "array-union": "^2.1.0", + "arrify": "^2.0.1", + "minimatch": "^3.0.4" + } + }, "nise": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz", @@ -2257,6 +2334,15 @@ "integrity": "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA==", "dev": true }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "requires": { + "path-key": "^3.0.0" + } + }, "nyc": { "version": "15.1.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-15.1.0.tgz", @@ -2663,6 +2749,41 @@ "parse-ms": "^2.1.0" } }, + "pretty-quick": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/pretty-quick/-/pretty-quick-3.1.3.tgz", + "integrity": "sha512-kOCi2FJabvuh1as9enxYmrnBC6tVMoVOenMaBqRfsvBHB0cbpYHjdQEpSglpASDFEXVwplpcGR4CLEaisYAFcA==", + "dev": true, + "requires": { + "chalk": "^3.0.0", + "execa": "^4.0.0", + "find-up": "^4.1.0", + "ignore": "^5.1.4", + "mri": "^1.1.5", + "multimatch": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", + "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + } + } + }, "process-on-spawn": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/process-on-spawn/-/process-on-spawn-1.0.0.tgz", @@ -3114,6 +3235,12 @@ "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "dev": true }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true + }, "strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", diff --git a/package.json b/package.json index 59f1765..1ebdfde 100644 --- a/package.json +++ b/package.json @@ -46,8 +46,10 @@ "@types/node": "^15.12.4", "@types/sinon": "^10.0.2", "ava": "^3.15.0", + "husky": "^7.0.4", "nyc": "^15.1.0", "prettier": "^2.3.1", + "pretty-quick": "^3.1.3", "sinon": "^11.1.1", "ts-node": "^10.0.0", "typescript": "^4.3.4" @@ -64,6 +66,8 @@ "scripts": { "test": "nyc ava", "build": "tsc -p tsconfig.json", - "prepublish": "npm run build" + "prepublish": "npm run build", + "prepare": "husky install", + "version": "node update-version.js" } } diff --git a/test/request-track-api.ts b/test/request-track-api.ts index 71ffeec..30466e8 100644 --- a/test/request-track-api.ts +++ b/test/request-track-api.ts @@ -112,26 +112,6 @@ test('#options sets Content-Length using body length in bytes', (t) => { t.deepEqual(resultOptions, expectedOptions); }); -test('#options sets User-Agent even if package.json cannot be read', (t) => { - const jsonParseStub = sinon.stub(JSON, 'parse').throws(); - const body = { bad_agent: true }; - const method = 'POST'; - const expectedOptions = { - ...baseOptions, - method, - headers: { - ...baseOptions.headers, - 'Content-Length': 18, - 'User-Agent': 'Customer.io Node Client/Unknown', - }, - body: JSON.stringify(body), - }; - const resultOptions = t.context.req.options(uri, method, body); - - t.deepEqual(resultOptions, expectedOptions); - jsonParseStub.restore(); -}); - test('#handler returns a promise', (t) => { createMockRequest(t.context.httpsReq, 200); const promise = t.context.req.handler(putOptions); diff --git a/test/track.ts b/test/track.ts index e9f641c..1b38af7 100644 --- a/test/track.ts +++ b/test/track.ts @@ -135,22 +135,20 @@ test('#trackAnonymous works', (t) => { test('#trackPush works', (t) => { sinon.stub(t.context.client.request, 'post'); t.context.client.trackPush(); - t.truthy( - (t.context.client.request.post as SinonStub).calledWith(`${RegionUS.trackUrl}/push/events`, {}), - ); + t.truthy((t.context.client.request.post as SinonStub).calledWith(`${RegionUS.trackUrl}/push/events`, {})); t.context.client.trackPush({ - delivery_id: "RPILAgUBcRhIBqSfeiIwdIYJKxTY", - event: "opened", - device_id: "CIO-Delivery-Token from the notification", - timestamp: 1613063089 + delivery_id: 'RPILAgUBcRhIBqSfeiIwdIYJKxTY', + event: 'opened', + device_id: 'CIO-Delivery-Token from the notification', + timestamp: 1613063089, }); t.truthy( (t.context.client.request.post as SinonStub).calledWith(`${RegionUS.trackUrl}/push/events`, { - delivery_id: "RPILAgUBcRhIBqSfeiIwdIYJKxTY", - event: "opened", - device_id: "CIO-Delivery-Token from the notification", - timestamp: 1613063089 + delivery_id: 'RPILAgUBcRhIBqSfeiIwdIYJKxTY', + event: 'opened', + device_id: 'CIO-Delivery-Token from the notification', + timestamp: 1613063089, }), ); }); @@ -240,27 +238,31 @@ test('#deleteDevice works', (t) => { }); test('#mergeCustomers validations work', (t) => { - t.throws(() => t.context.client.mergeCustomers(IdentifierType.Id, "", IdentifierType.Id, "id2"), { message: 'primaryId is required' }); - t.throws(() => t.context.client.mergeCustomers(IdentifierType.Email, "id1", IdentifierType.CioId, ""), { message: 'secondaryId is required' }); + t.throws(() => t.context.client.mergeCustomers(IdentifierType.Id, '', IdentifierType.Id, 'id2'), { + message: 'primaryId is required', + }); + t.throws(() => t.context.client.mergeCustomers(IdentifierType.Email, 'id1', IdentifierType.CioId, ''), { + message: 'secondaryId is required', + }); }); test('#mergeCustomers works', (t) => { sinon.stub(t.context.client.request, 'post'); [ - ["email", "cool.person@company.com", "email", "cperson@gmail.com"], - ["id", "cool.person@company.com", "cio_id", "person2"], - ["cio_id", "CIO123", "id", "person1"], + ['email', 'cool.person@company.com', 'email', 'cperson@gmail.com'], + ['id', 'cool.person@company.com', 'cio_id', 'person2'], + ['cio_id', 'CIO123', 'id', 'person1'], ].forEach(([pTypeString, pId, sTypeString, sId]) => { t.context.client.mergeCustomers(pTypeString as IdentifierType, pId, sTypeString as IdentifierType, sId); t.truthy( (t.context.client.request.post as SinonStub).calledWith(`${RegionUS.apiUrl}/merge_customers`, { primary: { - [pTypeString]: pId + [pTypeString]: pId, }, secondary: { - [sTypeString]: sId - } + [sTypeString]: sId, + }, }), ); }); -}); \ No newline at end of file +}); diff --git a/test/util.ts b/test/util.ts deleted file mode 100644 index edff0b7..0000000 --- a/test/util.ts +++ /dev/null @@ -1,53 +0,0 @@ -import test from 'ava'; -import sinon from 'sinon'; -import fs from 'fs'; -import { resolve } from 'path'; -import { findPackageJson } from '../lib/utils'; - -const PACKAGE_JSON = JSON.parse(fs.readFileSync(resolve(__dirname, '..', 'package.json')).toString()); - -test.serial('#findPackageJson walks the tree to find package.json', (t) => { - const sandbox = sinon.createSandbox(); - const statSpy = sandbox.spy(fs, 'statSync'); - const readSpy = sandbox.spy(fs, 'readFileSync'); - - // No package.json in the test directory - let json = findPackageJson(__dirname); - - t.deepEqual(JSON.parse(json), PACKAGE_JSON, 'returns the correct package.json'); - t.is(statSpy.callCount, 3, 'statSync called three times'); - t.deepEqual( - statSpy.getCall(0).args, - [resolve(__dirname, 'package.json')], - 'called with the correct arguments initially', - ); - t.deepEqual(statSpy.getCall(1).args, [resolve(__dirname, '..')], 'checks if parent directory exists'); - t.deepEqual( - statSpy.getCall(2).args, - [resolve(__dirname, '..', 'package.json')], - 'checks if package.json in parent directory exists', - ); - - statSpy.restore(); - readSpy.restore(); -}); - -test.serial('#findPackageJson returns a default if no package.json is found', (t) => { - const sandbox = sinon.createSandbox(); - const statStub = sandbox.stub(fs, 'statSync').throws(); - const readSpy = sandbox.spy(fs, 'readFileSync'); - - let json = findPackageJson(__dirname); - - t.is(json, '', 'returns an empty string'); - t.is(statStub.callCount, 2, 'statSync called two times'); - t.deepEqual( - statStub.getCall(0).args, - [resolve(__dirname, 'package.json')], - 'called with the correct arguments initially', - ); - t.deepEqual(statStub.getCall(1).args, [resolve(__dirname, '..')], 'checks if parent directory exists'); - - statStub.restore(); - readSpy.restore(); -}); diff --git a/tsconfig.json b/tsconfig.json index c2bc3da..6133d4e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,6 +20,7 @@ "noUnusedParameters": true, "outDir": "dist", "paths": {}, + "resolveJsonModule": true, "sourceMap": false, "strict": true, "strictNullChecks": true, diff --git a/update-version.js b/update-version.js new file mode 100644 index 0000000..8fed619 --- /dev/null +++ b/update-version.js @@ -0,0 +1,5 @@ +const packageJson = require('./package.json'); +const { resolve } = require('path'); +const { writeFileSync } = require('fs'); + +writeFileSync(resolve(__dirname, 'lib/version.ts'), `export const version = '${packageJson.version}';\n`);