From 5b5bedab71ab0433f2bb0f96874f308c0663981d Mon Sep 17 00:00:00 2001 From: Josh Sherman Date: Sat, 20 Jun 2020 11:06:59 -0500 Subject: [PATCH] feat: add workday endpoint Added support for our latest endpoint, which calculates the workday a given number of business days into the future from the given date for the given country. Also: * perf: updated dependencies * ci: pinned eslint to 7.2.x due to airbnb incompatibility with 7.3.x. * ci: tweaked / improved eslint rules a bit. * refactor: updated types to use `;` and include weekday response. * docs: added example for new endpoint. --- .eslintrc.json | 26 ++++-- README.md | 12 +++ dist/holidayapi.d.ts | 3 +- dist/holidayapi.js | 20 ++++ dist/types.d.ts | 27 +++++- package-lock.json | 195 +++++++++++++++++++-------------------- package.json | 16 ++-- src/holidayapi.ts | 28 +++++- src/types.ts | 126 ++++++++++++++----------- tests/holidayapi.test.ts | 101 ++++++++++++++++++++ 10 files changed, 377 insertions(+), 177 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index aeeafb0..6948695 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -22,23 +22,37 @@ "jest" ], "rules": { - "@typescript-eslint/no-unused-vars": "error", + "@typescript-eslint/no-unused-vars": [ + "error", + { + "vars": "all", + "args": "after-used", + "ignoreRestSiblings": false + } + ], "import/extensions": [ "error", - "always", + "ignorePackages", { - "ts": "never", - "tsx": "never" + "ts": "never" } ], "import/no-extraneous-dependencies": [ "error", { - "devDependencies": ["**/*.test.ts"] + "devDependencies": [ + "**/*.test.ts" + ] } ], "import/prefer-default-export": "off", - "lines-between-class-members": "off", + "lines-between-class-members": [ + "error", + "always", + { + "exceptAfterSingleLine": true + } + ], "no-unused-vars": "off" } } diff --git a/README.md b/README.md index df62651..d2ec39b 100644 --- a/README.md +++ b/README.md @@ -239,3 +239,15 @@ holidayApi.holidays({ year: 2019, }); ``` + +### Workday + +#### Fetch workday 7 business days after a date + +```javascript +holidayApi.workday({ + country: 'US', + start: '2019-07-01', + days: 7, +}); +``` diff --git a/dist/holidayapi.d.ts b/dist/holidayapi.d.ts index 5414274..762625c 100644 --- a/dist/holidayapi.d.ts +++ b/dist/holidayapi.d.ts @@ -1,4 +1,4 @@ -import { CountriesRequest, CountriesResponse, HolidaysResponse, HolidaysRequest, LanguagesRequest, LanguagesResponse } from './types'; +import { CountriesRequest, CountriesResponse, HolidaysRequest, HolidaysResponse, LanguagesRequest, LanguagesResponse, WorkdayRequest, WorkdayResponse } from './types'; export declare class HolidayAPI { baseUrl: string; key: string; @@ -11,4 +11,5 @@ export declare class HolidayAPI { countries(request?: CountriesRequest): Promise; holidays(request?: HolidaysRequest): Promise; languages(request?: LanguagesRequest): Promise; + workday(request?: WorkdayRequest): Promise; } diff --git a/dist/holidayapi.js b/dist/holidayapi.js index 1cac533..954d962 100644 --- a/dist/holidayapi.js +++ b/dist/holidayapi.js @@ -132,6 +132,26 @@ var HolidayAPI = (function () { }); }); }; + HolidayAPI.prototype.workday = function (request) { + if (request === void 0) { request = {}; } + return __awaiter(this, void 0, void 0, function () { + return __generator(this, function (_a) { + if (!request.country) { + throw new Error('Missing country'); + } + else if (!request.start) { + throw new Error('Missing start date'); + } + else if (!request.days) { + throw new Error('Missing days'); + } + else if (request.days < 1) { + throw new Error('Days must be 1 or more'); + } + return [2, this.request('workday', request)]; + }); + }); + }; return HolidayAPI; }()); exports.HolidayAPI = HolidayAPI; diff --git a/dist/types.d.ts b/dist/types.d.ts index c970d87..13ba4f9 100644 --- a/dist/types.d.ts +++ b/dist/types.d.ts @@ -1,11 +1,15 @@ -export declare type Endpoint = 'countries' | 'holidays' | 'languages'; +export declare type Endpoint = 'countries' | 'holidays' | 'languages' | 'workday'; +declare type Weekday = { + name: 'Monday' | 'Tuesday' | 'Wednesday' | 'Thursday' | 'Friday' | 'Saturday' | 'Sunday'; + numeric: 1 | 2 | 3 | 4 | 5 | 6 | 7; +}; declare type Request = { - format?: 'csv' | 'json' | 'php' | 'tsv' | 'yaml' | 'xml'; + format?: 'csv' | 'json' | 'php' | 'tsv' | 'xml' | 'yaml'; key?: string; pretty?: boolean; search?: string; }; -export declare type Requests = CountriesRequest | HolidaysRequest | LanguagesRequest; +export declare type Requests = CountriesRequest | HolidaysRequest | LanguagesRequest | WorkdayRequest; export declare type CountriesRequest = Request & { country?: string; public?: boolean; @@ -24,6 +28,11 @@ export declare type HolidaysRequest = Request & { export declare type LanguagesRequest = Request & { language?: string; }; +export declare type WorkdayRequest = Request & { + country?: string; + start?: string; + days?: number; +}; export declare type Response = { requests: { available: number; @@ -33,7 +42,7 @@ export declare type Response = { status: number; error?: string; }; -export declare type Responses = (CountriesResponse | HolidaysResponse | LanguagesResponse); +export declare type Responses = CountriesResponse | HolidaysResponse | LanguagesResponse | WorkdayResponse; export declare type CountriesResponse = Response & { countries?: { code: string; @@ -50,6 +59,10 @@ export declare type CountriesResponse = Response & { languages: string[]; name: string; }[]; + weekday: { + date: Weekday; + observed: Weekday; + }; }[]; }; export declare type HolidaysResponse = Response & { @@ -69,4 +82,10 @@ export declare type LanguagesResponse = Response & { name: string; }[]; }; +export declare type WorkdayResponse = Response & { + workday?: { + date: Date; + weekday: Weekday; + }; +}; export {}; diff --git a/package-lock.json b/package-lock.json index 420d897..f6765ff 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "holidayapi", - "version": "4.0.0", + "version": "4.1.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -921,9 +921,9 @@ } }, "@types/jest": { - "version": "25.2.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.2.3.tgz", - "integrity": "sha512-JXc1nK/tXHiDhV55dvfzqtmP4S3sy3T3ouV2tkViZgxY/zeUkcpQcQPGRlgF4KmWzWW5oiWYSZwtCB+2RsE4Fw==", + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.0.tgz", + "integrity": "sha512-/yeMsH9HQ1RLORlXAwoLXe8S98xxvhNtUz3yrgrwbaxYjT+6SFPZZRksmRKRA6L5vsUtSHeN71viDOTTyYAD+g==", "dev": true, "requires": { "jest-diff": "^25.2.1", @@ -952,9 +952,9 @@ } }, "@types/node": { - "version": "14.0.12", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.12.tgz", - "integrity": "sha512-/sjzehvjkkpvLpYtN6/2dv5kg41otMGuHQUt9T2aiAuIfleCQRQHXXzF1eAw/qkZTj5Kcf4JSTf7EIizHocy6Q==", + "version": "14.0.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.13.tgz", + "integrity": "sha512-rouEWBImiRaSJsVA+ITTFM6ZxibuAlTuNOCyxVbwreu6k6+ujs7DfnU9o+PShFhET78pMBl3eH+AGSI5eOTkPA==", "dev": true }, "@types/node-fetch": { @@ -1001,12 +1001,12 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.2.0.tgz", - "integrity": "sha512-t9RTk/GyYilIXt6BmZurhBzuMT9kLKw3fQoJtK9ayv0tXTlznXEAnx07sCLXdkN3/tZDep1s1CEV95CWuARYWA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-3.3.0.tgz", + "integrity": "sha512-Ybx/wU75Tazz6nU2d7nN6ll0B98odoiYLXwcuwS5WSttGzK46t0n7TPRQ4ozwcTv82UY6TQoIvI+sJfTzqK9dQ==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "3.2.0", + "@typescript-eslint/experimental-utils": "3.3.0", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.0.0", "semver": "^7.3.2", @@ -1014,21 +1014,21 @@ }, "dependencies": { "@typescript-eslint/experimental-utils": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.2.0.tgz", - "integrity": "sha512-UbJBsk+xO9dIFKtj16+m42EvUvsjZbbgQ2O5xSTSfVT1Z3yGkL90DVu0Hd3029FZ5/uBgl+F3Vo8FAcEcqc6aQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.3.0.tgz", + "integrity": "sha512-d4pGIAbu/tYsrPrdHCQ5xfadJGvlkUxbeBB56nO/VGmEDi/sKmfa5fGty5t5veL1OyJBrUmSiRn1R1qfVDydrg==", "dev": true, "requires": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "3.2.0", + "@typescript-eslint/typescript-estree": "3.3.0", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0" } }, "@typescript-eslint/typescript-estree": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.2.0.tgz", - "integrity": "sha512-uh+Y2QO7dxNrdLw7mVnjUqkwO/InxEqwN0wF+Za6eo3coxls9aH9kQ/5rSvW2GcNanebRTmsT5w1/92lAOb1bA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.3.0.tgz", + "integrity": "sha512-3SqxylENltEvJsjjMSDCUx/edZNSC7wAqifUU1Ywp//0OWEZwMZJfecJud9XxJ/40rAKEbJMKBOQzeOjrLJFzQ==", "dev": true, "requires": { "debug": "^4.1.1", @@ -1066,33 +1066,33 @@ } }, "@typescript-eslint/parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.2.0.tgz", - "integrity": "sha512-Vhu+wwdevDLVDjK1lIcoD6ZbuOa93fzqszkaO3iCnmrScmKwyW/AGkzc2UvfE5TCoCXqq7Jyt6SOXjsIlpqF4A==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-3.3.0.tgz", + "integrity": "sha512-a7S0Sqn/+RpOOWTcaLw6RD4obsharzxmgMfdK24l364VxuBODXjuJM7ImCkSXEN7oz52aiZbXSbc76+2EsE91w==", "dev": true, "requires": { "@types/eslint-visitor-keys": "^1.0.0", - "@typescript-eslint/experimental-utils": "3.2.0", - "@typescript-eslint/typescript-estree": "3.2.0", + "@typescript-eslint/experimental-utils": "3.3.0", + "@typescript-eslint/typescript-estree": "3.3.0", "eslint-visitor-keys": "^1.1.0" }, "dependencies": { "@typescript-eslint/experimental-utils": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.2.0.tgz", - "integrity": "sha512-UbJBsk+xO9dIFKtj16+m42EvUvsjZbbgQ2O5xSTSfVT1Z3yGkL90DVu0Hd3029FZ5/uBgl+F3Vo8FAcEcqc6aQ==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-3.3.0.tgz", + "integrity": "sha512-d4pGIAbu/tYsrPrdHCQ5xfadJGvlkUxbeBB56nO/VGmEDi/sKmfa5fGty5t5veL1OyJBrUmSiRn1R1qfVDydrg==", "dev": true, "requires": { "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "3.2.0", + "@typescript-eslint/typescript-estree": "3.3.0", "eslint-scope": "^5.0.0", "eslint-utils": "^2.0.0" } }, "@typescript-eslint/typescript-estree": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.2.0.tgz", - "integrity": "sha512-uh+Y2QO7dxNrdLw7mVnjUqkwO/InxEqwN0wF+Za6eo3coxls9aH9kQ/5rSvW2GcNanebRTmsT5w1/92lAOb1bA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-3.3.0.tgz", + "integrity": "sha512-3SqxylENltEvJsjjMSDCUx/edZNSC7wAqifUU1Ywp//0OWEZwMZJfecJud9XxJ/40rAKEbJMKBOQzeOjrLJFzQ==", "dev": true, "requires": { "debug": "^4.1.1", @@ -2021,30 +2021,22 @@ } }, "es-abstract": { - "version": "1.17.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", - "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", "dev": true, "requires": { "es-to-primitive": "^1.2.1", "function-bind": "^1.1.1", "has": "^1.0.3", "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", "object-inspect": "^1.7.0", "object-keys": "^1.1.1", "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - }, - "dependencies": { - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - } + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" } }, "es-to-primitive": { @@ -2173,28 +2165,28 @@ } }, "eslint-visitor-keys": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.2.0.tgz", - "integrity": "sha512-WFb4ihckKil6hu3Dp798xdzSfddwKKU3+nGniKF6HfeW6OLd2OUDEPP7TcHtB5+QXOKg2s6B2DaMPE1Nn/kxKQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true } } }, "eslint-config-airbnb-base": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.1.0.tgz", - "integrity": "sha512-+XCcfGyCnbzOnktDVhwsCAx+9DmrzEmuwxyHUJpw+kqBVT744OUBrB09khgFKlK1lshVww6qXGsYPZpavoNjJw==", + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-14.2.0.tgz", + "integrity": "sha512-Snswd5oC6nJaevs3nZoLSTvGJBvzTfnBqOIArkf3cbyTyq9UD79wOk8s+RiL6bhca0p/eRO6veczhf6A/7Jy8Q==", "dev": true, "requires": { "confusing-browser-globals": "^1.0.9", "object.assign": "^4.1.0", - "object.entries": "^1.1.1" + "object.entries": "^1.1.2" } }, "eslint-import-resolver-node": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz", - "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", + "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", "dev": true, "requires": { "debug": "^2.6.9", @@ -2246,9 +2238,9 @@ } }, "eslint-plugin-import": { - "version": "2.21.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.21.1.tgz", - "integrity": "sha512-qYOOsgUv63vHof7BqbzuD+Ud34bXHxFJxntuAC1ZappFZXYbRIek3aJ7jc9i2dHDGDyZ/0zlO0cpioES265Lsw==", + "version": "2.21.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.21.2.tgz", + "integrity": "sha512-FEmxeGI6yaz+SnEB6YgNHlQK1Bs2DKLM+YF+vuTk5H8J9CLbJLtlPvRFgZZ2+sXiKAlN5dpdlrWOjK8ZoZJpQA==", "dev": true, "requires": { "array-includes": "^3.1.1", @@ -2313,9 +2305,9 @@ } }, "eslint-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.0.0.tgz", - "integrity": "sha512-0HCPuJv+7Wv1bACm8y5/ECVfYdfsAm9xmVb7saeFlxjPYALefjhbYoCkBjPdPzGH8wWyTpAez82Fh3VKYEZ8OA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", "dev": true, "requires": { "eslint-visitor-keys": "^1.1.0" @@ -2339,9 +2331,9 @@ }, "dependencies": { "eslint-visitor-keys": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.2.0.tgz", - "integrity": "sha512-WFb4ihckKil6hu3Dp798xdzSfddwKKU3+nGniKF6HfeW6OLd2OUDEPP7TcHtB5+QXOKg2s6B2DaMPE1Nn/kxKQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true } } @@ -2942,9 +2934,9 @@ "dev": true }, "has-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", - "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", "dev": true }, "has-value": { @@ -3155,9 +3147,9 @@ "dev": true }, "inquirer": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz", - "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.2.0.tgz", + "integrity": "sha512-E0c4rPwr9ByePfNlTIB8z51kK1s2n6jrHuJeEHENl/sbq2G/S1auvibgEwNR4uSyiU+PiYHqSwsgGiXjG8p5ZQ==", "dev": true, "requires": { "ansi-escapes": "^4.2.1", @@ -3266,9 +3258,9 @@ "dev": true }, "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==", "dev": true }, "is-ci": { @@ -3301,9 +3293,9 @@ } }, "is-date-object": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", - "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", "dev": true }, "is-descriptor": { @@ -3387,12 +3379,12 @@ "dev": true }, "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.0.tgz", + "integrity": "sha512-iI97M8KTWID2la5uYXlkbSDQIg4F6o1sYboZKKTDpnDQMLtUL86zxhgDet3Q2SriaYsyGqZ6Mn2SjbRKeLHdqw==", "dev": true, "requires": { - "has": "^1.0.3" + "has-symbols": "^1.0.1" } }, "is-stream": { @@ -3408,12 +3400,12 @@ "dev": true }, "is-symbol": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", - "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", "dev": true, "requires": { - "has-symbols": "^1.0.0" + "has-symbols": "^1.0.1" } }, "is-typedarray": { @@ -5286,9 +5278,9 @@ } }, "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz", + "integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==", "dev": true }, "object-keys": { @@ -5319,14 +5311,13 @@ } }, "object.entries": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.1.tgz", - "integrity": "sha512-ilqR7BgdyZetJutmDPfXCDffGa0/Yzl2ivVNpbx/g4UeWrCdRnFDUBrKJGLhGieRHDATnyZXWBeCb29k9CJysQ==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.2.tgz", + "integrity": "sha512-BQdB9qKmb/HyNdMNWVr7O3+z5MUIx3aiegEIJqjMBbBf0YT9RRxTJSim4mzFqtyr7PDAHigq0N9dO0m0tRakQA==", "dev": true, "requires": { "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", + "es-abstract": "^1.17.5", "has": "^1.0.3" } }, @@ -6400,24 +6391,24 @@ "strip-ansi": "^6.0.0" } }, - "string.prototype.trimleft": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", - "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", "dev": true, "requires": { "define-properties": "^1.1.3", - "function-bind": "^1.1.1" + "es-abstract": "^1.17.5" } }, - "string.prototype.trimright": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", - "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", "dev": true, "requires": { "define-properties": "^1.1.3", - "function-bind": "^1.1.1" + "es-abstract": "^1.17.5" } }, "strip-ansi": { diff --git a/package.json b/package.json index 9e633e8..c77ed33 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "holidayapi", - "version": "4.0.0", + "version": "4.1.0", "description": "Official Node.js library for Holiday API", "main": "dist/index.js", "types": "dist/index.d.ts", @@ -24,15 +24,15 @@ "node": ">= 10.0.0" }, "devDependencies": { - "@types/jest": "^25.2.3", + "@types/jest": "^26.0.0", "@types/nock": "^11.1.0", - "@types/node": "^14.0.12", + "@types/node": "^14.0.13", "@types/node-fetch": "^2.5.7", - "@typescript-eslint/eslint-plugin": "^3.2.0", - "@typescript-eslint/parser": "^3.2.0", - "eslint": "^7.2.0", - "eslint-config-airbnb-base": "^14.1.0", - "eslint-plugin-import": "^2.21.1", + "@typescript-eslint/eslint-plugin": "^3.3.0", + "@typescript-eslint/parser": "^3.3.0", + "eslint": "~7.2.0", + "eslint-config-airbnb-base": "^14.2.0", + "eslint-plugin-import": "^2.21.2", "eslint-plugin-jest": "^23.13.2", "jest": "^26.0.1", "nock": "^12.0.3", diff --git a/src/holidayapi.ts b/src/holidayapi.ts index a205f43..457c717 100644 --- a/src/holidayapi.ts +++ b/src/holidayapi.ts @@ -8,8 +8,17 @@ import fetch from 'node-fetch'; import { URL, URLSearchParams } from 'url'; import { - CountriesRequest, CountriesResponse, Endpoint, HolidaysResponse, - HolidaysRequest, LanguagesRequest, LanguagesResponse, Requests, Responses, + CountriesRequest, + CountriesResponse, + Endpoint, + HolidaysRequest, + HolidaysResponse, + LanguagesRequest, + LanguagesResponse, + Requests, + Responses, + WorkdayRequest, + WorkdayResponse, } from './types'; export class HolidayAPI { @@ -28,7 +37,6 @@ export class HolidayAPI { throw new Error(`Invalid API key, ${getYours}`); } - if (version !== 1) { throw new Error('Invalid version number, expected "1"'); } @@ -80,4 +88,18 @@ export class HolidayAPI { async languages(request?: LanguagesRequest): Promise { return this.request('languages', request); } + + async workday(request: WorkdayRequest = {}): Promise { + if (!request.country) { + throw new Error('Missing country'); + } else if (!request.start) { + throw new Error('Missing start date'); + } else if (!request.days) { + throw new Error('Missing days'); + } else if (request.days < 1) { + throw new Error('Days must be 1 or more'); + } + + return this.request('workday', request); + } } diff --git a/src/types.ts b/src/types.ts index 373ce50..b478175 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,90 +1,110 @@ /** - * Copyright (c) Gravity Boulevard, LLC + * Copyright (c) Gravity Boulevard; LLC * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ -export type Endpoint = 'countries' | 'holidays' | 'languages'; +export type Endpoint = 'countries' | 'holidays' | 'languages' | 'workday'; -type Request = { - format?: 'csv' | 'json' | 'php' | 'tsv' | 'yaml' | 'xml', - key?: string, - pretty?: boolean, - search?: string, +type Weekday = { + name: 'Monday' | 'Tuesday' | 'Wednesday' | 'Thursday' | 'Friday' | 'Saturday' | 'Sunday'; + numeric: 1 | 2 | 3 | 4 | 5 | 6 | 7; }; -export type Requests = CountriesRequest | HolidaysRequest | LanguagesRequest; +type Request = { + format?: 'csv' | 'json' | 'php' | 'tsv' | 'xml' | 'yaml'; + key?: string; + pretty?: boolean; + search?: string; +}; + +export type Requests = CountriesRequest | HolidaysRequest | LanguagesRequest | WorkdayRequest; export type CountriesRequest = Request & { - country?: string, - public?: boolean, + country?: string; + public?: boolean; }; export type HolidaysRequest = Request & { - country?: string, - day?: number, - language?: string, - month?: number, - previous?: boolean, - public?: boolean, - subdivisions?: boolean, - upcoming?: boolean, - year?: number, + country?: string; + day?: number; + language?: string; + month?: number; + previous?: boolean; + public?: boolean; + subdivisions?: boolean; + upcoming?: boolean; + year?: number; }; export type LanguagesRequest = Request & { - language?: string, + language?: string; +}; + +export type WorkdayRequest = Request & { + country?: string; + start?: string; + days?: number; }; export type Response = { requests: { - available: number, - resets: Date, - used: number, - }, - status: number, - error?: string, + available: number; + resets: Date; + used: number; + }; + status: number; + error?: string; }; -export type Responses = ( - CountriesResponse | HolidaysResponse | LanguagesResponse -); +export type Responses = CountriesResponse | HolidaysResponse | LanguagesResponse | WorkdayResponse; export type CountriesResponse = Response & { countries?: { - code: string, + code: string; codes: { - 'alpha-2': string, - 'alpha-3': string, - numeric: string, - }, - flag: string, - languages: string[], - name: string, + 'alpha-2': string; + 'alpha-3': string; + numeric: string; + }; + flag: string; + languages: string[]; + name: string; subdivisions: { - code: string, - languages: string[], - name: string, - }[], - }[], + code: string; + languages: string[]; + name: string; + }[]; + weekday: { + date: Weekday; + observed: Weekday; + }; + }[]; }; export type HolidaysResponse = Response & { holidays?: { - country: string, - date: Date, - name: string, - observed: Date, - public: boolean, - uuid: string, - subdivisions?: string[], - }[], + country: string; + date: Date; + name: string; + observed: Date; + public: boolean; + uuid: string; + subdivisions?: string[]; + }[]; }; export type LanguagesResponse = Response & { languages?: { - code: string, - name: string, - }[], + code: string; + name: string; + }[]; +}; + +export type WorkdayResponse = Response & { + workday?: { + date: Date; + weekday: Weekday; + } }; diff --git a/tests/holidayapi.test.ts b/tests/holidayapi.test.ts index 7fd694f..1d32b28 100644 --- a/tests/holidayapi.test.ts +++ b/tests/holidayapi.test.ts @@ -506,5 +506,106 @@ describe('holidayapi', () => { } }); }); + + describe('/v1/workday', () => { + const basePath = `/workday?key=${key}`; + + it('should return workday', async () => { + const expectedResponse = { + status: 200, + requests: { + used: 1000, + available: 9000, + resets: '2019-10-01 00:00:00', + }, + workday: [ + { + date: '2019-07-16', + weekday: { + name: 'Tuesday', + numeric: '2', + }, + }, + ], + }; + + mockRequest.get(`${basePath}&country=US&start=2019-07-01&days=10`) + .reply(200, expectedResponse); + + expect(await holidayapi.workday({ + country: 'US', + start: '2019-07-01', + days: 10, + })).toStrictEqual(expectedResponse); + }); + + it('should error when country is missing', async () => { + expect.assertions(1); + + try { + await holidayapi.workday(); + } catch (err) { + expect(err.message).toMatch(/missing country/i); + } + }); + + it('should error when start is missing', async () => { + expect.assertions(1); + + try { + await holidayapi.workday({ country: 'US' }); + } catch (err) { + expect(err.message).toMatch(/missing start date/i); + } + }); + + it('should error when days is missing', async () => { + expect.assertions(1); + + try { + await holidayapi.workday({ country: 'US', start: '2019-07-01' }); + } catch (err) { + expect(err.message).toMatch(/missing days/i); + } + }); + + it('should error when days is negative', async () => { + expect.assertions(1); + + try { + await holidayapi.workday({ country: 'US', start: '2019-07-01', days: -10 }); + } catch (err) { + expect(err.message).toMatch(/days must be 1 or more/i); + } + }); + + it('should raise 4xx errors', async () => { + const expectedResponse = { + status: 429, + error: 'Rate limit exceeded', + }; + + expect.assertions(1); + mockRequest.get(`${basePath}&country=US&start=2019-07-01&days=10`) + .reply(429, expectedResponse); + + try { + await holidayapi.workday({ country: 'US', start: '2019-07-01', days: 10 }); + } catch (err) { + expect(err.message).toMatch(/rate limit exceeded/i); + } + }); + + it('should raise 5xx errors', async () => { + expect.assertions(1); + mockRequest.get(`${basePath}&country=US&start=2019-07-01&days=10`).reply(500); + + try { + await holidayapi.workday({ country: 'US', start: '2019-07-01', days: 10 }); + } catch (err) { + expect(err.message).toMatch(/internal server error/i); + } + }); + }); }); });