@pbvision/fastify-firestore-service 0.0.16 → 0.0.18
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 +0 -5
- package/package.json +5 -4
- package/src/fetch-wrapper.js +9 -7
- package/test/base-test.js +46 -20
package/README.md
CHANGED
|
@@ -14,7 +14,6 @@ provides a way to define APIs, including managing data.
|
|
|
14
14
|
- [Components](#components)
|
|
15
15
|
- [Customizing Component Registration](#customizing-component-registration)
|
|
16
16
|
- [Unit testing](#unit-testing)
|
|
17
|
-
- [Setup](#setup)
|
|
18
17
|
- [Generating SDKs](#generating-sdks)
|
|
19
18
|
- [Swagger UI](#swagger-ui)
|
|
20
19
|
- [OpenAPI SDKs](#openapi-sdks)
|
|
@@ -175,10 +174,6 @@ workflow with custom components. For example, to add a new type of component
|
|
|
175
174
|
`makeService({ components: { ExampleComponent } })`
|
|
176
175
|
|
|
177
176
|
# Unit testing
|
|
178
|
-
## Setup
|
|
179
|
-
Apps store data in Firestore. You must start the Firestore emulator before
|
|
180
|
-
running tests. You can run `yarn start-local-db` to start the local emulator.
|
|
181
|
-
|
|
182
177
|
# Generating SDKs
|
|
183
178
|
## Swagger UI
|
|
184
179
|
This library generates an interactive Swagger UI for all APIs at /[service]/docs.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pbvision/fastify-firestore-service",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.18",
|
|
4
4
|
"description": "Web Framework using Fastify and Firestore ORM",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -11,9 +11,10 @@
|
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build-doc": "./docs/build.sh",
|
|
13
13
|
"debug": "./node_modules/nodemon/bin/nodemon.js --no-lazy --legacy-watch --watch ./src --watch ./examples --watch ./test --inspect=9229 ./node_modules/jest/bin/jest.js --coverage --config=./jest.config.json --runInBand",
|
|
14
|
-
"test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --config=./jest.config.json --coverage",
|
|
15
14
|
"setup": "yarn install --frozen-lockfile && pip install -r requirements.txt",
|
|
16
|
-
"start-local-db": "./node_modules/@pbvision/firestore-orm/scripts/start-local-db.sh"
|
|
15
|
+
"start-local-db": "./node_modules/@pbvision/firestore-orm/scripts/start-local-db.sh",
|
|
16
|
+
"test": "yarn -s start-local-db && yarn -s test-without-starting-db",
|
|
17
|
+
"test-without-starting-db": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --config=./jest.config.json --coverage"
|
|
17
18
|
},
|
|
18
19
|
"contributors": [
|
|
19
20
|
"David Underhill",
|
|
@@ -41,7 +42,7 @@
|
|
|
41
42
|
"@fastify/static": "^6.12",
|
|
42
43
|
"@fastify/swagger": "^8.1.0",
|
|
43
44
|
"@fastify/swagger-ui": "^2.0.1",
|
|
44
|
-
"@pbvision/firestore-orm": "^0.0.
|
|
45
|
+
"@pbvision/firestore-orm": "^0.0.7",
|
|
45
46
|
"@pbvision/schema": "^0.2.15",
|
|
46
47
|
"@sentry/node": "^7.91.0",
|
|
47
48
|
"fastify": "^4.10.0",
|
package/src/fetch-wrapper.js
CHANGED
|
@@ -3,23 +3,24 @@ import zlib from 'node:zlib'
|
|
|
3
3
|
|
|
4
4
|
import realFetch from 'node-fetch'
|
|
5
5
|
|
|
6
|
-
async function fetchWrapper (
|
|
7
|
-
const { compress = true, method = 'POST', url, qsParams, json } =
|
|
8
|
-
let { body, headers } =
|
|
6
|
+
async function fetchWrapper (request, mockedFetch) {
|
|
7
|
+
const { compress = true, method = 'POST', url, qsParams, json } = request
|
|
8
|
+
let { body, headers } = request
|
|
9
9
|
headers = { ...headers } // copy the headers before we change them
|
|
10
10
|
|
|
11
11
|
// compress the body using brotli
|
|
12
12
|
if (json) {
|
|
13
13
|
headers['content-type'] = 'application/json'
|
|
14
|
-
body = JSON.stringify(
|
|
14
|
+
body = JSON.stringify(request.json)
|
|
15
15
|
}
|
|
16
16
|
if (body && compress) {
|
|
17
17
|
body = zlib.brotliCompressSync(body)
|
|
18
18
|
headers['content-encoding'] = 'br'
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
const fetch = (mockedFetch === false)
|
|
22
|
+
? realFetch
|
|
23
|
+
: (mockedFetch ?? fetchWrapper.__mock ?? realFetch)
|
|
23
24
|
|
|
24
25
|
// compute the full URL including search params
|
|
25
26
|
let fullURL = url
|
|
@@ -29,7 +30,8 @@ async function fetchWrapper (options, mockedFetch) {
|
|
|
29
30
|
fullURL += `?${qsStr}`
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
|
-
|
|
33
|
+
const options = { body, headers, method, compress: false }
|
|
34
|
+
return fetch(fullURL, options)
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
export default fetchWrapper
|
package/test/base-test.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import querystring from 'node:querystring'
|
|
1
2
|
import zlib from 'node:zlib'
|
|
2
3
|
|
|
3
4
|
import { jest } from '@jest/globals'
|
|
@@ -24,6 +25,15 @@ class BaseAppTest extends BaseTest {
|
|
|
24
25
|
return func
|
|
25
26
|
}
|
|
26
27
|
|
|
28
|
+
beforeEach () {
|
|
29
|
+
this.fetchMock = fetchWrapper.__mock
|
|
30
|
+
fetchWrapper.__mock.mockClear()
|
|
31
|
+
|
|
32
|
+
// default response is a weird error to help make it obvious the unit test
|
|
33
|
+
// author forgot to provide a custom mock value
|
|
34
|
+
this.fetchMock.mockResp('custom mock value not set', 555)
|
|
35
|
+
}
|
|
36
|
+
|
|
27
37
|
async beforeAll () {
|
|
28
38
|
const makeService = await this.getMakeServiceFunc()
|
|
29
39
|
this.fastify = FASTIFY_CACHE ?? await makeService()
|
|
@@ -56,10 +66,6 @@ class BaseAppTest extends BaseTest {
|
|
|
56
66
|
}
|
|
57
67
|
})
|
|
58
68
|
}
|
|
59
|
-
|
|
60
|
-
afterEach () {
|
|
61
|
-
fetchWrapper.__mock.mockReset()
|
|
62
|
-
}
|
|
63
69
|
}
|
|
64
70
|
|
|
65
71
|
function makeHeadersObj (headers) {
|
|
@@ -72,15 +78,15 @@ function makeHeadersObj (headers) {
|
|
|
72
78
|
// so you can pass the output of app.post(), etc. as the promise here as is
|
|
73
79
|
function makeNodeFetchMockValueFromPromise (promise) {
|
|
74
80
|
const mockValue = new Promise(resolve => {
|
|
75
|
-
promise.then(
|
|
76
|
-
let body =
|
|
81
|
+
promise.then(httpResponse => {
|
|
82
|
+
let body = httpResponse.text || httpResponse.body || ''
|
|
77
83
|
const headers = {}
|
|
78
84
|
if (typeof body !== 'string') {
|
|
79
85
|
body = JSON.stringify(body)
|
|
80
86
|
headers['content-type'] = 'application/json'
|
|
81
87
|
}
|
|
82
88
|
resolve({
|
|
83
|
-
status:
|
|
89
|
+
status: httpResponse.status || 200,
|
|
84
90
|
headers: makeHeadersObj(headers),
|
|
85
91
|
text: async () => body
|
|
86
92
|
})
|
|
@@ -92,8 +98,8 @@ function makeNodeFetchMockValueFromPromise (promise) {
|
|
|
92
98
|
function makeNodeFetchMockValue (body, status, callback) {
|
|
93
99
|
const mockValue = new Promise(resolve => {
|
|
94
100
|
// setTimeout is used so that this promise does not synchronously resolve
|
|
95
|
-
// because unmocked
|
|
96
|
-
// functions which call
|
|
101
|
+
// because unmocked fetch will NEVER return synchronously. This ensures
|
|
102
|
+
// functions which call fetch() never resolve synchronously (which can
|
|
97
103
|
// change their behavior... e.g., allow them to throw when called,
|
|
98
104
|
// instead of only rejecting later when await'ed).
|
|
99
105
|
// https://github.com/facebook/jest/issues/6028 (since jest 21.x)
|
|
@@ -119,9 +125,7 @@ function makeNodeFetchMockValue (body, status, callback) {
|
|
|
119
125
|
}
|
|
120
126
|
|
|
121
127
|
function mockNodeFetch () {
|
|
122
|
-
const nodeFetchMock = jest.fn().mockImplementation(
|
|
123
|
-
expect(body).toEqual(zlib.brotliCompressSync('321'))
|
|
124
|
-
})
|
|
128
|
+
const nodeFetchMock = jest.fn().mockImplementation()
|
|
125
129
|
|
|
126
130
|
nodeFetchMock.mockResp = (body = '', statusCode = 200, callback) => {
|
|
127
131
|
nodeFetchMock.mockReturnValue(makeNodeFetchMockValue(body, statusCode, callback))
|
|
@@ -135,13 +139,34 @@ function mockNodeFetch () {
|
|
|
135
139
|
* provides a mock response then an error will be thrown
|
|
136
140
|
*/
|
|
137
141
|
nodeFetchMock.mockRespWithCallback = (...callbacks) => {
|
|
138
|
-
nodeFetchMock.mockImplementation(
|
|
139
|
-
|
|
142
|
+
nodeFetchMock.mockImplementation((fullURL, options) => {
|
|
143
|
+
// construct the fetchWrapper's request parameter from the fullURL and
|
|
144
|
+
// options passed as parameters to node-fetch
|
|
145
|
+
const [baseUrl, qsParams] = fullURL.split('?', 2)
|
|
146
|
+
const request = {
|
|
147
|
+
compress: options.headers['content-encoding'] === 'br',
|
|
148
|
+
method: options.method,
|
|
149
|
+
url: baseUrl,
|
|
150
|
+
qsParams: querystring.parse(qsParams),
|
|
151
|
+
headers: options.headers
|
|
152
|
+
}
|
|
153
|
+
delete request.headers['content-encoding']
|
|
154
|
+
const body = options.body
|
|
155
|
+
? (request.compress
|
|
156
|
+
? zlib.brotliDecompressSync(options.body)
|
|
157
|
+
: options.body)
|
|
158
|
+
: options.body
|
|
159
|
+
if (options.headers['content-type'] === 'application/json') {
|
|
160
|
+
delete options.headers['content-type']
|
|
161
|
+
request.json = JSON.parse(body)
|
|
162
|
+
} else {
|
|
163
|
+
request.body = body // may be undefined
|
|
164
|
+
}
|
|
165
|
+
|
|
140
166
|
for (const callback of callbacks) {
|
|
141
167
|
const desiredHTTPResponse = callback(request)
|
|
142
168
|
if (desiredHTTPResponse === true) {
|
|
143
|
-
|
|
144
|
-
return unmockedFetch(url, options)
|
|
169
|
+
return fetchWrapper(request, false)
|
|
145
170
|
}
|
|
146
171
|
if (desiredHTTPResponse) {
|
|
147
172
|
if (desiredHTTPResponse.then) {
|
|
@@ -153,7 +178,7 @@ function mockNodeFetch () {
|
|
|
153
178
|
desiredHTTPResponse.status || 200)
|
|
154
179
|
}
|
|
155
180
|
}
|
|
156
|
-
throw new Error(`un-mocked
|
|
181
|
+
throw new Error(`un-mocked fetchWrapper() request: ${JSON.stringify(request)}`)
|
|
157
182
|
})
|
|
158
183
|
}
|
|
159
184
|
|
|
@@ -162,10 +187,11 @@ function mockNodeFetch () {
|
|
|
162
187
|
let idx = 0
|
|
163
188
|
function setupNextResponse () {
|
|
164
189
|
if (idx < responses.length) {
|
|
165
|
-
|
|
166
|
-
|
|
190
|
+
const [body, code] = responses[idx]
|
|
191
|
+
nodeFetchMock.mockResp(body ?? '', code ?? 200, setupNextResponse)
|
|
192
|
+
} else {
|
|
167
193
|
// exactly equal means we just got our last callback (okay)
|
|
168
|
-
|
|
194
|
+
nodeFetchMock.mockResp('ran out of mocked responses', 556)
|
|
169
195
|
}
|
|
170
196
|
idx += 1
|
|
171
197
|
}
|