@budibase/server 1.0.27-alpha.13 → 1.0.27-alpha.17

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.
Files changed (191) hide show
  1. package/builder/assets/{index.176f00e0.css → index.39e01193.css} +1 -1
  2. package/builder/assets/{index.5776ef20.js → index.4ba89e2d.js} +232 -232
  3. package/builder/index.html +2 -2
  4. package/coverage/clover.xml +155 -152
  5. package/coverage/coverage-final.json +4 -4
  6. package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
  7. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  8. package/coverage/lcov-report/api/controllers/application.js.html +1 -1
  9. package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
  10. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  11. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  12. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/component.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
  15. package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
  16. package/coverage/lcov-report/api/controllers/hosting.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/index.html +1 -1
  18. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/query/index.html +1 -1
  23. package/coverage/lcov-report/api/controllers/query/index.js.html +7 -4
  24. package/coverage/lcov-report/api/controllers/query/validation.js.html +1 -1
  25. package/coverage/lcov-report/api/controllers/role.js.html +1 -1
  26. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/index.html +1 -1
  29. package/coverage/lcov-report/api/controllers/row/index.js.html +1 -1
  30. package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
  31. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
  32. package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
  33. package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
  34. package/coverage/lcov-report/api/controllers/script.js.html +1 -1
  35. package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
  36. package/coverage/lcov-report/api/controllers/table/index.html +1 -1
  37. package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
  38. package/coverage/lcov-report/api/controllers/table/internal.js.html +1 -1
  39. package/coverage/lcov-report/api/controllers/table/utils.js.html +1 -1
  40. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  41. package/coverage/lcov-report/api/controllers/user.js.html +1 -1
  42. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  43. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  44. package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
  45. package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
  46. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
  47. package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
  48. package/coverage/lcov-report/api/index.html +1 -1
  49. package/coverage/lcov-report/api/index.js.html +1 -1
  50. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  51. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/index.html +1 -1
  63. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/static.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  80. package/coverage/lcov-report/automations/actions.js.html +1 -1
  81. package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
  82. package/coverage/lcov-report/automations/bullboard.js.html +1 -1
  83. package/coverage/lcov-report/automations/index.html +1 -1
  84. package/coverage/lcov-report/automations/index.js.html +1 -1
  85. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  87. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  89. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/index.html +1 -1
  94. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  96. package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  98. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  99. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  100. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  101. package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
  102. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  103. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  104. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  105. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  106. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  107. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  108. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  109. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  110. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  111. package/coverage/lcov-report/automations/triggers.js.html +1 -1
  112. package/coverage/lcov-report/automations/utils.js.html +1 -1
  113. package/coverage/lcov-report/constants/index.html +1 -1
  114. package/coverage/lcov-report/constants/index.js.html +1 -1
  115. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  116. package/coverage/lcov-report/constants/screens.js.html +1 -1
  117. package/coverage/lcov-report/db/client.js.html +1 -1
  118. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  119. package/coverage/lcov-report/db/index.html +1 -1
  120. package/coverage/lcov-report/db/index.js.html +1 -1
  121. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
  122. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  123. package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
  124. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  125. package/coverage/lcov-report/db/newid.js.html +1 -1
  126. package/coverage/lcov-report/db/utils.js.html +1 -1
  127. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  128. package/coverage/lcov-report/events/index.html +1 -1
  129. package/coverage/lcov-report/events/index.js.html +1 -1
  130. package/coverage/lcov-report/events/utils.js.html +1 -1
  131. package/coverage/lcov-report/index.html +16 -16
  132. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  133. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  134. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  135. package/coverage/lcov-report/middleware/authorized.js.html +1 -1
  136. package/coverage/lcov-report/middleware/builder.js.html +1 -1
  137. package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
  138. package/coverage/lcov-report/middleware/index.html +1 -1
  139. package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
  140. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  141. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  142. package/coverage/lcov-report/middleware/usageQuota.js.html +1 -1
  143. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  144. package/coverage/lcov-report/threads/automation.js.html +1 -1
  145. package/coverage/lcov-report/threads/index.html +28 -28
  146. package/coverage/lcov-report/threads/index.js.html +1 -1
  147. package/coverage/lcov-report/threads/query.js.html +181 -13
  148. package/coverage/lcov-report/threads/utils.js.html +11 -161
  149. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  150. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  151. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  152. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  153. package/coverage/lcov-report/utilities/global.js.html +1 -1
  154. package/coverage/lcov-report/utilities/index.html +4 -4
  155. package/coverage/lcov-report/utilities/index.js.html +9 -9
  156. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +1 -1
  157. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  158. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  159. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  160. package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
  161. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
  162. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
  163. package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
  164. package/coverage/lcov-report/utilities/security.js.html +1 -1
  165. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  166. package/coverage/lcov-report/utilities/users.js.html +1 -1
  167. package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
  168. package/coverage/lcov.info +291 -284
  169. package/dist/api/controllers/query/index.js +3 -2
  170. package/dist/environment.js +1 -0
  171. package/dist/integrations/dynamodb.js +19 -0
  172. package/dist/integrations/rest.js +68 -19
  173. package/dist/integrations/s3.js +17 -8
  174. package/dist/integrations/utils.js +40 -37
  175. package/dist/package.json +4 -4
  176. package/dist/threads/query.js +52 -3
  177. package/dist/threads/utils.js +0 -44
  178. package/dist/tsconfig.tsbuildinfo +1 -1
  179. package/dist/utilities/index.js +1 -1
  180. package/package.json +5 -5
  181. package/src/api/controllers/query/index.js +3 -2
  182. package/src/definitions/datasource.ts +15 -0
  183. package/src/environment.js +1 -0
  184. package/src/integrations/dynamodb.ts +18 -0
  185. package/src/integrations/rest.ts +97 -17
  186. package/src/integrations/s3.ts +18 -7
  187. package/src/integrations/tests/rest.spec.js +287 -24
  188. package/src/integrations/utils.ts +8 -7
  189. package/src/threads/query.js +59 -3
  190. package/src/threads/utils.js +0 -50
  191. 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
- text: jest.fn()
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
- "Accept": "application/json",
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: { get: key => key === "content-length" ? 100 : header, raw: () => ({ "content-type": header }) }
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 : AuthType.BASIC,
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 : AuthType.BEARER,
216
- config : {
217
- "token": "mytoken"
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 : [basicAuth, bearerAuth]
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
  })
@@ -1,12 +1,8 @@
1
1
  import { SqlQuery } from "../definitions/datasource"
2
2
  import { Datasource, Table } from "../definitions/common"
3
3
  import { SourceNames } from "../definitions/datasource"
4
- const { DocumentTypes, SEPARATOR } = require("../db/utils")
5
- const {
6
- FieldTypes,
7
- BuildSchemaErrors,
8
- InvalidColumns,
9
- } = require("../constants")
4
+ import { DocumentTypes, SEPARATOR } from "../db/utils"
5
+ import { FieldTypes, BuildSchemaErrors, InvalidColumns } from "../constants"
10
6
 
11
7
  const DOUBLE_SEPARATOR = `${SEPARATOR}${SEPARATOR}`
12
8
  const ROW_ID_REGEX = /^\[.*]$/g
@@ -158,7 +154,12 @@ function copyExistingPropsOver(
158
154
  if (!existingTableSchema.hasOwnProperty(key)) {
159
155
  continue
160
156
  }
161
- if (existingTableSchema[key].type === "link") {
157
+ if (
158
+ existingTableSchema[key].type === FieldTypes.LINK ||
159
+ existingTableSchema[key].type === FieldTypes.OPTIONS ||
160
+ ((!table.schema[key] || table.schema[key].type === FieldTypes.NUMBER) &&
161
+ existingTableSchema[key].type === FieldTypes.BOOLEAN)
162
+ ) {
162
163
  table.schema[key] = existingTableSchema[key]
163
164
  }
164
165
  }
@@ -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
- const query = threadUtils.enrichQueryFields(fields, parameters)
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) => {
@@ -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
- }
@@ -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 {