Compare commits

...

21 commits

Author SHA1 Message Date
e3a345347e
chore: update license years 2024-08-09 16:39:51 -05:00
c3be93e6c4
chore: build latest 2024-08-09 16:37:12 -05:00
4cde62af13 chore: update dependencies 2024-08-09 16:35:18 -05:00
250fb4b4e9 chore: updating dependencies and node versions
* Updated supported Node.js versions

Dropped support for 14 and 16 in favor of the current LTS versions 18
and 20.

* Updated Node.js dependencies

Bumped nearly all dependencies to the latest. The exception being
`node-fetch` which made the jump to using ESM which is still causing
folks a lot of grief.

* Shored code coverage back up to 100%

Had some remnants from when this library did sanity checks pre-flight to
the API that required additional tests to cover them, or just adjusting
the argument defaults.

* Removed the "build" badge from the README

Seems there were some gotchas with that badge that makes it kind of a
pain, as it has to be hard coded a certain way. I messed with it and
couldn't get it working, seems like more trouble than it's worth.

Further details: https://github.com/badges/shields/issues/8671

* Added .nvmrc file

Pretty self explanatory.

* Bumped version number

Lucky number 7, y'all!
2023-10-26 17:19:54 -05:00
5a62ac8b5f fix: forgot to rebuild 2022-04-06 08:50:54 -05:00
046aadeea4 feat: get previous workday
This started as a feature request for our upstream API (closes #33) that
led to a bit of rework to help make the library a bit more flexible in
terms of not needing modification when something on the API changes.

This update also includes your typical round of dependency updates and a
bit of preemptive planning by way of dropping support for Node.js as
we're on the cusp of it being out of maintenance and the release of
Node.js v18.
2022-04-05 17:48:55 -05:00
c3de7ff9bd chore(deps): bump packages with security issues 2022-02-17 21:07:35 -06:00
7d37113bd3 chore: bump version number to prep for release 2022-02-17 21:07:35 -06:00
2478cb47c3 chore: updated dist files
Just realized that we don't have Husky or something similar in the mix
to ensure that the dist files are being created automatically.
2022-02-17 21:07:35 -06:00
Marrtin Rylander
0b58b614dc Suggested type updates 2022-02-17 20:47:27 -06:00
bbda365fe7 feat: add workdays endpoint
Added support for our new workdays endpoint that allows you to pull the
number of working days that occur between two dates.

In addition to the new endpoint:
* chore: upgraded dependencies.
* ci: dropped Node.js 10 because it past EOL.
* ci: swapped Node.js 15 for 16 now that it's out.
* docs: added example of new endpoint.
2021-06-14 18:05:16 -05:00
dependabot[bot]
fa2f709f82 chore(deps): bump lodash from 4.17.19 to 4.17.21
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.19 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.19...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-10 22:28:52 -05:00
dependabot[bot]
7f7d1af247 chore(deps): bump hosted-git-info from 2.8.8 to 2.8.9
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-10 22:28:11 -05:00
dependabot[bot]
7b1b47ddc4 chore(deps): bump ws from 7.4.1 to 7.4.6
Bumps [ws](https://github.com/websockets/ws) from 7.4.1 to 7.4.6.
- [Release notes](https://github.com/websockets/ws/releases)
- [Commits](https://github.com/websockets/ws/compare/7.4.1...7.4.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-10 22:27:49 -05:00
dependabot[bot]
5fc9c31883 chore(deps): bump glob-parent from 5.1.1 to 5.1.2
Bumps [glob-parent](https://github.com/gulpjs/glob-parent) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/gulpjs/glob-parent/releases)
- [Changelog](https://github.com/gulpjs/glob-parent/blob/main/CHANGELOG.md)
- [Commits](https://github.com/gulpjs/glob-parent/compare/v5.1.1...v5.1.2)

---
updated-dependencies:
- dependency-name: glob-parent
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-06-10 22:27:31 -05:00
3eca80185c chore(deps): bump typescript to 4
Bumped `typescript` up to v4.x as well as a bunch of other dependencies,
including `jest` which had a moderate severity from it's `node-notifier`
dependency. Also added Node.js v15.x to the automated testing.
2020-12-29 16:43:47 -06:00
dependabot[bot]
b3bbab4e6b chore(deps): bump node-fetch from 2.6.0 to 2.6.1
Bumps [node-fetch](https://github.com/bitinn/node-fetch) from 2.6.0 to 2.6.1.
- [Release notes](https://github.com/bitinn/node-fetch/releases)
- [Changelog](https://github.com/node-fetch/node-fetch/blob/master/docs/CHANGELOG.md)
- [Commits](https://github.com/bitinn/node-fetch/compare/v2.6.0...v2.6.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-09-21 15:40:01 -05:00
dependabot[bot]
790c6c8d02 chore(deps): bump lodash from 4.17.15 to 4.17.19
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.19.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.19)

Signed-off-by: dependabot[bot] <support@github.com>
2020-07-27 17:08:38 -05:00
2230488d14 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.
2020-06-20 11:12:10 -05:00
07988d8c71 test: drop end of life Node.js version
* test: Dropped Node v13.x since it's now past it's expiration.
* test: Migrated from Travis CI to GitHub Actions.
* test: Migrated from Coveralls to CodeCov.
* docs: Dropped over 1.x stuff from the read me.
* perf: Updated dependencies and rebuilt against latest TypeScript.
* feat: Bumped major version number due to Node.js support changing.
2020-06-08 18:30:21 -05:00
2edef851ca test: add Node.js v14.x to test suite
* test: Added Node.js v14.x to the test suite.
* perf: Updated dependencies.
* docs: Drop blurb about migration from v2 as it will be covered in
  release notes.
* chore: Bumped minor version since compatibility has increased.
2020-04-27 17:39:03 -05:00
19 changed files with 5897 additions and 7910 deletions

View file

@ -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"
}
}

31
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,31 @@
name: Test
on: [push, pull_request]
jobs:
test:
name: Test Node.js ${{ matrix.node-version }}
runs-on: ubuntu-latest
strategy:
matrix:
node-version: ['18', '20', '22']
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Setup Node.js
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Node.js Version
run: node --version
- name: NPM Version
run: npm --version
- name: Install Dependencies
run: npm install
- name: Run Linter
run: npm run lint
- name: Run Tests
run: npm run test:coverage
- name: Upload Coverage
if: ${{ matrix.node-version == '20' }}
uses: codecov/codecov-action@v1
with:
file: ./coverage/lcov.info

1
.gitignore vendored
View file

@ -1,2 +1,3 @@
node_modules
npm-debug.log*
coverage/

1
.nvmrc Normal file
View file

@ -0,0 +1 @@
v20

View file

@ -1,11 +0,0 @@
language: node_js
node_js:
- 8
- 10
- 12
- 13
script:
- npm run lint
- npm run test:coverage

View file

@ -1,6 +1,6 @@
MIT License
Copyright (c) 2016, 2017, 2018, 2019, 2020 Gravity Boulevard, LLC
Copyright (c) 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024 Gravity Boulevard, LLC
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View file

@ -2,43 +2,12 @@
[![License](https://img.shields.io/npm/l/holidayapi-node?style=for-the-badge)](https://github.com/holidayapi/holidayapi-node/blob/master/LICENSE)
![Node Version](https://img.shields.io/node/v/holidayapi?style=for-the-badge)
![Build Status](https://img.shields.io/travis/holidayapi/holidayapi-node/master?style=for-the-badge)
[![Coverage Status](https://img.shields.io/coveralls/github/holidayapi/holidayapi-node/master?style=for-the-badge)](https://coveralls.io/github/holidayapi/holidayapi-node?branch=master)
[![Code Coverage](https://img.shields.io/codecov/c/github/holidayapi/holidayapi-node?style=for-the-badge)](https://codecov.io/gh/holidayapi/holidayapi-node)
Official Node.js library for [Holiday API](https://holidayapi.com) providing
quick and easy access to holiday information from applications written in
server-side JavaScript.
## Migrating from 2.x
In an attempt to stay current with both our development and production
dependencies, the decision was made to drop testing and advertised compatibility
with Node.js versions that are outside of their maintenance window.
For the 3.x release, testing for Node.js 13.x was added while testing for Node.js
7.x, 9.x and 11.x was dropped. Even though these older versions may still
continue to work, they become harder to test against as our dependencies improve
and drop support for those versions.
In the future, when compatibility changes, we will increment the major version
number of the release as well as document the additions and deprecations in the
release notes.
## Migrating from 1.x
Please note, version 2.x of this library is a full rewrite of the 1.x series in
TypeScript. The interfacing to the library has been simplified and existing
applications upgrading to 2.x will need to be updated.
| Version 1.x Syntax (Old) | Version 2.x Syntax (New) |
|---------------------------------------------------|---------------------------------------------------|
| `const HolidayAPI = require('node-holidayapi');` | `import { HolidayAPI } from 'holidayapi';` |
| `const holidayApi = new HolidayAPI(key).v1;` | `const holidayApi = new HolidayAPI({ key });` |
| `holidayApi.holidays(params, (err, data) => {});` | `holidayApi.holidays(params).then((data) => {});` |
Version 1.x of the library can still be found
[here](https://github.com/joshtronic/node-holidayapi).
## Documentation
Full documentation of the Holiday API endpoints is available
@ -269,3 +238,27 @@ holidayApi.holidays({
year: 2019,
});
```
### Workday
#### Fetch workday 7 business days after a date
```javascript
holidayApi.workday({
country: 'US',
start: '2019-07-01',
days: 7,
});
```
### Workdays
#### Fetch number of workdays between two dates
```javascript
holidayApi.workday({
country: 'US',
start: '2019-07-01',
end: '2019-07-10',
});
```

20
codecov.yml Normal file
View file

@ -0,0 +1,20 @@
codecov:
require_ci_to_pass: yes
coverage:
precision: 2
round: down
range: "70...100"
parsers:
gcov:
branch_detection:
conditional: yes
loop: yes
method: no
macro: no
comment:
layout: "reach,diff,flags,tree"
behavior: default
require_changes: no

View file

@ -1,4 +1,4 @@
import { CountriesRequest, CountriesResponse, HolidaysResponse, HolidaysRequest, LanguagesRequest, LanguagesResponse } from './types';
import { CountriesRequest, CountriesResponse, HolidaysRequest, HolidaysResponse, LanguagesRequest, LanguagesResponse, WorkdayRequest, WorkdayResponse, WorkdaysRequest, WorkdaysResponse } from './types';
export declare class HolidayAPI {
baseUrl: string;
key: string;
@ -11,4 +11,6 @@ export declare class HolidayAPI {
countries(request?: CountriesRequest): Promise<CountriesResponse>;
holidays(request?: HolidaysRequest): Promise<HolidaysResponse>;
languages(request?: LanguagesRequest): Promise<LanguagesResponse>;
workday(request?: WorkdayRequest): Promise<WorkdayResponse>;
workdays(request?: WorkdaysRequest): Promise<WorkdaysResponse>;
}

40
dist/holidayapi.js vendored
View file

@ -25,7 +25,7 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
function verb(n) { return function (v) { return step([n, v]); }; }
function step(op) {
if (f) throw new TypeError("Generator is already executing.");
while (_) try {
while (g && (g = 0, op[0] && (_ = 0)), _) try {
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
if (y = 0, t) op = [op[0] & 2, t.value];
switch (op[0]) {
@ -47,29 +47,29 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
}
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.HolidayAPI = void 0;
var node_fetch_1 = require("node-fetch");
var url_1 = require("url");
var HolidayAPI = (function () {
function HolidayAPI(_a) {
var key = _a.key, _b = _a.version, version = _b === void 0 ? 1 : _b;
var getYours = 'get yours at HolidayAPI.com';
var uuidRegExp = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/;
if (!key) {
throw new Error("Missing API key, " + getYours);
throw new Error("Missing API key, ".concat(getYours));
}
if (!uuidRegExp.test(key)) {
throw new Error("Invalid API key, " + getYours);
throw new Error("Invalid API key, ".concat(getYours));
}
if (version !== 1) {
throw new Error('Invalid version number, expected "1"');
}
this.baseUrl = "https://holidayapi.com/v" + version + "/";
this.baseUrl = "https://holidayapi.com/v".concat(version, "/");
this.key = key;
}
HolidayAPI.prototype.createUrl = function (endpoint, request) {
var parameters = __assign({ key: this.key }, request);
var url = new url_1.URL(endpoint, this.baseUrl);
url.search = new url_1.URLSearchParams(parameters).toString();
var url = new URL(endpoint, this.baseUrl);
url.search = new URLSearchParams(parameters).toString();
return url.toString();
};
HolidayAPI.prototype.request = function (endpoint, request) {
@ -77,7 +77,7 @@ var HolidayAPI = (function () {
var response, payload, err_1;
return __generator(this, function (_a) {
switch (_a.label) {
case 0: return [4, node_fetch_1.default(this.createUrl(endpoint, request))];
case 0: return [4, (0, node_fetch_1.default)(this.createUrl(endpoint, request))];
case 1:
response = _a.sent();
_a.label = 2;
@ -108,18 +108,8 @@ var HolidayAPI = (function () {
});
};
HolidayAPI.prototype.holidays = 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.year) {
throw new Error('Missing year');
}
else if (request.previous && request.upcoming) {
throw new Error('Previous and upcoming are mutually exclusive');
}
return [2, this.request('holidays', request)];
});
});
@ -131,6 +121,20 @@ var HolidayAPI = (function () {
});
});
};
HolidayAPI.prototype.workday = function (request) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2, this.request('workday', request)];
});
});
};
HolidayAPI.prototype.workdays = function (request) {
return __awaiter(this, void 0, void 0, function () {
return __generator(this, function (_a) {
return [2, this.request('workdays', request)];
});
});
};
return HolidayAPI;
}());
exports.HolidayAPI = HolidayAPI;

18
dist/index.js vendored
View file

@ -1,6 +1,18 @@
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__export(require("./holidayapi"));
__exportStar(require("./types"), exports);
__exportStar(require("./holidayapi"), exports);

57
dist/types.d.ts vendored
View file

@ -1,16 +1,19 @@
export declare type Endpoint = 'countries' | 'holidays' | 'languages';
declare type Request = {
format?: 'csv' | 'json' | 'php' | 'tsv' | 'yaml' | 'xml';
export type Endpoint = 'countries' | 'holidays' | 'languages' | 'workday' | 'workdays';
export type Weekday = {
name: 'Monday' | 'Tuesday' | 'Wednesday' | 'Thursday' | 'Friday' | 'Saturday' | 'Sunday';
numeric: 1 | 2 | 3 | 4 | 5 | 6 | 7;
};
type Request = {
format?: 'csv' | 'json' | 'php' | 'tsv' | 'xml' | 'yaml';
key?: string;
pretty?: boolean;
search?: string;
};
export declare type Requests = CountriesRequest | HolidaysRequest | LanguagesRequest;
export declare type CountriesRequest = Request & {
export type CountriesRequest = Request & {
country?: string;
public?: boolean;
};
export declare type HolidaysRequest = Request & {
export type HolidaysRequest = Request & {
country?: string;
day?: number;
language?: string;
@ -21,20 +24,29 @@ export declare type HolidaysRequest = Request & {
upcoming?: boolean;
year?: number;
};
export declare type LanguagesRequest = Request & {
export type LanguagesRequest = Request & {
language?: string;
};
export declare type Response = {
export type WorkdayRequest = Request & {
country?: string;
start?: string;
days?: number;
};
export type WorkdaysRequest = Request & {
country?: string;
start?: string;
end?: string;
};
export type Response = {
requests: {
available: number;
resets: Date;
resets: string;
used: number;
};
status: number;
error?: string;
};
export declare type Responses = (CountriesResponse | HolidaysResponse | LanguagesResponse);
export declare type CountriesResponse = Response & {
export type CountriesResponse = Response & {
countries?: {
code: string;
codes: {
@ -50,23 +62,38 @@ export declare type CountriesResponse = Response & {
languages: string[];
name: string;
}[];
weekday: {
date: Weekday;
observed: Weekday;
};
}[];
};
export declare type HolidaysResponse = Response & {
export type HolidaysResponse = Response & {
holidays?: {
country: string;
date: Date;
date: string;
name: string;
observed: Date;
observed: string;
public: boolean;
uuid: string;
subdivisions?: string[];
}[];
};
export declare type LanguagesResponse = Response & {
export type LanguagesResponse = Response & {
languages?: {
code: string;
name: string;
}[];
};
export type WorkdayResponse = Response & {
workday?: {
date: string;
weekday: Weekday;
};
};
export type WorkdaysResponse = Response & {
workdays?: number;
};
export type Requests = (CountriesRequest | HolidaysRequest | LanguagesRequest | WorkdayRequest | WorkdaysRequest);
export type Responses = (CountriesResponse | HolidaysResponse | LanguagesResponse | WorkdayResponse | WorkdaysResponse);
export {};

View file

@ -1,4 +1,5 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
coverageReporters: ['lcov'],
};

14171
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "holidayapi",
"version": "3.2.1",
"version": "7.1.0",
"description": "Official Node.js library for Holiday API",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@ -21,32 +21,31 @@
},
"homepage": "https://holidayapi.com",
"engines": {
"node": ">= 8.0.0"
"node": ">= 18.0.0"
},
"devDependencies": {
"@types/jest": "^25.1.4",
"@types/jest": "^29.5.6",
"@types/nock": "^11.1.0",
"@types/node": "^13.9.2",
"@types/node-fetch": "^2.5.4",
"@typescript-eslint/eslint-plugin": "^2.24.0",
"@typescript-eslint/parser": "^2.24.0",
"coveralls": "^3.0.11",
"eslint": "^6.8.0",
"eslint-config-airbnb-base": "^14.1.0",
"eslint-plugin-import": "^2.20.1",
"eslint-plugin-jest": "^23.8.2",
"jest": "^25.1.0",
"nock": "^12.0.3",
"ts-jest": "^25.2.0",
"typescript": "^3.7.5"
"@types/node": "^22.2.0",
"@types/node-fetch": "^2.6.7",
"@typescript-eslint/eslint-plugin": "^6.9.0",
"@typescript-eslint/parser": "^6.9.0",
"eslint": "^8.12.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "^27.4.3",
"jest": "^29.7.0",
"nock": "^13.2.4",
"ts-jest": "^29.1.1",
"typescript": "^5.2.2"
},
"dependencies": {
"node-fetch": "^2.6.0"
"node-fetch": "^2.7.0"
},
"scripts": {
"build": "tsc",
"lint": "eslint --ext .js,.ts src tests",
"test": "jest",
"test:coverage": "jest --coverage --coverageReporters=text-lcov | coveralls"
"test:coverage": "jest --coverage"
}
}

View file

@ -6,10 +6,20 @@
*/
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,
WorkdaysRequest,
WorkdaysResponse,
} from './types';
export class HolidayAPI {
@ -28,7 +38,6 @@ export class HolidayAPI {
throw new Error(`Invalid API key, ${getYours}`);
}
if (version !== 1) {
throw new Error('Invalid version number, expected "1"');
}
@ -46,7 +55,7 @@ export class HolidayAPI {
private async request(endpoint: Endpoint, request?: Requests): Promise<Responses> {
const response = await fetch(this.createUrl(endpoint, request));
let payload;
let payload: any;
try {
payload = await response.json();
@ -65,19 +74,19 @@ export class HolidayAPI {
return this.request('countries', request);
}
async holidays(request: HolidaysRequest = {}): Promise<HolidaysResponse> {
if (!request.country) {
throw new Error('Missing country');
} else if (!request.year) {
throw new Error('Missing year');
} else if (request.previous && request.upcoming) {
throw new Error('Previous and upcoming are mutually exclusive');
}
async holidays(request?: HolidaysRequest): Promise<HolidaysResponse> {
return this.request('holidays', request);
}
async languages(request?: LanguagesRequest): Promise<LanguagesResponse> {
return this.request('languages', request);
}
async workday(request?: WorkdayRequest): Promise<WorkdayResponse> {
return this.request('workday', request);
}
async workdays(request?: WorkdaysRequest): Promise<WorkdaysResponse> {
return this.request('workdays', request);
}
}

View file

@ -5,86 +5,128 @@
* LICENSE file in the root directory of this source tree.
*/
export type Endpoint = 'countries' | 'holidays' | 'languages';
export type Endpoint = 'countries' | 'holidays' | 'languages' | 'workday' | 'workdays';
type Request = {
format?: 'csv' | 'json' | 'php' | 'tsv' | 'yaml' | 'xml',
key?: string,
pretty?: boolean,
search?: string,
export 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 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 WorkdaysRequest = Request & {
country?: string;
start?: string;
end?: string;
};
export type Response = {
requests: {
available: number,
resets: Date,
used: number,
},
status: number,
error?: string,
available: number;
resets: string;
used: number;
};
status: number;
error?: string;
};
export type Responses = (
CountriesResponse | HolidaysResponse | LanguagesResponse
);
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: string;
name: string;
observed: string;
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: string;
weekday: Weekday;
}
};
export type WorkdaysResponse = Response & {
workdays?: number;
};
export type Requests = (
| CountriesRequest
| HolidaysRequest
| LanguagesRequest
| WorkdayRequest
| WorkdaysRequest
);
export type Responses = (
| CountriesResponse
| HolidaysResponse
| LanguagesResponse
| WorkdayResponse
| WorkdaysResponse
);

View file

@ -93,129 +93,6 @@ describe('holidayapi', () => {
expect(await holidayapi.countries()).toStrictEqual(expectedResponse);
});
it('should return only countries with public holidays', async () => {
const expectedResponse = {
status: 200,
requests: {
used: 1000,
available: 9000,
resets: '2019-10-01 00:00:00',
},
countries: [
{
code: 'ST',
name: 'Sao Tome and Principe',
languages: ['pt'],
codes: {
'alpha-2': 'ST',
'alpha-3': 'STP',
numeric: 678,
},
flag: 'https://www.countryflags.io/ST/flat/64.png',
subdivisions: [
{
code: 'ST-P',
name: 'Príncipe',
languages: ['pt'],
},
{
code: 'ST-S',
name: 'São Tomé',
languages: ['pt'],
},
],
},
],
};
mockRequest.get(`${basePath}&public=true`).reply(200, expectedResponse);
expect(await holidayapi.countries({
public: true,
})).toStrictEqual(expectedResponse);
});
it('should return one country', async () => {
const expectedResponse = {
status: 200,
requests: {
used: 1000,
available: 9000,
resets: '2019-10-01 00:00:00',
},
countries: [
{
code: 'ST',
name: 'Sao Tome and Principe',
languages: ['pt'],
codes: {
'alpha-2': 'ST',
'alpha-3': 'STP',
numeric: 678,
},
flag: 'https://www.countryflags.io/ST/flat/64.png',
subdivisions: [
{
code: 'ST-P',
name: 'Príncipe',
languages: ['pt'],
},
{
code: 'ST-S',
name: 'São Tomé',
languages: ['pt'],
},
],
},
],
};
mockRequest.get(`${basePath}&country=ST`).reply(200, expectedResponse);
expect(await holidayapi.countries({
country: 'ST',
})).toStrictEqual(expectedResponse);
});
it('should search countries', async () => {
const expectedResponse = {
status: 200,
requests: {
used: 1000,
available: 9000,
resets: '2019-10-01 00:00:00',
},
countries: [
{
code: 'ST',
name: 'Sao Tome and Principe',
languages: ['pt'],
codes: {
'alpha-2': 'ST',
'alpha-3': 'STP',
numeric: 678,
},
flag: 'https://www.countryflags.io/ST/flat/64.png',
subdivisions: [
{
code: 'ST-P',
name: 'Príncipe',
languages: ['pt'],
},
{
code: 'ST-S',
name: 'São Tomé',
languages: ['pt'],
},
],
},
],
};
mockRequest.get(`${basePath}&search=Sao`).reply(200, expectedResponse);
expect(await holidayapi.countries({
search: 'Sao',
})).toStrictEqual(expectedResponse);
});
it('should raise 4xx errors', async () => {
const expectedResponse = {
status: 429,
@ -288,81 +165,6 @@ describe('holidayapi', () => {
})).toStrictEqual(expectedResponse);
});
it('should search holidays', async () => {
const expectedResponse = {
status: 200,
requests: {
used: 1000,
available: 9000,
resets: '2019-10-01 00:00:00',
},
holidays: [
{
name: 'Independence Day',
date: '2015-07-04',
observed: '2015-07-03',
public: true,
country: 'US',
uuid: '88268759-9b90-468c-804f-b729b8418e7c',
weekday: {
date: {
name: 'Saturday',
numeric: '6',
},
observed: {
name: 'Friday',
numeric: '5',
},
},
},
],
};
mockRequest.get(`${basePath}&country=US&year=2019&search=Independence`)
.reply(200, expectedResponse);
expect(await holidayapi.holidays({
country: 'US',
year: 2019,
search: 'Independence',
})).toStrictEqual(expectedResponse);
});
it('should error when country is missing', async () => {
expect.assertions(1);
try {
await holidayapi.holidays();
} catch (err) {
expect(err.message).toMatch(/missing country/i);
}
});
it('should error when year is missing', async () => {
expect.assertions(1);
try {
await holidayapi.holidays({ country: 'US' });
} catch (err) {
expect(err.message).toMatch(/missing year/i);
}
});
it('should error when both previous and upcoming', async () => {
expect.assertions(1);
try {
await holidayapi.holidays({
country: 'US',
year: 2019,
previous: true,
upcoming: true,
});
} catch (err) {
expect(err.message).toMatch(/previous and upcoming/i);
}
});
it('should raise 4xx errors', async () => {
const expectedResponse = {
status: 429,
@ -431,54 +233,6 @@ describe('holidayapi', () => {
expect(await holidayapi.languages()).toStrictEqual(expectedResponse);
});
it('should return one language', async () => {
const expectedResponse = {
status: 200,
requests: {
used: 1000,
available: 9000,
resets: '2019-10-01 00:00:00',
},
languages: [
{
code: 'hi',
name: 'Hindi',
},
],
};
mockRequest.get(`${basePath}&language=hi`).reply(200, expectedResponse);
expect(await holidayapi.languages({
language: 'hi',
})).toStrictEqual(expectedResponse);
});
it('should search languages', async () => {
const expectedResponse = {
status: 200,
requests: {
used: 1000,
available: 9000,
resets: '2019-10-01 00:00:00',
},
languages: [
{
code: 'hi',
name: 'Hindi',
},
{
code: 'zh',
name: 'Chinese (Simplified)',
},
],
};
mockRequest.get(`${basePath}&search=in`).reply(200, expectedResponse);
expect(await holidayapi.languages({
search: 'in',
})).toStrictEqual(expectedResponse);
});
it('should raise 4xx errors', async () => {
const expectedResponse = {
status: 429,
@ -506,5 +260,119 @@ 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 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);
}
});
});
describe('/v1/workdays', () => {
const basePath = `/workdays?key=${key}`;
it('should return workdays', async () => {
const expectedResponse = {
status: 200,
requests: {
used: 1000,
available: 9000,
resets: '2019-10-01 00:00:00',
},
workdays: 7,
};
mockRequest.get(`${basePath}&country=US&start=2019-07-01&end=2019-07-10`)
.reply(200, expectedResponse);
expect(await holidayapi.workdays({
country: 'US',
start: '2019-07-01',
end: '2019-07-10',
})).toStrictEqual(expectedResponse);
});
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&end=2019-07-10`)
.reply(429, expectedResponse);
try {
await holidayapi.workdays({ country: 'US', start: '2019-07-01', end: '2019-07-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&end=2019-07-10`).reply(500);
try {
await holidayapi.workdays({ country: 'US', start: '2019-07-01', end: '2019-07-10' });
} catch (err) {
expect(err.message).toMatch(/internal server error/i);
}
});
});
});
});

View file

@ -7,7 +7,8 @@
"strict": true,
"removeComments": true,
"noUnusedLocals": true,
"noUnusedParameters": true
"noUnusedParameters": true,
"useUnknownInCatchVariables": false
},
"include": [
"src/**/*"