@budibase/server 1.2.58 → 1.3.1

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 (211) hide show
  1. package/__mocks__/mongodb.ts +4 -0
  2. package/__mocks__/node-fetch.ts +8 -1
  3. package/builder/assets/index.00650f14.js +875 -0
  4. package/builder/assets/index.e50bda9f.css +6 -0
  5. package/builder/assets/{vendor.6bcafa60.js → vendor.67dbd8fa.js} +1 -1
  6. package/builder/index.html +3 -3
  7. package/coverage/clover.xml +549 -524
  8. package/coverage/coverage-final.json +20 -20
  9. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  10. package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
  11. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  12. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/component.js.html +1 -1
  15. package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
  16. package/coverage/lcov-report/api/controllers/dev.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/migrations.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
  23. package/coverage/lcov-report/api/controllers/query/index.html +1 -1
  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 +25 -25
  29. package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
  30. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +317 -191
  31. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +1 -1
  32. package/coverage/lcov-report/api/controllers/row/utils.js.html +27 -18
  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/bulkFormula.js.html +1 -1
  36. package/coverage/lcov-report/api/controllers/table/external.js.html +2 -2
  37. package/coverage/lcov-report/api/controllers/table/index.html +1 -1
  38. package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
  39. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  40. package/coverage/lcov-report/api/controllers/user.js.html +19 -19
  41. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  42. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  43. package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
  44. package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
  45. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
  46. package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
  47. package/coverage/lcov-report/api/index.html +1 -1
  48. package/coverage/lcov-report/api/index.js.html +5 -5
  49. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  50. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  51. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/index.html +1 -1
  60. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/migrations.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/utils/index.html +1 -1
  74. package/coverage/lcov-report/api/routes/utils/validators.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  77. package/coverage/lcov-report/automations/actions.js.html +1 -1
  78. package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
  79. package/coverage/lcov-report/automations/bullboard.js.html +9 -6
  80. package/coverage/lcov-report/automations/index.html +11 -11
  81. package/coverage/lcov-report/automations/index.js.html +1 -1
  82. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  83. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  84. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  85. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  87. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/index.html +1 -1
  89. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/loop.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  96. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
  98. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  99. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  100. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  101. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  102. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  103. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  104. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  105. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  106. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  107. package/coverage/lcov-report/automations/triggers.js.html +1 -1
  108. package/coverage/lcov-report/constants/index.html +1 -1
  109. package/coverage/lcov-report/constants/index.js.html +30 -30
  110. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  111. package/coverage/lcov-report/constants/screens.js.html +1 -1
  112. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  113. package/coverage/lcov-report/db/index.html +1 -1
  114. package/coverage/lcov-report/db/index.js.html +1 -1
  115. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
  116. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  117. package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
  118. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  119. package/coverage/lcov-report/db/newid.js.html +4 -4
  120. package/coverage/lcov-report/db/utils.js.html +73 -73
  121. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  122. package/coverage/lcov-report/events/index.html +1 -1
  123. package/coverage/lcov-report/events/index.js.html +1 -1
  124. package/coverage/lcov-report/events/utils.js.html +1 -1
  125. package/coverage/lcov-report/index.html +33 -33
  126. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  127. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  128. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  129. package/coverage/lcov-report/middleware/builder.js.html +12 -12
  130. package/coverage/lcov-report/middleware/currentapp.js.html +33 -33
  131. package/coverage/lcov-report/middleware/index.html +10 -10
  132. package/coverage/lcov-report/middleware/joi-validator.js.html +18 -9
  133. package/coverage/lcov-report/middleware/publicApi.js.html +1 -1
  134. package/coverage/lcov-report/middleware/resourceId.js.html +12 -12
  135. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  136. package/coverage/lcov-report/middleware/utils.js.html +2 -2
  137. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  138. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  139. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  140. package/coverage/lcov-report/utilities/global.js.html +31 -31
  141. package/coverage/lcov-report/utilities/index.html +1 -1
  142. package/coverage/lcov-report/utilities/index.js.html +24 -24
  143. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +2 -2
  144. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  145. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  146. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  147. package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
  148. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
  149. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
  150. package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
  151. package/coverage/lcov-report/utilities/security.js.html +2 -2
  152. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  153. package/coverage/lcov-report/utilities/usageQuota/index.html +1 -1
  154. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +20 -20
  155. package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +1 -1
  156. package/coverage/lcov-report/utilities/users.js.html +7 -7
  157. package/coverage/lcov-report/utilities/workerRequests.js.html +13 -13
  158. package/coverage/lcov.info +888 -844
  159. package/dist/api/controllers/application.js +80 -14
  160. package/dist/api/controllers/row/internalSearch.js +39 -4
  161. package/dist/api/controllers/row/utils.js +3 -1
  162. package/dist/api/controllers/static/index.js +26 -2
  163. package/dist/api/controllers/static/templates/preview.hbs +103 -0
  164. package/dist/api/controllers/table/external.js +1 -1
  165. package/dist/api/routes/static.js +3 -0
  166. package/dist/app.js +4 -3
  167. package/dist/automations/bullboard.js +1 -0
  168. package/dist/automations/logging/index.js +3 -0
  169. package/dist/integrations/base/query.js +27 -0
  170. package/dist/integrations/base/sql.js +62 -30
  171. package/dist/integrations/base/utils.js +13 -27
  172. package/dist/integrations/microsoftSqlServer.js +5 -2
  173. package/dist/integrations/mongodb.js +35 -8
  174. package/dist/integrations/mysql.js +2 -2
  175. package/dist/integrations/postgres.js +5 -2
  176. package/dist/middleware/joi-validator.js +7 -4
  177. package/dist/package.json +7 -7
  178. package/dist/threads/index.js +1 -0
  179. package/dist/tsconfig.build.tsbuildinfo +1 -1
  180. package/dist/utilities/fileSystem/index.js +2 -1
  181. package/dist/utilities/redis.js +1 -0
  182. package/package.json +8 -8
  183. package/src/api/controllers/application.ts +96 -15
  184. package/src/api/controllers/row/internalSearch.js +45 -3
  185. package/src/api/controllers/row/utils.js +4 -1
  186. package/src/api/controllers/static/index.ts +21 -1
  187. package/src/api/controllers/static/templates/preview.hbs +103 -0
  188. package/src/api/controllers/table/external.js +1 -1
  189. package/src/api/routes/static.ts +12 -0
  190. package/src/api/routes/tests/application.spec.js +26 -0
  191. package/src/api/routes/tests/data/old-app.txt +3 -0
  192. package/src/api/routes/tests/internalSearch.spec.js +176 -0
  193. package/src/api/routes/tests/static.spec.js +19 -7
  194. package/src/app.ts +5 -3
  195. package/src/automations/bullboard.js +1 -0
  196. package/src/automations/logging/index.ts +3 -0
  197. package/src/integrations/base/query.ts +16 -0
  198. package/src/integrations/base/sql.ts +67 -30
  199. package/src/integrations/base/utils.ts +9 -18
  200. package/src/integrations/microsoftSqlServer.ts +3 -2
  201. package/src/integrations/mongodb.ts +35 -8
  202. package/src/integrations/mysql.ts +1 -1
  203. package/src/integrations/postgres.ts +2 -1
  204. package/src/integrations/tests/mongo.spec.js +218 -0
  205. package/src/integrations/tests/sql.spec.js +101 -11
  206. package/src/middleware/joi-validator.js +7 -4
  207. package/src/threads/index.ts +1 -0
  208. package/src/utilities/fileSystem/index.js +2 -0
  209. package/src/utilities/redis.js +1 -0
  210. package/builder/assets/index.08dd0c5e.css +0 -6
  211. package/builder/assets/index.5cf58e77.js +0 -975
@@ -14,7 +14,7 @@ const { join } = require("path");
14
14
  const uuid = require("uuid/v4");
15
15
  const { doWithDB, dangerousGetDB, closeDB, } = require("@budibase/backend-core/db");
16
16
  const { ObjectStoreBuckets } = require("../../constants");
17
- const { upload, retrieve, retrieveToTmp, streamUpload, deleteFolder, downloadTarball, } = require("./utilities");
17
+ const { upload, retrieve, retrieveToTmp, streamUpload, deleteFolder, downloadTarball, deleteFiles, } = require("./utilities");
18
18
  const { updateClientLibrary } = require("./clientLibrary");
19
19
  const env = require("../../environment");
20
20
  const { USER_METDATA_PREFIX, LINK_USER_METADATA_PREFIX, TABLE_ROW_PREFIX, } = require("../../db/utils");
@@ -291,5 +291,6 @@ exports.cleanup = appIds => {
291
291
  exports.upload = upload;
292
292
  exports.retrieve = retrieve;
293
293
  exports.retrieveToTmp = retrieveToTmp;
294
+ exports.deleteFiles = deleteFiles;
294
295
  exports.TOP_LEVEL_PATH = TOP_LEVEL_PATH;
295
296
  exports.NODE_MODULES_PATH = NODE_MODULES_PATH;
@@ -30,6 +30,7 @@ exports.shutdown = () => __awaiter(void 0, void 0, void 0, function* () {
30
30
  yield debounceClient.finish();
31
31
  if (flagClient)
32
32
  yield flagClient.finish();
33
+ console.log("Redis shutdown");
33
34
  });
34
35
  exports.doesUserHaveLock = (devAppId, user) => __awaiter(void 0, void 0, void 0, function* () {
35
36
  const value = yield devAppClient.get(devAppId);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/server",
3
3
  "email": "hi@budibase.com",
4
- "version": "1.2.58",
4
+ "version": "1.3.1",
5
5
  "description": "Budibase Web Server",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -77,11 +77,11 @@
77
77
  "license": "GPL-3.0",
78
78
  "dependencies": {
79
79
  "@apidevtools/swagger-parser": "10.0.3",
80
- "@budibase/backend-core": "^1.2.58",
81
- "@budibase/client": "^1.2.58",
82
- "@budibase/pro": "1.2.57",
83
- "@budibase/string-templates": "^1.2.58",
84
- "@budibase/types": "^1.2.58",
80
+ "@budibase/backend-core": "^1.3.1",
81
+ "@budibase/client": "^1.3.1",
82
+ "@budibase/pro": "1.2.59",
83
+ "@budibase/string-templates": "^1.3.1",
84
+ "@budibase/types": "^1.3.1",
85
85
  "@bull-board/api": "3.7.0",
86
86
  "@bull-board/koa": "3.9.4",
87
87
  "@elastic/elasticsearch": "7.10.0",
@@ -107,7 +107,7 @@
107
107
  "google-auth-library": "7.12.0",
108
108
  "google-spreadsheet": "3.2.0",
109
109
  "jimp": "0.16.1",
110
- "joi": "17.2.1",
110
+ "joi": "17.6.0",
111
111
  "js-yaml": "4.1.0",
112
112
  "jsonschema": "1.4.0",
113
113
  "knex": "0.95.15",
@@ -196,5 +196,5 @@
196
196
  "optionalDependencies": {
197
197
  "oracledb": "5.3.0"
198
198
  },
199
- "gitHead": "2922b47620173f41097d36b9dc5b1c0f8f7b063a"
199
+ "gitHead": "68d741c482a7a9c2ca2f4de6fedd809d06bd242b"
200
200
  }
@@ -47,7 +47,14 @@ import { checkAppMetadata } from "../../automations/logging"
47
47
  import { getUniqueRows } from "../../utilities/usageQuota/rows"
48
48
  import { quotas } from "@budibase/pro"
49
49
  import { errors, events, migrations } from "@budibase/backend-core"
50
- import { App, MigrationType } from "@budibase/types"
50
+ import {
51
+ App,
52
+ Layout,
53
+ Screen,
54
+ MigrationType,
55
+ AppNavigation,
56
+ } from "@budibase/types"
57
+ import { BASE_LAYOUT_PROP_IDS } from "../../constants/layouts"
51
58
 
52
59
  const URL_REGEX_SLASH = /\/|\\/g
53
60
 
@@ -243,27 +250,19 @@ const performAppCreate = async (ctx: any) => {
243
250
  }
244
251
  const instance = await createInstance(instanceConfig)
245
252
  const appId = instance._id
246
-
247
253
  const db = context.getAppDB()
248
- let _rev
249
- try {
250
- // if template there will be an existing doc
251
- const existing = await db.get(DocumentType.APP_METADATA)
252
- _rev = existing._rev
253
- } catch (err) {
254
- // nothing to do
255
- }
256
- const newApplication: App = {
254
+
255
+ let newApplication: App = {
257
256
  _id: DocumentType.APP_METADATA,
258
- _rev,
259
- appId: instance._id,
257
+ _rev: undefined,
258
+ appId,
260
259
  type: "app",
261
260
  version: packageJson.version,
262
261
  componentLibraries: ["@budibase/standard-components"],
263
262
  name: name,
264
263
  url: url,
265
- template: ctx.request.body.template,
266
- instance: instance,
264
+ template: templateKey,
265
+ instance,
267
266
  tenantId: getTenantId(),
268
267
  updatedAt: new Date().toISOString(),
269
268
  createdAt: new Date().toISOString(),
@@ -285,6 +284,36 @@ const performAppCreate = async (ctx: any) => {
285
284
  buttonBorderRadius: "16px",
286
285
  },
287
286
  }
287
+
288
+ // If we used a template or imported an app there will be an existing doc.
289
+ // Fetch and migrate some metadata from the existing app.
290
+ try {
291
+ const existing: App = await db.get(DocumentType.APP_METADATA)
292
+ const keys: (keyof App)[] = [
293
+ "_rev",
294
+ "navigation",
295
+ "theme",
296
+ "customTheme",
297
+ "icon",
298
+ ]
299
+ keys.forEach(key => {
300
+ if (existing[key]) {
301
+ // @ts-ignore
302
+ newApplication[key] = existing[key]
303
+ }
304
+ })
305
+
306
+ // Migrate navigation settings and screens if required
307
+ if (existing && !existing.navigation) {
308
+ const navigation = await migrateAppNavigation()
309
+ if (navigation) {
310
+ newApplication.navigation = navigation
311
+ }
312
+ }
313
+ } catch (err) {
314
+ // Nothing to do
315
+ }
316
+
288
317
  const response = await db.put(newApplication, { force: true })
289
318
  newApplication._rev = response.rev
290
319
 
@@ -567,3 +596,55 @@ const updateAppPackage = async (appPackage: any, appId: any) => {
567
596
  return newAppPackage
568
597
  })
569
598
  }
599
+
600
+ const migrateAppNavigation = async () => {
601
+ const db = context.getAppDB()
602
+ const existing: App = await db.get(DocumentType.APP_METADATA)
603
+ const layouts: Layout[] = await getLayouts()
604
+ const screens: Screen[] = await getScreens()
605
+
606
+ // Migrate all screens, removing custom layouts
607
+ for (let screen of screens) {
608
+ if (!screen.layoutId) {
609
+ return
610
+ }
611
+ const layout = layouts.find(layout => layout._id === screen.layoutId)
612
+ screen.layoutId = undefined
613
+ screen.showNavigation = layout?.props.navigation !== "None"
614
+ screen.width = layout?.props.width || "Large"
615
+ await db.put(screen)
616
+ }
617
+
618
+ // Migrate layout navigation settings
619
+ const { name, customTheme } = existing
620
+ const layout = layouts?.find(
621
+ (layout: Layout) => layout._id === BASE_LAYOUT_PROP_IDS.PRIVATE
622
+ )
623
+ if (layout) {
624
+ let navigationSettings: any = {
625
+ navigation: "Top",
626
+ title: name,
627
+ navWidth: "Large",
628
+ navBackground:
629
+ customTheme?.navBackground || "var(--spectrum-global-color-gray-50)",
630
+ navTextColor:
631
+ customTheme?.navTextColor || "var(--spectrum-global-color-gray-800)",
632
+ }
633
+ if (layout) {
634
+ navigationSettings.hideLogo = layout.props.hideLogo
635
+ navigationSettings.hideTitle = layout.props.hideTitle
636
+ navigationSettings.title = layout.props.title || name
637
+ navigationSettings.logoUrl = layout.props.logoUrl
638
+ navigationSettings.links = layout.props.links
639
+ navigationSettings.navigation = layout.props.navigation || "Top"
640
+ navigationSettings.sticky = layout.props.sticky
641
+ navigationSettings.navWidth = layout.props.width || "Large"
642
+ if (navigationSettings.navigation === "None") {
643
+ navigationSettings.navigation = "Top"
644
+ }
645
+ }
646
+ return navigationSettings
647
+ } else {
648
+ return null
649
+ }
650
+ }
@@ -1,4 +1,5 @@
1
1
  const { SearchIndexes } = require("../../../db/utils")
2
+ const { removeKeyNumbering } = require("./utils")
2
3
  const fetch = require("node-fetch")
3
4
  const { getCouchInfo } = require("@budibase/backend-core/db")
4
5
  const { getAppId } = require("@budibase/backend-core/context")
@@ -20,6 +21,8 @@ class QueryBuilder {
20
21
  notEmpty: {},
21
22
  oneOf: {},
22
23
  contains: {},
24
+ notContains: {},
25
+ containsAny: {},
23
26
  ...base,
24
27
  }
25
28
  this.limit = 50
@@ -125,6 +128,16 @@ class QueryBuilder {
125
128
  return this
126
129
  }
127
130
 
131
+ addNotContains(key, value) {
132
+ this.query.notContains[key] = value
133
+ return this
134
+ }
135
+
136
+ addContainsAny(key, value) {
137
+ this.query.containsAny[key] = value
138
+ return this
139
+ }
140
+
128
141
  /**
129
142
  * Preprocesses a value before going into a lucene search.
130
143
  * Transforms strings to lowercase and wraps strings and bools in quotes.
@@ -170,11 +183,29 @@ class QueryBuilder {
170
183
  return `${key}:${builder.preprocess(value, allPreProcessingOpts)}`
171
184
  }
172
185
 
173
- const contains = (key, value) => {
174
- if (!value && value !== 0) {
186
+ const contains = (key, value, mode = "AND") => {
187
+ if (Array.isArray(value) && value.length === 0) {
175
188
  return null
176
189
  }
177
- return `${key}:${builder.preprocess(value, { escape: true })}`
190
+ if (!Array.isArray(value)) {
191
+ return `${key}:${value}`
192
+ }
193
+ let statement = `${builder.preprocess(value[0], { escape: true })}`
194
+ for (let i = 1; i < value.length; i++) {
195
+ statement += ` ${mode} ${builder.preprocess(value[i], {
196
+ escape: true,
197
+ })}`
198
+ }
199
+ return `${key}:(${statement})`
200
+ }
201
+
202
+ const notContains = (key, value) => {
203
+ const allPrefix = allOr === "" ? "*:* AND" : ""
204
+ return allPrefix + "NOT " + contains(key, value)
205
+ }
206
+
207
+ const containsAny = (key, value) => {
208
+ return contains(key, value, "OR")
178
209
  }
179
210
 
180
211
  const oneOf = (key, value) => {
@@ -197,6 +228,8 @@ class QueryBuilder {
197
228
 
198
229
  function build(structure, queryFn) {
199
230
  for (let [key, value] of Object.entries(structure)) {
231
+ // check for new format - remove numbering if needed
232
+ key = removeKeyNumbering(key)
200
233
  key = builder.preprocess(key.replace(/ /g, "_"), {
201
234
  escape: true,
202
235
  })
@@ -275,6 +308,12 @@ class QueryBuilder {
275
308
  if (this.query.contains) {
276
309
  build(this.query.contains, contains)
277
310
  }
311
+ if (this.query.notContains) {
312
+ build(this.query.notContains, notContains)
313
+ }
314
+ if (this.query.containsAny) {
315
+ build(this.query.containsAny, containsAny)
316
+ }
278
317
  // make sure table ID is always added as an AND
279
318
  if (tableId) {
280
319
  query = `(${query})`
@@ -310,6 +349,9 @@ class QueryBuilder {
310
349
  }
311
350
  }
312
351
 
352
+ // exported for unit testing
353
+ exports.QueryBuilder = QueryBuilder
354
+
313
355
  /**
314
356
  * Executes a lucene search query.
315
357
  * @param url The query URL
@@ -3,8 +3,9 @@ const { cloneDeep } = require("lodash/fp")
3
3
  const { InternalTables } = require("../../../db/utils")
4
4
  const userController = require("../user")
5
5
  const { FieldTypes } = require("../../../constants")
6
- const { makeExternalQuery } = require("../../../integrations/base/utils")
7
6
  const { getAppDB } = require("@budibase/backend-core/context")
7
+ const { makeExternalQuery } = require("../../../integrations/base/query")
8
+ const { removeKeyNumbering } = require("../../../integrations/base/utils")
8
9
 
9
10
  validateJs.extend(validateJs.validators.datetime, {
10
11
  parse: function (value) {
@@ -16,6 +17,8 @@ validateJs.extend(validateJs.validators.datetime, {
16
17
  },
17
18
  })
18
19
 
20
+ exports.removeKeyNumbering = removeKeyNumbering
21
+
19
22
  exports.getDatasourceAndQuery = async json => {
20
23
  const datasourceId = json.endpoint.datasourceId
21
24
  const db = getAppDB()
@@ -12,7 +12,7 @@ const {
12
12
  } = require("../../../utilities/fileSystem")
13
13
  const env = require("../../../environment")
14
14
  const { clientLibraryPath } = require("../../../utilities")
15
- const { upload } = require("../../../utilities/fileSystem")
15
+ const { upload, deleteFiles } = require("../../../utilities/fileSystem")
16
16
  const { attachmentsRelativeURL } = require("../../../utilities")
17
17
  const { DocumentType } = require("../../../db/utils")
18
18
  const { getAppDB, getAppId } = require("@budibase/backend-core/context")
@@ -97,6 +97,10 @@ export const uploadFile = async function (ctx: any) {
97
97
  ctx.body = await Promise.all(uploads)
98
98
  }
99
99
 
100
+ export const deleteObjects = async function (ctx: any) {
101
+ ctx.body = await deleteFiles(ObjectStoreBuckets.APPS, ctx.request.body.keys)
102
+ }
103
+
100
104
  export const serveApp = async function (ctx: any) {
101
105
  const db = getAppDB({ skip_setup: true })
102
106
  const appInfo = await db.get(DocumentType.APP_METADATA)
@@ -124,6 +128,22 @@ export const serveApp = async function (ctx: any) {
124
128
  }
125
129
  }
126
130
 
131
+ export const serveBuilderPreview = async function (ctx: any) {
132
+ const db = getAppDB({ skip_setup: true })
133
+ const appInfo = await db.get(DocumentType.APP_METADATA)
134
+
135
+ if (!env.isJest()) {
136
+ let appId = getAppId()
137
+ const previewHbs = loadHandlebarsFile(`${__dirname}/templates/preview.hbs`)
138
+ ctx.body = await processString(previewHbs, {
139
+ clientLibPath: clientLibraryPath(appId, appInfo.version, ctx),
140
+ })
141
+ } else {
142
+ // just return the app info for jest to assert on
143
+ ctx.body = { ...appInfo, builderPreview: true }
144
+ }
145
+ }
146
+
127
147
  export const serveClientLibrary = async function (ctx: any) {
128
148
  return send(ctx, "budibase-client.js", {
129
149
  root: join(NODE_MODULES_PATH, "@budibase", "client", "dist"),
@@ -0,0 +1,103 @@
1
+ <html lang="en">
2
+ <head>
3
+ <title>Budibase Builder Preview</title>
4
+ <link rel="stylesheet" href="https://rsms.me/inter/inter.css" />
5
+ <link rel="preconnect" href="https://fonts.gstatic.com" />
6
+ <link
7
+ href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600;700&display=swap"
8
+ rel="stylesheet"
9
+ />
10
+ <link
11
+ href="https://cdn.jsdelivr.net/npm/remixicon@2.5.0/fonts/remixicon.css"
12
+ rel="stylesheet"
13
+ />
14
+ <style>
15
+ html, body {
16
+ padding: 0;
17
+ margin: 0;
18
+ }
19
+ html {
20
+ height: 100%;
21
+ width: 100%;
22
+ overflow: hidden;
23
+ display: flex;
24
+ flex-direction: column;
25
+ justify-content: flex-start;
26
+ align-items: stretch;
27
+ }
28
+ body {
29
+ flex: 1 1 auto;
30
+ overflow: hidden;
31
+ }
32
+
33
+ *,
34
+ *:before,
35
+ *:after {
36
+ box-sizing: border-box;
37
+ }
38
+ </style>
39
+ <script src='{{ clientLibPath }}'></script>
40
+ <script>
41
+ function receiveMessage(event) {
42
+ if (!event.data) {
43
+ return
44
+ }
45
+
46
+ // Parse received message
47
+ // If parsing fails, just ignore and wait for the next message
48
+ let parsed
49
+ try {
50
+ parsed = JSON.parse(event.data)
51
+ } catch (error) {
52
+ console.error("Client received invalid JSON")
53
+ // Ignore
54
+ }
55
+ if (!parsed || !parsed.isBudibaseEvent) {
56
+ return
57
+ }
58
+
59
+ // Extract data from message
60
+ const {
61
+ selectedComponentId,
62
+ layout,
63
+ screen,
64
+ appId,
65
+ theme,
66
+ customTheme,
67
+ previewDevice,
68
+ navigation,
69
+ hiddenComponentIds
70
+ } = parsed
71
+
72
+ // Set some flags so the app knows we're in the builder
73
+ window["##BUDIBASE_IN_BUILDER##"] = true
74
+ window["##BUDIBASE_APP_ID##"] = appId
75
+ window["##BUDIBASE_PREVIEW_LAYOUT##"] = layout
76
+ window["##BUDIBASE_PREVIEW_SCREEN##"] = screen
77
+ window["##BUDIBASE_SELECTED_COMPONENT_ID##"] = selectedComponentId
78
+ window["##BUDIBASE_PREVIEW_ID##"] = Math.random()
79
+ window["##BUDIBASE_PREVIEW_THEME##"] = theme
80
+ window["##BUDIBASE_PREVIEW_CUSTOM_THEME##"] = customTheme
81
+ window["##BUDIBASE_PREVIEW_DEVICE##"] = previewDevice
82
+ window["##BUDIBASE_PREVIEW_NAVIGATION##"] = navigation
83
+ window["##BUDIBASE_HIDDEN_COMPONENT_IDS##"] = hiddenComponentIds
84
+
85
+ // Initialise app
86
+ try {
87
+ if (window.loadBudibase) {
88
+ window.loadBudibase()
89
+ document.documentElement.classList.add("loaded")
90
+ } else {
91
+ throw "The client library couldn't be loaded"
92
+ }
93
+ } catch (error) {
94
+ window.parent.postMessage({ type: "error", error })
95
+ }
96
+ }
97
+
98
+ window.addEventListener("message", receiveMessage)
99
+ window.parent.postMessage({ type: "ready" })
100
+ </script>
101
+ </head>
102
+ <body></body>
103
+ </html>
@@ -14,7 +14,7 @@ const {
14
14
  FieldTypes,
15
15
  RelationshipTypes,
16
16
  } = require("../../../constants")
17
- const { makeExternalQuery } = require("../../../integrations/base/utils")
17
+ const { makeExternalQuery } = require("../../../integrations/base/query")
18
18
  const { cloneDeep } = require("lodash/fp")
19
19
  const csvParser = require("../../../utilities/csvParser")
20
20
  const { handleRequest } = require("../row/external")
@@ -38,6 +38,11 @@ router
38
38
  // TODO: for now this builder endpoint is not authorized/secured, will need to be
39
39
  .get("/builder/:file*", controller.serveBuilder)
40
40
  .post("/api/attachments/process", authorized(BUILDER), controller.uploadFile)
41
+ .post(
42
+ "/api/attachments/delete",
43
+ authorized(BUILDER),
44
+ controller.deleteObjects
45
+ )
41
46
  .post("/api/beta/:feature", controller.toggleBetaUiFeature)
42
47
  .post(
43
48
  "/api/attachments/:tableId/upload",
@@ -45,6 +50,13 @@ router
45
50
  authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
46
51
  controller.uploadFile
47
52
  )
53
+ .post(
54
+ "/api/attachments/:tableId/delete",
55
+ paramResource("tableId"),
56
+ authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
57
+ controller.deleteObjects
58
+ )
59
+ .get("/preview", authorized(BUILDER), controller.serveBuilderPreview)
48
60
  .get("/:appId/:path*", controller.serveApp)
49
61
  .get("/app/:appUrl/:path*", controller.serveApp)
50
62
  .post(
@@ -17,6 +17,7 @@ const {
17
17
  checkBuilderEndpoint,
18
18
  } = require("./utilities/TestFunctions")
19
19
  const setup = require("./utilities")
20
+ const { basicScreen, basicLayout } = setup.structures
20
21
  const { AppStatus } = require("../../../db/utils")
21
22
  const { events } = require("@budibase/backend-core")
22
23
 
@@ -81,6 +82,31 @@ describe("/applications", () => {
81
82
  body: { name: "My App" },
82
83
  })
83
84
  })
85
+
86
+ it("migrates navigation settings from old apps", async () => {
87
+ const res = await request
88
+ .post("/api/applications")
89
+ .field("name", "Old App")
90
+ .field("useTemplate", "true")
91
+ .set(config.defaultHeaders())
92
+ .attach("templateFile", "src/api/routes/tests/data/old-app.txt")
93
+ .expect("Content-Type", /json/)
94
+ .expect(200)
95
+ expect(res.body._id).toBeDefined()
96
+ expect(res.body.navigation).toBeDefined()
97
+ expect(res.body.navigation.hideLogo).toBe(true)
98
+ expect(res.body.navigation.title).toBe("Custom Title")
99
+ expect(res.body.navigation.hideLogo).toBe(true)
100
+ expect(res.body.navigation.navigation).toBe("Left")
101
+ expect(res.body.navigation.navBackground).toBe(
102
+ "var(--spectrum-global-color-blue-600)"
103
+ )
104
+ expect(res.body.navigation.navTextColor).toBe(
105
+ "var(--spectrum-global-color-gray-50)"
106
+ )
107
+ expect(events.app.created).toBeCalledTimes(1)
108
+ expect(events.app.fileImported).toBeCalledTimes(1)
109
+ })
84
110
  })
85
111
 
86
112
  describe("fetch", () => {
@@ -0,0 +1,3 @@
1
+ {"version":"1.2.9","db_type":"http","start_time":"2022-08-31T13:18:50.740Z","db_info":{"db_name":"app_dev_default_c45f987cfa274060840878557ff2f547","purge_seq":"0-g1AAAABXeJzLYWBgYMpgTmEQTM4vTc5ISXIwNDLXMwBCwxyQVB4LkGRoAFL_gSArkQGP2kSGpHqIoiwAtOgYRA","update_seq":"28-g1AAAABXeJzLYWBgYMpgTmEQTM4vTc5ISXIwNDLXMwBCwxyQVB4LkGRoAFL_gSArkQ2P2kSGpHqwIrEsALX-GGA","sizes":{"file":233890,"external":5856,"active":19326},"props":{},"doc_del_count":0,"doc_count":7,"disk_format_version":8,"compact_running":false,"cluster":{"q":2,"n":1,"w":1,"r":1},"instance_start_time":"0","host":"http://localhost:10000/db/app_dev_default_c45f987cfa274060840878557ff2f547/","auto_compaction":false,"adapter":"http"}}
2
+ {"docs":[{"_id":"_design/database","_rev":"4-afb0361e5ebb6b7e464c6c146ac3f559","views":{"by_link":{"map":"function (doc) {\n // everything in this must remain constant as its going to Pouch, no external variables\n if (doc.type === \"link\") {\n let doc1 = doc.doc1;\n let doc2 = doc.doc2;\n emit([doc1.tableId, doc1.rowId], {\n id: doc2.rowId,\n thisId: doc1.rowId,\n fieldName: doc1.fieldName,\n });\n // if linking to same table can't emit twice\n if (doc1.tableId !== doc2.tableId) {\n emit([doc2.tableId, doc2.rowId], {\n id: doc1.rowId,\n thisId: doc2.rowId,\n fieldName: doc2.fieldName,\n });\n }\n }\n }"},"screen_routes":{"map":"function(doc) {\n if (doc._id.startsWith(\"screen_\")) {\n emit(doc._id, {\n id: doc._id,\n routing: doc.routing,\n })\n }\n }"}},"indexes":{"rows":{"index":"function (doc) {\n function idx(input, prev) {\n for (let key of Object.keys(input)) {\n let idxKey = prev != null ? `${prev}.${key}` : key;\n idxKey = idxKey.replace(/ /g, \"_\");\n if (Array.isArray(input[key])) {\n for (let val of input[key]) {\n if (typeof val !== \"object\") {\n // eslint-disable-next-line no-undef\n index(idxKey, val, { store: true });\n }\n }\n }\n else if (key === \"_id\" || key === \"_rev\" || input[key] == null) {\n continue;\n }\n if (typeof input[key] === \"string\") {\n // eslint-disable-next-line no-undef\n index(idxKey, input[key].toLowerCase(), { store: true });\n }\n else if (typeof input[key] !== \"object\") {\n // eslint-disable-next-line no-undef\n index(idxKey, input[key], { store: true });\n }\n else {\n idx(input[key], idxKey);\n }\n }\n }\n if (doc._id.startsWith(\"ro_\")) {\n // eslint-disable-next-line no-undef\n index(\"default\", doc._id);\n idx(doc);\n }\n }","analyzer":"keyword"}},"_revisions":{"start":4,"ids":["afb0361e5ebb6b7e464c6c146ac3f559","d1eafe2832e6554b5d348e16d6a1b0d3","6e20795c5ba67179b478f0337a585b56","0b24e44a44af45e51e562fd124ce3007"]}},{"_id":"app_metadata","_rev":"8-9c8540f72e647e3ca7108978e862ff17","appId":"app_dev_default_c45f987cfa274060840878557ff2f547","type":"app","version":"1.0.79-alpha.7","componentLibraries":["@budibase/standard-components"],"name":"Old App","url":"/old%20app","instance":{"_id":"app_dev_default_c45f987cfa274060840878557ff2f547"},"tenantId":"default","updatedAt":"2022-08-31T13:18:05.273Z","createdAt":"2022-08-31T13:16:36.639Z","status":"development","customTheme":{"navBackground":"var(--spectrum-global-color-blue-600)","navTextColor":"var(--spectrum-global-color-gray-50)","buttonBorderRadius":"4px","primaryColor":"var(--spectrum-global-color-red-600)"},"_revisions":{"start":8,"ids":["9c8540f72e647e3ca7108978e862ff17","5c86ff8a96e1f3b4505e205f25a92656","75473d58cf92b61dbcdcd74157f9e2e1","ebc34a96a7ad678bae76d7d70880f4de","1c7397be4ef470d66bd0fd4b3fe4a002","89222c47e9d254b4885e1818cd0aac79","66bed1c391eabcea6ad3557d8ede5f27","0f1136445fd7c2cf73bb5cf41aaa19d2"]}},{"_id":"layout_private_master","_rev":"12-fa664c0538f1478b2a3695c8909a04e9","componentLibraries":["@budibase/standard-components"],"title":"Old App","favicon":"./_shared/favicon.png","stylesheets":[],"name":"Navigation Layout","props":{"_id":"4f569166-a4f3-47ea-a09e-6d218c75586f","_instanceName":"Navigation Layout","_component":"@budibase/standard-components/layout","_children":[{"_id":"7fcf11e4-6f5b-4085-8e0d-9f3d44c98967","_component":"@budibase/standard-components/screenslot","_instanceName":"Screen slot","_styles":{"normal":{"flex":"1 1 auto","display":"flex","flex-direction":"column","justify-content":"flex-start","align-items":"stretch"},"hover":{},"active":{},"selected":{}},"_children":[]}],"_styles":{"active":{},"hover":{},"normal":{},"selected":{}},"title":"Custom Title","navigation":"Left","width":"Small","links":[{"text":"Home","url":"/"}],"hideLogo":true,"hideTitle":false},"_revisions":{"start":12,"ids":["fa664c0538f1478b2a3695c8909a04e9","54b4084d5c10369ecd7a3b33d4cb7775","61e5d376e9f00d20d9905bf011680943","f222416fe5bcf0085ceb72bdc94327c1","b6d9abe1caa1a8ffec0b1e8cb7324c4b","85a347b8c8acb5ad4e31929384062e05","a7ce68d677f2fa7e9a0d54b2fb4275bb","83811e8befcdac752cdbe16cd8b33fa0","362cb9ad2c87ef9c92c58c0a41ed69d4","0a3bba6cdc98539abb3a4e98e13ef98a","d964020cc8907b37047bcd2bd1387735","156b45a34bbee17b6a4a97bfaaeb6f90"]}},{"_id":"layout_public_master","_rev":"1-c7d57c9c42e9e8f988d82f0e6cc7d32c","componentLibraries":["@budibase/standard-components"],"title":"Old App","favicon":"./_shared/favicon.png","stylesheets":[],"name":"Empty Layout","props":{"_id":"3723ffa1-f9e0-4c05-8013-98195c788ed6","_instanceName":"Empty Layout","_component":"@budibase/standard-components/layout","_children":[{"_id":"7fcf11e4-6f5b-4085-8e0d-9f3d44c98967","_component":"@budibase/standard-components/screenslot","_instanceName":"Screen slot","_styles":{"normal":{"flex":"1 1 auto","display":"flex","flex-direction":"column","justify-content":"flex-start","align-items":"stretch"},"hover":{},"active":{},"selected":{}},"_children":[]}],"_styles":{"active":{},"hover":{},"normal":{},"selected":{}},"navigation":"None","width":"Large","links":[{"text":"Home","url":"/"}]}},{"_id":"screen_c483ef802772479196a28c8a6eafe6a5","_rev":"1-db29ceb02c9c6c1ade847c8086d28a70","layoutId":"layout_private_master","props":{"_id":"cafd93b74e18b45a49a58c894d7ef29b4","_component":"@budibase/standard-components/container","_styles":{"normal":{},"hover":{},"active":{},"selected":{}},"_children":[],"_instanceName":"Home","direction":"column","hAlign":"stretch","vAlign":"top","size":"grow","gap":"M"},"routing":{"route":"/home","roleId":"BASIC"},"name":"cafd93b74e18b45a49a58c894d7ef29b4","template":"createFromScratch"},{"_id":"ta_users","_rev":"1-30a4344f056c24cf776d5736eb3c7ed5","type":"table","views":{},"name":"Users","schema":{"email":{"type":"string","constraints":{"type":"string","email":true,"length":{"maximum":""},"presence":true},"fieldName":"email","name":"email"},"firstName":{"name":"firstName","fieldName":"firstName","type":"string","constraints":{"type":"string","presence":false}},"lastName":{"name":"lastName","fieldName":"lastName","type":"string","constraints":{"type":"string","presence":false}},"roleId":{"fieldName":"roleId","name":"roleId","type":"options","constraints":{"type":"string","presence":false,"inclusion":["ADMIN","POWER","BASIC","PUBLIC"]}},"status":{"fieldName":"status","name":"status","type":"options","constraints":{"type":"string","presence":false,"inclusion":["active","inactive"]}}},"primaryDisplay":"email"}]}
3
+ {"seq":"28-g1AAAACbeJzLYWBgYMpgTmEQTM4vTc5ISXIwNDLXMwBCwxyQVCJDUv3___-zMpgTxXKBAuxJiZYGxgZG2DTgMSaPBUgyNACp_1DT2MCmGRmZWBglG2PTlwUAEaUn-w"}