mockaton 8.20.0 → 8.20.2
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 +73 -42
- package/index.d.ts +22 -24
- package/package.json +3 -3
- package/src/Api.js +29 -19
- package/src/Commander.js +52 -36
- package/src/Dashboard.js +199 -138
- package/src/Mockaton.js +1 -1
- package/src/logo.svg +17 -0
- package/src/mockBrokersCollection.js +3 -2
- package/src/staticCollection.js +2 -2
- package/src/utils/http-request.js +9 -3
- package/src/mockaton-logo.svg +0 -12
package/README.md
CHANGED
|
@@ -1,25 +1,43 @@
|
|
|
1
|
-
<img src="src/
|
|
1
|
+
<img src="src/logo.svg" alt="Mockaton Logo" width="210" style="margin-top: 30px"/>
|
|
2
2
|
|
|
3
3
|

|
|
4
4
|

|
|
5
5
|
[](https://github.com/ericfortis/mockaton/actions/workflows/test.yml)
|
|
6
6
|
[](https://github.com/ericfortis/mockaton/actions/workflows/github-code-scanning/codeql)
|
|
7
7
|
|
|
8
|
-
An HTTP mock server for simulating APIs with minimal setup
|
|
9
|
-
|
|
8
|
+
An HTTP mock server for simulating APIs with minimal setup — ideal for testing
|
|
9
|
+
difficult to reproduce states. For example, triggering an error on a third-party API.
|
|
10
|
+
Similarly, if you are a frontend developer, triggering it on your project’s backend.
|
|
10
11
|
|
|
12
|
+
<br/>
|
|
13
|
+
|
|
14
|
+
## Motivation
|
|
15
|
+
|
|
16
|
+
**No API state should be too hard to test.**
|
|
17
|
+
With Mockaton, developers can achieve correctness without sacrificing speed.
|
|
18
|
+
|
|
19
|
+
### Correctness
|
|
20
|
+
- Enables testing of complex or rare scenarios that would otherwise be skipped.
|
|
21
|
+
- Allows for deterministic, comprehensive, and consistent state.
|
|
22
|
+
|
|
23
|
+
### Speed
|
|
24
|
+
- Prevents progress from being blocked by waiting for APIs.
|
|
25
|
+
- Avoids spinning up and updating hefty backends when developing UIs.
|
|
26
|
+
|
|
27
|
+
<br/>
|
|
11
28
|
|
|
12
29
|
## Overview
|
|
13
30
|
With Mockaton, you don’t need to write code for wiring up your
|
|
14
31
|
mocks. Instead, a given directory is scanned for filenames
|
|
15
|
-
following a convention similar to the URLs.
|
|
32
|
+
following a convention similar to the URLs.
|
|
16
33
|
|
|
17
|
-
For example, for [/api/user/123](#), the
|
|
34
|
+
For example, for [/api/user/123](#), the filename could be:
|
|
18
35
|
|
|
19
36
|
<pre>
|
|
20
37
|
<code>my-mocks-dir/<b>api/user</b>/[user-id].GET.200.json</code>
|
|
21
38
|
</pre>
|
|
22
39
|
|
|
40
|
+
<br/>
|
|
23
41
|
|
|
24
42
|
## Dashboard
|
|
25
43
|
|
|
@@ -62,30 +80,34 @@ api/videos.GET.<b>500</b>.txt # Internal Server Error
|
|
|
62
80
|
|
|
63
81
|
<br/>
|
|
64
82
|
|
|
65
|
-
## Scraping
|
|
83
|
+
## Scraping Mocks from your Backend
|
|
66
84
|
|
|
67
85
|
### Option 1: Browser Extension
|
|
68
|
-
This [browser extension](https://github.com/ericfortis/download-http-requests-browser-ext)
|
|
69
|
-
lets you download all the HTTP responses, and they
|
|
86
|
+
This companion [browser-devtools extension](https://github.com/ericfortis/download-http-requests-browser-ext)
|
|
87
|
+
lets you download all the HTTP responses at once, and they
|
|
70
88
|
get saved following Mockaton’s filename convention.
|
|
71
89
|
|
|
72
90
|
### Option 2: Fallback to Your Backend
|
|
73
|
-
|
|
91
|
+
<details>
|
|
92
|
+
<summary>Details</summary>
|
|
93
|
+
|
|
94
|
+
This option could be a bit elaborate if your backend uses third-party auth,
|
|
74
95
|
because you’d have to manually inject cookies or `sessionStorage` tokens.
|
|
75
96
|
|
|
76
97
|
On the other hand, proxying to your backend is straightforward if your backend
|
|
77
|
-
handles the session cookie, or if you can develop without auth.
|
|
98
|
+
handles the session cookie, or if you can develop without auth.
|
|
78
99
|
|
|
79
100
|
Either way you can forward requests to your backend for routes you don’t have
|
|
80
101
|
mocks for, or routes that have the ☁️ **Cloud Checkbox** checked. In addition, by
|
|
81
102
|
checking ✅ **Save Mocks**, you can collect the responses that hit your backend.
|
|
82
103
|
They will be saved in your `config.mocksDir` following the filename convention.
|
|
104
|
+
</details>
|
|
83
105
|
|
|
84
106
|
|
|
85
107
|
<br/>
|
|
86
108
|
|
|
87
109
|
|
|
88
|
-
## Basic Usage
|
|
110
|
+
## Basic Usage
|
|
89
111
|
```sh
|
|
90
112
|
npm install mockaton --save-dev
|
|
91
113
|
```
|
|
@@ -95,23 +117,25 @@ Create a `my-mockaton.js` file
|
|
|
95
117
|
import { resolve } from 'node:path'
|
|
96
118
|
import { Mockaton } from 'mockaton'
|
|
97
119
|
|
|
98
|
-
// See the Config section for more options
|
|
99
120
|
Mockaton({
|
|
100
121
|
mocksDir: resolve('my-mocks-dir'), // must exist
|
|
101
122
|
port: 2345
|
|
102
|
-
})
|
|
123
|
+
}) // The Config section below documents more options
|
|
103
124
|
```
|
|
104
125
|
|
|
105
126
|
```sh
|
|
106
127
|
node my-mockaton.js
|
|
107
128
|
```
|
|
108
129
|
|
|
109
|
-
|
|
130
|
+
<details>
|
|
131
|
+
<summary>About TypeScript in Node < 23.6</summary>
|
|
110
132
|
If you want to write mocks in TypeScript in a version older than Node 23.6:
|
|
133
|
+
|
|
111
134
|
```shell
|
|
112
135
|
npm install tsx
|
|
113
136
|
node --import=tsx my-mockaton.js
|
|
114
137
|
```
|
|
138
|
+
</details>
|
|
115
139
|
|
|
116
140
|
|
|
117
141
|
<br/>
|
|
@@ -128,11 +152,13 @@ npm run start # in another terminal
|
|
|
128
152
|
```
|
|
129
153
|
|
|
130
154
|
The demo app has a list of colors containing all of their possible states. For example,
|
|
131
|
-
permutations for out-of-stock, new-arrival, and discontinued.
|
|
155
|
+
permutations for out-of-stock, new-arrival, and discontinued.
|
|
132
156
|
|
|
133
157
|
<img src="./demo-app-vite/pixaton-tests/pic-for-readme.vp740x880.light.gold.png" alt="Mockaton Demo App Screenshot" width="740" />
|
|
134
158
|
|
|
135
159
|
<br/>
|
|
160
|
+
<br/>
|
|
161
|
+
|
|
136
162
|
|
|
137
163
|
## Use Cases
|
|
138
164
|
### Testing Backend or Frontend
|
|
@@ -163,21 +189,12 @@ putting its built assets in `config.staticDir`. And simulate the flow by Bulk Se
|
|
|
163
189
|
The [aot-fetch-demo repo](https://github.com/ericfortis/aot-fetch-demo) has a working example.
|
|
164
190
|
|
|
165
191
|
|
|
166
|
-
<br/>
|
|
167
|
-
|
|
168
|
-
## Motivation
|
|
169
|
-
- Avoids spinning up and updating hefty backends when developing UIs.
|
|
170
|
-
- Allows for a deterministic, comprehensive, and consistent backend state. For example, having
|
|
171
|
-
a collection with all the possible state variants helps for spotting inadvertent bugs.
|
|
172
|
-
- Sometimes frontend progress is blocked by waiting for backend APIs.
|
|
173
|
-
|
|
174
192
|
<br/>
|
|
175
193
|
|
|
176
194
|
|
|
177
195
|
## You can write JSON mocks in JavaScript or TypeScript
|
|
178
196
|
For example, `api/foo.GET.200.js`
|
|
179
197
|
|
|
180
|
-
|
|
181
198
|
**Option A:** An Object, Array, or String is sent as JSON.
|
|
182
199
|
|
|
183
200
|
```js
|
|
@@ -189,7 +206,7 @@ export default { foo: 'bar' }
|
|
|
189
206
|
Return a `string | Buffer | Uint8Array`, but don’t call `response.end()`
|
|
190
207
|
|
|
191
208
|
```js
|
|
192
|
-
export default (request, response) =>
|
|
209
|
+
export default (request, response) =>
|
|
193
210
|
JSON.stringify({ foo: 'bar' })
|
|
194
211
|
```
|
|
195
212
|
|
|
@@ -206,6 +223,7 @@ you want to concatenate newly added colors.
|
|
|
206
223
|
```js
|
|
207
224
|
import { parseJSON } from 'mockaton'
|
|
208
225
|
|
|
226
|
+
|
|
209
227
|
export default async function insertColor(request, response) {
|
|
210
228
|
const color = await parseJSON(request)
|
|
211
229
|
globalThis.newColorsDatabase ??= []
|
|
@@ -223,6 +241,7 @@ export default async function insertColor(request, response) {
|
|
|
223
241
|
```js
|
|
224
242
|
import colorsFixture from './colors.json' with { type: 'json' }
|
|
225
243
|
|
|
244
|
+
|
|
226
245
|
export default function listColors() {
|
|
227
246
|
return JSON.stringify([
|
|
228
247
|
...colorsFixture,
|
|
@@ -305,7 +324,7 @@ A filename can have many comments.
|
|
|
305
324
|
<br/>
|
|
306
325
|
|
|
307
326
|
### Default mock for a route
|
|
308
|
-
You can add the comment: `(default)`.
|
|
327
|
+
You can add the comment: `(default)`.
|
|
309
328
|
Otherwise, the first file in **alphabetical order** wins.
|
|
310
329
|
|
|
311
330
|
<pre>
|
|
@@ -322,7 +341,8 @@ api/video<b>?limit=[limit]</b>.GET.200.json
|
|
|
322
341
|
</pre>
|
|
323
342
|
|
|
324
343
|
On Windows, filenames containing "?" are [not
|
|
325
|
-
permitted](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file), but since that’s part of the query
|
|
344
|
+
permitted](https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file), but since that’s part of the query
|
|
345
|
+
string it’s ignored anyway.
|
|
326
346
|
|
|
327
347
|
<br/>
|
|
328
348
|
|
|
@@ -353,9 +373,9 @@ it’s convenient for serving 200 GET requests without having to add the filenam
|
|
|
353
373
|
extension convention. For example, for using Mockaton as a standalone demo server,
|
|
354
374
|
as explained above in the _Use Cases_ section.
|
|
355
375
|
|
|
356
|
-
Files under `config.staticDir`
|
|
357
|
-
|
|
358
|
-
|
|
376
|
+
Files under `config.staticDir` take precedence over corresponding
|
|
377
|
+
`GET` mocks in `config.mocksDir` (regardless of status code).
|
|
378
|
+
For example, if you have two files for `GET /foo/bar.jpg`:
|
|
359
379
|
<pre>
|
|
360
380
|
my-static-dir<b>/foo/bar.jpg</b> <span style="color:green"> // Wins</span>
|
|
361
381
|
my-mocks-dir<b>/foo/bar.jpg</b>.GET.200.jpg <span style="color:red"> // Unreachable</span>
|
|
@@ -365,7 +385,9 @@ my-static-dir<b>/foo/bar.jpg</b> <span style="color:green"> // Wins</span>
|
|
|
365
385
|
<br/>
|
|
366
386
|
|
|
367
387
|
### `ignore?: RegExp`
|
|
368
|
-
Defaults to `/(\.DS_Store|~)
|
|
388
|
+
Defaults to `/(\.DS_Store|~)$/`. The regex rule is
|
|
389
|
+
tested against the basename (filename without directory path).
|
|
390
|
+
|
|
369
391
|
|
|
370
392
|
<br/>
|
|
371
393
|
|
|
@@ -378,13 +400,13 @@ Defaults to `0`, which means auto-assigned
|
|
|
378
400
|
|
|
379
401
|
<br/>
|
|
380
402
|
|
|
381
|
-
### `delay?: number`
|
|
403
|
+
### `delay?: number`
|
|
382
404
|
Defaults to `1200` milliseconds. Although routes can individually be delayed
|
|
383
405
|
with the 🕓 Checkbox, the amount is globally configurable with this option.
|
|
384
406
|
|
|
385
407
|
### `delayJitter?: number`
|
|
386
408
|
Defaults to `0`. Range: `[0.0, 3.0]`. Maximum percentage of the delay to add.
|
|
387
|
-
For example, `0.5` will add at most `600ms` to the default delay.
|
|
409
|
+
For example, `0.5` will add at most `600ms` to the default delay.
|
|
388
410
|
|
|
389
411
|
<br/>
|
|
390
412
|
|
|
@@ -396,7 +418,7 @@ or that you manually picked with the ☁️ **Cloud Checkbox**.
|
|
|
396
418
|
|
|
397
419
|
### `collectProxied?: boolean`
|
|
398
420
|
Defaults to `false`. With this flag you can save mocks that hit
|
|
399
|
-
your proxy fallback to `config.mocksDir`. If the URL has v4 UUIDs,
|
|
421
|
+
your proxy fallback to `config.mocksDir`. If the URL has v4 UUIDs,
|
|
400
422
|
the filename will have `[id]` in their place. For example:
|
|
401
423
|
|
|
402
424
|
<pre>
|
|
@@ -423,7 +445,7 @@ the predefined list. For that, you can add it to <code>config.extraMimes</code>
|
|
|
423
445
|
|
|
424
446
|
|
|
425
447
|
### `formatCollectedJSON?: boolean`
|
|
426
|
-
Defaults to `true`. Saves the mock with two spaces indentation —
|
|
448
|
+
Defaults to `true`. Saves the mock with two spaces indentation —
|
|
427
449
|
the formatting output of `JSON.stringify(data, null, ' ')`
|
|
428
450
|
|
|
429
451
|
|
|
@@ -435,6 +457,7 @@ the formatting output of `JSON.stringify(data, null, ' ')`
|
|
|
435
457
|
```js
|
|
436
458
|
import { jwtCookie } from 'mockaton'
|
|
437
459
|
|
|
460
|
+
|
|
438
461
|
config.cookies = {
|
|
439
462
|
'My Admin User': 'my-cookie=1;Path=/;SameSite=strict',
|
|
440
463
|
'My Normal User': 'my-cookie=0;Path=/;SameSite=strict',
|
|
@@ -449,7 +472,7 @@ The selected cookie, which is the first one by default, is sent in every respons
|
|
|
449
472
|
`Set-Cookie` header (as long as its value is not an empty string). The object key is just
|
|
450
473
|
a label for UI display purposes, and also for selecting a cookie via the Commander API.
|
|
451
474
|
|
|
452
|
-
If you need to send more than one cookie, you can inject them globally
|
|
475
|
+
If you need to send more than one cookie, you can inject them globally
|
|
453
476
|
in `config.extraHeaders`, or individually in a function `.js` or `.ts` mock.
|
|
454
477
|
|
|
455
478
|
By the way, the `jwtCookie` helper has a hardcoded header and signature.
|
|
@@ -509,12 +532,13 @@ import { parse } from 'yaml'
|
|
|
509
532
|
import { readFileSync } from 'node:js'
|
|
510
533
|
import { jsToJsonPlugin } from 'mockaton'
|
|
511
534
|
|
|
535
|
+
|
|
512
536
|
config.plugins = [
|
|
513
|
-
|
|
537
|
+
|
|
514
538
|
// Although `jsToJsonPlugin` is set by default, you need to include it if you need it.
|
|
515
539
|
// IOW, your plugins array overwrites the default list. This way you can remove it.
|
|
516
|
-
[/\.(js|ts)$/, jsToJsonPlugin],
|
|
517
|
-
|
|
540
|
+
[/\.(js|ts)$/, jsToJsonPlugin],
|
|
541
|
+
|
|
518
542
|
[/\.yml$/, yamlToJsonPlugin],
|
|
519
543
|
[/foo\.GET\.200\.txt$/, capitalizePlugin], // e.g. GET /api/foo would be capitalized
|
|
520
544
|
]
|
|
@@ -575,6 +599,7 @@ All of its methods return their `fetch` response promise.
|
|
|
575
599
|
```js
|
|
576
600
|
import { Commander } from 'mockaton'
|
|
577
601
|
|
|
602
|
+
|
|
578
603
|
const myMockatonAddr = 'http://localhost:2345'
|
|
579
604
|
const mockaton = new Commander(myMockatonAddr)
|
|
580
605
|
```
|
|
@@ -649,11 +674,17 @@ example, if you are polling, and you want to test the state change.
|
|
|
649
674
|
- Chrome DevTools allows for [overriding responses](https://developer.chrome.com/docs/devtools/overrides)
|
|
650
675
|
- Reverse Proxies such as [Burp](https://portswigger.net/burp) are also handy for overriding responses
|
|
651
676
|
|
|
652
|
-
### Client
|
|
677
|
+
### Client Side
|
|
653
678
|
In contrast to Mockaton, which is an HTTP Server, these programs
|
|
654
|
-
|
|
679
|
+
hijack the client (e.g., `fetch`) in Node.js and browsers.
|
|
655
680
|
|
|
656
|
-
- [
|
|
681
|
+
- [Mock Server Worker (MSW)](https://mswjs.io)
|
|
657
682
|
- [Nock](https://github.com/nock/nock)
|
|
658
683
|
- [Fetch Mock](https://github.com/wheresrhys/fetch-mock)
|
|
659
684
|
- [Mentoss](https://github.com/humanwhocodes/mentoss) Has a server side too
|
|
685
|
+
|
|
686
|
+
<br/>
|
|
687
|
+
|
|
688
|
+
---
|
|
689
|
+
|
|
690
|
+

|
package/index.d.ts
CHANGED
|
@@ -55,33 +55,31 @@ export function jwtCookie(cookieName: string, payload: any, path?: string): stri
|
|
|
55
55
|
|
|
56
56
|
export function parseJSON(request: IncomingMessage): Promise<any>
|
|
57
57
|
|
|
58
|
+
export type JsonPromise<T> = Promise<Response & { json(): Promise<T> }>
|
|
58
59
|
|
|
59
|
-
export class Commander {
|
|
60
|
-
constructor(addr: string)
|
|
61
60
|
|
|
62
|
-
|
|
61
|
+
// API
|
|
63
62
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
listComments(): Promise<Response>
|
|
78
|
-
|
|
79
|
-
setProxyFallback(proxyAddr: string): Promise<Response>
|
|
80
|
-
|
|
81
|
-
reset(): Promise<Response>
|
|
63
|
+
export type ClientMockBroker = {
|
|
64
|
+
mocks: string[]
|
|
65
|
+
currentMock: {
|
|
66
|
+
file: string
|
|
67
|
+
delayed: boolean
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export type ClientBrokersByMethod = {
|
|
71
|
+
[method: string]: {
|
|
72
|
+
[urlMask: string]: ClientMockBroker
|
|
73
|
+
}
|
|
74
|
+
}
|
|
82
75
|
|
|
76
|
+
export type ClientStaticBroker = {
|
|
77
|
+
route: string
|
|
78
|
+
delayed: boolean
|
|
79
|
+
status: number
|
|
80
|
+
}
|
|
81
|
+
export type ClientStaticBrokers = {
|
|
82
|
+
[route: string]: ClientStaticBroker
|
|
83
|
+
}
|
|
83
84
|
|
|
84
|
-
getCorsAllowed(): Promise<Response>
|
|
85
85
|
|
|
86
|
-
setCorsAllowed(value: boolean): Promise<Response>
|
|
87
|
-
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "mockaton",
|
|
3
3
|
"description": "HTTP Mock Server",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "8.20.
|
|
5
|
+
"version": "8.20.2",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"types": "index.d.ts",
|
|
8
8
|
"license": "MIT",
|
|
@@ -18,12 +18,12 @@
|
|
|
18
18
|
"scripts": {
|
|
19
19
|
"test": "node --test \"src/**/*.test.js\"",
|
|
20
20
|
"coverage": "node --test --test-reporter=lcov --test-reporter-destination=.coverage/lcov.info --experimental-test-coverage \"src/**/*.test.js\"",
|
|
21
|
-
"start": "node dev-mockaton.js",
|
|
21
|
+
"start": "node --watch dev-mockaton.js",
|
|
22
22
|
"pixaton": "node --test --import=./pixaton-tests/_setup.js --experimental-test-isolation=none \"pixaton-tests/**/*.test.js\"",
|
|
23
23
|
"outdated": "npm outdated --parseable | awk -F: '{ printf \"npm i %-30s ;# %s\\n\", $4, $2 }'"
|
|
24
24
|
},
|
|
25
25
|
"optionalDependencies": {
|
|
26
|
-
"open": "^10
|
|
26
|
+
"open": "^10"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"pixaton": "1.1.2",
|
package/src/Api.js
CHANGED
|
@@ -20,7 +20,7 @@ const dashboardAssets = [
|
|
|
20
20
|
'/Dashboard.css',
|
|
21
21
|
'/Dashboard.js',
|
|
22
22
|
'/Filename.js',
|
|
23
|
-
'/
|
|
23
|
+
'/logo.svg'
|
|
24
24
|
]
|
|
25
25
|
|
|
26
26
|
export const apiGetRequests = new Map([
|
|
@@ -101,7 +101,7 @@ function longPollClientSyncVersion(req, response) {
|
|
|
101
101
|
|
|
102
102
|
function reinitialize(_, response) {
|
|
103
103
|
mockBrokersCollection.init()
|
|
104
|
-
staticCollection.init()
|
|
104
|
+
staticCollection.init()
|
|
105
105
|
sendOK(response)
|
|
106
106
|
}
|
|
107
107
|
|
|
@@ -131,17 +131,17 @@ async function setRouteIsDelayed(req, response) {
|
|
|
131
131
|
body[DF.routeMethod],
|
|
132
132
|
body[DF.routeUrlMask])
|
|
133
133
|
|
|
134
|
-
if (!broker)
|
|
134
|
+
if (!broker)
|
|
135
135
|
sendUnprocessableContent(response, `Route does not exist: ${body[DF.routeMethod]} ${body[DF.routeUrlMask]}`)
|
|
136
136
|
else if (typeof delayed !== 'boolean')
|
|
137
|
-
sendUnprocessableContent(response, `Expected
|
|
137
|
+
sendUnprocessableContent(response, `Expected boolean for "delayed"`)
|
|
138
138
|
else {
|
|
139
139
|
broker.setDelayed(delayed)
|
|
140
140
|
sendOK(response)
|
|
141
141
|
}
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
async function setRouteIsProxied(req, response) {
|
|
144
|
+
async function setRouteIsProxied(req, response) {
|
|
145
145
|
const body = await parseJSON(req)
|
|
146
146
|
const proxied = body[DF.proxied]
|
|
147
147
|
const broker = mockBrokersCollection.brokerByRoute(
|
|
@@ -151,7 +151,7 @@ async function setRouteIsProxied(req, response) { // TESTME
|
|
|
151
151
|
if (!broker)
|
|
152
152
|
sendUnprocessableContent(response, `Route does not exist: ${body[DF.routeMethod]} ${body[DF.routeUrlMask]}`)
|
|
153
153
|
else if (typeof proxied !== 'boolean')
|
|
154
|
-
sendUnprocessableContent(response, `Expected
|
|
154
|
+
sendUnprocessableContent(response, `Expected boolean for "proxied"`)
|
|
155
155
|
else if (proxied && !config.proxyFallback)
|
|
156
156
|
sendUnprocessableContent(response, `There’s no proxy fallback`)
|
|
157
157
|
else {
|
|
@@ -163,10 +163,10 @@ async function setRouteIsProxied(req, response) { // TESTME
|
|
|
163
163
|
async function updateProxyFallback(req, response) {
|
|
164
164
|
const fallback = await parseJSON(req)
|
|
165
165
|
if (!ConfigValidator.proxyFallback(fallback)) {
|
|
166
|
-
sendUnprocessableContent(response)
|
|
166
|
+
sendUnprocessableContent(response, `Invalid Proxy Fallback URL`)
|
|
167
167
|
return
|
|
168
168
|
}
|
|
169
|
-
if (!fallback)
|
|
169
|
+
if (!fallback)
|
|
170
170
|
mockBrokersCollection.ensureAllRoutesHaveSelectedMock()
|
|
171
171
|
config.proxyFallback = fallback
|
|
172
172
|
sendOK(response)
|
|
@@ -174,8 +174,8 @@ async function updateProxyFallback(req, response) {
|
|
|
174
174
|
|
|
175
175
|
async function setCollectProxied(req, response) {
|
|
176
176
|
const collectProxied = await parseJSON(req)
|
|
177
|
-
if (!ConfigValidator.collectProxied(collectProxied)) {
|
|
178
|
-
sendUnprocessableContent(response)
|
|
177
|
+
if (!ConfigValidator.collectProxied(collectProxied)) {
|
|
178
|
+
sendUnprocessableContent(response, `Expected a boolean for "collectProxied"`)
|
|
179
179
|
return
|
|
180
180
|
}
|
|
181
181
|
config.collectProxied = collectProxied
|
|
@@ -188,12 +188,22 @@ async function bulkUpdateBrokersByCommentTag(req, response) {
|
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
async function setCorsAllowed(req, response) {
|
|
191
|
-
|
|
191
|
+
const corsAllowed = await parseJSON(req)
|
|
192
|
+
if (!ConfigValidator.corsAllowed(corsAllowed)) {
|
|
193
|
+
sendUnprocessableContent(response, `Expected boolean for "corsAllowed"`)
|
|
194
|
+
return
|
|
195
|
+
}
|
|
196
|
+
config.corsAllowed = corsAllowed
|
|
192
197
|
sendOK(response)
|
|
193
198
|
}
|
|
194
199
|
|
|
195
|
-
async function setGlobalDelay(req, response) {
|
|
196
|
-
|
|
200
|
+
async function setGlobalDelay(req, response) {
|
|
201
|
+
const delay = await parseJSON(req)
|
|
202
|
+
if (!ConfigValidator.delay(delay)) {
|
|
203
|
+
sendUnprocessableContent(response, `Expected non-negative integer for "delay"`)
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
config.delay = delay
|
|
197
207
|
sendOK(response)
|
|
198
208
|
}
|
|
199
209
|
|
|
@@ -203,10 +213,10 @@ async function setStaticRouteStatusCode(req, response) {
|
|
|
203
213
|
const status = Number(body[DF.statusCode])
|
|
204
214
|
const broker = staticCollection.brokerByRoute(body[DF.routeUrlMask])
|
|
205
215
|
|
|
206
|
-
if (!broker)
|
|
207
|
-
sendUnprocessableContent(response, `
|
|
216
|
+
if (!broker)
|
|
217
|
+
sendUnprocessableContent(response, `Static route does not exist: ${body[DF.routeUrlMask]}`)
|
|
208
218
|
else if (!(status === 200 || status === 404))
|
|
209
|
-
sendUnprocessableContent(response, `Expected
|
|
219
|
+
sendUnprocessableContent(response, `Expected 200 or 404 status code`)
|
|
210
220
|
else {
|
|
211
221
|
broker.setStatus(status)
|
|
212
222
|
sendOK(response)
|
|
@@ -219,10 +229,10 @@ async function setStaticRouteIsDelayed(req, response) {
|
|
|
219
229
|
const delayed = body[DF.delayed]
|
|
220
230
|
const broker = staticCollection.brokerByRoute(body[DF.routeUrlMask])
|
|
221
231
|
|
|
222
|
-
if (!broker)
|
|
223
|
-
sendUnprocessableContent(response, `
|
|
232
|
+
if (!broker)
|
|
233
|
+
sendUnprocessableContent(response, `Static route does not exist: ${body[DF.routeUrlMask]}`)
|
|
224
234
|
else if (typeof delayed !== 'boolean')
|
|
225
|
-
sendUnprocessableContent(response, `Expected
|
|
235
|
+
sendUnprocessableContent(response, `Expected boolean for "delayed"`)
|
|
226
236
|
else {
|
|
227
237
|
broker.setDelayed(delayed)
|
|
228
238
|
sendOK(response)
|
package/src/Commander.js
CHANGED
|
@@ -18,13 +18,65 @@ export class Commander {
|
|
|
18
18
|
})
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
+
/** @type {JsonPromise<ClientBrokersByMethod>} */
|
|
21
22
|
listMocks() {
|
|
22
23
|
return this.#get(API.mocks)
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
/** @type {JsonPromise<ClientStaticBrokers>} */
|
|
27
|
+
listStaticFiles() {
|
|
28
|
+
return this.#get(API.static)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** @type {JsonPromise<[label:string, selected:boolean][]>} */
|
|
32
|
+
listCookies() {
|
|
33
|
+
return this.#get(API.cookies)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** @type {JsonPromise<string[]>} */
|
|
37
|
+
listComments() {
|
|
38
|
+
return this.#get(API.comments)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** @type {JsonPromise<string>} */
|
|
42
|
+
getProxyFallback() {
|
|
43
|
+
return this.#get(API.fallback)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** @type {JsonPromise<boolean>} */
|
|
47
|
+
getCollectProxied() {
|
|
48
|
+
return this.#get(API.collectProxied)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** @type {JsonPromise<boolean>} */
|
|
52
|
+
getCorsAllowed() {
|
|
53
|
+
return this.#get(API.cors)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** @type {JsonPromise<number>} */
|
|
57
|
+
getGlobalDelay() {
|
|
58
|
+
return this.#get(API.globalDelay)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** @type {JsonPromise<number>} */
|
|
62
|
+
getSyncVersion(currentSyncVersion, abortSignal) {
|
|
63
|
+
return fetch(API.syncVersion, {
|
|
64
|
+
signal: AbortSignal.any([abortSignal, AbortSignal.timeout(LONG_POLL_SERVER_TIMEOUT + 1000)]),
|
|
65
|
+
headers: {
|
|
66
|
+
[DF.syncVersion]: currentSyncVersion
|
|
67
|
+
}
|
|
68
|
+
})
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
reset() {
|
|
73
|
+
return this.#patch(API.reset)
|
|
74
|
+
}
|
|
75
|
+
|
|
25
76
|
select(file) {
|
|
26
77
|
return this.#patch(API.select, file)
|
|
27
78
|
}
|
|
79
|
+
|
|
28
80
|
bulkSelectByComment(comment) {
|
|
29
81
|
return this.#patch(API.bulkSelect, comment)
|
|
30
82
|
}
|
|
@@ -59,59 +111,23 @@ export class Commander {
|
|
|
59
111
|
})
|
|
60
112
|
}
|
|
61
113
|
|
|
62
|
-
listCookies() {
|
|
63
|
-
return this.#get(API.cookies)
|
|
64
|
-
}
|
|
65
114
|
selectCookie(cookieKey) {
|
|
66
115
|
return this.#patch(API.cookies, cookieKey)
|
|
67
116
|
}
|
|
68
117
|
|
|
69
|
-
listComments() {
|
|
70
|
-
return this.#get(API.comments)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
getProxyFallback() {
|
|
74
|
-
return this.#get(API.fallback)
|
|
75
|
-
}
|
|
76
118
|
setProxyFallback(proxyAddr) {
|
|
77
119
|
return this.#patch(API.fallback, proxyAddr)
|
|
78
120
|
}
|
|
79
121
|
|
|
80
|
-
getCollectProxied() {
|
|
81
|
-
return this.#get(API.collectProxied)
|
|
82
|
-
}
|
|
83
122
|
setCollectProxied(shouldCollect) {
|
|
84
123
|
return this.#patch(API.collectProxied, shouldCollect)
|
|
85
124
|
}
|
|
86
125
|
|
|
87
|
-
getCorsAllowed() {
|
|
88
|
-
return this.#get(API.cors)
|
|
89
|
-
}
|
|
90
126
|
setCorsAllowed(value) {
|
|
91
127
|
return this.#patch(API.cors, value)
|
|
92
128
|
}
|
|
93
129
|
|
|
94
|
-
getGlobalDelay() {
|
|
95
|
-
return this.#get(API.globalDelay)
|
|
96
|
-
}
|
|
97
130
|
setGlobalDelay(delay) {
|
|
98
131
|
return this.#patch(API.globalDelay, delay)
|
|
99
132
|
}
|
|
100
|
-
|
|
101
|
-
listStaticFiles() {
|
|
102
|
-
return this.#get(API.static)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
reset() {
|
|
106
|
-
return this.#patch(API.reset)
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
getSyncVersion(currentSyncVersion, abortSignal) {
|
|
110
|
-
return fetch(API.syncVersion, {
|
|
111
|
-
signal: AbortSignal.any([abortSignal, AbortSignal.timeout(LONG_POLL_SERVER_TIMEOUT + 1000)]),
|
|
112
|
-
headers: {
|
|
113
|
-
[DF.syncVersion]: currentSyncVersion
|
|
114
|
-
}
|
|
115
|
-
})
|
|
116
|
-
}
|
|
117
133
|
}
|