@budibase/server 1.1.27 → 1.1.29-alpha.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 (185) hide show
  1. package/builder/assets/{index.5cf5be06.css → index.39026912.css} +2 -2
  2. package/builder/assets/index.6d12ed96.js +978 -0
  3. package/builder/assets/{vendor.7f11b0ca.js → vendor.b0c43a06.js} +1 -1
  4. package/builder/index.html +3 -3
  5. package/coverage/clover.xml +253 -234
  6. package/coverage/coverage-final.json +9 -9
  7. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  8. package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
  9. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  10. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  11. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  12. package/coverage/lcov-report/api/controllers/component.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/datasource.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
  15. package/coverage/lcov-report/api/controllers/index.html +1 -1
  16. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  18. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/migrations.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/query/index.html +1 -1
  22. package/coverage/lcov-report/api/controllers/query/validation.js.html +2 -2
  23. package/coverage/lcov-report/api/controllers/role.js.html +1 -1
  24. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  25. package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
  26. package/coverage/lcov-report/api/controllers/row/index.html +18 -18
  27. package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +94 -49
  29. package/coverage/lcov-report/api/controllers/row/staticFormula.js.html +1 -1
  30. package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
  31. package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
  32. package/coverage/lcov-report/api/controllers/script.js.html +1 -1
  33. package/coverage/lcov-report/api/controllers/table/bulkFormula.js.html +1 -1
  34. package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
  35. package/coverage/lcov-report/api/controllers/table/index.html +1 -1
  36. package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
  37. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  38. package/coverage/lcov-report/api/controllers/user.js.html +1 -1
  39. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  40. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  41. package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
  42. package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
  43. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
  44. package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
  45. package/coverage/lcov-report/api/index.html +1 -1
  46. package/coverage/lcov-report/api/index.js.html +1 -1
  47. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  48. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  49. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  50. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  51. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/index.html +1 -1
  58. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/migrations.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/utils/index.html +1 -1
  72. package/coverage/lcov-report/api/routes/utils/validators.js.html +2 -2
  73. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  75. package/coverage/lcov-report/automations/actions.js.html +1 -1
  76. package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
  77. package/coverage/lcov-report/automations/bullboard.js.html +1 -1
  78. package/coverage/lcov-report/automations/index.html +1 -1
  79. package/coverage/lcov-report/automations/index.js.html +1 -1
  80. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  81. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  82. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  83. package/coverage/lcov-report/automations/steps/executeQuery.js.html +5 -2
  84. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  85. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/index.html +17 -17
  87. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/loop.js.html +1 -1
  89. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/queryRows.js.html +12 -6
  91. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/utils.js.html +25 -13
  96. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  97. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  98. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  99. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  100. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  101. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  102. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  103. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  104. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  105. package/coverage/lcov-report/automations/triggers.js.html +1 -1
  106. package/coverage/lcov-report/constants/index.html +1 -1
  107. package/coverage/lcov-report/constants/index.js.html +1 -1
  108. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  109. package/coverage/lcov-report/constants/screens.js.html +1 -1
  110. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  111. package/coverage/lcov-report/db/index.html +1 -1
  112. package/coverage/lcov-report/db/index.js.html +1 -1
  113. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
  114. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  115. package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
  116. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  117. package/coverage/lcov-report/db/newid.js.html +1 -1
  118. package/coverage/lcov-report/db/utils.js.html +1 -1
  119. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  120. package/coverage/lcov-report/events/index.html +1 -1
  121. package/coverage/lcov-report/events/index.js.html +1 -1
  122. package/coverage/lcov-report/events/utils.js.html +1 -1
  123. package/coverage/lcov-report/index.html +46 -46
  124. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  125. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  126. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  127. package/coverage/lcov-report/middleware/builder.js.html +1 -1
  128. package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
  129. package/coverage/lcov-report/middleware/index.html +21 -21
  130. package/coverage/lcov-report/middleware/joi-validator.js.html +43 -43
  131. package/coverage/lcov-report/middleware/publicApi.js.html +1 -1
  132. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  133. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  134. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  135. package/coverage/lcov-report/threads/automation.js.html +1 -1
  136. package/coverage/lcov-report/threads/index.html +1 -1
  137. package/coverage/lcov-report/threads/query.js.html +1 -1
  138. package/coverage/lcov-report/threads/utils.js.html +1 -1
  139. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  140. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  141. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  142. package/coverage/lcov-report/utilities/global.js.html +67 -13
  143. package/coverage/lcov-report/utilities/index.html +20 -20
  144. package/coverage/lcov-report/utilities/index.js.html +1 -1
  145. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +1 -1
  146. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  147. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  148. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  149. package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
  150. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
  151. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
  152. package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
  153. package/coverage/lcov-report/utilities/security.js.html +2 -2
  154. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  155. package/coverage/lcov-report/utilities/usageQuota/index.html +1 -1
  156. package/coverage/lcov-report/utilities/usageQuota/rows.js.html +1 -1
  157. package/coverage/lcov-report/utilities/usageQuota/usageQuoteReset.js.html +1 -1
  158. package/coverage/lcov-report/utilities/users.js.html +1 -1
  159. package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
  160. package/coverage/lcov.info +485 -445
  161. package/dist/api/controllers/query/index.js +1 -1
  162. package/dist/api/controllers/query/validation.js +1 -1
  163. package/dist/api/controllers/row/internalSearch.js +30 -16
  164. package/dist/api/routes/utils/validators.js +1 -1
  165. package/dist/automations/steps/queryRows.js +4 -2
  166. package/dist/automations/steps/utils.js +9 -5
  167. package/dist/integrations/base/sql.js +43 -14
  168. package/dist/package.json +8 -8
  169. package/dist/tsconfig.build.tsbuildinfo +1 -1
  170. package/dist/utilities/global.js +21 -4
  171. package/package.json +9 -9
  172. package/src/api/controllers/query/index.ts +1 -1
  173. package/src/api/controllers/query/validation.js +1 -1
  174. package/src/api/controllers/row/internalSearch.js +35 -20
  175. package/src/api/routes/tests/user.spec.js +2 -0
  176. package/src/api/routes/utils/validators.js +1 -1
  177. package/src/automations/steps/executeQuery.js +1 -0
  178. package/src/automations/steps/queryRows.js +4 -2
  179. package/src/automations/steps/utils.js +9 -5
  180. package/src/definitions/datasource.ts +3 -0
  181. package/src/integrations/base/sql.ts +44 -13
  182. package/src/integrations/tests/sql.spec.js +50 -4
  183. package/src/middleware/tests/currentapp.spec.js +11 -0
  184. package/src/utilities/global.js +20 -2
  185. package/builder/assets/index.e96881ab.js +0 -979
@@ -16,6 +16,7 @@ const { user: userCache } = require("@budibase/backend-core/cache");
16
16
  const { getGlobalDB, isUserInAppTenant, } = require("@budibase/backend-core/tenancy");
17
17
  const env = require("../environment");
18
18
  const { getAppId } = require("@budibase/backend-core/context");
19
+ const { groups } = require("@budibase/pro");
19
20
  exports.updateAppRole = (user, { appId } = {}) => {
20
21
  appId = appId || getAppId();
21
22
  if (!user || !user.roles) {
@@ -40,11 +41,27 @@ exports.updateAppRole = (user, { appId } = {}) => {
40
41
  delete user.roles;
41
42
  return user;
42
43
  };
44
+ function checkGroupRoles(user, { appId } = {}) {
45
+ return __awaiter(this, void 0, void 0, function* () {
46
+ if (!user.roleId) {
47
+ let roleId = yield groups.getGroupRoleId(user, appId);
48
+ user.roleId = roleId;
49
+ }
50
+ return user;
51
+ });
52
+ }
43
53
  function processUser(user, { appId } = {}) {
44
- if (user) {
45
- delete user.password;
46
- }
47
- return exports.updateAppRole(user, { appId });
54
+ var _a;
55
+ return __awaiter(this, void 0, void 0, function* () {
56
+ if (user) {
57
+ delete user.password;
58
+ }
59
+ user = yield exports.updateAppRole(user, { appId });
60
+ if ((_a = user === null || user === void 0 ? void 0 : user.userGroups) === null || _a === void 0 ? void 0 : _a.length) {
61
+ user = yield checkGroupRoles(user, { appId });
62
+ }
63
+ return user;
64
+ });
48
65
  }
49
66
  exports.getCachedSelf = (ctx, appId) => __awaiter(void 0, void 0, void 0, function* () {
50
67
  // this has to be tenant aware, can't depend on the context to find it out
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/server",
3
3
  "email": "hi@budibase.com",
4
- "version": "1.1.27",
4
+ "version": "1.1.29-alpha.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.1.27",
81
- "@budibase/client": "^1.1.27",
82
- "@budibase/pro": "1.1.26",
83
- "@budibase/string-templates": "^1.1.27",
84
- "@budibase/types": "^1.1.27",
80
+ "@budibase/backend-core": "^1.1.29-alpha.1",
81
+ "@budibase/client": "^1.1.29-alpha.1",
82
+ "@budibase/pro": "1.1.29-alpha.0",
83
+ "@budibase/string-templates": "^1.1.29-alpha.1",
84
+ "@budibase/types": "^1.1.29-alpha.1",
85
85
  "@bull-board/api": "3.7.0",
86
86
  "@bull-board/koa": "3.9.4",
87
87
  "@elastic/elasticsearch": "7.10.0",
@@ -118,7 +118,7 @@
118
118
  "koa-send": "5.0.0",
119
119
  "koa-session": "5.12.0",
120
120
  "koa-static": "5.0.0",
121
- "koa2-ratelimit": "1.1.0",
121
+ "koa2-ratelimit": "1.1.1",
122
122
  "lodash": "4.17.21",
123
123
  "memorystream": "0.3.1",
124
124
  "mongodb": "3.6.3",
@@ -137,7 +137,7 @@
137
137
  "redis": "4",
138
138
  "server-destroy": "1.0.1",
139
139
  "snowflake-promise": "^4.5.0",
140
- "svelte": "3.44.1",
140
+ "svelte": "3.49.0",
141
141
  "swagger-parser": "10.0.3",
142
142
  "to-json-schema": "0.2.5",
143
143
  "uuid": "3.3.2",
@@ -195,5 +195,5 @@
195
195
  "optionalDependencies": {
196
196
  "oracledb": "5.3.0"
197
197
  },
198
- "gitHead": "ce7b4989f2a9e575370f4ccfd7fa9d8db27dfc13"
198
+ "gitHead": "f00000b82e47fffa6341c813cfce59f76384b3a5"
199
199
  }
@@ -246,7 +246,7 @@ async function execute(
246
246
  }
247
247
 
248
248
  export async function executeV1(ctx: any) {
249
- return execute(ctx, { rowsOnly: true })
249
+ return execute(ctx, { rowsOnly: true, isAutomation: false })
250
250
  }
251
251
 
252
252
  export async function executeV2(
@@ -1,4 +1,4 @@
1
- const joiValidator = require("../../../middleware/joi-validator")
1
+ const { joiValidator } = require("@budibase/backend-core/auth")
2
2
  const Joi = require("joi")
3
3
 
4
4
  const OPTIONAL_STRING = Joi.string().optional().allow(null).allow("")
@@ -19,6 +19,7 @@ class QueryBuilder {
19
19
  empty: {},
20
20
  notEmpty: {},
21
21
  oneOf: {},
22
+ contains: {},
22
23
  ...base,
23
24
  }
24
25
  this.limit = 50
@@ -119,6 +120,11 @@ class QueryBuilder {
119
120
  return this
120
121
  }
121
122
 
123
+ addContains(key, value) {
124
+ this.query.contains[key] = value
125
+ return this
126
+ }
127
+
122
128
  /**
123
129
  * Preprocesses a value before going into a lucene search.
124
130
  * Transforms strings to lowercase and wraps strings and bools in quotes.
@@ -164,6 +170,31 @@ class QueryBuilder {
164
170
  return `${key}:${builder.preprocess(value, allPreProcessingOpts)}`
165
171
  }
166
172
 
173
+ const contains = (key, value) => {
174
+ if (!value && value !== 0) {
175
+ return null
176
+ }
177
+ return `${key}:${builder.preprocess(value, { escape: true })}`
178
+ }
179
+
180
+ const oneOf = (key, value) => {
181
+ if (!Array.isArray(value)) {
182
+ if (typeof value === "string") {
183
+ value = value.split(",")
184
+ } else {
185
+ return ""
186
+ }
187
+ }
188
+ let orStatement = `${builder.preprocess(value[0], allPreProcessingOpts)}`
189
+ for (let i = 1; i < value.length; i++) {
190
+ orStatement += ` OR ${builder.preprocess(
191
+ value[i],
192
+ allPreProcessingOpts
193
+ )}`
194
+ }
195
+ return `${key}:(${orStatement})`
196
+ }
197
+
167
198
  function build(structure, queryFn) {
168
199
  for (let [key, value] of Object.entries(structure)) {
169
200
  key = builder.preprocess(key.replace(/ /g, "_"), {
@@ -239,26 +270,10 @@ class QueryBuilder {
239
270
  build(this.query.notEmpty, key => `${key}:["" TO *]`)
240
271
  }
241
272
  if (this.query.oneOf) {
242
- build(this.query.oneOf, (key, value) => {
243
- if (!Array.isArray(value)) {
244
- if (typeof value === "string") {
245
- value = value.split(",")
246
- } else {
247
- return ""
248
- }
249
- }
250
- let orStatement = `${builder.preprocess(
251
- value[0],
252
- allPreProcessingOpts
253
- )}`
254
- for (let i = 1; i < value.length; i++) {
255
- orStatement += ` OR ${builder.preprocess(
256
- value[i],
257
- allPreProcessingOpts
258
- )}`
259
- }
260
- return `${key}:(${orStatement})`
261
- })
273
+ build(this.query.oneOf, oneOf)
274
+ }
275
+ if (this.query.contains) {
276
+ build(this.query.contains, contains)
262
277
  }
263
278
  // make sure table ID is always added as an AND
264
279
  if (tableId) {
@@ -23,6 +23,7 @@ describe("/users", () => {
23
23
  })
24
24
 
25
25
  describe("fetch", () => {
26
+
26
27
  it("returns a list of users from an instance db", async () => {
27
28
  await config.createUser("uuidx")
28
29
  await config.createUser("uuidy")
@@ -37,6 +38,7 @@ describe("/users", () => {
37
38
  expect(res.body.find(u => u._id === `ro_ta_users_us_uuidy`)).toBeDefined()
38
39
  })
39
40
 
41
+
40
42
  it("should apply authorization to endpoint", async () => {
41
43
  await config.createUser()
42
44
  await checkPermissionsEndpoint({
@@ -1,4 +1,4 @@
1
- const joiValidator = require("../../../middleware/joi-validator")
1
+ const { joiValidator } = require("@budibase/backend-core/auth")
2
2
  const { DataSourceOperation } = require("../../../constants")
3
3
  const { WebhookType } = require("../../../constants")
4
4
  const {
@@ -74,6 +74,7 @@ exports.run = async function ({ inputs, appId, emitter }) {
74
74
  try {
75
75
  await queryController.executeV2(ctx, { isAutomation: true })
76
76
  const { data, ...rest } = ctx.body
77
+
77
78
  return {
78
79
  response: data,
79
80
  info: rest,
@@ -96,12 +96,14 @@ exports.run = async function ({ inputs, appId }) {
96
96
  tableId,
97
97
  },
98
98
  body: {
99
- sortOrder,
100
99
  sortType,
100
+ limit,
101
101
  sort: sortColumn,
102
102
  query: filters || {},
103
- limit,
103
+ // default to ascending, like data tab
104
+ sortOrder: sortOrder || SortOrders.ASCENDING,
104
105
  },
106
+ version: "1",
105
107
  })
106
108
  try {
107
109
  await rowController.search(ctx)
@@ -17,7 +17,8 @@ exports.getFetchResponse = async fetched => {
17
17
  // need to make sure all ctx structures have the
18
18
  // throw added to them, so that controllers don't
19
19
  // throw a ctx.throw undefined when error occurs
20
- exports.buildCtx = (appId, emitter, { body, params } = {}) => {
20
+ // opts can contain, body, params and version
21
+ exports.buildCtx = (appId, emitter, opts = {}) => {
21
22
  const ctx = {
22
23
  appId,
23
24
  user: { appId },
@@ -26,11 +27,14 @@ exports.buildCtx = (appId, emitter, { body, params } = {}) => {
26
27
  throw error
27
28
  },
28
29
  }
29
- if (body) {
30
- ctx.request = { body }
30
+ if (opts.body) {
31
+ ctx.request = { body: opts.body }
31
32
  }
32
- if (params) {
33
- ctx.params = params
33
+ if (opts.params) {
34
+ ctx.params = opts.params
35
+ }
36
+ if (opts.version) {
37
+ ctx.version = opts.version
34
38
  }
35
39
  return ctx
36
40
  }
@@ -131,6 +131,9 @@ export interface SearchFilters {
131
131
  oneOf?: {
132
132
  [key: string]: any[]
133
133
  }
134
+ contains?: {
135
+ [key: string]: any
136
+ }
134
137
  }
135
138
 
136
139
  export interface SortJson {
@@ -142,6 +142,21 @@ class InternalBuilder {
142
142
  }
143
143
  }
144
144
  }
145
+
146
+ const like = (key: string, value: any) => {
147
+ const fnc = allOr ? "orWhere" : "where"
148
+ // postgres supports ilike, nothing else does
149
+ if (this.client === SqlClients.POSTGRES) {
150
+ query = query[fnc](key, "ilike", `%${value}%`)
151
+ } else {
152
+ const rawFnc = `${fnc}Raw`
153
+ // @ts-ignore
154
+ query = query[rawFnc](`LOWER(${likeKey(this.client, key)}) LIKE ?`, [
155
+ `%${value}%`,
156
+ ])
157
+ }
158
+ }
159
+
145
160
  if (!filters) {
146
161
  return query
147
162
  }
@@ -168,19 +183,7 @@ class InternalBuilder {
168
183
  })
169
184
  }
170
185
  if (filters.fuzzy) {
171
- iterate(filters.fuzzy, (key, value) => {
172
- const fnc = allOr ? "orWhere" : "where"
173
- // postgres supports ilike, nothing else does
174
- if (this.client === SqlClients.POSTGRES) {
175
- query = query[fnc](key, "ilike", `%${value}%`)
176
- } else {
177
- const rawFnc = `${fnc}Raw`
178
- // @ts-ignore
179
- query = query[rawFnc](`LOWER(${likeKey(this.client, key)}) LIKE ?`, [
180
- `%${value}%`,
181
- ])
182
- }
183
- })
186
+ iterate(filters.fuzzy, like)
184
187
  }
185
188
  if (filters.range) {
186
189
  iterate(filters.range, (key, value) => {
@@ -223,6 +226,34 @@ class InternalBuilder {
223
226
  query = query[fnc](key)
224
227
  })
225
228
  }
229
+ if (filters.contains) {
230
+ const fnc = allOr ? "orWhere" : "where"
231
+ const rawFnc = `${fnc}Raw`
232
+ if (this.client === SqlClients.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 === SqlClients.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
+ }
256
+ }
226
257
  return query
227
258
  }
228
259
 
@@ -1,4 +1,5 @@
1
1
  const Sql = require("../base/sql")
2
+ const { SqlClients } = require("../utils")
2
3
 
3
4
  const TABLE_NAME = "test"
4
5
 
@@ -46,7 +47,7 @@ function generateDeleteJson(table = TABLE_NAME, filters = {}) {
46
47
 
47
48
  describe("SQL query builder", () => {
48
49
  const limit = 500
49
- const client = "pg"
50
+ const client = SqlClients.POSTGRES
50
51
  let sql
51
52
 
52
53
  beforeEach(() => {
@@ -173,15 +174,15 @@ describe("SQL query builder", () => {
173
174
  })
174
175
 
175
176
  it("should work with MS-SQL", () => {
176
- const query = new Sql("mssql", 10)._query(generateReadJson())
177
+ const query = new Sql(SqlClients.MS_SQL, 10)._query(generateReadJson())
177
178
  expect(query).toEqual({
178
179
  bindings: [10],
179
180
  sql: `select * from (select top (@p0) * from [${TABLE_NAME}]) as [${TABLE_NAME}]`
180
181
  })
181
182
  })
182
183
 
183
- it("should work with mySQL", () => {
184
- const query = new Sql("mysql", 10)._query(generateReadJson())
184
+ it("should work with MySQL", () => {
185
+ const query = new Sql(SqlClients.MY_SQL, 10)._query(generateReadJson())
185
186
  expect(query).toEqual({
186
187
  bindings: [10],
187
188
  sql: `select * from (select * from \`${TABLE_NAME}\` limit ?) as \`${TABLE_NAME}\``
@@ -238,4 +239,49 @@ describe("SQL query builder", () => {
238
239
  sql: `select * from (select * from "${TABLE_NAME}" where "${TABLE_NAME}"."property" > $1 limit $2) as "${TABLE_NAME}"`
239
240
  })
240
241
  })
242
+
243
+ it("should use like expression for MS-SQL when filter is contains", () => {
244
+ const query = new Sql(SqlClients.MS_SQL, 10)._query(generateReadJson({
245
+ filters: {
246
+ contains: {
247
+ age: 20,
248
+ name: "John"
249
+ }
250
+ }
251
+ }))
252
+ expect(query).toEqual({
253
+ bindings: [10, "%20%", "%John%"],
254
+ sql: `select * from (select top (@p0) * from [${TABLE_NAME}] where LOWER(${TABLE_NAME}.age) LIKE @p1 and LOWER(${TABLE_NAME}.name) LIKE @p2) as [${TABLE_NAME}]`
255
+ })
256
+ })
257
+
258
+ it("should use JSON_CONTAINS expression for MySQL when filter is contains", () => {
259
+ const query = new Sql(SqlClients.MY_SQL, 10)._query(generateReadJson({
260
+ filters: {
261
+ contains: {
262
+ age: 20,
263
+ name: "John"
264
+ }
265
+ }
266
+ }))
267
+ expect(query).toEqual({
268
+ bindings: [10],
269
+ sql: `select * from (select * from \`${TABLE_NAME}\` where JSON_CONTAINS(${TABLE_NAME}.age, '20') and JSON_CONTAINS(${TABLE_NAME}.name, '"John"') limit ?) as \`${TABLE_NAME}\``
270
+ })
271
+ })
272
+
273
+ it("should use jsonb operator expression for PostgreSQL when filter is contains", () => {
274
+ const query = new Sql(SqlClients.POSTGRES, 10)._query(generateReadJson({
275
+ filters: {
276
+ contains: {
277
+ age: 20,
278
+ name: "John"
279
+ }
280
+ }
281
+ }))
282
+ expect(query).toEqual({
283
+ bindings: [10],
284
+ sql: `select * from (select * from \"${TABLE_NAME}\" where \"${TABLE_NAME}\".\"age\"::jsonb @> '[20]' and \"${TABLE_NAME}\".\"name\"::jsonb @> '["John"]' limit $1) as \"${TABLE_NAME}\"`
285
+ })
286
+ })
241
287
  })
@@ -1,5 +1,6 @@
1
1
  mockAuthWithNoCookie()
2
2
  mockWorker()
3
+ mockUserGroups()
3
4
 
4
5
  jest.mock("@budibase/backend-core/db", () => {
5
6
  const coreDb = jest.requireActual("@budibase/backend-core/db")
@@ -29,6 +30,16 @@ function mockReset() {
29
30
  mockWorker()
30
31
  }
31
32
 
33
+ function mockUserGroups() {
34
+ jest.mock("@budibase/pro", () => ({
35
+ groups: {
36
+ getGroupRoleId: () => {
37
+ return "BASIC"
38
+ },
39
+ },
40
+ }))
41
+ }
42
+
32
43
  function mockAuthWithNoCookie() {
33
44
  jest.resetModules()
34
45
  mockWorker()
@@ -12,9 +12,11 @@ const {
12
12
  } = require("@budibase/backend-core/tenancy")
13
13
  const env = require("../environment")
14
14
  const { getAppId } = require("@budibase/backend-core/context")
15
+ const { groups } = require("@budibase/pro")
15
16
 
16
17
  exports.updateAppRole = (user, { appId } = {}) => {
17
18
  appId = appId || getAppId()
19
+
18
20
  if (!user || !user.roles) {
19
21
  return user
20
22
  }
@@ -33,15 +35,30 @@ exports.updateAppRole = (user, { appId } = {}) => {
33
35
  } else if (!user.roleId) {
34
36
  user.roleId = BUILTIN_ROLE_IDS.PUBLIC
35
37
  }
38
+
36
39
  delete user.roles
37
40
  return user
38
41
  }
39
42
 
40
- function processUser(user, { appId } = {}) {
43
+ async function checkGroupRoles(user, { appId } = {}) {
44
+ if (!user.roleId) {
45
+ let roleId = await groups.getGroupRoleId(user, appId)
46
+ user.roleId = roleId
47
+ }
48
+
49
+ return user
50
+ }
51
+
52
+ async function processUser(user, { appId } = {}) {
41
53
  if (user) {
42
54
  delete user.password
43
55
  }
44
- return exports.updateAppRole(user, { appId })
56
+ user = await exports.updateAppRole(user, { appId })
57
+ if (user?.userGroups?.length) {
58
+ user = await checkGroupRoles(user, { appId })
59
+ }
60
+
61
+ return user
45
62
  }
46
63
 
47
64
  exports.getCachedSelf = async (ctx, appId) => {
@@ -89,6 +106,7 @@ exports.getGlobalUsers = async (users = null) => {
89
106
  if (!appId) {
90
107
  return globalUsers
91
108
  }
109
+
92
110
  return globalUsers.map(user => exports.updateAppRole(user))
93
111
  }
94
112