@budibase/server 1.2.43 → 1.2.44-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 (194) hide show
  1. package/__mocks__/mongodb.ts +4 -0
  2. package/__mocks__/node-fetch.ts +8 -1
  3. package/builder/assets/index.6b1c808b.css +6 -0
  4. package/builder/assets/index.7e1eb5c8.js +977 -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 +422 -399
  8. package/coverage/coverage-final.json +10 -10
  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 +1 -1
  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 +1 -1
  80. package/coverage/lcov-report/automations/index.html +1 -1
  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 +3 -3
  120. package/coverage/lcov-report/db/utils.js.html +61 -61
  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 +19 -19
  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 +1 -1
  130. package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
  131. package/coverage/lcov-report/middleware/index.html +1 -1
  132. package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
  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 +1 -1
  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 +16 -16
  141. package/coverage/lcov-report/utilities/index.html +1 -1
  142. package/coverage/lcov-report/utilities/index.js.html +23 -23
  143. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +1 -1
  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 +1 -1
  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 +1 -1
  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 +1 -1
  158. package/coverage/lcov.info +662 -622
  159. package/dist/api/controllers/row/internalSearch.js +39 -4
  160. package/dist/api/controllers/row/utils.js +3 -1
  161. package/dist/api/controllers/static/index.js +8 -2
  162. package/dist/api/controllers/table/external.js +1 -1
  163. package/dist/api/routes/static.js +2 -0
  164. package/dist/automations/logging/index.js +3 -0
  165. package/dist/integrations/base/query.js +27 -0
  166. package/dist/integrations/base/sql.js +62 -30
  167. package/dist/integrations/base/utils.js +13 -27
  168. package/dist/integrations/microsoftSqlServer.js +5 -2
  169. package/dist/integrations/mongodb.js +35 -8
  170. package/dist/integrations/mysql.js +2 -2
  171. package/dist/integrations/postgres.js +5 -2
  172. package/dist/package.json +7 -7
  173. package/dist/tsconfig.build.tsbuildinfo +1 -1
  174. package/dist/utilities/fileSystem/index.js +2 -1
  175. package/package.json +8 -8
  176. package/src/api/controllers/row/internalSearch.js +45 -3
  177. package/src/api/controllers/row/utils.js +4 -1
  178. package/src/api/controllers/static/index.ts +5 -1
  179. package/src/api/controllers/table/external.js +1 -1
  180. package/src/api/routes/static.ts +11 -0
  181. package/src/api/routes/tests/internalSearch.spec.js +176 -0
  182. package/src/automations/logging/index.ts +3 -0
  183. package/src/integrations/base/query.ts +16 -0
  184. package/src/integrations/base/sql.ts +67 -30
  185. package/src/integrations/base/utils.ts +9 -18
  186. package/src/integrations/microsoftSqlServer.ts +3 -2
  187. package/src/integrations/mongodb.ts +35 -8
  188. package/src/integrations/mysql.ts +1 -1
  189. package/src/integrations/postgres.ts +2 -1
  190. package/src/integrations/tests/mongo.spec.js +218 -0
  191. package/src/integrations/tests/sql.spec.js +101 -11
  192. package/src/utilities/fileSystem/index.js +2 -0
  193. package/builder/assets/index.08dd0c5e.css +0 -6
  194. package/builder/assets/index.3996c5a7.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");
@@ -289,5 +289,6 @@ exports.cleanup = appIds => {
289
289
  exports.upload = upload;
290
290
  exports.retrieve = retrieve;
291
291
  exports.retrieveToTmp = retrieveToTmp;
292
+ exports.deleteFiles = deleteFiles;
292
293
  exports.TOP_LEVEL_PATH = TOP_LEVEL_PATH;
293
294
  exports.NODE_MODULES_PATH = NODE_MODULES_PATH;
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.43",
4
+ "version": "1.2.44-alpha.0",
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.43",
81
- "@budibase/client": "^1.2.43",
82
- "@budibase/pro": "1.2.42",
83
- "@budibase/string-templates": "^1.2.43",
84
- "@budibase/types": "^1.2.43",
80
+ "@budibase/backend-core": "1.2.44-alpha.0",
81
+ "@budibase/client": "1.2.44-alpha.0",
82
+ "@budibase/pro": "1.2.43",
83
+ "@budibase/string-templates": "1.2.44-alpha.0",
84
+ "@budibase/types": "1.2.44-alpha.0",
85
85
  "@bull-board/api": "3.7.0",
86
86
  "@bull-board/koa": "3.9.4",
87
87
  "@elastic/elasticsearch": "7.10.0",
@@ -106,7 +106,7 @@
106
106
  "google-auth-library": "7.12.0",
107
107
  "google-spreadsheet": "3.2.0",
108
108
  "jimp": "0.16.1",
109
- "joi": "17.2.1",
109
+ "joi": "17.6.0",
110
110
  "js-yaml": "4.1.0",
111
111
  "jsonschema": "1.4.0",
112
112
  "knex": "0.95.15",
@@ -195,5 +195,5 @@
195
195
  "optionalDependencies": {
196
196
  "oracledb": "5.3.0"
197
197
  },
198
- "gitHead": "bfdbb8672db3f9160c8a579fca565f7a455db25a"
198
+ "gitHead": "a9aebf552f8f7e69aa0afa1d88c6f090a609f470"
199
199
  }
@@ -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)
@@ -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,12 @@ 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
+ )
48
59
  .get("/:appId/:path*", controller.serveApp)
49
60
  .get("/app/:appUrl/:path*", controller.serveApp)
50
61
  .post(
@@ -0,0 +1,176 @@
1
+ const search = require("../../controllers/row/internalSearch")
2
+ // this will be mocked out for _search endpoint
3
+ const fetch = require("node-fetch")
4
+ const PARAMS = {
5
+ tableId: "ta_12345679abcdef",
6
+ version: "1",
7
+ bookmark: null,
8
+ sort: null,
9
+ sortOrder: "ascending",
10
+ sortType: "string",
11
+ }
12
+
13
+ function checkLucene(resp, expected, params = PARAMS) {
14
+ const query = resp.rows[0].query
15
+ const json = JSON.parse(query)
16
+ if (PARAMS.sort) {
17
+ expect(json.sort).toBe(`${PARAMS.sort}<${PARAMS.sortType}>`)
18
+ }
19
+ if (PARAMS.bookmark) {
20
+ expect(json.bookmark).toBe(PARAMS.bookmark)
21
+ }
22
+ expect(json.include_docs).toBe(true)
23
+ expect(json.q).toBe(`(${expected}) AND tableId:"${params.tableId}"`)
24
+ expect(json.limit).toBe(params.limit || 50)
25
+ }
26
+
27
+ describe("internal search", () => {
28
+ it("default query", async () => {
29
+ const response = await search.paginatedSearch({
30
+ }, PARAMS)
31
+ checkLucene(response, `*:*`)
32
+ })
33
+
34
+ it("test equal query", async () => {
35
+ const response = await search.paginatedSearch({
36
+ equal: {
37
+ "column": "1",
38
+ }
39
+ }, PARAMS)
40
+ checkLucene(response, `*:* AND column:"1"`)
41
+ })
42
+
43
+ it("test notEqual query", async () => {
44
+ const response = await search.paginatedSearch({
45
+ notEqual: {
46
+ "column": "1",
47
+ }
48
+ }, PARAMS)
49
+ checkLucene(response, `*:* AND !column:"1"`)
50
+ })
51
+
52
+ it("test OR query", async () => {
53
+ const response = await search.paginatedSearch({
54
+ allOr: true,
55
+ equal: {
56
+ "column": "2",
57
+ },
58
+ notEqual: {
59
+ "column": "1",
60
+ }
61
+ }, PARAMS)
62
+ checkLucene(response, `column:"2" OR !column:"1"`)
63
+ })
64
+
65
+ it("test AND query", async () => {
66
+ const response = await search.paginatedSearch({
67
+ equal: {
68
+ "column": "2",
69
+ },
70
+ notEqual: {
71
+ "column": "1",
72
+ }
73
+ }, PARAMS)
74
+ checkLucene(response, `*:* AND column:"2" AND !column:"1"`)
75
+ })
76
+
77
+ it("test pagination query", async () => {
78
+ const updatedParams = {
79
+ ...PARAMS,
80
+ limit: 100,
81
+ bookmark: "awd",
82
+ sort: "column",
83
+ }
84
+ const response = await search.paginatedSearch({
85
+ string: {
86
+ "column": "2",
87
+ },
88
+ }, updatedParams)
89
+ checkLucene(response, `*:* AND column:2*`, updatedParams)
90
+ })
91
+
92
+ it("test range query", async () => {
93
+ const response = await search.paginatedSearch({
94
+ range: {
95
+ "column": { low: 1, high: 2 },
96
+ },
97
+ }, PARAMS)
98
+ checkLucene(response, `*:* AND column:[1 TO 2]`, PARAMS)
99
+ })
100
+
101
+ it("test empty query", async () => {
102
+ const response = await search.paginatedSearch({
103
+ empty: {
104
+ "column": "",
105
+ },
106
+ }, PARAMS)
107
+ checkLucene(response, `*:* AND !column:["" TO *]`, PARAMS)
108
+ })
109
+
110
+ it("test notEmpty query", async () => {
111
+ const response = await search.paginatedSearch({
112
+ notEmpty: {
113
+ "column": "",
114
+ },
115
+ }, PARAMS)
116
+ checkLucene(response, `*:* AND column:["" TO *]`, PARAMS)
117
+ })
118
+
119
+ it("test oneOf query", async () => {
120
+ const response = await search.paginatedSearch({
121
+ oneOf: {
122
+ "column": ["a", "b"],
123
+ },
124
+ }, PARAMS)
125
+ checkLucene(response, `*:* AND column:("a" OR "b")`, PARAMS)
126
+ })
127
+
128
+ it("test contains query", async () => {
129
+ const response = await search.paginatedSearch({
130
+ contains: {
131
+ "column": "a",
132
+ "colArr": [1, 2, 3],
133
+ },
134
+ }, PARAMS)
135
+ checkLucene(response, `*:* AND column:a AND colArr:(1 AND 2 AND 3)`, PARAMS)
136
+ })
137
+
138
+ it("test multiple of same column", async () => {
139
+ const response = await search.paginatedSearch({
140
+ allOr: true,
141
+ equal: {
142
+ "1:column": "a",
143
+ "2:column": "b",
144
+ "3:column": "c",
145
+ },
146
+ }, PARAMS)
147
+ checkLucene(response, `column:"a" OR column:"b" OR column:"c"`, PARAMS)
148
+ })
149
+
150
+ it("check a weird case for lucene building", async () => {
151
+ const response = await search.paginatedSearch({
152
+ equal: {
153
+ "1:1:column": "a",
154
+ },
155
+ }, PARAMS)
156
+ checkLucene(response, `*:* AND 1\\:column:"a"`, PARAMS)
157
+ })
158
+
159
+ it("test containsAny query", async () => {
160
+ const response = await search.paginatedSearch({
161
+ containsAny: {
162
+ "column": ["a", "b", "c"]
163
+ },
164
+ }, PARAMS)
165
+ checkLucene(response, `*:* AND column:(a OR b OR c)`, PARAMS)
166
+ })
167
+
168
+ it("test notContains query", async () => {
169
+ const response = await search.paginatedSearch({
170
+ notContains: {
171
+ "column": ["a", "b", "c"]
172
+ },
173
+ }, PARAMS)
174
+ checkLucene(response, `*:* AND NOT column:(a AND b AND c)`, PARAMS)
175
+ })
176
+ })
@@ -23,6 +23,9 @@ export async function checkAppMetadata(apps: App[]) {
23
23
  for (let [key, errors] of Object.entries(metadata.automationErrors)) {
24
24
  const updated = []
25
25
  for (let error of errors) {
26
+ if (!error) {
27
+ continue
28
+ }
26
29
  const startDate = error.split(dbUtils.SEPARATOR)[2]
27
30
  if (startDate > maxStartDate) {
28
31
  updated.push(error)
@@ -0,0 +1,16 @@
1
+ import { QueryJson, Datasource } from "@budibase/types"
2
+ const { integrations } = require("../index")
3
+
4
+ export async function makeExternalQuery(
5
+ datasource: Datasource,
6
+ json: QueryJson
7
+ ) {
8
+ const Integration = integrations[datasource.source]
9
+ // query is the opinionated function
10
+ if (Integration.prototype.query) {
11
+ const integration = new Integration(datasource.config)
12
+ return integration.query(json)
13
+ } else {
14
+ throw "Datasource does not support query."
15
+ }
16
+ }
@@ -10,6 +10,7 @@ import { QueryOptions } from "../../definitions/datasource"
10
10
  import { isIsoDateString, SqlClient } from "../utils"
11
11
  import SqlTableQueryBuilder from "./sqlTable"
12
12
  import environment from "../../environment"
13
+ import { removeKeyNumbering } from "./utils"
13
14
 
14
15
  const envLimit = environment.SQL_MAX_ROWS
15
16
  ? parseInt(environment.SQL_MAX_ROWS)
@@ -133,12 +134,13 @@ class InternalBuilder {
133
134
  fn: (key: string, value: any) => void
134
135
  ) {
135
136
  for (let [key, value] of Object.entries(structure)) {
136
- const isRelationshipField = key.includes(".")
137
+ const updatedKey = removeKeyNumbering(key)
138
+ const isRelationshipField = updatedKey.includes(".")
137
139
  if (!opts.relationship && !isRelationshipField) {
138
- fn(`${opts.tableName}.${key}`, value)
140
+ fn(`${opts.tableName}.${updatedKey}`, value)
139
141
  }
140
142
  if (opts.relationship && isRelationshipField) {
141
- fn(key, value)
143
+ fn(updatedKey, value)
142
144
  }
143
145
  }
144
146
  }
@@ -157,6 +159,61 @@ class InternalBuilder {
157
159
  }
158
160
  }
159
161
 
162
+ const contains = (mode: object, any: boolean = false) => {
163
+ const fnc = allOr ? "orWhere" : "where"
164
+ const rawFnc = `${fnc}Raw`
165
+ const not = mode === filters?.notContains ? "NOT " : ""
166
+ function stringifyArray(value: Array<any>, quoteStyle = '"'): string {
167
+ for (let i in value) {
168
+ if (typeof value[i] === "string") {
169
+ value[i] = `${quoteStyle}${value[i]}${quoteStyle}`
170
+ }
171
+ }
172
+ return `[${value.join(",")}]`
173
+ }
174
+ if (this.client === SqlClient.POSTGRES) {
175
+ iterate(mode, (key: string, value: Array<any>) => {
176
+ const wrap = any ? "" : "'"
177
+ const containsOp = any ? "\\?| array" : "@>"
178
+ const fieldNames = key.split(/\./g)
179
+ const tableName = fieldNames[0]
180
+ const columnName = fieldNames[1]
181
+ // @ts-ignore
182
+ query = query[rawFnc](
183
+ `${not}"${tableName}"."${columnName}"::jsonb ${containsOp} ${wrap}${stringifyArray(
184
+ value,
185
+ any ? "'" : '"'
186
+ )}${wrap}`
187
+ )
188
+ })
189
+ } else if (this.client === SqlClient.MY_SQL) {
190
+ const jsonFnc = any ? "JSON_OVERLAPS" : "JSON_CONTAINS"
191
+ iterate(mode, (key: string, value: Array<any>) => {
192
+ // @ts-ignore
193
+ query = query[rawFnc](
194
+ `${not}${jsonFnc}(${key}, '${stringifyArray(value)}')`
195
+ )
196
+ })
197
+ } else {
198
+ const andOr = mode === filters?.containsAny ? " OR " : " AND "
199
+ iterate(mode, (key: string, value: Array<any>) => {
200
+ let statement = ""
201
+ for (let i in value) {
202
+ if (typeof value[i] === "string") {
203
+ value[i] = `%"${value[i]}"%`
204
+ } else {
205
+ value[i] = `%${value[i]}%`
206
+ }
207
+ statement +=
208
+ (statement ? andOr : "") +
209
+ `LOWER(${likeKey(this.client, key)}) LIKE ?`
210
+ }
211
+ // @ts-ignore
212
+ query = query[rawFnc](`${not}(${statement})`, value)
213
+ })
214
+ }
215
+ }
216
+
160
217
  if (!filters) {
161
218
  return query
162
219
  }
@@ -227,32 +284,13 @@ class InternalBuilder {
227
284
  })
228
285
  }
229
286
  if (filters.contains) {
230
- const fnc = allOr ? "orWhere" : "where"
231
- const rawFnc = `${fnc}Raw`
232
- if (this.client === SqlClient.POSTGRES) {
233
- iterate(filters.contains, (key: string, value: any) => {
234
- const fieldNames = key.split(/\./g)
235
- const tableName = fieldNames[0]
236
- const columnName = fieldNames[1]
237
- if (typeof value === "string") {
238
- value = `"${value}"`
239
- }
240
- // @ts-ignore
241
- query = query[rawFnc](
242
- `"${tableName}"."${columnName}"::jsonb @> '[${value}]'`
243
- )
244
- })
245
- } else if (this.client === SqlClient.MY_SQL) {
246
- iterate(filters.contains, (key: string, value: any) => {
247
- if (typeof value === "string") {
248
- value = `"${value}"`
249
- }
250
- // @ts-ignore
251
- query = query[rawFnc](`JSON_CONTAINS(${key}, '${value}')`)
252
- })
253
- } else {
254
- iterate(filters.contains, like)
255
- }
287
+ contains(filters.contains)
288
+ }
289
+ if (filters.notContains) {
290
+ contains(filters.notContains)
291
+ }
292
+ if (filters.containsAny) {
293
+ contains(filters.containsAny, true)
256
294
  }
257
295
  return query
258
296
  }
@@ -582,4 +620,3 @@ class SqlQueryBuilder extends SqlTableQueryBuilder {
582
620
  }
583
621
 
584
622
  export default SqlQueryBuilder
585
- module.exports = SqlQueryBuilder
@@ -1,21 +1,12 @@
1
- import { QueryJson, Datasource } from "@budibase/types"
1
+ const QUERY_START_REGEX = /\d[0-9]*:/g
2
2
 
3
- module DatasourceUtils {
4
- const { integrations } = require("../index")
5
-
6
- export async function makeExternalQuery(
7
- datasource: Datasource,
8
- json: QueryJson
9
- ) {
10
- const Integration = integrations[datasource.source]
11
- // query is the opinionated function
12
- if (Integration.prototype.query) {
13
- const integration = new Integration(datasource.config)
14
- return integration.query(json)
15
- } else {
16
- throw "Datasource does not support query."
17
- }
3
+ export function removeKeyNumbering(key: any): string {
4
+ if (typeof key === "string" && key.match(QUERY_START_REGEX) != null) {
5
+ const parts = key.split(":")
6
+ // remove the number
7
+ parts.shift()
8
+ return parts.join(":")
9
+ } else {
10
+ return key
18
11
  }
19
-
20
- module.exports.makeExternalQuery = makeExternalQuery
21
12
  }
@@ -16,10 +16,10 @@ import {
16
16
  finaliseExternalTables,
17
17
  SqlClient,
18
18
  } from "./utils"
19
+ import Sql from "./base/sql"
19
20
 
20
21
  module MSSQLModule {
21
22
  const sqlServer = require("mssql")
22
- const Sql = require("./base/sql")
23
23
  const DEFAULT_SCHEMA = "dbo"
24
24
 
25
25
  interface MSSQLConfig {
@@ -97,7 +97,8 @@ module MSSQLModule {
97
97
  class SqlServerIntegration extends Sql implements DatasourcePlus {
98
98
  private readonly config: MSSQLConfig
99
99
  private index: number = 0
100
- static pool: any
100
+ private readonly pool: any
101
+ private client: any
101
102
  public tables: Record<string, Table> = {}
102
103
  public schemaErrors: Record<string, string> = {}
103
104