@budibase/server 1.0.27-alpha.13 → 1.0.27-alpha.14
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/builder/assets/{index.176f00e0.css → index.39e01193.css} +1 -1
- package/builder/assets/{index.5776ef20.js → index.411e6960.js} +232 -232
- package/builder/index.html +2 -2
- package/coverage/clover.xml +155 -152
- package/coverage/coverage-final.json +4 -4
- package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
- package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/controllers/application.js.html +1 -1
- package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
- package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
- package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
- package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
- package/coverage/lcov-report/api/controllers/component.js.html +1 -1
- package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
- package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
- package/coverage/lcov-report/api/controllers/hosting.js.html +1 -1
- package/coverage/lcov-report/api/controllers/index.html +1 -1
- package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
- package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
- package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
- package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
- package/coverage/lcov-report/api/controllers/query/index.html +1 -1
- package/coverage/lcov-report/api/controllers/query/index.js.html +7 -4
- package/coverage/lcov-report/api/controllers/query/validation.js.html +1 -1
- package/coverage/lcov-report/api/controllers/role.js.html +1 -1
- package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/index.html +1 -1
- package/coverage/lcov-report/api/controllers/row/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
- package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
- package/coverage/lcov-report/api/controllers/script.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/index.html +1 -1
- package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/internal.js.html +1 -1
- package/coverage/lcov-report/api/controllers/table/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
- package/coverage/lcov-report/api/controllers/user.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.html +1 -1
- package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
- package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
- package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
- package/coverage/lcov-report/api/index.html +1 -1
- package/coverage/lcov-report/api/index.js.html +1 -1
- package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
- package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
- package/coverage/lcov-report/api/routes/application.js.html +1 -1
- package/coverage/lcov-report/api/routes/auth.js.html +1 -1
- package/coverage/lcov-report/api/routes/automation.js.html +1 -1
- package/coverage/lcov-report/api/routes/backup.js.html +1 -1
- package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
- package/coverage/lcov-report/api/routes/component.js.html +1 -1
- package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
- package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
- package/coverage/lcov-report/api/routes/dev.js.html +1 -1
- package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
- package/coverage/lcov-report/api/routes/index.html +1 -1
- package/coverage/lcov-report/api/routes/index.js.html +1 -1
- package/coverage/lcov-report/api/routes/integration.js.html +1 -1
- package/coverage/lcov-report/api/routes/layout.js.html +1 -1
- package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
- package/coverage/lcov-report/api/routes/permission.js.html +1 -1
- package/coverage/lcov-report/api/routes/query.js.html +1 -1
- package/coverage/lcov-report/api/routes/role.js.html +1 -1
- package/coverage/lcov-report/api/routes/routing.js.html +1 -1
- package/coverage/lcov-report/api/routes/row.js.html +1 -1
- package/coverage/lcov-report/api/routes/screen.js.html +1 -1
- package/coverage/lcov-report/api/routes/script.js.html +1 -1
- package/coverage/lcov-report/api/routes/static.js.html +1 -1
- package/coverage/lcov-report/api/routes/table.js.html +1 -1
- package/coverage/lcov-report/api/routes/templates.js.html +1 -1
- package/coverage/lcov-report/api/routes/user.js.html +1 -1
- package/coverage/lcov-report/api/routes/view.js.html +1 -1
- package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/actions.js.html +1 -1
- package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
- package/coverage/lcov-report/automations/bullboard.js.html +1 -1
- package/coverage/lcov-report/automations/index.html +1 -1
- package/coverage/lcov-report/automations/index.js.html +1 -1
- package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
- package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
- package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
- package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
- package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
- package/coverage/lcov-report/automations/steps/index.html +1 -1
- package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
- package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
- package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
- package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
- package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
- package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
- package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
- package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
- package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
- package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
- package/coverage/lcov-report/automations/triggers.js.html +1 -1
- package/coverage/lcov-report/automations/utils.js.html +1 -1
- package/coverage/lcov-report/constants/index.html +1 -1
- package/coverage/lcov-report/constants/index.js.html +1 -1
- package/coverage/lcov-report/constants/layouts.js.html +1 -1
- package/coverage/lcov-report/constants/screens.js.html +1 -1
- package/coverage/lcov-report/db/client.js.html +1 -1
- package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
- package/coverage/lcov-report/db/index.html +1 -1
- package/coverage/lcov-report/db/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.html +1 -1
- package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
- package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
- package/coverage/lcov-report/db/newid.js.html +1 -1
- package/coverage/lcov-report/db/utils.js.html +1 -1
- package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
- package/coverage/lcov-report/events/index.html +1 -1
- package/coverage/lcov-report/events/index.js.html +1 -1
- package/coverage/lcov-report/events/utils.js.html +1 -1
- package/coverage/lcov-report/index.html +16 -16
- package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
- package/coverage/lcov-report/integrations/tests/index.html +1 -1
- package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
- package/coverage/lcov-report/middleware/authorized.js.html +1 -1
- package/coverage/lcov-report/middleware/builder.js.html +1 -1
- package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
- package/coverage/lcov-report/middleware/index.html +1 -1
- package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
- package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
- package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
- package/coverage/lcov-report/middleware/usageQuota.js.html +1 -1
- package/coverage/lcov-report/middleware/utils.js.html +1 -1
- package/coverage/lcov-report/threads/automation.js.html +1 -1
- package/coverage/lcov-report/threads/index.html +28 -28
- package/coverage/lcov-report/threads/index.js.html +1 -1
- package/coverage/lcov-report/threads/query.js.html +181 -13
- package/coverage/lcov-report/threads/utils.js.html +11 -161
- package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
- package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
- package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
- package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
- package/coverage/lcov-report/utilities/global.js.html +1 -1
- package/coverage/lcov-report/utilities/index.html +4 -4
- package/coverage/lcov-report/utilities/index.js.html +9 -9
- package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +1 -1
- package/coverage/lcov-report/utilities/queue/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.html +1 -1
- package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
- package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
- package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
- package/coverage/lcov-report/utilities/security.js.html +1 -1
- package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
- package/coverage/lcov-report/utilities/users.js.html +1 -1
- package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
- package/coverage/lcov.info +291 -284
- package/dist/api/controllers/query/index.js +3 -2
- package/dist/environment.js +1 -0
- package/dist/integrations/dynamodb.js +19 -0
- package/dist/integrations/rest.js +68 -19
- package/dist/integrations/s3.js +17 -8
- package/dist/package.json +4 -4
- package/dist/threads/query.js +52 -3
- package/dist/threads/utils.js +0 -44
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utilities/index.js +1 -1
- package/package.json +5 -5
- package/src/api/controllers/query/index.js +3 -2
- package/src/definitions/datasource.ts +15 -0
- package/src/environment.js +1 -0
- package/src/integrations/dynamodb.ts +18 -0
- package/src/integrations/rest.ts +78 -17
- package/src/integrations/s3.ts +18 -7
- package/src/integrations/tests/rest.spec.js +287 -24
- package/src/threads/query.js +59 -3
- package/src/threads/utils.js +0 -50
- package/src/utilities/index.js +1 -1
|
@@ -4,19 +4,23 @@ jest.mock("node-fetch", () =>
|
|
|
4
4
|
raw: () => {
|
|
5
5
|
return { "content-type": ["application/json"] }
|
|
6
6
|
},
|
|
7
|
-
get: () => ["application/json"]
|
|
7
|
+
get: () => ["application/json"],
|
|
8
8
|
},
|
|
9
|
-
json: jest.fn()
|
|
10
|
-
|
|
9
|
+
json: jest.fn(() => ({
|
|
10
|
+
my_next_cursor: 123,
|
|
11
|
+
})),
|
|
12
|
+
text: jest.fn(),
|
|
11
13
|
}))
|
|
12
14
|
)
|
|
13
15
|
const fetch = require("node-fetch")
|
|
14
16
|
const RestIntegration = require("../rest")
|
|
15
17
|
const { AuthType } = require("../rest")
|
|
18
|
+
const FormData = require("form-data")
|
|
19
|
+
const { URLSearchParams } = require("url")
|
|
16
20
|
|
|
17
21
|
const HEADERS = {
|
|
18
|
-
|
|
19
|
-
"Content-Type": "application/json"
|
|
22
|
+
Accept: "application/json",
|
|
23
|
+
"Content-Type": "application/json",
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
class TestConfiguration {
|
|
@@ -165,17 +169,20 @@ describe("REST Integration", () => {
|
|
|
165
169
|
status: 200,
|
|
166
170
|
json: json ? async () => json : undefined,
|
|
167
171
|
text: text ? async () => text : undefined,
|
|
168
|
-
headers: {
|
|
172
|
+
headers: {
|
|
173
|
+
get: key => (key === "content-length" ? 100 : header),
|
|
174
|
+
raw: () => ({ "content-type": header }),
|
|
175
|
+
},
|
|
169
176
|
}
|
|
170
177
|
}
|
|
171
178
|
|
|
172
179
|
it("should be able to parse JSON response", async () => {
|
|
173
|
-
const input = buildInput({a: 1}, null, "application/json")
|
|
180
|
+
const input = buildInput({ a: 1 }, null, "application/json")
|
|
174
181
|
const output = await config.integration.parseResponse(input)
|
|
175
|
-
expect(output.data).toEqual({a: 1})
|
|
182
|
+
expect(output.data).toEqual({ a: 1 })
|
|
176
183
|
expect(output.info.code).toEqual(200)
|
|
177
184
|
expect(output.info.size).toEqual("100B")
|
|
178
|
-
expect(output.extra.raw).toEqual(JSON.stringify({a: 1}))
|
|
185
|
+
expect(output.extra.raw).toEqual(JSON.stringify({ a: 1 }))
|
|
179
186
|
expect(output.extra.headers["content-type"]).toEqual("application/json")
|
|
180
187
|
})
|
|
181
188
|
|
|
@@ -192,7 +199,7 @@ describe("REST Integration", () => {
|
|
|
192
199
|
const text = "<root><a>1</a><b>2</b></root>"
|
|
193
200
|
const input = buildInput(null, text, "application/xml")
|
|
194
201
|
const output = await config.integration.parseResponse(input)
|
|
195
|
-
expect(output.data).toEqual({a: "1", b: "2"})
|
|
202
|
+
expect(output.data).toEqual({ a: "1", b: "2" })
|
|
196
203
|
expect(output.extra.raw).toEqual(text)
|
|
197
204
|
expect(output.extra.headers["content-type"]).toEqual("application/xml")
|
|
198
205
|
})
|
|
@@ -202,53 +209,309 @@ describe("REST Integration", () => {
|
|
|
202
209
|
const basicAuth = {
|
|
203
210
|
_id: "c59c14bd1898a43baa08da68959b24686",
|
|
204
211
|
name: "basic-1",
|
|
205
|
-
type
|
|
206
|
-
config
|
|
212
|
+
type: AuthType.BASIC,
|
|
213
|
+
config: {
|
|
207
214
|
username: "user",
|
|
208
|
-
password: "password"
|
|
209
|
-
}
|
|
215
|
+
password: "password",
|
|
216
|
+
},
|
|
210
217
|
}
|
|
211
|
-
|
|
218
|
+
|
|
212
219
|
const bearerAuth = {
|
|
213
220
|
_id: "0d91d732f34e4befabeff50b392a8ff3",
|
|
214
221
|
name: "bearer-1",
|
|
215
|
-
type
|
|
216
|
-
config
|
|
217
|
-
|
|
218
|
-
}
|
|
222
|
+
type: AuthType.BEARER,
|
|
223
|
+
config: {
|
|
224
|
+
token: "mytoken",
|
|
225
|
+
},
|
|
219
226
|
}
|
|
220
227
|
|
|
221
228
|
beforeEach(() => {
|
|
222
229
|
config = new TestConfiguration({
|
|
223
230
|
url: BASE_URL,
|
|
224
|
-
authConfigs
|
|
231
|
+
authConfigs: [basicAuth, bearerAuth],
|
|
225
232
|
})
|
|
226
233
|
})
|
|
227
234
|
|
|
228
235
|
it("adds basic auth", async () => {
|
|
229
236
|
const query = {
|
|
230
|
-
authConfigId: "c59c14bd1898a43baa08da68959b24686"
|
|
237
|
+
authConfigId: "c59c14bd1898a43baa08da68959b24686",
|
|
231
238
|
}
|
|
232
239
|
await config.integration.read(query)
|
|
233
240
|
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/?`, {
|
|
234
241
|
method: "GET",
|
|
235
242
|
headers: {
|
|
236
|
-
Authorization: "Basic dXNlcjpwYXNzd29yZA=="
|
|
243
|
+
Authorization: "Basic dXNlcjpwYXNzd29yZA==",
|
|
237
244
|
},
|
|
238
245
|
})
|
|
239
246
|
})
|
|
240
247
|
|
|
241
248
|
it("adds bearer auth", async () => {
|
|
242
249
|
const query = {
|
|
243
|
-
authConfigId: "0d91d732f34e4befabeff50b392a8ff3"
|
|
250
|
+
authConfigId: "0d91d732f34e4befabeff50b392a8ff3",
|
|
244
251
|
}
|
|
245
252
|
await config.integration.read(query)
|
|
246
253
|
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/?`, {
|
|
247
254
|
method: "GET",
|
|
248
255
|
headers: {
|
|
249
|
-
Authorization: "Bearer mytoken"
|
|
256
|
+
Authorization: "Bearer mytoken",
|
|
257
|
+
},
|
|
258
|
+
})
|
|
259
|
+
})
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
describe("page based pagination", () => {
|
|
263
|
+
it("can paginate using query params", async () => {
|
|
264
|
+
const pageParam = "my_page_param"
|
|
265
|
+
const sizeParam = "my_size_param"
|
|
266
|
+
const pageValue = 3
|
|
267
|
+
const sizeValue = 10
|
|
268
|
+
const query = {
|
|
269
|
+
path: "api",
|
|
270
|
+
pagination: {
|
|
271
|
+
type: "page",
|
|
272
|
+
location: "query",
|
|
273
|
+
pageParam,
|
|
274
|
+
sizeParam,
|
|
275
|
+
},
|
|
276
|
+
paginationValues: {
|
|
277
|
+
page: pageValue,
|
|
278
|
+
limit: sizeValue,
|
|
279
|
+
},
|
|
280
|
+
}
|
|
281
|
+
await config.integration.read(query)
|
|
282
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
283
|
+
`${BASE_URL}/api?${pageParam}=${pageValue}&${sizeParam}=${sizeValue}&`,
|
|
284
|
+
{
|
|
285
|
+
headers: {},
|
|
286
|
+
method: "GET",
|
|
287
|
+
}
|
|
288
|
+
)
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
it("can paginate using JSON request body", async () => {
|
|
292
|
+
const pageParam = "my_page_param"
|
|
293
|
+
const sizeParam = "my_size_param"
|
|
294
|
+
const pageValue = 3
|
|
295
|
+
const sizeValue = 10
|
|
296
|
+
const query = {
|
|
297
|
+
bodyType: "json",
|
|
298
|
+
path: "api",
|
|
299
|
+
pagination: {
|
|
300
|
+
type: "page",
|
|
301
|
+
location: "body",
|
|
302
|
+
pageParam,
|
|
303
|
+
sizeParam,
|
|
304
|
+
},
|
|
305
|
+
paginationValues: {
|
|
306
|
+
page: pageValue,
|
|
307
|
+
limit: sizeValue,
|
|
308
|
+
},
|
|
309
|
+
}
|
|
310
|
+
await config.integration.create(query)
|
|
311
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?`, {
|
|
312
|
+
body: JSON.stringify({
|
|
313
|
+
[pageParam]: pageValue,
|
|
314
|
+
[sizeParam]: sizeValue,
|
|
315
|
+
}),
|
|
316
|
+
headers: {
|
|
317
|
+
"Content-Type": "application/json",
|
|
318
|
+
},
|
|
319
|
+
method: "POST",
|
|
320
|
+
})
|
|
321
|
+
})
|
|
322
|
+
|
|
323
|
+
it("can paginate using form-data request body", async () => {
|
|
324
|
+
const pageParam = "my_page_param"
|
|
325
|
+
const sizeParam = "my_size_param"
|
|
326
|
+
const pageValue = 3
|
|
327
|
+
const sizeValue = 10
|
|
328
|
+
const query = {
|
|
329
|
+
bodyType: "form",
|
|
330
|
+
path: "api",
|
|
331
|
+
pagination: {
|
|
332
|
+
type: "page",
|
|
333
|
+
location: "body",
|
|
334
|
+
pageParam,
|
|
335
|
+
sizeParam,
|
|
336
|
+
},
|
|
337
|
+
paginationValues: {
|
|
338
|
+
page: pageValue,
|
|
339
|
+
limit: sizeValue,
|
|
340
|
+
},
|
|
341
|
+
}
|
|
342
|
+
await config.integration.create(query)
|
|
343
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?`, {
|
|
344
|
+
body: expect.any(FormData),
|
|
345
|
+
headers: {},
|
|
346
|
+
method: "POST",
|
|
347
|
+
})
|
|
348
|
+
const sentData = JSON.stringify(fetch.mock.calls[0][1].body)
|
|
349
|
+
expect(sentData).toContain(pageParam)
|
|
350
|
+
expect(sentData).toContain(sizeParam)
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
it("can paginate using form-encoded request body", async () => {
|
|
354
|
+
const pageParam = "my_page_param"
|
|
355
|
+
const sizeParam = "my_size_param"
|
|
356
|
+
const pageValue = 3
|
|
357
|
+
const sizeValue = 10
|
|
358
|
+
const query = {
|
|
359
|
+
bodyType: "encoded",
|
|
360
|
+
path: "api",
|
|
361
|
+
pagination: {
|
|
362
|
+
type: "page",
|
|
363
|
+
location: "body",
|
|
364
|
+
pageParam,
|
|
365
|
+
sizeParam,
|
|
366
|
+
},
|
|
367
|
+
paginationValues: {
|
|
368
|
+
page: pageValue,
|
|
369
|
+
limit: sizeValue,
|
|
370
|
+
},
|
|
371
|
+
}
|
|
372
|
+
await config.integration.create(query)
|
|
373
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?`, {
|
|
374
|
+
body: expect.any(URLSearchParams),
|
|
375
|
+
headers: {},
|
|
376
|
+
method: "POST",
|
|
377
|
+
})
|
|
378
|
+
const sentData = fetch.mock.calls[0][1].body
|
|
379
|
+
expect(sentData.has(pageParam))
|
|
380
|
+
expect(sentData.get(pageParam)).toEqual(pageValue.toString())
|
|
381
|
+
expect(sentData.has(sizeParam))
|
|
382
|
+
expect(sentData.get(sizeParam)).toEqual(sizeValue.toString())
|
|
383
|
+
})
|
|
384
|
+
})
|
|
385
|
+
|
|
386
|
+
describe("cursor based pagination", () => {
|
|
387
|
+
it("can paginate using query params", async () => {
|
|
388
|
+
const pageParam = "my_page_param"
|
|
389
|
+
const sizeParam = "my_size_param"
|
|
390
|
+
const pageValue = 3
|
|
391
|
+
const sizeValue = 10
|
|
392
|
+
const query = {
|
|
393
|
+
path: "api",
|
|
394
|
+
pagination: {
|
|
395
|
+
type: "cursor",
|
|
396
|
+
location: "query",
|
|
397
|
+
pageParam,
|
|
398
|
+
sizeParam,
|
|
399
|
+
responseParam: "my_next_cursor",
|
|
400
|
+
},
|
|
401
|
+
paginationValues: {
|
|
402
|
+
page: pageValue,
|
|
403
|
+
limit: sizeValue,
|
|
404
|
+
},
|
|
405
|
+
}
|
|
406
|
+
const res = await config.integration.read(query)
|
|
407
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
408
|
+
`${BASE_URL}/api?${pageParam}=${pageValue}&${sizeParam}=${sizeValue}&`,
|
|
409
|
+
{
|
|
410
|
+
headers: {},
|
|
411
|
+
method: "GET",
|
|
412
|
+
}
|
|
413
|
+
)
|
|
414
|
+
expect(res.pagination.cursor).toEqual(123)
|
|
415
|
+
})
|
|
416
|
+
|
|
417
|
+
it("can paginate using JSON request body", async () => {
|
|
418
|
+
const pageParam = "my_page_param"
|
|
419
|
+
const sizeParam = "my_size_param"
|
|
420
|
+
const pageValue = 3
|
|
421
|
+
const sizeValue = 10
|
|
422
|
+
const query = {
|
|
423
|
+
bodyType: "json",
|
|
424
|
+
path: "api",
|
|
425
|
+
pagination: {
|
|
426
|
+
type: "page",
|
|
427
|
+
location: "body",
|
|
428
|
+
pageParam,
|
|
429
|
+
sizeParam,
|
|
430
|
+
responseParam: "my_next_cursor",
|
|
431
|
+
},
|
|
432
|
+
paginationValues: {
|
|
433
|
+
page: pageValue,
|
|
434
|
+
limit: sizeValue,
|
|
435
|
+
},
|
|
436
|
+
}
|
|
437
|
+
const res = await config.integration.create(query)
|
|
438
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?`, {
|
|
439
|
+
body: JSON.stringify({
|
|
440
|
+
[pageParam]: pageValue,
|
|
441
|
+
[sizeParam]: sizeValue,
|
|
442
|
+
}),
|
|
443
|
+
headers: {
|
|
444
|
+
"Content-Type": "application/json",
|
|
445
|
+
},
|
|
446
|
+
method: "POST",
|
|
447
|
+
})
|
|
448
|
+
expect(res.pagination.cursor).toEqual(123)
|
|
449
|
+
})
|
|
450
|
+
|
|
451
|
+
it("can paginate using form-data request body", async () => {
|
|
452
|
+
const pageParam = "my_page_param"
|
|
453
|
+
const sizeParam = "my_size_param"
|
|
454
|
+
const pageValue = 3
|
|
455
|
+
const sizeValue = 10
|
|
456
|
+
const query = {
|
|
457
|
+
bodyType: "form",
|
|
458
|
+
path: "api",
|
|
459
|
+
pagination: {
|
|
460
|
+
type: "page",
|
|
461
|
+
location: "body",
|
|
462
|
+
pageParam,
|
|
463
|
+
sizeParam,
|
|
464
|
+
responseParam: "my_next_cursor",
|
|
465
|
+
},
|
|
466
|
+
paginationValues: {
|
|
467
|
+
page: pageValue,
|
|
468
|
+
limit: sizeValue,
|
|
250
469
|
},
|
|
470
|
+
}
|
|
471
|
+
const res = await config.integration.create(query)
|
|
472
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?`, {
|
|
473
|
+
body: expect.any(FormData),
|
|
474
|
+
headers: {},
|
|
475
|
+
method: "POST",
|
|
476
|
+
})
|
|
477
|
+
const sentData = JSON.stringify(fetch.mock.calls[0][1].body)
|
|
478
|
+
expect(sentData).toContain(pageParam)
|
|
479
|
+
expect(sentData).toContain(sizeParam)
|
|
480
|
+
expect(res.pagination.cursor).toEqual(123)
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
it("can paginate using form-encoded request body", async () => {
|
|
484
|
+
const pageParam = "my_page_param"
|
|
485
|
+
const sizeParam = "my_size_param"
|
|
486
|
+
const pageValue = 3
|
|
487
|
+
const sizeValue = 10
|
|
488
|
+
const query = {
|
|
489
|
+
bodyType: "encoded",
|
|
490
|
+
path: "api",
|
|
491
|
+
pagination: {
|
|
492
|
+
type: "page",
|
|
493
|
+
location: "body",
|
|
494
|
+
pageParam,
|
|
495
|
+
sizeParam,
|
|
496
|
+
responseParam: "my_next_cursor",
|
|
497
|
+
},
|
|
498
|
+
paginationValues: {
|
|
499
|
+
page: pageValue,
|
|
500
|
+
limit: sizeValue,
|
|
501
|
+
},
|
|
502
|
+
}
|
|
503
|
+
const res = await config.integration.create(query)
|
|
504
|
+
expect(fetch).toHaveBeenCalledWith(`${BASE_URL}/api?`, {
|
|
505
|
+
body: expect.any(URLSearchParams),
|
|
506
|
+
headers: {},
|
|
507
|
+
method: "POST",
|
|
251
508
|
})
|
|
509
|
+
const sentData = fetch.mock.calls[0][1].body
|
|
510
|
+
expect(sentData.has(pageParam))
|
|
511
|
+
expect(sentData.get(pageParam)).toEqual(pageValue.toString())
|
|
512
|
+
expect(sentData.has(sizeParam))
|
|
513
|
+
expect(sentData.get(sizeParam)).toEqual(sizeValue.toString())
|
|
514
|
+
expect(res.pagination.cursor).toEqual(123)
|
|
252
515
|
})
|
|
253
516
|
})
|
|
254
517
|
})
|
package/src/threads/query.js
CHANGED
|
@@ -5,6 +5,9 @@ const { integrations } = require("../integrations")
|
|
|
5
5
|
const { processStringSync } = require("@budibase/string-templates")
|
|
6
6
|
const CouchDB = require("../db")
|
|
7
7
|
|
|
8
|
+
const IS_TRIPLE_BRACE = new RegExp(/^{{3}.*}{3}$/)
|
|
9
|
+
const IS_HANDLEBARS = new RegExp(/^{{2}.*}{2}$/)
|
|
10
|
+
|
|
8
11
|
class QueryRunner {
|
|
9
12
|
constructor(input, flags = { noRecursiveQuery: false }) {
|
|
10
13
|
this.appId = input.appId
|
|
@@ -12,6 +15,7 @@ class QueryRunner {
|
|
|
12
15
|
this.queryVerb = input.queryVerb
|
|
13
16
|
this.fields = input.fields
|
|
14
17
|
this.parameters = input.parameters
|
|
18
|
+
this.pagination = input.pagination
|
|
15
19
|
this.transformer = input.transformer
|
|
16
20
|
this.queryId = input.queryId
|
|
17
21
|
this.noRecursiveQuery = flags.noRecursiveQuery
|
|
@@ -27,7 +31,13 @@ class QueryRunner {
|
|
|
27
31
|
let { datasource, fields, queryVerb, transformer } = this
|
|
28
32
|
// pre-query, make sure datasource variables are added to parameters
|
|
29
33
|
const parameters = await this.addDatasourceVariables()
|
|
30
|
-
|
|
34
|
+
let query = this.enrichQueryFields(fields, parameters)
|
|
35
|
+
|
|
36
|
+
// Add pagination values for REST queries
|
|
37
|
+
if (this.pagination) {
|
|
38
|
+
query.paginationValues = this.pagination
|
|
39
|
+
}
|
|
40
|
+
|
|
31
41
|
const Integration = integrations[datasource.source]
|
|
32
42
|
if (!Integration) {
|
|
33
43
|
throw "Integration type does not exist."
|
|
@@ -37,11 +47,13 @@ class QueryRunner {
|
|
|
37
47
|
let output = threadUtils.formatResponse(await integration[queryVerb](query))
|
|
38
48
|
let rows = output,
|
|
39
49
|
info = undefined,
|
|
40
|
-
extra = undefined
|
|
50
|
+
extra = undefined,
|
|
51
|
+
pagination = undefined
|
|
41
52
|
if (threadUtils.hasExtraData(output)) {
|
|
42
53
|
rows = output.data
|
|
43
54
|
info = output.info
|
|
44
55
|
extra = output.extra
|
|
56
|
+
pagination = output.pagination
|
|
45
57
|
}
|
|
46
58
|
|
|
47
59
|
// transform as required
|
|
@@ -83,7 +95,7 @@ class QueryRunner {
|
|
|
83
95
|
integration.end()
|
|
84
96
|
}
|
|
85
97
|
|
|
86
|
-
return { rows, keys, info, extra }
|
|
98
|
+
return { rows, keys, info, extra, pagination }
|
|
87
99
|
}
|
|
88
100
|
|
|
89
101
|
async runAnotherQuery(queryId, parameters) {
|
|
@@ -159,6 +171,50 @@ class QueryRunner {
|
|
|
159
171
|
}
|
|
160
172
|
return parameters
|
|
161
173
|
}
|
|
174
|
+
|
|
175
|
+
enrichQueryFields(fields, parameters = {}) {
|
|
176
|
+
const enrichedQuery = {}
|
|
177
|
+
|
|
178
|
+
// enrich the fields with dynamic parameters
|
|
179
|
+
for (let key of Object.keys(fields)) {
|
|
180
|
+
if (fields[key] == null) {
|
|
181
|
+
continue
|
|
182
|
+
}
|
|
183
|
+
if (typeof fields[key] === "object") {
|
|
184
|
+
// enrich nested fields object
|
|
185
|
+
enrichedQuery[key] = this.enrichQueryFields(fields[key], parameters)
|
|
186
|
+
} else if (typeof fields[key] === "string") {
|
|
187
|
+
// enrich string value as normal
|
|
188
|
+
let value = fields[key]
|
|
189
|
+
// add triple brace to avoid escaping e.g. '=' in cookie header
|
|
190
|
+
if (IS_HANDLEBARS.test(value) && !IS_TRIPLE_BRACE.test(value)) {
|
|
191
|
+
value = `{${value}}`
|
|
192
|
+
}
|
|
193
|
+
enrichedQuery[key] = processStringSync(value, parameters, {
|
|
194
|
+
noHelpers: true,
|
|
195
|
+
})
|
|
196
|
+
} else {
|
|
197
|
+
enrichedQuery[key] = fields[key]
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
if (
|
|
201
|
+
enrichedQuery.json ||
|
|
202
|
+
enrichedQuery.customData ||
|
|
203
|
+
enrichedQuery.requestBody
|
|
204
|
+
) {
|
|
205
|
+
try {
|
|
206
|
+
enrichedQuery.json = JSON.parse(
|
|
207
|
+
enrichedQuery.json ||
|
|
208
|
+
enrichedQuery.customData ||
|
|
209
|
+
enrichedQuery.requestBody
|
|
210
|
+
)
|
|
211
|
+
} catch (err) {
|
|
212
|
+
// no json found, ignore
|
|
213
|
+
}
|
|
214
|
+
delete enrichedQuery.customData
|
|
215
|
+
}
|
|
216
|
+
return enrichedQuery
|
|
217
|
+
}
|
|
162
218
|
}
|
|
163
219
|
|
|
164
220
|
module.exports = (input, callback) => {
|
package/src/threads/utils.js
CHANGED
|
@@ -3,14 +3,10 @@ const CouchDB = require("../db")
|
|
|
3
3
|
const { init } = require("@budibase/backend-core")
|
|
4
4
|
const redis = require("@budibase/backend-core/redis")
|
|
5
5
|
const { SEPARATOR } = require("@budibase/backend-core/db")
|
|
6
|
-
const { processStringSync } = require("@budibase/string-templates")
|
|
7
6
|
|
|
8
7
|
const VARIABLE_TTL_SECONDS = 3600
|
|
9
8
|
let client
|
|
10
9
|
|
|
11
|
-
const IS_TRIPLE_BRACE = new RegExp(/^{{3}.*}{3}$/)
|
|
12
|
-
const IS_HANDLEBARS = new RegExp(/^{{2}.*}{2}$/)
|
|
13
|
-
|
|
14
10
|
async function getClient() {
|
|
15
11
|
if (!client) {
|
|
16
12
|
client = await new redis.Client(redis.utils.Databases.QUERY_VARS).init()
|
|
@@ -80,49 +76,3 @@ exports.hasExtraData = response => {
|
|
|
80
76
|
response.info != null
|
|
81
77
|
)
|
|
82
78
|
}
|
|
83
|
-
|
|
84
|
-
exports.enrichQueryFields = (fields, parameters = {}) => {
|
|
85
|
-
const enrichedQuery = {}
|
|
86
|
-
|
|
87
|
-
// enrich the fields with dynamic parameters
|
|
88
|
-
for (let key of Object.keys(fields)) {
|
|
89
|
-
if (fields[key] == null) {
|
|
90
|
-
continue
|
|
91
|
-
}
|
|
92
|
-
if (typeof fields[key] === "object") {
|
|
93
|
-
// enrich nested fields object
|
|
94
|
-
enrichedQuery[key] = this.enrichQueryFields(fields[key], parameters)
|
|
95
|
-
} else if (typeof fields[key] === "string") {
|
|
96
|
-
// enrich string value as normal
|
|
97
|
-
let value = fields[key]
|
|
98
|
-
// add triple brace to avoid escaping e.g. '=' in cookie header
|
|
99
|
-
if (IS_HANDLEBARS.test(value) && !IS_TRIPLE_BRACE.test(value)) {
|
|
100
|
-
value = `{${value}}`
|
|
101
|
-
}
|
|
102
|
-
enrichedQuery[key] = processStringSync(value, parameters, {
|
|
103
|
-
noHelpers: true,
|
|
104
|
-
})
|
|
105
|
-
} else {
|
|
106
|
-
enrichedQuery[key] = fields[key]
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
if (
|
|
111
|
-
enrichedQuery.json ||
|
|
112
|
-
enrichedQuery.customData ||
|
|
113
|
-
enrichedQuery.requestBody
|
|
114
|
-
) {
|
|
115
|
-
try {
|
|
116
|
-
enrichedQuery.json = JSON.parse(
|
|
117
|
-
enrichedQuery.json ||
|
|
118
|
-
enrichedQuery.customData ||
|
|
119
|
-
enrichedQuery.requestBody
|
|
120
|
-
)
|
|
121
|
-
} catch (err) {
|
|
122
|
-
// no json found, ignore
|
|
123
|
-
}
|
|
124
|
-
delete enrichedQuery.customData
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
return enrichedQuery
|
|
128
|
-
}
|
package/src/utilities/index.js
CHANGED
|
@@ -34,7 +34,7 @@ exports.checkSlashesInUrl = url => {
|
|
|
34
34
|
* @return {string} The base URL of the object store (MinIO or S3).
|
|
35
35
|
*/
|
|
36
36
|
exports.objectStoreUrl = () => {
|
|
37
|
-
if (env.SELF_HOSTED) {
|
|
37
|
+
if (env.SELF_HOSTED || env.MINIO_URL) {
|
|
38
38
|
// can use a relative url for this as all goes through the proxy (this is hosted in minio)
|
|
39
39
|
return OBJ_STORE_DIRECTORY
|
|
40
40
|
} else {
|