Skip to content

Commit d2d3c26

Browse files
authored
Merge pull request #28 from hackmdio/release/2.4.0
Release 2.4.0
2 parents aac5c1b + a93e0bc commit d2d3c26

File tree

5 files changed

+108
-29
lines changed

5 files changed

+108
-29
lines changed

nodejs/package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nodejs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@hackmd/api",
3-
"version": "2.3.0",
3+
"version": "2.4.0",
44
"description": "HackMD Node.js API Client",
55
"main": "dist/index.js",
66
"declaration": "./dist/index.d.ts",

nodejs/src/error.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,23 @@ class HttpResponseError extends HackMDError {
1818

1919
class MissingRequiredArgument extends HackMDError {}
2020
class InternalServerError extends HttpResponseError {}
21-
21+
class TooManyRequestsError extends HttpResponseError {
22+
public constructor (
23+
message: string,
24+
readonly code: number,
25+
readonly statusText: string,
26+
readonly userLimit: number,
27+
readonly userRemaining: number,
28+
readonly resetAfter?: number,
29+
) {
30+
super(message, code, statusText)
31+
}
32+
}
2233

2334
export {
2435
HackMDError,
2536
HttpResponseError,
2637
MissingRequiredArgument,
27-
InternalServerError
38+
InternalServerError,
39+
TooManyRequestsError,
2840
}

nodejs/src/index.ts

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,14 @@ const defaultOption: RequestOptions = {
1212

1313
type OptionReturnType<Opt, T> = Opt extends { unwrapData: false } ? AxiosResponse<T> : Opt extends { unwrapData: true } ? T : T
1414

15+
export type APIClientOptions = {
16+
wrapResponseErrors: boolean
17+
}
18+
1519
export class API {
1620
private axios: AxiosInstance
1721

18-
constructor (readonly accessToken: string, public hackmdAPIEndpointURL: string = "https://api.hackmd.io/v1") {
22+
constructor (readonly accessToken: string, public hackmdAPIEndpointURL: string = "https://api.hackmd.io/v1", public options: APIClientOptions = { wrapResponseErrors: true }) {
1923
if (!accessToken) {
2024
throw new HackMDErrors.MissingRequiredArgument('Missing access token when creating HackMD client')
2125
}
@@ -37,30 +41,41 @@ export class API {
3741
}
3842
)
3943

40-
this.axios.interceptors.response.use(
41-
(response: AxiosResponse) => {
42-
return response
43-
},
44-
async (err: AxiosError) => {
45-
if (!err.response) {
46-
return Promise.reject(err)
47-
}
48-
49-
if (err.response.status >= 500) {
50-
throw new HackMDErrors.InternalServerError(
51-
`HackMD internal error (${err.response.status} ${err.response.statusText})`,
52-
err.response.status,
53-
err.response.statusText,
54-
)
55-
} else {
56-
throw new HackMDErrors.HttpResponseError(
57-
`Received an error response (${err.response.status} ${err.response.statusText}) from HackMD`,
58-
err.response.status,
59-
err.response.statusText,
60-
)
44+
if (options.wrapResponseErrors) {
45+
this.axios.interceptors.response.use(
46+
(response: AxiosResponse) => {
47+
return response
48+
},
49+
async (err: AxiosError) => {
50+
if (!err.response) {
51+
return Promise.reject(err)
52+
}
53+
54+
if (err.response.status >= 500) {
55+
throw new HackMDErrors.InternalServerError(
56+
`HackMD internal error (${err.response.status} ${err.response.statusText})`,
57+
err.response.status,
58+
err.response.statusText,
59+
)
60+
} else if (err.response.status === 429) {
61+
throw new HackMDErrors.TooManyRequestsError(
62+
`Too many requests (${err.response.status} ${err.response.statusText})`,
63+
err.response.status,
64+
err.response.statusText,
65+
parseInt(err.response.headers['x-ratelimit-userlimit'], 10),
66+
parseInt(err.response.headers['x-ratelimit-userremaining'], 10),
67+
parseInt(err.response.headers['x-ratelimit-userreset'], 10),
68+
)
69+
} else {
70+
throw new HackMDErrors.HttpResponseError(
71+
`Received an error response (${err.response.status} ${err.response.statusText}) from HackMD`,
72+
err.response.status,
73+
err.response.statusText,
74+
)
75+
}
6176
}
62-
}
63-
)
77+
)
78+
}
6479
}
6580

6681
async getMe<Opt extends RequestOptions> (options = defaultOption as Opt): Promise<OptionReturnType<Opt, GetMe>> {

nodejs/tests/api.spec.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { server } from './mock'
22
import { API } from '../src'
3+
import { rest } from 'msw'
4+
import { TooManyRequestsError } from '../src/error'
35

46
let client: API
57

@@ -34,3 +36,53 @@ test('getMe unwrapped', async () => {
3436
expect(response).toHaveProperty('userPath')
3537
expect(response).toHaveProperty('photo')
3638
})
39+
40+
test('should throw axios error object if set wrapResponseErrors to false', async () => {
41+
const customCilent = new API(process.env.HACKMD_ACCESS_TOKEN!, undefined, {
42+
wrapResponseErrors: false,
43+
})
44+
45+
server.use(
46+
rest.get('https://api.hackmd.io/v1/me', (req, res, ctx) => {
47+
return res(ctx.status(429))
48+
}),
49+
)
50+
51+
try {
52+
await customCilent.getMe()
53+
} catch (error: any) {
54+
expect(error).toHaveProperty('response')
55+
expect(error.response).toHaveProperty('status', 429)
56+
}
57+
})
58+
59+
test.only('should throw HackMD error object', async () => {
60+
server.use(
61+
rest.get('https://api.hackmd.io/v1/me', (req, res, ctx) => {
62+
return res(
63+
ctx.status(429),
64+
ctx.set({
65+
'X-RateLimit-UserLimit': '100',
66+
'x-RateLimit-UserRemaining': '0',
67+
'x-RateLimit-UserReset': String(
68+
new Date().getTime() + 1000 * 60 * 60 * 24,
69+
),
70+
}),
71+
)
72+
}),
73+
)
74+
75+
try {
76+
await client.getMe()
77+
} catch (error: any) {
78+
expect(error).toBeInstanceOf(TooManyRequestsError)
79+
80+
console.log(JSON.stringify(error))
81+
82+
expect(error).toHaveProperty('code', 429)
83+
expect(error).toHaveProperty('statusText', 'Too Many Requests')
84+
expect(error).toHaveProperty('userLimit', 100)
85+
expect(error).toHaveProperty('userRemaining', 0)
86+
expect(error).toHaveProperty('resetAfter')
87+
}
88+
})

0 commit comments

Comments
 (0)