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.
This commit is contained in:
Josh Sherman 2022-04-05 17:39:24 -05:00
parent c3de7ff9bd
commit 7de5339090
No known key found for this signature in database
GPG key ID: 55B058A80530EF22
7 changed files with 3512 additions and 6265 deletions

View file

@ -6,7 +6,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: ['12', '14', '16']
node-version: ['14', '16']
steps:
- name: Checkout Code
uses: actions/checkout@v2

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 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

9346
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
{
"name": "holidayapi",
"version": "5.1.0",
"version": "6.0.0",
"description": "Official Node.js library for Holiday API",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@ -21,26 +21,26 @@
},
"homepage": "https://holidayapi.com",
"engines": {
"node": ">= 12.0.0"
"node": ">= 14.0.0"
},
"devDependencies": {
"@types/jest": "^26.0.19",
"@types/jest": "^27.4.1",
"@types/nock": "^11.1.0",
"@types/node": "^15.12.2",
"@types/node-fetch": "^2.5.7",
"@typescript-eslint/eslint-plugin": "^4.11.1",
"@typescript-eslint/parser": "^4.11.1",
"eslint": "^7.16.0",
"eslint-config-airbnb-base": "^14.2.1",
"eslint-plugin-import": "^2.22.1",
"eslint-plugin-jest": "^24.1.3",
"jest": "^27.0.4",
"nock": "^13.0.5",
"ts-jest": "^27.0.3",
"typescript": "^4.1.3"
"@types/node": "^17.0.23",
"@types/node-fetch": "^2.6.1",
"@typescript-eslint/eslint-plugin": "^5.18.0",
"@typescript-eslint/parser": "^5.18.0",
"eslint": "^8.12.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "^26.1.3",
"jest": "^27.5.1",
"nock": "^13.2.4",
"ts-jest": "^27.1.4",
"typescript": "^4.6.3"
},
"dependencies": {
"node-fetch": "^2.6.1"
"node-fetch": "^2"
},
"scripts": {
"build": "tsc",

View file

@ -56,7 +56,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();
@ -76,14 +76,6 @@ export class HolidayAPI {
}
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');
}
return this.request('holidays', request);
}
@ -92,28 +84,10 @@ export class HolidayAPI {
}
async workday(request: WorkdayRequest = {}): Promise<WorkdayResponse> {
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);
}
async workdays(request: WorkdaysRequest = {}): Promise<WorkdaysResponse> {
if (!request.country) {
throw new Error('Missing country');
} else if (!request.start) {
throw new Error('Missing start date');
} else if (!request.end) {
throw new Error('Missing end date');
}
return this.request('workdays', request);
}
}

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,
@ -539,46 +293,6 @@ describe('holidayapi', () => {
})).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,
@ -632,36 +346,6 @@ describe('holidayapi', () => {
})).toStrictEqual(expectedResponse);
});
it('should error when country is missing', async () => {
expect.assertions(1);
try {
await holidayapi.workdays();
} catch (err) {
expect(err.message).toMatch(/missing country/i);
}
});
it('should error when start is missing', async () => {
expect.assertions(1);
try {
await holidayapi.workdays({ country: 'US' });
} catch (err) {
expect(err.message).toMatch(/missing start date/i);
}
});
it('should error when end is missing', async () => {
expect.assertions(1);
try {
await holidayapi.workdays({ country: 'US', start: '2019-07-01' });
} catch (err) {
expect(err.message).toMatch(/missing end date/i);
}
});
it('should raise 4xx errors', async () => {
const expectedResponse = {
status: 429,

View file

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