@budibase/server 1.0.27-alpha.12 → 1.0.27-alpha.16

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 (210) hide show
  1. package/builder/assets/{index.0dce895b.css → index.39e01193.css} +1 -1
  2. package/builder/assets/{index.cd16a193.js → index.7d39f11e.js} +239 -239
  3. package/builder/index.html +2 -2
  4. package/coverage/clover.xml +197 -194
  5. package/coverage/coverage-final.json +6 -6
  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 +2 -2
  16. package/coverage/lcov-report/api/controllers/hosting.js.html +2 -2
  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 +2 -2
  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 +2 -2
  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 +3 -3
  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 +17 -5
  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 +2 -2
  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 +2 -2
  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 +10 -10
  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 +373 -366
  169. package/dist/api/controllers/dev.js +1 -1
  170. package/dist/api/controllers/hosting.js +1 -1
  171. package/dist/api/controllers/query/index.js +3 -2
  172. package/dist/api/index.js +1 -1
  173. package/dist/automations/steps/executeQuery.js +1 -1
  174. package/dist/constants/index.js +2 -2
  175. package/dist/environment.js +1 -0
  176. package/dist/integrations/dynamodb.js +19 -0
  177. package/dist/integrations/rest.js +68 -19
  178. package/dist/integrations/s3.js +17 -8
  179. package/dist/middleware/currentapp.js +2 -2
  180. package/dist/package.json +4 -4
  181. package/dist/tests/utilities/TestConfiguration.js +2 -2
  182. package/dist/threads/automation.js +1 -1
  183. package/dist/threads/query.js +52 -3
  184. package/dist/threads/utils.js +0 -44
  185. package/dist/tsconfig.tsbuildinfo +1 -1
  186. package/dist/utilities/budibaseDir.js +1 -1
  187. package/dist/utilities/fileSystem/utilities.js +1 -1
  188. package/dist/utilities/index.js +2 -2
  189. package/package.json +5 -5
  190. package/src/api/controllers/dev.js +1 -1
  191. package/src/api/controllers/hosting.js +1 -1
  192. package/src/api/controllers/query/index.js +3 -2
  193. package/src/api/index.js +1 -1
  194. package/src/automations/steps/executeQuery.js +1 -1
  195. package/src/constants/index.js +2 -2
  196. package/src/definitions/datasource.ts +15 -0
  197. package/src/environment.js +1 -0
  198. package/src/integrations/dynamodb.ts +18 -0
  199. package/src/integrations/rest.ts +78 -17
  200. package/src/integrations/s3.ts +18 -7
  201. package/src/integrations/tests/rest.spec.js +287 -24
  202. package/src/middleware/currentapp.js +7 -3
  203. package/src/middleware/tests/currentapp.spec.js +34 -40
  204. package/src/tests/utilities/TestConfiguration.js +2 -2
  205. package/src/threads/automation.js +1 -1
  206. package/src/threads/query.js +59 -3
  207. package/src/threads/utils.js +0 -50
  208. package/src/utilities/budibaseDir.js +1 -1
  209. package/src/utilities/fileSystem/utilities.js +1 -1
  210. package/src/utilities/index.js +2 -2
@@ -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,6 +1,10 @@
1
- const { getAppId, setCookie, getCookie, clearCookie } =
2
- require("@budibase/backend-core").utils
3
- const { Cookies } = require("@budibase/backend-core").constants
1
+ const {
2
+ getAppId,
3
+ setCookie,
4
+ getCookie,
5
+ clearCookie,
6
+ } = require("@budibase/backend-core/utils")
7
+ const { Cookies } = require("@budibase/backend-core/constants")
4
8
  const { getRole } = require("@budibase/backend-core/roles")
5
9
  const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles")
6
10
  const { generateUserMetadataID, isDevAppID } = require("../db/utils")
@@ -32,34 +32,30 @@ function mockAuthWithNoCookie() {
32
32
  },
33
33
  },
34
34
  }))
35
- jest.mock("@budibase/backend-core", () => ({
36
- utils: {
37
- getAppId: jest.fn(),
38
- setCookie: jest.fn(),
39
- getCookie: jest.fn(),
40
- },
41
- constants: {
42
- Cookies: {},
43
- },
35
+ jest.mock("@budibase/backend-core/utils", () => ({
36
+ getAppId: jest.fn(),
37
+ setCookie: jest.fn(),
38
+ getCookie: jest.fn(),
39
+ }))
40
+ jest.mock("@budibase/backend-core/constants", () => ({
41
+ Cookies: {},
44
42
  }))
45
43
  }
46
44
 
47
45
  function mockAuthWithCookie() {
48
46
  jest.resetModules()
49
47
  mockWorker()
50
- jest.mock("@budibase/backend-core", () => ({
51
- utils: {
52
- getAppId: () => {
53
- return "app_test"
54
- },
55
- setCookie: jest.fn(),
56
- getCookie: () => ({appId: "app_different", roleId: "PUBLIC"}),
48
+ jest.mock("@budibase/backend-core/utils", () => ({
49
+ getAppId: () => {
50
+ return "app_test"
57
51
  },
58
- constants: {
59
- Cookies: {
60
- Auth: "auth",
61
- CurrentApp: "currentapp",
62
- },
52
+ setCookie: jest.fn(),
53
+ getCookie: () => ({appId: "app_different", roleId: "PUBLIC"}),
54
+ }))
55
+ jest.mock("@budibase/backend-core/constants", () => ({
56
+ Cookies: {
57
+ Auth: "auth",
58
+ CurrentApp: "currentapp",
63
59
  },
64
60
  }))
65
61
  }
@@ -121,7 +117,7 @@ describe("Current app middleware", () => {
121
117
  async function checkExpected(setCookie) {
122
118
  config.setUser()
123
119
  await config.executeMiddleware()
124
- const cookieFn = require("@budibase/backend-core").utils.setCookie
120
+ let { setCookie: cookieFn } = require("@budibase/backend-core/utils")
125
121
  if (setCookie) {
126
122
  expect(cookieFn).toHaveBeenCalled()
127
123
  } else {
@@ -140,32 +136,30 @@ describe("Current app middleware", () => {
140
136
 
141
137
  it("should perform correct when no cookie exists", async () => {
142
138
  mockReset()
143
- jest.mock("@budibase/backend-core", () => ({
144
- utils: {
145
- getAppId: () => {
146
- return "app_test"
147
- },
148
- setCookie: jest.fn(),
149
- getCookie: jest.fn(),
150
- },
151
- constants: {
152
- Cookies: {},
139
+ jest.mock("@budibase/backend-core/utils", () => ({
140
+ getAppId: () => {
141
+ return "app_test"
153
142
  },
143
+ setCookie: jest.fn(),
144
+ getCookie: jest.fn(),
145
+ }))
146
+ jest.mock("@budibase/backend-core/constants", () => ({
147
+ Cookies: {},
154
148
  }))
155
149
  await checkExpected(true)
156
150
  })
157
151
 
158
152
  it("lastly check what occurs when cookie doesn't need updated", async () => {
159
153
  mockReset()
160
- jest.mock("@budibase/backend-core", () => ({
161
- utils: {
162
- getAppId: () => {
163
- return "app_test"
164
- },
165
- setCookie: jest.fn(),
166
- getCookie: () => ({appId: "app_test", roleId: "PUBLIC"}),
154
+ jest.mock("@budibase/backend-core/utils", () => ({
155
+ getAppId: () => {
156
+ return "app_test"
167
157
  },
168
- constants: { Cookies: {} },
158
+ setCookie: jest.fn(),
159
+ getCookie: () => ({appId: "app_test", roleId: "PUBLIC"}),
160
+ }))
161
+ jest.mock("@budibase/backend-core/constants", () => ({
162
+ Cookies: {},
169
163
  }))
170
164
  await checkExpected(false)
171
165
  })
@@ -15,8 +15,8 @@ const {
15
15
  const controllers = require("./controllers")
16
16
  const supertest = require("supertest")
17
17
  const { cleanup } = require("../../utilities/fileSystem")
18
- const { Cookies, Headers } = require("@budibase/backend-core").constants
19
- const { jwt } = require("@budibase/backend-core").auth
18
+ const { Cookies, Headers } = require("@budibase/backend-core/constants")
19
+ const { jwt } = require("@budibase/backend-core/auth")
20
20
  const core = require("@budibase/backend-core")
21
21
  const { getGlobalDB } = require("@budibase/backend-core/tenancy")
22
22
  const { createASession } = require("@budibase/backend-core/sessions")
@@ -4,7 +4,7 @@ const actions = require("../automations/actions")
4
4
  const automationUtils = require("../automations/automationUtils")
5
5
  const AutomationEmitter = require("../events/AutomationEmitter")
6
6
  const { processObject } = require("@budibase/string-templates")
7
- const { DEFAULT_TENANT_ID } = require("@budibase/backend-core").constants
7
+ const { DEFAULT_TENANT_ID } = require("@budibase/backend-core/constants")
8
8
  const CouchDB = require("../db")
9
9
  const { DocumentTypes, isDevAppID } = require("../db/utils")
10
10
  const { doInTenant } = require("@budibase/backend-core/tenancy")
@@ -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) => {