mockaton 8.2.23 → 8.3.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 +29 -7
- package/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/ApiConstants.js +2 -0
- package/src/Config.js +2 -0
- package/src/Filename.js +9 -1
- package/src/ProxyRelay.js +13 -1
- package/src/utils/fs.js +6 -1
- package/src/utils/mime.js +20 -0
package/README.md
CHANGED
|
@@ -13,13 +13,10 @@ URL paths. For example, the following file will be served on `/api/user/1234`
|
|
|
13
13
|
my-mocks-dir/api/user/[user-id].GET.200.json
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
By the way,
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
Nonetheless, you don’t need to mock all your APIs. Mockaton
|
|
21
|
-
can request from your backend the routes you don’t have mocks for.
|
|
22
|
-
That’s done with `config.proxyFallback = 'http://mybackend'`
|
|
16
|
+
By the way, you don’t need to mock all your APIs. Mockaton can request
|
|
17
|
+
from your backend the routes you don’t have mocks for. That’s done
|
|
18
|
+
with `config.proxyFallback = 'http://mybackend'`. For convenience, you
|
|
19
|
+
can save mocks for those responses with `config.collectProxied = true`
|
|
23
20
|
|
|
24
21
|
## Multiple Mock Variants
|
|
25
22
|
Each route can have many mocks, which could either be:
|
|
@@ -280,6 +277,31 @@ the amount is globally configurable. It defaults to `config.delay=1200` millisec
|
|
|
280
277
|
Lets you specify a target server for serving routes you don’t have mocks for.
|
|
281
278
|
For example, `config.proxyFallback = 'http://example.com'`
|
|
282
279
|
|
|
280
|
+
### `collectProxied?: boolean`
|
|
281
|
+
Defaults to `false`. With this flag you can save mocks that hit
|
|
282
|
+
your proxy fallback to `config.mocksDir`. If there are UUIDv4 in the
|
|
283
|
+
URL the filename will have `[id]` in their place. For example,
|
|
284
|
+
|
|
285
|
+
```
|
|
286
|
+
/api/user/d14e09c8-d970-4b07-be42-b2f4ee22f0a6/likes =>
|
|
287
|
+
my-mocks-dir/api/user/[id]/likes.GET.200.json
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Note that newly saved mocks won’t be served until you
|
|
291
|
+
**register them** by reinitializing Mockaton or clicking "Reset".
|
|
292
|
+
|
|
293
|
+
Registered mocks won’t be overwritten (they don’t hit the fallback server).
|
|
294
|
+
On the other hand, newly saved mocks get overwritten while they are unregistered.
|
|
295
|
+
|
|
296
|
+
<details>
|
|
297
|
+
<summary>Extension Details</summary>
|
|
298
|
+
<p>
|
|
299
|
+
If you see an <code>.unknown</code> extension, that’s because the
|
|
300
|
+
<code>Content-Type</code> sent by your backend is either missing or not present
|
|
301
|
+
in the predefined list. For the latter, add it to <code>config.extraMimes</code>
|
|
302
|
+
</p>
|
|
303
|
+
</details>
|
|
304
|
+
|
|
283
305
|
|
|
284
306
|
### `staticDir?: string`
|
|
285
307
|
- Use Case 1: If you have a bunch of static assets you don’t want to add `.GET.200.ext`
|
package/index.d.ts
CHANGED
package/package.json
CHANGED
package/src/ApiConstants.js
CHANGED
package/src/Config.js
CHANGED
|
@@ -14,6 +14,7 @@ export const Config = Object.seal({
|
|
|
14
14
|
host: '127.0.0.1',
|
|
15
15
|
port: 0, // auto-assigned
|
|
16
16
|
proxyFallback: '', // e.g. http://localhost:9999
|
|
17
|
+
collectProxied: false,
|
|
17
18
|
|
|
18
19
|
delay: 1200, // milliseconds
|
|
19
20
|
cookies: {}, // defaults to the first kv
|
|
@@ -47,6 +48,7 @@ export function setup(options) {
|
|
|
47
48
|
host: is(String),
|
|
48
49
|
port: port => Number.isInteger(port) && port >= 0 && port < 2 ** 16,
|
|
49
50
|
proxyFallback: optional(URL.canParse),
|
|
51
|
+
collectProxied: is(Boolean),
|
|
50
52
|
|
|
51
53
|
delay: ms => Number.isInteger(ms) && ms > 0,
|
|
52
54
|
cookies: is(Object),
|
package/src/Filename.js
CHANGED
|
@@ -44,7 +44,6 @@ export function parseFilename(file) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
|
|
48
47
|
function removeTrailingSlash(url = '') {
|
|
49
48
|
return url
|
|
50
49
|
.replace(/\/$/, '')
|
|
@@ -59,5 +58,14 @@ function responseStatusIsValid(status) {
|
|
|
59
58
|
}
|
|
60
59
|
|
|
61
60
|
|
|
61
|
+
export function makeMockFilename(url, method, status, ext) {
|
|
62
|
+
const urlMask = replaceIds(removeTrailingSlash(url))
|
|
63
|
+
return [urlMask, method, status, ext].join('.')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const reUuidV4 = /([0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})/gi
|
|
67
|
+
function replaceIds(filename) {
|
|
68
|
+
return filename.replaceAll(reUuidV4, '[id]')
|
|
69
|
+
}
|
|
62
70
|
|
|
63
71
|
|
package/src/ProxyRelay.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { write } from './utils/fs.js'
|
|
1
3
|
import { Config } from './Config.js'
|
|
4
|
+
import { extFor } from './utils/mime.js'
|
|
5
|
+
import { makeMockFilename } from './Filename.js'
|
|
2
6
|
|
|
3
7
|
|
|
4
8
|
export async function proxy(req, response) {
|
|
@@ -6,6 +10,14 @@ export async function proxy(req, response) {
|
|
|
6
10
|
method: req.method,
|
|
7
11
|
headers: req.headers
|
|
8
12
|
})
|
|
13
|
+
// TODO investigate how to include many repeated headers such as set-cookie
|
|
9
14
|
response.writeHead(proxyResponse.status, Object.fromEntries(proxyResponse.headers))
|
|
10
|
-
|
|
15
|
+
const body = await proxyResponse.text()
|
|
16
|
+
response.end(body)
|
|
17
|
+
|
|
18
|
+
if (Config.collectProxied) { // TESTME
|
|
19
|
+
const ext = extFor(proxyResponse.headers.get('content-type'))
|
|
20
|
+
const filename = makeMockFilename(req.url, req.method, proxyResponse.status, ext)
|
|
21
|
+
write(join(Config.mocksDir, filename), body)
|
|
22
|
+
}
|
|
11
23
|
}
|
package/src/utils/fs.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path, { join } from 'node:path'
|
|
2
|
-
import { lstatSync, readFileSync, readdirSync } from 'node:fs'
|
|
2
|
+
import { lstatSync, readFileSync, readdirSync, writeFileSync, mkdirSync } from 'node:fs'
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
export const isFile = path => lstatSync(path, { throwIfNoEntry: false })?.isFile()
|
|
@@ -14,3 +14,8 @@ export const listFilesRecursively = dir => {
|
|
|
14
14
|
? files.map(f => f.replaceAll(path.sep, path.posix.sep)) // TESTME
|
|
15
15
|
: files
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
export const write = (fPath, body) => {
|
|
19
|
+
mkdirSync(path.dirname(fPath), { recursive: true })
|
|
20
|
+
writeFileSync(fPath, body)
|
|
21
|
+
}
|
package/src/utils/mime.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Config } from '../Config.js'
|
|
2
|
+
import { EXT_FOR_UNKNOWN_MIME } from '../ApiConstants.js'
|
|
2
3
|
|
|
3
4
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
|
|
4
5
|
// m = {};
|
|
@@ -93,3 +94,22 @@ export function mimeFor(filename) {
|
|
|
93
94
|
console.info(`Missing MIME for ${filename}`)
|
|
94
95
|
return mime
|
|
95
96
|
}
|
|
97
|
+
|
|
98
|
+
export function extFor(mime) {
|
|
99
|
+
const ext = findExt(mime)
|
|
100
|
+
if (!ext) {
|
|
101
|
+
console.info(`Missing extension for ${mime}`)
|
|
102
|
+
return EXT_FOR_UNKNOWN_MIME
|
|
103
|
+
}
|
|
104
|
+
return ext
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function findExt(targetMime) {
|
|
108
|
+
for (const [ext, mime] of Object.entries(Config.extraMimes))
|
|
109
|
+
if (targetMime === mime)
|
|
110
|
+
return ext
|
|
111
|
+
for (const [ext, mime] of Object.entries(mimes))
|
|
112
|
+
if (targetMime === mime)
|
|
113
|
+
return ext
|
|
114
|
+
return ''
|
|
115
|
+
}
|