unb-api 1.3.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,18 +1,21 @@
1
1
  # UnbelievaBoat API
2
2
 
3
- [![Discord](https://discordapp.com/api/guilds/305129477627969547/embed.png)](https://discord.gg/YMJ2dGp)
3
+ [![Discord](https://discordapp.com/api/guilds/305129477627969547/embed.png)](https://discord.gg/unb)
4
4
  [![npm](https://img.shields.io/npm/v/npm.svg)](https://www.npmjs.com/package/unb-api)
5
5
  [![npm downloads](https://img.shields.io/npm/dt/unb-api.svg?maxAge=3600)](https://www.npmjs.com/package/unb-api)
6
6
  [![Open Source Love](https://badges.frapsoft.com/os/mit/mit.svg?v=102)](https://github.com/ellerbrock/open-source-badge/)
7
7
  [![NPM](https://nodei.co/npm/unb-api.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/unb-api/)
8
8
 
9
9
  ## Installation
10
+
10
11
  `npm install unb-api`
11
12
 
12
13
  ## Documentation
14
+
13
15
  Full API documentation is located at [https://unbelievaboat.com/api/docs](https://unbelievaboat.com/api/docs)
14
16
 
15
17
  ### Example
18
+
16
19
  ```javascript
17
20
  const { Client } = require('unb-api');
18
21
  const client = new Client('TOKEN'); // Get your API token from https://unbelievaboat.com/api/docs
@@ -25,84 +28,165 @@ client.getGuildLeaderboard(guildID, { sort: 'cash' }).then(lb => console.log(lb)
25
28
  ```
26
29
 
27
30
  ### Constructor
31
+
28
32
  `new Client(token, [options])`
29
33
 
30
34
  ##### Options
31
- Type | Property | Description
32
- ------- | --------- | -----------
33
- String | `baseURL?` | API hostname. Defaults to https://unbelievaboat.com/api
34
- Number | `version?` | API version. Defaults to the latest version
35
- Number | `maxRetries?` | Maximum number of times to retry a request if it's ratelimited. Defaults to 3
36
35
 
36
+ | Type | Property | Description |
37
+ |--------|---------------|--------------------------------------------------------------------------------|
38
+ | String | `baseURL?` | API hostname. Defaults to https://unbelievaboat.com/api |
39
+ | Number | `version?` | API version. Defaults to the latest version |
40
+ | Number | `maxRetries?` | Maximum number of times to retry a request if it's rate-limited. Defaults to 3 |
37
41
 
38
42
  ### Methods
39
- ```
40
- getUserBalance(guild_id, user_id)
41
- ```
43
+
44
+ #### Economy
45
+
46
+ `getUserBalance(guildId: string, userId: string)`
42
47
  Returns: `Promise<User>`
43
- #
44
- ```
45
- setUserBalance(guild_id, user_id, { cash, bank }, reason)
46
- ```
48
+
49
+ `setUserBalance(guildId: string, userId: string, balance: Balance, reason?: string)`
47
50
  Returns: `Promise<User>`
48
- #
49
- ```
50
- editUserBalance(guild_id, user_id, { cash, bank }, reason)
51
- ```
51
+
52
+ `editUserBalance(guildId: string, userId: string, balance: Balance, reason?: string)`
52
53
  Returns: `Promise<User>`
53
- #
54
- ```
55
- getGuildLeaderboard(guild_id, [query])
56
- ```
57
54
 
58
- ##### JSON Query Parameters
59
- Type | Property | Description
60
- ------- | --------- | -----------
61
- String | `sort?` | Sort the leaderboard by `cash`, `bank` or `total`. Default is `total`.
62
- Number | `limit?` | Limit the number of users returned. Default is no limit, unless a page is given then it's 1000.
63
- Number | `offset?` | Specify the offset of the first user.
64
- Number | `page?` | Specify the page. If a page is given, the response will be `Promise<{ users: User[], totalPages: Number }>`
55
+ `getGuildLeaderboard(guildId: string, query?: ListOptions)`
56
+ Returns: `Promise<User[]> | Promise<{ users: User[], totalPages: number }>`
57
+ > [!NOTE]
58
+ > If the query for `getGuildLeaderboard` includes a page parameter, the response will be an object containing both users and totalPages.
59
+ > Otherwise, it will return a simple array of User[].
65
60
 
66
- Returns: `Promise<User[]>`
67
- #
68
- ```
69
- getGuild(guild_id)
70
- ```
61
+ #### Generic
62
+
63
+ `getGuild(guildId: string)`
71
64
  Returns: `Promise<Guild>`
72
- #
73
- ```
74
- getApplicationPermission(guild_id)
75
- ```
65
+
66
+ `getApplicationPermission(guildId: string)`
76
67
  Returns: `Promise<Permission>`
77
68
 
69
+ #### Items
70
+
71
+ `getItems(guildId: string, options?: ListOptions)`
72
+ Returns: `Promise<{ page: number, totalPages: number, items: StoreItem[] }>`
73
+
74
+ `getItem(guildId: string, itemId: string)`
75
+ Returns: `Promise<StoreItem>`
76
+
77
+ `createItem(guildId: string)`
78
+ Returns: `Promise<StoreItem>`
79
+
80
+ `editItem(guildId: string, itemId: string, data: StoreItem | object, options?: { cascadeUpdate?: boolean })`
81
+ Returns: `Promise<StoreItem>`
82
+
83
+ `deleteItem(guildId: string, itemId: string)`
84
+ Returns: `Promise<any>`
85
+
86
+ `getInventoryItems(guildId: string, userId: string, options?: ListOptions)`
87
+ Returns: `Promise<{ page: number, totalPages: number, items: InventoryItem[] }>`
88
+
89
+ `getInventoryItem(guildId: string, userId: string, itemId: string)`
90
+ Returns: `Promise<InventoryItem>`
91
+
92
+ `addInventoryItem(guildId: string, userId: string, itemId: string, quantity?: string | number, options?: { inventoryUserId?: string })`
93
+ Returns: `Promise<InventoryItem>`
94
+
95
+ `removeInventoryItem(guildId: string, userId: string, itemId: string, quantity?: string | number)`
96
+ Returns: `Promise<any>`
97
+
98
+ ##### ListOptions Parameters
99
+
100
+ | Type | Property |
101
+ |----------|-----------|
102
+ | String[] | `sort?` |
103
+ | Number | `limit?` |
104
+ | Number | `offset?` |
105
+ | Number | `page?` |
106
+
78
107
  ### Structures
108
+
79
109
  #### User
80
- Type | Property | Description
81
- --- | --- | ---
82
- Number | `rank?` | Leaderboard rank of the user. This is only present on getUserBalance() and getGuildLeaderboard()
83
- String | `user_id` | User ID of the discord user.
84
- Number | `cash` | User's cash balance.
85
- Number | `bank` | User's bank balance.
86
- Number | `total` | User's total balance.
87
-
110
+
111
+ | Type | Property | Description |
112
+ |--------|-----------|--------------------------------------------------------------------------------------------------|
113
+ | Number | `rank?` | Leaderboard rank of the user. This is only present on getUserBalance() and getGuildLeaderboard() |
114
+ | String | `user_id` | User ID of the discord user. |
115
+ | Number | `cash` | User's cash balance. |
116
+ | Number | `bank` | User's bank balance. |
117
+ | Number | `total` | User's total balance. |
118
+
88
119
  #### Guild
89
- Type | Property | Description
90
- --- | --- | ---
91
- String | `id` | Guild ID.
92
- String | `name` | Guild name.
93
- Number | `icon` | Icon hash.
94
- Number | `iconURL` | Icon url.
95
- String | `ownerID` | User ID of the owner.
96
- Number | `memberCount` | Total number of members.
97
- String | `currencySymbol` | Currency symbol
98
120
 
121
+ | Type | Property | Description |
122
+ |--------|------------------|--------------------------|
123
+ | String | `id` | Guild ID. |
124
+ | String | `name` | Guild name. |
125
+ | Number | `icon` | Icon hash. |
126
+ | Number | `iconURL` | Icon url. |
127
+ | String | `ownerID` | User ID of the owner. |
128
+ | Number | `memberCount` | Total number of members. |
129
+ | String | `currencySymbol` | Currency symbol |
130
+
131
+ #### StoreItem
132
+
133
+ | Type | Property | Description |
134
+ |---------|------------------|---------------------------------------------------------------------------|
135
+ | String | `id` | Unique ID of the item in the store. |
136
+ | String | `name` | Name of the item. |
137
+ | String | `description?` | Description of the item. |
138
+ | Number | `price` | Price of the item. |
139
+ | Number | `stockRemaining` | Amount of stock remaining for the item. |
140
+ | Boolean | `unlimitedStock` | Whether the item has unlimited stock. |
141
+ | Boolean | `isInventory` | Indicates if the item will be added to a user’s inventory when purchased. |
142
+ | Date | `expiresAt?` | Expiration date of the item. |
143
+ | Boolean | `isUsable` | Whether the item can be used by the user. |
144
+ | Boolean | `isSellable` | Whether the item can be sold by the user. |
145
+ | Array | `requirements` | Requirements needed to purchase the item. |
146
+ | Array | `actions` | Actions performed when the item is used. |
147
+ | String | `emojiUnicode?` | Unicode emoji associated with the item. |
148
+ | String | `emojiId?` | Custom emoji ID associated with the item. |
149
+
150
+ #### InventoryItem
151
+
152
+ | Type | Property | Description |
153
+ |-------------------------------------------------|-----------------|-----------------------------------------------|
154
+ | String | `itemId` | Unique ID of the item in the inventory. |
155
+ | Number | `quantity` | Quantity of the item in the user's inventory. |
156
+ | String | `name` | Name of the item. |
157
+ | String | `description?` | Description of the item. |
158
+ | Boolean | `isUsable` | Whether the item can be used by the user. |
159
+ | Boolean | `isSellable` | Whether the item can be sold by the user. |
160
+ | [StoreItemRequirement[]](#StoreItemRequirement) | `requirements` | Requirements needed to use the item. |
161
+ | [StoreItemAction[]](#StoreItemAction) | `actions` | Actions performed when the item is used. |
162
+ | String | `emojiUnicode?` | Unicode emoji associated with the item. |
163
+ | String | `emojiId?` | Custom emoji ID associated with the item. |
164
+
165
+ #### StoreItemRequirement
166
+
167
+ | Type | Property | Description |
168
+ |-------------------------------|-------------|---------------------------------------------------------------------------------------|
169
+ | StoreItemRequirementType | `type` | Type of requirement. Could be one of `ROLE`, `TOTAL_BALANCE`, or `ITEM`. |
170
+ | StoreItemRequirementMatchType | `matchType` | Specifies how the requirement must be met. Can be `EVERY`, `AT_LEAST_ONE`, or `NONE`. |
171
+ | String[] | `ids` | An array of IDs that correspond to roles, items, or other required entities. |
172
+ | Number | `balance` | Minimum balance required (applicable if `type` is `TOTAL_BALANCE`). |
173
+
174
+ #### StoreItemAction
175
+
176
+ | Type | Property | Description |
177
+ |---------------------|------------|------------------------------------------------------------------------------------------------------------------------------------------|
178
+ | StoreItemActionType | `type` | Type of action. Could be one of `RESPOND`, `ADD_ROLES`, `REMOVE_ROLES`, `ADD_BALANCE`, `REMOVE_BALANCE`, `ADD_ITEMS`, or `REMOVE_ITEMS`. |
179
+ | String[] | `ids` | An array of IDs corresponding to roles, items, or other entities involved in the action. |
180
+ | Object | `message?` | Discord message object, only applicable if `type` is `RESPOND`. |
181
+ | Number | `balance?` | Balance amount to be added or removed (applicable if `type` is `ADD_BALANCE` or `REMOVE_BALANCE`). |
99
182
 
100
183
  #### Permission
101
- Type | Property | Description
102
- --- | --- | ---
103
- Number | `allow` | The allowed bitwise permissions number.
104
- String | `json` | JSON representation of the allowed and denied permissions.
105
184
 
185
+ | Type | Property | Description |
186
+ |--------|----------|------------------------------------------------------------|
187
+ | Number | `allow` | The allowed bitwise permissions number. |
188
+ | String | `json` | JSON representation of the allowed and denied permissions. |
106
189
 
107
190
  ## Support
108
- [**Discord Server**](https://discord.gg/YMJ2dGp)
191
+
192
+ [**Discord Server**](https://discord.gg/unb)
package/index.d.ts CHANGED
@@ -15,23 +15,25 @@ declare module 'unb-api' {
15
15
 
16
16
  public editUserBalance(guildId: string, userId: string, balance: Balance, reason?: string): Promise<User>;
17
17
 
18
- public getGuildLeaderboard(guildId: string, query?: object): Promise<User[]> | Promise<{ users: User[], totalPages: number }>;
18
+ public getGuildLeaderboard(guildId: string, query?: ListOptions): Promise<User[]> | Promise<{ users: User[], totalPages: number }>;
19
19
 
20
20
  public getGuild(guildId: string): Promise<Guild>;
21
21
 
22
22
  public getApplicationPermission(guildId: string): Promise<Permission>;
23
23
 
24
24
 
25
- public getItems(guildId: string, options?: ItemListOptions): Promise<{ page: number, totalPages: number, items: StoreItem[] }>
25
+ public getItems(guildId: string, options?: ListOptions): Promise<{ page: number, totalPages: number, items: StoreItem[] }>
26
26
 
27
27
  public getItem(guildId: string, itemId: string): Promise<StoreItem>
28
+
29
+ public createItem(guildId: string): Promise<StoreItem>
28
30
 
29
31
  public editItem(guildId: string, itemId: string, data: StoreItem | object, options?: { cascadeUpdate?: boolean }): Promise<StoreItem>
30
32
 
31
33
  public deleteItem(guildId: string, itemId: string): Promise<any>
32
34
 
33
35
 
34
- public getInventoryItems(guildId: string, userId: string, options?: ItemListOptions): Promise<{ page: number, totalPages: number, items: InventoryItem[] }>
36
+ public getInventoryItems(guildId: string, userId: string, options?: ListOptions): Promise<{ page: number, totalPages: number, items: InventoryItem[] }>
35
37
 
36
38
  public getInventoryItem(guildId: string, userId: string, itemId: string): Promise<InventoryItem>
37
39
 
@@ -157,10 +159,10 @@ declare module 'unb-api' {
157
159
  balance: number;
158
160
  }
159
161
 
160
- export type ItemListOptions = {
161
- limit: number;
162
- page: number;
163
- sort: string[]
164
- query: string;
162
+ export type ListOptions = {
163
+ limit?: number;
164
+ page?: number;
165
+ sort?: string[] | string
166
+ query?: string;
165
167
  }
166
168
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unb-api",
3
- "version": "1.3.2",
3
+ "version": "1.4.0",
4
4
  "description": "API wrapper for UnbelievaBoat Discord Bot API",
5
5
  "main": "src/index.js",
6
6
  "types": "index.d.ts",
@@ -22,6 +22,6 @@
22
22
  "typescript": "^2.6.2"
23
23
  },
24
24
  "dependencies": {
25
- "axios": "^0.21.1"
25
+ "axios": "1.6.0"
26
26
  }
27
27
  }
package/src/lib/Client.js CHANGED
@@ -221,6 +221,23 @@ class Client {
221
221
  .then(data => new StoreItem(data));
222
222
  }
223
223
 
224
+ /**
225
+ * Create an item
226
+ *
227
+ * @public
228
+ * @param {string} guildId - Guild ID
229
+ * @param {StoreItem|object} data - Item properties to create the item with
230
+ * @returns {Promise<StoreItem>}
231
+ *
232
+ * @see {@link https://unbelievaboat-api.readme.io/reference/create-store-item|Create Store Item}
233
+ */
234
+ createItem(guildId, data) {
235
+ if (typeof guildId !== 'string') throw new TypeError('guildId must be a string');
236
+
237
+ return this._request('POST', `guilds/${guildId}/items`, toSnakeCaseDeep(data))
238
+ .then(data => new StoreItem(data));
239
+ }
240
+
224
241
  /**
225
242
  * Edit an item
226
243
  *
@@ -231,6 +248,8 @@ class Client {
231
248
  * @param [options]
232
249
  * @param [options.cascadeUpdate=false] - Inventory items will also be updated if set to true
233
250
  * @returns {Promise<StoreItem>}
251
+ *
252
+ * @see {@link https://unbelievaboat-api.readme.io/reference/edit-store-item|Edit Store Item}
234
253
  */
235
254
  editItem(guildId, itemId, data, options) {
236
255
  if (typeof guildId !== 'string') throw new TypeError('guildId must be a string');
@@ -248,6 +267,8 @@ class Client {
248
267
  * @param {object} [options]
249
268
  * @param {boolean} [options.cascadeDelete=false] - Inventory items will also be deleted if set to true
250
269
  * @returns {Promise<*>}
270
+ *
271
+ * @see {@link https://unbelievaboat-api.readme.io/reference/delete-store-item|Delete Store Item}
251
272
  */
252
273
  deleteItem(guildId, itemId, options) {
253
274
  if (typeof guildId !== 'string') throw new TypeError('guildId must be a string');
@@ -268,6 +289,8 @@ class Client {
268
289
  * @param {number} [options.limit=100] - Maximum number of items to return
269
290
  * @param {string} [options.query] - Search for an item by name
270
291
  * @returns {Promise<{totalPages: number, page: number, items: InventoryItem[]}>}
292
+ *
293
+ * @see {@link https://unbelievaboat-api.readme.io/reference/get-user-inventory-items|Get Inventory Items}
271
294
  */
272
295
  getInventoryItems(guildId, userId, options) {
273
296
  if (typeof guildId !== 'string') throw new TypeError('guildId must be a string');
@@ -290,7 +313,9 @@ class Client {
290
313
  * @param {string} guildId - Guild ID
291
314
  * @param {string} userId - User ID
292
315
  * @param {string} itemId - Item ID
293
- * @returns {Promise<{totalPages: number, page: number, items: InventoryItem[]}>}
316
+ * @returns {Promise<InventoryItem>}
317
+ *
318
+ * @see {@link https://unbelievaboat-api.readme.io/reference/get-inventory-item|Get Inventory Item}
294
319
  */
295
320
  getInventoryItem(guildId, userId, itemId) {
296
321
  if (typeof guildId !== 'string') throw new TypeError('guildId must be a string');
@@ -312,6 +337,8 @@ class Client {
312
337
  * @param {object} [options]
313
338
  * @param {string} [options.inventoryUserId] - User ID of the inventory to add the item from (can be used if the item no longer exists in the store or the item settings differ)
314
339
  * @returns {Promise<InventoryItem>}
340
+ *
341
+ * @see {@link https://unbelievaboat-api.readme.io/reference/add-inventory-item|Add Inventory Item}
315
342
  */
316
343
  addInventoryItem(guildId, userId, itemId, quantity, options) {
317
344
  if (typeof guildId !== 'string') throw new TypeError('guildId must be a string');
@@ -331,6 +358,8 @@ class Client {
331
358
  * @param {string} itemId - Item ID
332
359
  * @param {number|string} [quantity=1] - Quantity to remove
333
360
  * @returns {Promise<*>}
361
+ *
362
+ * @see {@link https://unbelievaboat-api.readme.io/reference/remove-inventory-item|Remove Inventory Item}
334
363
  */
335
364
  removeInventoryItem(guildId, userId, itemId, quantity) {
336
365
  if (typeof guildId !== 'string') throw new TypeError('guildId must be a string');
@@ -7,85 +7,86 @@ class RequestHandler {
7
7
  constructor(client) {
8
8
  this._client = client;
9
9
  this.ratelimits = {};
10
+ this.axiosInstance = axios.create({
11
+ baseURL: this._client.baseURL,
12
+ headers: {
13
+ 'Authorization': this._client.token,
14
+ 'Content-Type': 'application/json'
15
+ }
16
+ });
10
17
  }
11
18
 
12
- request(method, endpoint, data = {}, query = {}, _attempts = 0) {
19
+ async request(method, endpoint, data = {}, params = {}, _attempts = 0) {
13
20
  const route = this.getRoute(method, endpoint);
14
21
  if (!this.ratelimits[route]) {
15
22
  this.ratelimits[route] = new Bucket();
16
23
  }
17
24
 
18
25
  return new Promise((resolve, reject) => {
19
- const fn = (callback) => {
26
+ const fn = async (callback) => {
20
27
  const options = {
21
- validateStatus: null,
22
- headers: {
23
- 'Authorization': this._client.token,
24
- 'Content-Type': 'application/json'
25
- },
26
- baseURL: this._client.baseURL,
28
+ method,
27
29
  url: `/${this._client.version ? `${this._client.version}/` : ''}${endpoint}`,
28
- method: method,
29
- data: data,
30
- params: query
30
+ data,
31
+ params,
31
32
  };
32
33
 
33
- axios.request(options)
34
- .then(res => {
35
- // Increase the number of attempts
36
- ++_attempts;
37
-
38
- // Add the rate limit header data to the bucket
39
- this.parseRateLimitHeaders(route, res.headers);
40
-
41
- // Reject with an APIError or HTTPError
42
- const rejectWithError = () => {
43
- if (res.data && res.data.error) {
44
- reject(new APIError(res));
45
- } else {
46
- reject(new HTTPError(res));
47
- }
48
- };
49
-
50
- const retryRequest = () => {
51
- // Use the retry-after header to schedule the request to retry
52
- if (res.headers['retry-after']) {
53
- setTimeout(() => {
54
- this.request(method, endpoint, data, query, _attempts)
55
- .then(resolve)
56
- .catch(reject);
57
- }, +res.headers['retry-after']);
58
- } else {
59
- // Retry immediately if no retry-after header
60
- this.request(method, endpoint, data, query, _attempts)
61
- .then(resolve)
62
- .catch(reject);
63
- }
64
- };
65
-
66
- if (res.status >= 200 && res.status < 300) {
67
- resolve(res.data);
68
- } else if (res.status === 429) {
69
- // Check if too many retry attempts
70
- if (_attempts >= this._client.maxRetries) {
71
- rejectWithError();
72
- } else {
73
- retryRequest();
74
- }
34
+ try {
35
+ const { data: responseData, status, headers } = await this.axiosInstance(options);
36
+
37
+ // Increase the number of attempts
38
+ ++_attempts;
39
+
40
+ // Add the rate limit header data to the bucket
41
+ this.parseRateLimitHeaders(route, headers);
42
+
43
+ if (status >= 200 && status < 300) {
44
+ resolve(responseData);
45
+ } else if (status === 429) {
46
+ if (_attempts >= this._client.maxRetries) {
47
+ this.rejectWithError(options, responseData, status, reject);
75
48
  } else {
76
- rejectWithError();
49
+ this.retryRequest(method, endpoint, data, params, _attempts, headers, resolve, reject);
77
50
  }
78
-
79
- callback();
80
- });
51
+ } else {
52
+ this.rejectWithError(options, responseData, status, reject);
53
+ }
54
+
55
+ callback();
56
+ } catch (error) {
57
+ if (axios.isAxiosError(error)) {
58
+ this.rejectWithError(options, error.response.data, error.response.status, reject);
59
+ } else {
60
+ reject(error);
61
+ }
62
+ callback();
63
+ }
81
64
  };
82
65
 
83
66
  this.ratelimits[route].queue(fn);
84
67
  });
85
68
  }
86
69
 
70
+ rejectWithError(options, data, status, reject) {
71
+ if (data && data.message) {
72
+ reject(new APIError(options, { data, status }));
73
+ } else {
74
+ reject(new HTTPError(options, { data, status }));
75
+ }
76
+ }
77
+
78
+ retryRequest(method, endpoint, data, params, _attempts, headers, resolve, reject) {
79
+ const retryAfter = headers['retry-after'] ? +headers['retry-after'] : 0;
80
+
81
+ setTimeout(() => {
82
+ this.request(method, endpoint, data, params, _attempts)
83
+ .then(resolve)
84
+ .catch(reject);
85
+ }, retryAfter);
86
+ }
87
+
87
88
  getRoute(method, endpoint) {
88
- const route = endpoint.replace(/\/([a-z-]+)\/(?:(\d+))/g, (match, p) => {
89
+ const route = endpoint.replace(/\/([a-z-]+)\/(\d+)/g, (match, p) => {
89
90
  return MajorParams.includes(p) ? match : `/${p}/:id`;
90
91
  });
91
92
  return `${method}/${route}`;
@@ -111,7 +112,6 @@ class RequestHandler {
111
112
  } else {
112
113
  this.ratelimits[route].reset = now;
113
114
  }
114
-
115
115
  }
116
116
  }
117
117
 
@@ -1,15 +1,10 @@
1
- class APIError extends Error {
2
- constructor(response) {
3
- super();
4
- this.name = this.constructor.name;
5
- this.message = response.data.error || response.data.message || 'Unknown error';
6
- this.status = response.status;
7
- this.errors = response.data.errors || null;
1
+ const HTTPError = require('./HTTPError');
8
2
 
9
- Object.defineProperty(this, 'response', {
10
- enumerable: true,
11
- value: response
12
- });
3
+ class APIError extends HTTPError {
4
+ constructor(request, response) {
5
+ super(request, response);
6
+ this.message = response.data.message || response.data.error || response.statusText || 'Unknown error';
7
+ this.errors = response.data.errors || null;
13
8
  }
14
9
  }
15
10
 
@@ -1,10 +1,15 @@
1
1
  class HTTPError extends Error {
2
- constructor(response) {
2
+ constructor(request, response) {
3
3
  super();
4
4
  this.name = this.constructor.name;
5
5
  this.message = response.statusText || 'Unknown error';
6
6
  this.status = response.status;
7
7
 
8
+ Object.defineProperty(this, 'request', {
9
+ enumerable: true,
10
+ value: request
11
+ });
12
+
8
13
  Object.defineProperty(this, 'response', {
9
14
  enumerable: true,
10
15
  value: response