@budibase/server 1.2.58 → 1.2.59-alpha.0

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.48098778.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
@@ -36,6 +36,7 @@ const rows_1 = require("../../utilities/usageQuota/rows");
36
36
  const pro_1 = require("@budibase/pro");
37
37
  const backend_core_2 = require("@budibase/backend-core");
38
38
  const types_1 = require("@budibase/types");
39
+ const layouts_1 = require("../../constants/layouts");
39
40
  const URL_REGEX_SLASH = /\/|\\/g;
40
41
  // utility function, need to do away with this
41
42
  function getLayouts() {
@@ -209,26 +210,17 @@ const performAppCreate = (ctx) => __awaiter(void 0, void 0, void 0, function* ()
209
210
  const instance = yield createInstance(instanceConfig);
210
211
  const appId = instance._id;
211
212
  const db = backend_core_1.context.getAppDB();
212
- let _rev;
213
- try {
214
- // if template there will be an existing doc
215
- const existing = yield db.get(utils_1.DocumentType.APP_METADATA);
216
- _rev = existing._rev;
217
- }
218
- catch (err) {
219
- // nothing to do
220
- }
221
- const newApplication = {
213
+ let newApplication = {
222
214
  _id: utils_1.DocumentType.APP_METADATA,
223
- _rev,
224
- appId: instance._id,
215
+ _rev: undefined,
216
+ appId,
225
217
  type: "app",
226
218
  version: package_json_1.default.version,
227
219
  componentLibraries: ["@budibase/standard-components"],
228
220
  name: name,
229
221
  url: url,
230
- template: ctx.request.body.template,
231
- instance: instance,
222
+ template: templateKey,
223
+ instance,
232
224
  tenantId: getTenantId(),
233
225
  updatedAt: new Date().toISOString(),
234
226
  createdAt: new Date().toISOString(),
@@ -250,6 +242,34 @@ const performAppCreate = (ctx) => __awaiter(void 0, void 0, void 0, function* ()
250
242
  buttonBorderRadius: "16px",
251
243
  },
252
244
  };
245
+ // If we used a template or imported an app there will be an existing doc.
246
+ // Fetch and migrate some metadata from the existing app.
247
+ try {
248
+ const existing = yield db.get(utils_1.DocumentType.APP_METADATA);
249
+ const keys = [
250
+ "_rev",
251
+ "navigation",
252
+ "theme",
253
+ "customTheme",
254
+ "icon",
255
+ ];
256
+ keys.forEach(key => {
257
+ if (existing[key]) {
258
+ // @ts-ignore
259
+ newApplication[key] = existing[key];
260
+ }
261
+ });
262
+ // Migrate navigation settings and screens if required
263
+ if (existing && !existing.navigation) {
264
+ const navigation = yield migrateAppNavigation();
265
+ if (navigation) {
266
+ newApplication.navigation = navigation;
267
+ }
268
+ }
269
+ }
270
+ catch (err) {
271
+ // Nothing to do
272
+ }
253
273
  const response = yield db.put(newApplication, { force: true });
254
274
  newApplication._rev = response.rev;
255
275
  /* istanbul ignore next */
@@ -511,3 +531,49 @@ const updateAppPackage = (appPackage, appId) => __awaiter(void 0, void 0, void 0
511
531
  return newAppPackage;
512
532
  }));
513
533
  });
534
+ const migrateAppNavigation = () => __awaiter(void 0, void 0, void 0, function* () {
535
+ const db = backend_core_1.context.getAppDB();
536
+ const existing = yield db.get(utils_1.DocumentType.APP_METADATA);
537
+ const layouts = yield getLayouts();
538
+ const screens = yield getScreens();
539
+ // Migrate all screens, removing custom layouts
540
+ for (let screen of screens) {
541
+ if (!screen.layoutId) {
542
+ return;
543
+ }
544
+ const layout = layouts.find(layout => layout._id === screen.layoutId);
545
+ screen.layoutId = undefined;
546
+ screen.showNavigation = (layout === null || layout === void 0 ? void 0 : layout.props.navigation) !== "None";
547
+ screen.width = (layout === null || layout === void 0 ? void 0 : layout.props.width) || "Large";
548
+ yield db.put(screen);
549
+ }
550
+ // Migrate layout navigation settings
551
+ const { name, customTheme } = existing;
552
+ const layout = layouts === null || layouts === void 0 ? void 0 : layouts.find((layout) => layout._id === layouts_1.BASE_LAYOUT_PROP_IDS.PRIVATE);
553
+ if (layout) {
554
+ let navigationSettings = {
555
+ navigation: "Top",
556
+ title: name,
557
+ navWidth: "Large",
558
+ navBackground: (customTheme === null || customTheme === void 0 ? void 0 : customTheme.navBackground) || "var(--spectrum-global-color-gray-50)",
559
+ navTextColor: (customTheme === null || customTheme === void 0 ? void 0 : customTheme.navTextColor) || "var(--spectrum-global-color-gray-800)",
560
+ };
561
+ if (layout) {
562
+ navigationSettings.hideLogo = layout.props.hideLogo;
563
+ navigationSettings.hideTitle = layout.props.hideTitle;
564
+ navigationSettings.title = layout.props.title || name;
565
+ navigationSettings.logoUrl = layout.props.logoUrl;
566
+ navigationSettings.links = layout.props.links;
567
+ navigationSettings.navigation = layout.props.navigation || "Top";
568
+ navigationSettings.sticky = layout.props.sticky;
569
+ navigationSettings.navWidth = layout.props.width || "Large";
570
+ if (navigationSettings.navigation === "None") {
571
+ navigationSettings.navigation = "Top";
572
+ }
573
+ }
574
+ return navigationSettings;
575
+ }
576
+ else {
577
+ return null;
578
+ }
579
+ });
@@ -9,6 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  const { SearchIndexes } = require("../../../db/utils");
12
+ const { removeKeyNumbering } = require("./utils");
12
13
  const fetch = require("node-fetch");
13
14
  const { getCouchInfo } = require("@budibase/backend-core/db");
14
15
  const { getAppId } = require("@budibase/backend-core/context");
@@ -18,7 +19,7 @@ const { getAppId } = require("@budibase/backend-core/context");
18
19
  */
19
20
  class QueryBuilder {
20
21
  constructor(base) {
21
- this.query = Object.assign({ allOr: false, string: {}, fuzzy: {}, range: {}, equal: {}, notEqual: {}, empty: {}, notEmpty: {}, oneOf: {}, contains: {} }, base);
22
+ this.query = Object.assign({ allOr: false, string: {}, fuzzy: {}, range: {}, equal: {}, notEqual: {}, empty: {}, notEmpty: {}, oneOf: {}, contains: {}, notContains: {}, containsAny: {} }, base);
22
23
  this.limit = 50;
23
24
  this.sortOrder = "ascending";
24
25
  this.sortType = "string";
@@ -104,6 +105,14 @@ class QueryBuilder {
104
105
  this.query.contains[key] = value;
105
106
  return this;
106
107
  }
108
+ addNotContains(key, value) {
109
+ this.query.notContains[key] = value;
110
+ return this;
111
+ }
112
+ addContainsAny(key, value) {
113
+ this.query.containsAny[key] = value;
114
+ return this;
115
+ }
107
116
  /**
108
117
  * Preprocesses a value before going into a lucene search.
109
118
  * Transforms strings to lowercase and wraps strings and bools in quotes.
@@ -146,11 +155,27 @@ class QueryBuilder {
146
155
  }
147
156
  return `${key}:${builder.preprocess(value, allPreProcessingOpts)}`;
148
157
  };
149
- const contains = (key, value) => {
150
- if (!value && value !== 0) {
158
+ const contains = (key, value, mode = "AND") => {
159
+ if (Array.isArray(value) && value.length === 0) {
151
160
  return null;
152
161
  }
153
- return `${key}:${builder.preprocess(value, { escape: true })}`;
162
+ if (!Array.isArray(value)) {
163
+ return `${key}:${value}`;
164
+ }
165
+ let statement = `${builder.preprocess(value[0], { escape: true })}`;
166
+ for (let i = 1; i < value.length; i++) {
167
+ statement += ` ${mode} ${builder.preprocess(value[i], {
168
+ escape: true,
169
+ })}`;
170
+ }
171
+ return `${key}:(${statement})`;
172
+ };
173
+ const notContains = (key, value) => {
174
+ const allPrefix = allOr === "" ? "*:* AND" : "";
175
+ return allPrefix + "NOT " + contains(key, value);
176
+ };
177
+ const containsAny = (key, value) => {
178
+ return contains(key, value, "OR");
154
179
  };
155
180
  const oneOf = (key, value) => {
156
181
  if (!Array.isArray(value)) {
@@ -169,6 +194,8 @@ class QueryBuilder {
169
194
  };
170
195
  function build(structure, queryFn) {
171
196
  for (let [key, value] of Object.entries(structure)) {
197
+ // check for new format - remove numbering if needed
198
+ key = removeKeyNumbering(key);
172
199
  key = builder.preprocess(key.replace(/ /g, "_"), {
173
200
  escape: true,
174
201
  });
@@ -246,6 +273,12 @@ class QueryBuilder {
246
273
  if (this.query.contains) {
247
274
  build(this.query.contains, contains);
248
275
  }
276
+ if (this.query.notContains) {
277
+ build(this.query.notContains, notContains);
278
+ }
279
+ if (this.query.containsAny) {
280
+ build(this.query.containsAny, containsAny);
281
+ }
249
282
  // make sure table ID is always added as an AND
250
283
  if (tableId) {
251
284
  query = `(${query})`;
@@ -280,6 +313,8 @@ class QueryBuilder {
280
313
  });
281
314
  }
282
315
  }
316
+ // exported for unit testing
317
+ exports.QueryBuilder = QueryBuilder;
283
318
  /**
284
319
  * Executes a lucene search query.
285
320
  * @param url The query URL
@@ -13,8 +13,9 @@ const { cloneDeep } = require("lodash/fp");
13
13
  const { InternalTables } = require("../../../db/utils");
14
14
  const userController = require("../user");
15
15
  const { FieldTypes } = require("../../../constants");
16
- const { makeExternalQuery } = require("../../../integrations/base/utils");
17
16
  const { getAppDB } = require("@budibase/backend-core/context");
17
+ const { makeExternalQuery } = require("../../../integrations/base/query");
18
+ const { removeKeyNumbering } = require("../../../integrations/base/utils");
18
19
  validateJs.extend(validateJs.validators.datetime, {
19
20
  parse: function (value) {
20
21
  return new Date(value).getTime();
@@ -24,6 +25,7 @@ validateJs.extend(validateJs.validators.datetime, {
24
25
  return new Date(value).toISOString();
25
26
  },
26
27
  });
28
+ exports.removeKeyNumbering = removeKeyNumbering;
27
29
  exports.getDatasourceAndQuery = (json) => __awaiter(void 0, void 0, void 0, function* () {
28
30
  const datasourceId = json.endpoint.datasourceId;
29
31
  const db = getAppDB();
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  });
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.getSignedUploadURL = exports.serveClientLibrary = exports.serveApp = exports.uploadFile = exports.serveBuilder = exports.toggleBetaUiFeature = void 0;
12
+ exports.getSignedUploadURL = exports.serveClientLibrary = exports.serveBuilderPreview = exports.serveApp = exports.deleteObjects = exports.uploadFile = exports.serveBuilder = exports.toggleBetaUiFeature = void 0;
13
13
  require("svelte/register");
14
14
  const send = require("koa-send");
15
15
  const { resolve, join } = require("../../../utilities/centralPath");
@@ -19,7 +19,7 @@ const { processString } = require("@budibase/string-templates");
19
19
  const { loadHandlebarsFile, NODE_MODULES_PATH, TOP_LEVEL_PATH, } = require("../../../utilities/fileSystem");
20
20
  const env = require("../../../environment");
21
21
  const { clientLibraryPath } = require("../../../utilities");
22
- const { upload } = require("../../../utilities/fileSystem");
22
+ const { upload, deleteFiles } = require("../../../utilities/fileSystem");
23
23
  const { attachmentsRelativeURL } = require("../../../utilities");
24
24
  const { DocumentType } = require("../../../db/utils");
25
25
  const { getAppDB, getAppId } = require("@budibase/backend-core/context");
@@ -95,6 +95,12 @@ const uploadFile = function (ctx) {
95
95
  });
96
96
  };
97
97
  exports.uploadFile = uploadFile;
98
+ const deleteObjects = function (ctx) {
99
+ return __awaiter(this, void 0, void 0, function* () {
100
+ ctx.body = yield deleteFiles(ObjectStoreBuckets.APPS, ctx.request.body.keys);
101
+ });
102
+ };
103
+ exports.deleteObjects = deleteObjects;
98
104
  const serveApp = function (ctx) {
99
105
  return __awaiter(this, void 0, void 0, function* () {
100
106
  const db = getAppDB({ skip_setup: true });
@@ -123,6 +129,24 @@ const serveApp = function (ctx) {
123
129
  });
124
130
  };
125
131
  exports.serveApp = serveApp;
132
+ const serveBuilderPreview = function (ctx) {
133
+ return __awaiter(this, void 0, void 0, function* () {
134
+ const db = getAppDB({ skip_setup: true });
135
+ const appInfo = yield db.get(DocumentType.APP_METADATA);
136
+ if (!env.isJest()) {
137
+ let appId = getAppId();
138
+ const previewHbs = loadHandlebarsFile(`${__dirname}/templates/preview.hbs`);
139
+ ctx.body = yield processString(previewHbs, {
140
+ clientLibPath: clientLibraryPath(appId, appInfo.version, ctx),
141
+ });
142
+ }
143
+ else {
144
+ // just return the app info for jest to assert on
145
+ ctx.body = Object.assign(Object.assign({}, appInfo), { builderPreview: true });
146
+ }
147
+ });
148
+ };
149
+ exports.serveBuilderPreview = serveBuilderPreview;
126
150
  const serveClientLibrary = function (ctx) {
127
151
  return __awaiter(this, void 0, void 0, function* () {
128
152
  return send(ctx, "budibase-client.js", {
@@ -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>
@@ -11,7 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  const { buildExternalTableId, breakExternalTableId, } = require("../../../integrations/utils");
12
12
  const { getTable, generateForeignKey, generateJunctionTableName, foreignKeyStructure, hasTypeChanged, } = require("./utils");
13
13
  const { DataSourceOperation, FieldTypes, RelationshipTypes, } = require("../../../constants");
14
- const { makeExternalQuery } = require("../../../integrations/base/utils");
14
+ const { makeExternalQuery } = require("../../../integrations/base/query");
15
15
  const { cloneDeep } = require("lodash/fp");
16
16
  const csvParser = require("../../../utilities/csvParser");
17
17
  const { handleRequest } = require("../row/external");
@@ -68,8 +68,11 @@ router
68
68
  // TODO: for now this builder endpoint is not authorized/secured, will need to be
69
69
  .get("/builder/:file*", controller.serveBuilder)
70
70
  .post("/api/attachments/process", (0, authorized_1.default)(permissions_1.BUILDER), controller.uploadFile)
71
+ .post("/api/attachments/delete", (0, authorized_1.default)(permissions_1.BUILDER), controller.deleteObjects)
71
72
  .post("/api/beta/:feature", controller.toggleBetaUiFeature)
72
73
  .post("/api/attachments/:tableId/upload", (0, resourceId_1.paramResource)("tableId"), (0, authorized_1.default)(permissions_1.PermissionTypes.TABLE, permissions_1.PermissionLevels.WRITE), controller.uploadFile)
74
+ .post("/api/attachments/:tableId/delete", (0, resourceId_1.paramResource)("tableId"), (0, authorized_1.default)(permissions_1.PermissionTypes.TABLE, permissions_1.PermissionLevels.WRITE), controller.deleteObjects)
75
+ .get("/preview", (0, authorized_1.default)(permissions_1.BUILDER), controller.serveBuilderPreview)
73
76
  .get("/:appId/:path*", controller.serveApp)
74
77
  .get("/app/:appUrl/:path*", controller.serveApp)
75
78
  .post("/api/attachments/:datasourceId/url", (0, authorized_1.default)(permissions_1.PermissionTypes.TABLE, permissions_1.PermissionLevels.READ), controller.getSignedUploadURL);
package/dist/app.js CHANGED
@@ -104,9 +104,7 @@ server.on("close", () => __awaiter(void 0, void 0, void 0, function* () {
104
104
  return;
105
105
  }
106
106
  shuttingDown = true;
107
- if (!env.isTest()) {
108
- console.log("Server Closed");
109
- }
107
+ console.log("Server Closed");
110
108
  yield automations.shutdown();
111
109
  yield redis_1.default.shutdown();
112
110
  yield backend_core_1.events.shutdown();
@@ -174,3 +172,6 @@ process.on("uncaughtException", err => {
174
172
  process.on("SIGTERM", () => {
175
173
  shutdown();
176
174
  });
175
+ process.on("SIGINT", () => {
176
+ shutdown();
177
+ });
@@ -59,5 +59,6 @@ exports.shutdown = () => __awaiter(void 0, void 0, void 0, function* () {
59
59
  yield automationQueue.close();
60
60
  automationQueue = null;
61
61
  }
62
+ console.log("Bull shutdown");
62
63
  });
63
64
  exports.queue = automationQueue;
@@ -56,6 +56,9 @@ function checkAppMetadata(apps) {
56
56
  for (let [key, errors] of Object.entries(metadata.automationErrors)) {
57
57
  const updated = [];
58
58
  for (let error of errors) {
59
+ if (!error) {
60
+ continue;
61
+ }
59
62
  const startDate = error.split(backend_core_1.db.SEPARATOR)[2];
60
63
  if (startDate > maxStartDate) {
61
64
  updated.push(error);
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.makeExternalQuery = void 0;
13
+ const { integrations } = require("../index");
14
+ function makeExternalQuery(datasource, json) {
15
+ return __awaiter(this, void 0, void 0, function* () {
16
+ const Integration = integrations[datasource.source];
17
+ // query is the opinionated function
18
+ if (Integration.prototype.query) {
19
+ const integration = new Integration(datasource.config);
20
+ return integration.query(json);
21
+ }
22
+ else {
23
+ throw "Datasource does not support query.";
24
+ }
25
+ });
26
+ }
27
+ exports.makeExternalQuery = makeExternalQuery;
@@ -17,6 +17,7 @@ const types_1 = require("@budibase/types");
17
17
  const utils_1 = require("../utils");
18
18
  const sqlTable_1 = __importDefault(require("./sqlTable"));
19
19
  const environment_1 = __importDefault(require("../../environment"));
20
+ const utils_2 = require("./utils");
20
21
  const envLimit = environment_1.default.SQL_MAX_ROWS
21
22
  ? parseInt(environment_1.default.SQL_MAX_ROWS)
22
23
  : null;
@@ -116,12 +117,13 @@ class InternalBuilder {
116
117
  addFilters(query, filters, opts) {
117
118
  function iterate(structure, fn) {
118
119
  for (let [key, value] of Object.entries(structure)) {
119
- const isRelationshipField = key.includes(".");
120
+ const updatedKey = (0, utils_2.removeKeyNumbering)(key);
121
+ const isRelationshipField = updatedKey.includes(".");
120
122
  if (!opts.relationship && !isRelationshipField) {
121
- fn(`${opts.tableName}.${key}`, value);
123
+ fn(`${opts.tableName}.${updatedKey}`, value);
122
124
  }
123
125
  if (opts.relationship && isRelationshipField) {
124
- fn(key, value);
126
+ fn(updatedKey, value);
125
127
  }
126
128
  }
127
129
  }
@@ -139,6 +141,56 @@ class InternalBuilder {
139
141
  ]);
140
142
  }
141
143
  };
144
+ const contains = (mode, any = false) => {
145
+ const fnc = allOr ? "orWhere" : "where";
146
+ const rawFnc = `${fnc}Raw`;
147
+ const not = mode === (filters === null || filters === void 0 ? void 0 : filters.notContains) ? "NOT " : "";
148
+ function stringifyArray(value, quoteStyle = '"') {
149
+ for (let i in value) {
150
+ if (typeof value[i] === "string") {
151
+ value[i] = `${quoteStyle}${value[i]}${quoteStyle}`;
152
+ }
153
+ }
154
+ return `[${value.join(",")}]`;
155
+ }
156
+ if (this.client === utils_1.SqlClient.POSTGRES) {
157
+ iterate(mode, (key, value) => {
158
+ const wrap = any ? "" : "'";
159
+ const containsOp = any ? "\\?| array" : "@>";
160
+ const fieldNames = key.split(/\./g);
161
+ const tableName = fieldNames[0];
162
+ const columnName = fieldNames[1];
163
+ // @ts-ignore
164
+ query = query[rawFnc](`${not}"${tableName}"."${columnName}"::jsonb ${containsOp} ${wrap}${stringifyArray(value, any ? "'" : '"')}${wrap}`);
165
+ });
166
+ }
167
+ else if (this.client === utils_1.SqlClient.MY_SQL) {
168
+ const jsonFnc = any ? "JSON_OVERLAPS" : "JSON_CONTAINS";
169
+ iterate(mode, (key, value) => {
170
+ // @ts-ignore
171
+ query = query[rawFnc](`${not}${jsonFnc}(${key}, '${stringifyArray(value)}')`);
172
+ });
173
+ }
174
+ else {
175
+ const andOr = mode === (filters === null || filters === void 0 ? void 0 : filters.containsAny) ? " OR " : " AND ";
176
+ iterate(mode, (key, value) => {
177
+ let statement = "";
178
+ for (let i in value) {
179
+ if (typeof value[i] === "string") {
180
+ value[i] = `%"${value[i]}"%`;
181
+ }
182
+ else {
183
+ value[i] = `%${value[i]}%`;
184
+ }
185
+ statement +=
186
+ (statement ? andOr : "") +
187
+ `LOWER(${likeKey(this.client, key)}) LIKE ?`;
188
+ }
189
+ // @ts-ignore
190
+ query = query[rawFnc](`${not}(${statement})`, value);
191
+ });
192
+ }
193
+ };
142
194
  if (!filters) {
143
195
  return query;
144
196
  }
@@ -212,32 +264,13 @@ class InternalBuilder {
212
264
  });
213
265
  }
214
266
  if (filters.contains) {
215
- const fnc = allOr ? "orWhere" : "where";
216
- const rawFnc = `${fnc}Raw`;
217
- if (this.client === utils_1.SqlClient.POSTGRES) {
218
- iterate(filters.contains, (key, value) => {
219
- const fieldNames = key.split(/\./g);
220
- const tableName = fieldNames[0];
221
- const columnName = fieldNames[1];
222
- if (typeof value === "string") {
223
- value = `"${value}"`;
224
- }
225
- // @ts-ignore
226
- query = query[rawFnc](`"${tableName}"."${columnName}"::jsonb @> '[${value}]'`);
227
- });
228
- }
229
- else if (this.client === utils_1.SqlClient.MY_SQL) {
230
- iterate(filters.contains, (key, value) => {
231
- if (typeof value === "string") {
232
- value = `"${value}"`;
233
- }
234
- // @ts-ignore
235
- query = query[rawFnc](`JSON_CONTAINS(${key}, '${value}')`);
236
- });
237
- }
238
- else {
239
- iterate(filters.contains, like);
240
- }
267
+ contains(filters.contains);
268
+ }
269
+ if (filters.notContains) {
270
+ contains(filters.notContains);
271
+ }
272
+ if (filters.containsAny) {
273
+ contains(filters.containsAny, true);
241
274
  }
242
275
  return query;
243
276
  }
@@ -548,4 +581,3 @@ class SqlQueryBuilder extends sqlTable_1.default {
548
581
  }
549
582
  }
550
583
  exports.default = SqlQueryBuilder;
551
- module.exports = SqlQueryBuilder;
@@ -1,30 +1,16 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  Object.defineProperty(exports, "__esModule", { value: true });
12
- var DatasourceUtils;
13
- (function (DatasourceUtils) {
14
- const { integrations } = require("../index");
15
- function makeExternalQuery(datasource, json) {
16
- return __awaiter(this, void 0, void 0, function* () {
17
- const Integration = integrations[datasource.source];
18
- // query is the opinionated function
19
- if (Integration.prototype.query) {
20
- const integration = new Integration(datasource.config);
21
- return integration.query(json);
22
- }
23
- else {
24
- throw "Datasource does not support query.";
25
- }
26
- });
3
+ exports.removeKeyNumbering = void 0;
4
+ const QUERY_START_REGEX = /\d[0-9]*:/g;
5
+ function removeKeyNumbering(key) {
6
+ if (typeof key === "string" && key.match(QUERY_START_REGEX) != null) {
7
+ const parts = key.split(":");
8
+ // remove the number
9
+ parts.shift();
10
+ return parts.join(":");
27
11
  }
28
- DatasourceUtils.makeExternalQuery = makeExternalQuery;
29
- module.exports.makeExternalQuery = makeExternalQuery;
30
- })(DatasourceUtils || (DatasourceUtils = {}));
12
+ else {
13
+ return key;
14
+ }
15
+ }
16
+ exports.removeKeyNumbering = removeKeyNumbering;