@beyonk/http 12.0.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/.github/workflows/publish.yml +60 -0
- package/README.MD +301 -0
- package/lib/api/_just-compose.js +12 -0
- package/lib/api/index.js +231 -0
- package/lib/api/index.spec.js +954 -0
- package/lib/entrypoint.js +19 -0
- package/lib/error-handlers/access-denied.js +12 -0
- package/lib/error-handlers/bad-data.js +12 -0
- package/lib/error-handlers/conflict.js +12 -0
- package/lib/error-handlers/default.js +8 -0
- package/lib/error-handlers/expectation-failed.js +12 -0
- package/lib/error-handlers/forbidden.js +12 -0
- package/lib/error-handlers/gone.js +12 -0
- package/lib/error-handlers/handle.js +25 -0
- package/lib/error-handlers/not-acceptable.js +12 -0
- package/lib/error-handlers/not-found.js +12 -0
- package/lib/error-handlers/payment-required.js +12 -0
- package/lib/error-handlers/precondition-failed.js +12 -0
- package/lib/error-handlers/too-many-requests.js +12 -0
- package/lib/errors.js +73 -0
- package/package.json +53 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- '*'
|
|
7
|
+
tags:
|
|
8
|
+
- 'v*'
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v3
|
|
15
|
+
with:
|
|
16
|
+
ref: master
|
|
17
|
+
|
|
18
|
+
- uses: volta-cli/action@v4
|
|
19
|
+
|
|
20
|
+
- name: Cache pnpm modules
|
|
21
|
+
uses: actions/cache@v2
|
|
22
|
+
with:
|
|
23
|
+
path: ~/.pnpm-store
|
|
24
|
+
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
25
|
+
restore-keys: |
|
|
26
|
+
${{ runner.os }}-
|
|
27
|
+
|
|
28
|
+
- uses: pnpm/action-setup@v2.2.4
|
|
29
|
+
with:
|
|
30
|
+
run_install: true
|
|
31
|
+
|
|
32
|
+
- run: pnpm lint
|
|
33
|
+
|
|
34
|
+
publish-npm:
|
|
35
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
36
|
+
needs: build
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
steps:
|
|
39
|
+
- uses: actions/checkout@v3
|
|
40
|
+
with:
|
|
41
|
+
ref: master
|
|
42
|
+
|
|
43
|
+
- uses: volta-cli/action@v4
|
|
44
|
+
|
|
45
|
+
- name: Authorize NPM
|
|
46
|
+
run: npm config set //registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}
|
|
47
|
+
|
|
48
|
+
- name: Cache pnpm modules
|
|
49
|
+
uses: actions/cache@v2
|
|
50
|
+
with:
|
|
51
|
+
path: ~/.pnpm-store
|
|
52
|
+
key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
53
|
+
restore-keys: |
|
|
54
|
+
${{ runner.os }}-
|
|
55
|
+
|
|
56
|
+
- uses: pnpm/action-setup@v2.2.4
|
|
57
|
+
with:
|
|
58
|
+
run_install: true
|
|
59
|
+
|
|
60
|
+
- run: pnpm publish
|
package/README.MD
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
<a href="https://beyonk.com">
|
|
2
|
+
<img src="https://user-images.githubusercontent.com/218949/144224348-1b3a20d5-d68e-4a7a-b6ac-6946f19f4a86.png" width="198" />
|
|
3
|
+
</a>
|
|
4
|
+
|
|
5
|
+
## Http
|
|
6
|
+
|
|
7
|
+
[](http://standardjs.com) 
|
|
8
|
+
|
|
9
|
+
Isomorphic fetch library.
|
|
10
|
+
<br />
|
|
11
|
+
Formerly known as `@beyonk/sapper-httpclient`
|
|
12
|
+
|
|
13
|
+
## Why
|
|
14
|
+
|
|
15
|
+
In hybrid applications, there are three different ways of fetching data:
|
|
16
|
+
|
|
17
|
+
* client
|
|
18
|
+
* server
|
|
19
|
+
* isomorphic (client + server)
|
|
20
|
+
* retries (on various network errors)
|
|
21
|
+
|
|
22
|
+
This library helps you abstract over where you are fetching data, meaning that your code maintains consistency without having to worry about where your data is being fetched.
|
|
23
|
+
|
|
24
|
+
The way it does this is by trying to use the first available fetch method, and failing over to alternatives if a method is not available. The methods it tries are, in the following order:
|
|
25
|
+
|
|
26
|
+
1. Any fetch library you pass to `create()` (for example, `load`'s `fetch`)
|
|
27
|
+
1. window.fetch if the library detects it is running clientside
|
|
28
|
+
1. `node-fetch`, or whatever you want to pass in, if nothing else is available (pure server-side)
|
|
29
|
+
|
|
30
|
+
Generally this means that your usage is the same no matter where you call it, with one exception - using this library in the `load` method requires you to pass in SvelteKit's special `fetch` method, as it is not available outside of the load method. Examples of which are below.
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
### To use within an application:
|
|
35
|
+
|
|
36
|
+
### Install it
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm i -D @beyonk/http
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Configure it (both server-side and client-side as there are two bundles)
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
// src/client.js && src/server.js
|
|
46
|
+
import Api from '@beyonk/http'
|
|
47
|
+
|
|
48
|
+
Api.configure({ baseUrl: 'https://example.com/your/api/base' })
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Use it on the client:
|
|
52
|
+
|
|
53
|
+
```js
|
|
54
|
+
// src/routes/some-route.html
|
|
55
|
+
import { create } from '@beyonk/http'
|
|
56
|
+
|
|
57
|
+
// in a method (client-side)
|
|
58
|
+
const api = create()
|
|
59
|
+
const json = await api.endpoint('some/endpoint').get()
|
|
60
|
+
console.log(json)
|
|
61
|
+
|
|
62
|
+
// in load (isomorphic)
|
|
63
|
+
const api = create()
|
|
64
|
+
const json = await api
|
|
65
|
+
.context({ fetch }) // Pass in the "fetch" parameter from load
|
|
66
|
+
.endpoint('some/endpoint')
|
|
67
|
+
.get()
|
|
68
|
+
console.log(json)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Use it on the server:
|
|
72
|
+
|
|
73
|
+
```js
|
|
74
|
+
// src/routes/+page.server.js
|
|
75
|
+
import fetch from 'node-fetch' // or SvelteKit's built in fetch
|
|
76
|
+
import { create } from '@beyonk/http'
|
|
77
|
+
|
|
78
|
+
const api = create()
|
|
79
|
+
const json = await api
|
|
80
|
+
.context({ fetch }) // pass node fetch in here.
|
|
81
|
+
.endpoint('some/endpoint')
|
|
82
|
+
.get()
|
|
83
|
+
console.log(json)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Handling the response
|
|
87
|
+
|
|
88
|
+
```js
|
|
89
|
+
import { create } from '@beyonk/http'
|
|
90
|
+
|
|
91
|
+
const api = create()
|
|
92
|
+
const json = await api
|
|
93
|
+
.endpoint('some/endpoint')
|
|
94
|
+
.get((json, httpStatus) => {
|
|
95
|
+
console.log('json response is', json)
|
|
96
|
+
console.log('http status code is', httpStatus)
|
|
97
|
+
})
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Methods
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
const api = create()
|
|
104
|
+
const client = api
|
|
105
|
+
.endpoint('some/endpoint')
|
|
106
|
+
|
|
107
|
+
console.log(await client.get()) // Get endpoint
|
|
108
|
+
console.log(await client.payload({ foo: 'bar' }).put()) // Put with body
|
|
109
|
+
console.log(await client.payload({ foo: 'bar' }).post()) // Post with body
|
|
110
|
+
console.log(await client.query({ foo: 'bar' }).get()) // Get with query
|
|
111
|
+
console.log(await client.del()) // Delete
|
|
112
|
+
console.log(await client.headers({ foo: 'bar' }).put()) // Put with headers
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### client.query
|
|
116
|
+
|
|
117
|
+
The `query` method accepts an object of params as either a String or Array of Strings.
|
|
118
|
+
If any property passed into the query is `undefined` it will be ignored.
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
const api = create()
|
|
122
|
+
const client = api
|
|
123
|
+
.endpoint('some/endpoint')
|
|
124
|
+
|
|
125
|
+
console.log(await client.query({ foo: 'bar' }).get()) // will make a GET request to 'some/endpoint?foo=bar'
|
|
126
|
+
console.log(await client.query({ foo: 'bar', baz: 'qux' }).get()) // will make a GET request to 'some/endpoint?foo=bar&baz=qux
|
|
127
|
+
console.log(await client.query({ foo: ['bar', 'qux' ] }).get()) // will make a GET request to 'some/endpoint?foo=bar&foo=qux
|
|
128
|
+
console.log(await client.query({ foo: undefined, baz: 'qux' }).get()) // will make a GET request to 'some/endpoint?baz=qux
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Using built in response handling
|
|
133
|
+
|
|
134
|
+
```js
|
|
135
|
+
const api = create()
|
|
136
|
+
const profile = await api
|
|
137
|
+
.endpoint('some/endpoint')
|
|
138
|
+
.get(json => {
|
|
139
|
+
return json.profile
|
|
140
|
+
})
|
|
141
|
+
console.log(profile)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Catching errors
|
|
145
|
+
|
|
146
|
+
### Per request
|
|
147
|
+
|
|
148
|
+
If no local error handler is specified, the fallback handler `default` is called. If this isn't specified, the error is logged to the console.
|
|
149
|
+
|
|
150
|
+
```js
|
|
151
|
+
await client
|
|
152
|
+
.endpoint('some/url')
|
|
153
|
+
.forbidden(e => {
|
|
154
|
+
console.error('Forbidden', e)
|
|
155
|
+
})
|
|
156
|
+
.gone(e => {
|
|
157
|
+
console.error('Gone', e)
|
|
158
|
+
})
|
|
159
|
+
.notFound(e => {
|
|
160
|
+
console.error('Not found', e)
|
|
161
|
+
})
|
|
162
|
+
.accessDenied(e => {
|
|
163
|
+
console.error('Access denied', e)
|
|
164
|
+
})
|
|
165
|
+
.conflict(e => {
|
|
166
|
+
console.error('Conflict', e)
|
|
167
|
+
})
|
|
168
|
+
.paymentRequired(e => {
|
|
169
|
+
console.error('Payment Required', e)
|
|
170
|
+
})
|
|
171
|
+
.preconditionFailed(e => {
|
|
172
|
+
console.error('Precondition failed', e)
|
|
173
|
+
})
|
|
174
|
+
.badData(e => {
|
|
175
|
+
console.error('Bad data', e)
|
|
176
|
+
})
|
|
177
|
+
.default(e => {
|
|
178
|
+
// Any other error caught here
|
|
179
|
+
console.error('Some error', e)
|
|
180
|
+
})
|
|
181
|
+
.get()
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
#### Handler signature
|
|
185
|
+
|
|
186
|
+
Handlers have a signature with two items:
|
|
187
|
+
|
|
188
|
+
```js
|
|
189
|
+
.badData((e, ctx) => {
|
|
190
|
+
console.error('Bad data', e)
|
|
191
|
+
ctx.redirect('/foo/bar')
|
|
192
|
+
// or
|
|
193
|
+
ctx.error('/foo/bar')
|
|
194
|
+
})
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
ctx can be whatever you want really - it is whatever you pass in as `context(...)`.
|
|
198
|
+
|
|
199
|
+
However, if the `context` object you pass in has a `fetch` function, this is used as the `fetch` for XHR requests.
|
|
200
|
+
|
|
201
|
+
```js
|
|
202
|
+
export async function preload () {
|
|
203
|
+
await Api
|
|
204
|
+
.context(this) // fetch, redirect, error.
|
|
205
|
+
.endpoint('foo/bar')
|
|
206
|
+
...
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
you can also pass in other things to the context:
|
|
211
|
+
|
|
212
|
+
```js
|
|
213
|
+
export async function preload () {
|
|
214
|
+
await Api
|
|
215
|
+
.context({ ...this, baz: 'qux' })
|
|
216
|
+
.endpoint('foo/bar')
|
|
217
|
+
...
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### At a global level
|
|
222
|
+
|
|
223
|
+
Request local error handlers override global error handlers, but if a local error handler are not specified, these will be called instead, if it exists.
|
|
224
|
+
|
|
225
|
+
Names are the same as the local handlers:
|
|
226
|
+
|
|
227
|
+
```js
|
|
228
|
+
import Api from '@beyonk/http'
|
|
229
|
+
import { redirect } from '@sveltejs/kit'
|
|
230
|
+
|
|
231
|
+
Api.configure({
|
|
232
|
+
baseUrl: 'https://example.com/your/api/base',
|
|
233
|
+
handlers: {
|
|
234
|
+
paymentRequired(e => {
|
|
235
|
+
redirect(307, '/checkout')
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
await client
|
|
241
|
+
.endpoint('some/url')
|
|
242
|
+
.get()
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Retries
|
|
246
|
+
|
|
247
|
+
The http client can retry if a network error is encountered. The default is `retry: false`, and requests won't be retried.
|
|
248
|
+
|
|
249
|
+
Configure it as follows:
|
|
250
|
+
|
|
251
|
+
```js
|
|
252
|
+
import Api from '@beyonk/http'
|
|
253
|
+
|
|
254
|
+
Api.configure({
|
|
255
|
+
retry: {
|
|
256
|
+
attempts: 3 // How many times to retry before giving up
|
|
257
|
+
errors: [ 'ECONNRESET' ] // A list of error codes
|
|
258
|
+
}
|
|
259
|
+
})
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
errors is an array of any number of the [nodejs network error codes](https://nodejs.org/api/errors.html#errors_common_system_errors)
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
## Parsing error payloads
|
|
266
|
+
|
|
267
|
+
As of v7.0.0 the library defaults to parsing error payloads as JSON. This means you can use the data returned in your response.
|
|
268
|
+
|
|
269
|
+
```js
|
|
270
|
+
/** endpoint returns 401 with:
|
|
271
|
+
{
|
|
272
|
+
username: 'Naughty User'
|
|
273
|
+
}
|
|
274
|
+
**/
|
|
275
|
+
|
|
276
|
+
await client
|
|
277
|
+
.endpoint('some/url')
|
|
278
|
+
.accessDenied(e => {
|
|
279
|
+
console.error('You are not allowed', e.body.username)
|
|
280
|
+
})
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
To turn this behaviour off, pass the option `parseErrors` with value false:
|
|
284
|
+
|
|
285
|
+
```js
|
|
286
|
+
import Api from '@beyonk/http'
|
|
287
|
+
|
|
288
|
+
Api.configure({
|
|
289
|
+
parseErrors: false
|
|
290
|
+
})
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
## Running Tests
|
|
294
|
+
|
|
295
|
+
```sh
|
|
296
|
+
npm test
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Credits
|
|
300
|
+
|
|
301
|
+
* Original code by [Antony Jones](https://github.com/antony)
|
package/lib/api/index.js
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import { AccessDeniedMixin } from '../error-handlers/access-denied.js'
|
|
2
|
+
import { ConflictMixin } from '../error-handlers/conflict.js'
|
|
3
|
+
import { DefaultMixin } from '../error-handlers/default.js'
|
|
4
|
+
import { ForbiddenMixin } from '../error-handlers/forbidden.js'
|
|
5
|
+
import { HandleMixin } from '../error-handlers/handle.js'
|
|
6
|
+
import { NotFoundMixin } from '../error-handlers/not-found.js'
|
|
7
|
+
import { BadDataMixin } from '../error-handlers/bad-data.js'
|
|
8
|
+
import { PaymentRequiredMixin } from '../error-handlers/payment-required.js'
|
|
9
|
+
import { PreconditionFailedMixin } from '../error-handlers/precondition-failed.js'
|
|
10
|
+
import { TooManyRequestsMixin } from '../error-handlers/too-many-requests.js'
|
|
11
|
+
import { NotAcceptableMixin } from '../error-handlers/not-acceptable.js'
|
|
12
|
+
import { GoneMixin } from '../error-handlers/gone.js'
|
|
13
|
+
import { byCode } from '../errors.js'
|
|
14
|
+
import { compose } from './_just-compose.js'
|
|
15
|
+
|
|
16
|
+
class ApiBase {}
|
|
17
|
+
const Behaviours = compose(
|
|
18
|
+
AccessDeniedMixin,
|
|
19
|
+
ConflictMixin,
|
|
20
|
+
DefaultMixin,
|
|
21
|
+
ForbiddenMixin,
|
|
22
|
+
HandleMixin,
|
|
23
|
+
NotFoundMixin,
|
|
24
|
+
BadDataMixin,
|
|
25
|
+
PaymentRequiredMixin,
|
|
26
|
+
PreconditionFailedMixin,
|
|
27
|
+
TooManyRequestsMixin,
|
|
28
|
+
NotAcceptableMixin,
|
|
29
|
+
GoneMixin
|
|
30
|
+
)(ApiBase)
|
|
31
|
+
|
|
32
|
+
class Api extends Behaviours {
|
|
33
|
+
constructor (options) {
|
|
34
|
+
super()
|
|
35
|
+
this.options = Object.assign({
|
|
36
|
+
retry: false,
|
|
37
|
+
parseErrors: true,
|
|
38
|
+
handlers: {}
|
|
39
|
+
}, options)
|
|
40
|
+
|
|
41
|
+
this.handlers = {}
|
|
42
|
+
this.client = null
|
|
43
|
+
this.resetRequest()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
resetRequest () {
|
|
47
|
+
this.config = {
|
|
48
|
+
endpoint: null,
|
|
49
|
+
method: 'get',
|
|
50
|
+
payload: null,
|
|
51
|
+
query: null,
|
|
52
|
+
headers: {},
|
|
53
|
+
overrides: {}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
getClient () {
|
|
58
|
+
if (this.options.mock) {
|
|
59
|
+
console.warn('@beyonk/http: Using mocked http client')
|
|
60
|
+
return this.options.mock
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (this.client) {
|
|
64
|
+
return this.client
|
|
65
|
+
} else if (typeof window !== 'undefined') {
|
|
66
|
+
return window.fetch.bind(window)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
throw Error("No client provided and can't find one automatically")
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async send (fn) {
|
|
73
|
+
const endpoint = this.config.endpoint.includes('://') ? this.config.endpoint : `${this.options.baseUrl}/${this.config.endpoint}`
|
|
74
|
+
const hasPayload = !!this.config.payload
|
|
75
|
+
|
|
76
|
+
const options = Object.assign(
|
|
77
|
+
{
|
|
78
|
+
method: this.config.method,
|
|
79
|
+
cors: true,
|
|
80
|
+
credentials: 'include',
|
|
81
|
+
headers: Object.assign(
|
|
82
|
+
{ Accept: 'application/json' },
|
|
83
|
+
hasPayload ? { 'Content-Type': 'application/json' } : {},
|
|
84
|
+
this.config.headers
|
|
85
|
+
)
|
|
86
|
+
},
|
|
87
|
+
hasPayload ? { body: JSON.stringify(this.config.payload) } : {},
|
|
88
|
+
this.config.overrides
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
const client = this.getClient()
|
|
92
|
+
const ep = this.config.query ? `${endpoint}?${this.config.query}` : `${endpoint}`
|
|
93
|
+
|
|
94
|
+
let result
|
|
95
|
+
try {
|
|
96
|
+
result = await this._doQuery(1, client, ep, options)
|
|
97
|
+
} catch (e) {
|
|
98
|
+
console.log(e)
|
|
99
|
+
return this.handle(e, this.ctx)
|
|
100
|
+
} finally {
|
|
101
|
+
this.resetRequest()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const { json, httpStatus } = result
|
|
105
|
+
return fn ? fn(json, httpStatus) : json
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
_hasContent (response) {
|
|
109
|
+
const contentLength = parseInt(response.headers.get('content-length'), 10)
|
|
110
|
+
|
|
111
|
+
const isNoContentResponse = response.status === 204
|
|
112
|
+
if (isNoContentResponse) {
|
|
113
|
+
return false
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const headerExists = !isNaN(contentLength)
|
|
117
|
+
return !headerExists || contentLength > 0
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async _doQuery (attempt, client, endpoint, options) {
|
|
121
|
+
const retry = this.options.retry || { attempts: 1 }
|
|
122
|
+
try {
|
|
123
|
+
const r = await client(endpoint, options)
|
|
124
|
+
|
|
125
|
+
if (r.status >= 200 && r.status < 400) {
|
|
126
|
+
let json
|
|
127
|
+
|
|
128
|
+
if (this._hasContent(r)) {
|
|
129
|
+
try {
|
|
130
|
+
json = await r.json()
|
|
131
|
+
} catch (e) {
|
|
132
|
+
console.error('Unable to parse response json', e.message)
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return { httpStatus: r.status, json }
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
let content = ''
|
|
140
|
+
try {
|
|
141
|
+
content = this.options.parseErrors ? await r.json() : await r.text()
|
|
142
|
+
} catch (e) {
|
|
143
|
+
console.log('Failed to parse error body when asked.')
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const ClientError = byCode(r.status)
|
|
147
|
+
throw new ClientError(r.statusText, content)
|
|
148
|
+
} catch (e) {
|
|
149
|
+
if (attempt < retry.attempts && retry.errors.includes(e.code)) {
|
|
150
|
+
console.warn(`Got ${e.code} when calling ${endpoint}. Retrying request (${attempt}/${retry.attempts})`)
|
|
151
|
+
return this._doQuery(++attempt, client, endpoint, options)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
throw e
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
context (ctx) {
|
|
159
|
+
if (ctx.fetch) {
|
|
160
|
+
this.client = ctx.fetch
|
|
161
|
+
}
|
|
162
|
+
this.ctx = ctx
|
|
163
|
+
return this
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
override (override) {
|
|
167
|
+
this.config.overrides = override
|
|
168
|
+
return this
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
headers (headers) {
|
|
172
|
+
this.config.headers = headers
|
|
173
|
+
return this
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async get (fn) {
|
|
177
|
+
this.config.method = 'GET'
|
|
178
|
+
return this.send(fn)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
async post (fn) {
|
|
182
|
+
this.config.method = 'POST'
|
|
183
|
+
return this.send(fn)
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
async patch (fn) {
|
|
187
|
+
this.config.method = 'PATCH'
|
|
188
|
+
return this.send(fn)
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
async put (fn) {
|
|
192
|
+
this.config.method = 'PUT'
|
|
193
|
+
return this.send(fn)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async del (fn) {
|
|
197
|
+
this.config.method = 'DELETE'
|
|
198
|
+
return this.send(fn)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
endpoint (endpoint) {
|
|
202
|
+
this.config.endpoint = endpoint
|
|
203
|
+
return this
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
query (query) {
|
|
207
|
+
const q = Object.entries(query).reduce((curr, [ k, v ]) => {
|
|
208
|
+
if (typeof v === 'undefined') {
|
|
209
|
+
return curr
|
|
210
|
+
}
|
|
211
|
+
if (Array.isArray(v)) {
|
|
212
|
+
curr.push(...v.map(n => `${k}=${encodeURIComponent(n)}`))
|
|
213
|
+
} else {
|
|
214
|
+
curr.push(`${k}=${encodeURIComponent(v)}`)
|
|
215
|
+
}
|
|
216
|
+
return curr
|
|
217
|
+
}, [])
|
|
218
|
+
|
|
219
|
+
this.config.query = q.join('&')
|
|
220
|
+
return this
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
payload (payload) {
|
|
224
|
+
this.config.payload = payload
|
|
225
|
+
return this
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export {
|
|
230
|
+
Api
|
|
231
|
+
}
|