@budibase/server 0.9.188 → 0.9.190-alpha.10

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 (314) hide show
  1. package/Dockerfile +7 -1
  2. package/__mocks__/mssql.ts +1 -1
  3. package/__mocks__/oracledb.ts +31 -0
  4. package/__mocks__/pg.ts +3 -0
  5. package/builder/assets/bb-space-man.5be51a45.svg +290 -0
  6. package/builder/assets/index.2bd3b345.js +1316 -0
  7. package/builder/assets/index.4fb3f887.css +1 -0
  8. package/builder/assets/oracle.66592424.png +0 -0
  9. package/builder/assets/vendor.470042a4.js +152 -0
  10. package/builder/assets/vendor.5e1fe644.css +1 -0
  11. package/builder/index.html +4 -4
  12. package/coverage/clover.xml +1608 -1191
  13. package/coverage/coverage-final.json +97 -93
  14. package/coverage/lcov-report/api/controllers/analytics.js.html +2 -2
  15. package/coverage/lcov-report/api/controllers/apikeys.js.html +2 -2
  16. package/coverage/lcov-report/api/controllers/application.js.html +113 -80
  17. package/coverage/lcov-report/api/controllers/auth.js.html +19 -10
  18. package/coverage/lcov-report/api/controllers/automation.js.html +3 -3
  19. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/component.js.html +4 -4
  22. package/coverage/lcov-report/api/controllers/datasource.js.html +245 -41
  23. package/coverage/lcov-report/api/controllers/dev.js.html +16 -7
  24. package/coverage/lcov-report/api/controllers/hosting.js.html +10 -10
  25. package/coverage/lcov-report/api/controllers/index.html +98 -98
  26. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/layout.js.html +9 -9
  28. package/coverage/lcov-report/api/controllers/metadata.js.html +3 -3
  29. package/coverage/lcov-report/api/controllers/permission.js.html +3 -3
  30. package/coverage/lcov-report/api/controllers/query.js.html +79 -229
  31. package/coverage/lcov-report/api/controllers/role.js.html +9 -9
  32. package/coverage/lcov-report/api/controllers/routing.js.html +29 -29
  33. package/coverage/lcov-report/api/controllers/row/external.js.html +17 -8
  34. package/coverage/lcov-report/api/controllers/row/index.html +26 -26
  35. package/coverage/lcov-report/api/controllers/row/index.js.html +2 -2
  36. package/coverage/lcov-report/api/controllers/row/internal.js.html +9 -9
  37. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +2 -2
  38. package/coverage/lcov-report/api/controllers/row/utils.js.html +21 -30
  39. package/coverage/lcov-report/api/controllers/screen.js.html +3 -3
  40. package/coverage/lcov-report/api/controllers/script.js.html +3 -3
  41. package/coverage/lcov-report/api/controllers/table/external.js.html +1001 -0
  42. package/coverage/lcov-report/api/controllers/table/index.html +56 -26
  43. package/coverage/lcov-report/api/controllers/table/index.js.html +80 -350
  44. package/coverage/lcov-report/api/controllers/table/internal.js.html +533 -0
  45. package/coverage/lcov-report/api/controllers/table/utils.js.html +175 -52
  46. package/coverage/lcov-report/api/controllers/templates.js.html +4 -4
  47. package/coverage/lcov-report/api/controllers/user.js.html +390 -33
  48. package/coverage/lcov-report/api/controllers/view/exporters.js.html +19 -4
  49. package/coverage/lcov-report/api/controllers/view/index.html +22 -22
  50. package/coverage/lcov-report/api/controllers/view/index.js.html +52 -13
  51. package/coverage/lcov-report/api/controllers/view/utils.js.html +11 -11
  52. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +3 -3
  53. package/coverage/lcov-report/api/controllers/webhook.js.html +4 -4
  54. package/coverage/lcov-report/api/index.html +1 -1
  55. package/coverage/lcov-report/api/index.js.html +5 -5
  56. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/dev.js.html +2 -2
  67. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/index.html +4 -4
  69. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/query.js.html +5 -2
  75. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/row.js.html +536 -2
  78. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/static.js.html +2 -2
  81. package/coverage/lcov-report/api/routes/table.js.html +464 -2
  82. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  83. package/coverage/lcov-report/api/routes/user.js.html +17 -2
  84. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  85. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  86. package/coverage/lcov-report/automations/actions.js.html +2 -2
  87. package/coverage/lcov-report/automations/automationUtils.js.html +4 -4
  88. package/coverage/lcov-report/automations/bullboard.js.html +71 -20
  89. package/coverage/lcov-report/automations/index.html +35 -50
  90. package/coverage/lcov-report/automations/index.js.html +2 -2
  91. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/discord.js.html +3 -3
  96. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/executeScript.js.html +3 -3
  98. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  99. package/coverage/lcov-report/automations/steps/index.html +14 -14
  100. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  101. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +3 -3
  102. package/coverage/lcov-report/automations/steps/queryRows.js.html +3 -3
  103. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  104. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  105. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  106. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  107. package/coverage/lcov-report/automations/steps/utils.js.html +7 -7
  108. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  109. package/coverage/lcov-report/automations/triggerInfo/app.js.html +2 -2
  110. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +2 -2
  111. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  112. package/coverage/lcov-report/automations/triggerInfo/index.js.html +8 -8
  113. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +2 -2
  114. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +2 -2
  115. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +2 -2
  116. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +2 -2
  117. package/coverage/lcov-report/automations/triggers.js.html +22 -10
  118. package/coverage/lcov-report/automations/utils.js.html +46 -16
  119. package/coverage/lcov-report/constants/index.html +5 -5
  120. package/coverage/lcov-report/constants/index.js.html +87 -21
  121. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  122. package/coverage/lcov-report/constants/screens.js.html +2 -2
  123. package/coverage/lcov-report/db/client.js.html +2 -2
  124. package/coverage/lcov-report/db/inMemoryView.js.html +2 -2
  125. package/coverage/lcov-report/db/index.html +2 -2
  126. package/coverage/lcov-report/db/index.js.html +1 -1
  127. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +6 -6
  128. package/coverage/lcov-report/db/linkedRows/index.html +10 -10
  129. package/coverage/lcov-report/db/linkedRows/index.js.html +5 -5
  130. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +6 -6
  131. package/coverage/lcov-report/db/newid.js.html +4 -4
  132. package/coverage/lcov-report/db/utils.js.html +75 -75
  133. package/coverage/lcov-report/events/AutomationEmitter.js.html +6 -6
  134. package/coverage/lcov-report/events/index.html +6 -6
  135. package/coverage/lcov-report/events/index.js.html +1 -1
  136. package/coverage/lcov-report/events/utils.js.html +5 -5
  137. package/coverage/lcov-report/index.html +106 -91
  138. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  139. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  140. package/coverage/lcov-report/middleware/appInfo.js.html +2 -2
  141. package/coverage/lcov-report/middleware/authorized.js.html +19 -16
  142. package/coverage/lcov-report/middleware/builder.js.html +22 -13
  143. package/coverage/lcov-report/middleware/currentapp.js.html +33 -33
  144. package/coverage/lcov-report/middleware/index.html +23 -23
  145. package/coverage/lcov-report/middleware/joi-validator.js.html +2 -2
  146. package/coverage/lcov-report/middleware/resourceId.js.html +18 -18
  147. package/coverage/lcov-report/middleware/selfhost.js.html +2 -2
  148. package/coverage/lcov-report/middleware/usageQuota.js.html +4 -4
  149. package/coverage/lcov-report/middleware/utils.js.html +2 -2
  150. package/coverage/lcov-report/{automations/thread.js.html → threads/automation.js.html} +90 -33
  151. package/coverage/lcov-report/threads/index.html +141 -0
  152. package/coverage/lcov-report/threads/index.js.html +278 -0
  153. package/coverage/lcov-report/threads/query.js.html +278 -0
  154. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  155. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  156. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  157. package/coverage/lcov-report/utilities/csvParser.js.html +144 -15
  158. package/coverage/lcov-report/utilities/global.js.html +38 -23
  159. package/coverage/lcov-report/utilities/index.html +48 -48
  160. package/coverage/lcov-report/utilities/index.js.html +49 -16
  161. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +31 -10
  162. package/coverage/lcov-report/utilities/queue/index.html +14 -14
  163. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  164. package/coverage/lcov-report/utilities/routing/index.js.html +2 -2
  165. package/coverage/lcov-report/utilities/rowProcessor/index.html +4 -4
  166. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +2 -2
  167. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +3 -3
  168. package/coverage/lcov-report/utilities/scriptRunner.js.html +16 -7
  169. package/coverage/lcov-report/utilities/security.js.html +3 -3
  170. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  171. package/coverage/lcov-report/utilities/users.js.html +1 -1
  172. package/coverage/lcov-report/utilities/workerRequests.js.html +5 -5
  173. package/coverage/lcov.info +2687 -1942
  174. package/dist/api/controllers/application.js +16 -6
  175. package/dist/api/controllers/auth.js +3 -3
  176. package/dist/api/controllers/datasource.js +67 -16
  177. package/dist/api/controllers/deploy/index.js +2 -0
  178. package/dist/api/controllers/dev.js +4 -1
  179. package/dist/api/controllers/query.js +32 -62
  180. package/dist/api/controllers/row/ExternalRequest.js +83 -36
  181. package/dist/api/controllers/row/external.js +3 -1
  182. package/dist/api/controllers/row/utils.js +6 -12
  183. package/dist/api/controllers/table/external.js +249 -0
  184. package/dist/api/controllers/table/index.js +49 -123
  185. package/dist/api/controllers/table/internal.js +146 -0
  186. package/dist/api/controllers/table/utils.js +62 -28
  187. package/dist/api/controllers/user.js +123 -11
  188. package/dist/api/controllers/view/exporters.js +4 -0
  189. package/dist/api/controllers/view/index.js +14 -2
  190. package/dist/api/routes/query.js +1 -1
  191. package/dist/api/routes/row.js +178 -0
  192. package/dist/api/routes/table.js +150 -1
  193. package/dist/api/routes/user.js +2 -1
  194. package/dist/app.js +2 -11
  195. package/dist/automations/bullboard.js +37 -10
  196. package/dist/automations/triggers.js +7 -3
  197. package/dist/automations/utils.js +11 -2
  198. package/dist/constants/index.js +19 -0
  199. package/dist/definitions/datasource.js +17 -1
  200. package/dist/environment.js +13 -0
  201. package/dist/integrations/base/IntegrationBase.js +2 -0
  202. package/dist/integrations/base/datasourcePlus.js +2 -0
  203. package/dist/integrations/base/sql.js +350 -177
  204. package/dist/integrations/base/sqlTable.js +143 -0
  205. package/dist/integrations/base/utils.js +30 -0
  206. package/dist/integrations/index.js +6 -0
  207. package/dist/integrations/microsoftSqlServer.js +117 -9
  208. package/dist/integrations/mysql.js +16 -85
  209. package/dist/integrations/oracle.js +367 -0
  210. package/dist/integrations/postgres.js +35 -28
  211. package/dist/integrations/utils.js +71 -9
  212. package/dist/middleware/authorized.js +2 -1
  213. package/dist/middleware/builder.js +4 -1
  214. package/dist/package.json +18 -9
  215. package/dist/tests/utilities/TestConfiguration.js +2 -1
  216. package/dist/{automations/thread.js → threads/automation.js} +26 -8
  217. package/dist/threads/index.js +64 -0
  218. package/dist/threads/query.js +64 -0
  219. package/dist/tsconfig.tsbuildinfo +1 -1
  220. package/dist/utilities/csvParser.js +46 -5
  221. package/dist/utilities/global.js +6 -2
  222. package/dist/utilities/index.js +11 -0
  223. package/dist/utilities/queue/inMemoryQueue.js +8 -0
  224. package/dist/utilities/scriptRunner.js +4 -1
  225. package/package.json +19 -10
  226. package/scripts/dev/manage.js +1 -0
  227. package/scripts/docs/README.md +31 -0
  228. package/scripts/docs/generate.js +74 -0
  229. package/scripts/docs/toSwagger.js +320 -0
  230. package/scripts/integrations/mssql/data/Dockerfile +9 -0
  231. package/scripts/integrations/mssql/data/entrypoint.sh +24 -0
  232. package/scripts/integrations/mssql/data/setup.sql +54 -0
  233. package/scripts/integrations/mssql/docker-compose.yaml +12 -0
  234. package/scripts/integrations/mssql/reset.sh +3 -0
  235. package/scripts/integrations/oracle/docker-compose.yml +19 -0
  236. package/scripts/integrations/oracle/instantclient/linux/x86-64/basiclite-21.4.zip +0 -0
  237. package/scripts/integrations/oracle/instantclient/linux/x86-64/install.sh +23 -0
  238. package/scripts/integrations/oracle/oracle.md +90 -0
  239. package/scripts/integrations/postgres/docker-compose.yml +3 -3
  240. package/scripts/likeCypress.ts +33 -0
  241. package/src/api/controllers/application.js +18 -7
  242. package/src/api/controllers/auth.js +3 -0
  243. package/src/api/controllers/datasource.js +84 -16
  244. package/src/api/controllers/deploy/index.js +2 -0
  245. package/src/api/controllers/dev.js +4 -1
  246. package/src/api/controllers/query.js +35 -85
  247. package/src/api/controllers/row/ExternalRequest.ts +99 -38
  248. package/src/api/controllers/row/external.js +4 -1
  249. package/src/api/controllers/row/utils.js +8 -11
  250. package/src/api/controllers/table/external.js +307 -0
  251. package/src/api/controllers/table/index.js +47 -137
  252. package/src/api/controllers/table/internal.js +151 -0
  253. package/src/api/controllers/table/utils.js +74 -33
  254. package/src/api/controllers/user.js +132 -13
  255. package/src/api/controllers/view/exporters.js +5 -0
  256. package/src/api/controllers/view/index.js +15 -2
  257. package/src/api/routes/query.js +1 -0
  258. package/src/api/routes/row.js +178 -0
  259. package/src/api/routes/table.js +154 -0
  260. package/src/api/routes/tests/application.spec.js +0 -2
  261. package/src/api/routes/tests/datasource.spec.js +2 -2
  262. package/src/api/routes/tests/misc.spec.js +5 -1
  263. package/src/api/routes/tests/query.spec.js +38 -9
  264. package/src/api/routes/tests/screen.spec.js +1 -1
  265. package/src/api/routes/user.js +5 -0
  266. package/src/app.ts +2 -10
  267. package/src/automations/bullboard.js +28 -11
  268. package/src/automations/tests/automation.spec.js +3 -4
  269. package/src/automations/triggers.js +7 -3
  270. package/src/automations/utils.js +12 -2
  271. package/src/constants/index.js +22 -0
  272. package/src/definitions/common.ts +7 -1
  273. package/src/definitions/datasource.ts +21 -3
  274. package/src/environment.js +15 -0
  275. package/src/integrations/airtable.ts +2 -1
  276. package/src/integrations/arangodb.ts +2 -1
  277. package/src/integrations/base/IntegrationBase.ts +6 -0
  278. package/src/integrations/base/datasourcePlus.ts +9 -0
  279. package/src/integrations/base/sql.ts +375 -218
  280. package/src/integrations/base/sqlTable.ts +180 -0
  281. package/src/integrations/base/utils.ts +22 -0
  282. package/src/integrations/couchdb.ts +2 -1
  283. package/src/integrations/dynamodb.ts +2 -1
  284. package/src/integrations/elasticsearch.ts +2 -1
  285. package/src/integrations/index.ts +7 -0
  286. package/src/integrations/microsoftSqlServer.ts +145 -13
  287. package/src/integrations/mongodb.ts +2 -1
  288. package/src/integrations/mysql.ts +23 -95
  289. package/src/integrations/oracle.ts +464 -0
  290. package/src/integrations/postgres.ts +45 -35
  291. package/src/integrations/rest.ts +2 -1
  292. package/src/integrations/s3.ts +2 -1
  293. package/src/integrations/tests/microsoftSqlServer.spec.js +22 -15
  294. package/src/integrations/tests/oracle.spec.js +94 -0
  295. package/src/integrations/tests/postgres.spec.js +4 -0
  296. package/src/integrations/utils.ts +78 -8
  297. package/src/middleware/authorized.js +2 -1
  298. package/src/middleware/builder.js +4 -1
  299. package/src/tests/utilities/TestConfiguration.js +2 -1
  300. package/src/{automations/thread.js → threads/automation.js} +27 -8
  301. package/src/threads/index.js +66 -0
  302. package/src/threads/query.js +66 -0
  303. package/src/utilities/csvParser.js +48 -5
  304. package/src/utilities/global.js +7 -2
  305. package/src/utilities/index.js +11 -0
  306. package/src/utilities/queue/inMemoryQueue.js +7 -0
  307. package/src/utilities/scriptRunner.js +4 -1
  308. package/src/utilities/tests/__snapshots__/csvParser.spec.js.snap +0 -6
  309. package/src/utilities/tests/csvParser.spec.js +3 -0
  310. package/tsconfig.json +1 -1
  311. package/builder/assets/index.550fdba7.js +0 -201
  312. package/builder/assets/index.7469e1e4.css +0 -29
  313. package/builder/assets/vendor.278a305c.js +0 -9
  314. package/builder/assets/vendor.d75d5eb7.css +0 -1
@@ -8,7 +8,7 @@ const {
8
8
  const { isEqual } = require("lodash/fp")
9
9
  const { AutoFieldSubTypes, FieldTypes } = require("../../../constants")
10
10
  const { inputProcessing } = require("../../../utilities/rowProcessor")
11
- const { USERS_TABLE_SCHEMA } = require("../../../constants")
11
+ const { USERS_TABLE_SCHEMA, SwitchableTypes } = require("../../../constants")
12
12
  const {
13
13
  isExternalTable,
14
14
  breakExternalTableId,
@@ -72,43 +72,47 @@ exports.makeSureTableUpToDate = (table, tableToSave) => {
72
72
  }
73
73
 
74
74
  exports.handleDataImport = async (appId, user, table, dataImport) => {
75
+ if (!dataImport || !dataImport.csvString) {
76
+ return table
77
+ }
75
78
  const db = new CouchDB(appId)
76
- if (dataImport && dataImport.csvString) {
77
- // Populate the table with rows imported from CSV in a bulk update
78
- const data = await csvParser.transform(dataImport)
79
-
80
- let finalData = []
81
- for (let i = 0; i < data.length; i++) {
82
- let row = data[i]
83
- row._id = generateRowID(table._id)
84
- row.tableId = table._id
85
- const processed = inputProcessing(user, table, row, {
86
- noAutoRelationships: true,
87
- })
88
- table = processed.table
89
- row = processed.row
90
-
91
- for (let [fieldName, schema] of Object.entries(table.schema)) {
92
- // check whether the options need to be updated for inclusion as part of the data import
93
- if (
94
- schema.type === FieldTypes.OPTIONS &&
95
- (!schema.constraints.inclusion ||
96
- schema.constraints.inclusion.indexOf(row[fieldName]) === -1)
97
- ) {
98
- schema.constraints.inclusion = [
99
- ...schema.constraints.inclusion,
100
- row[fieldName],
101
- ]
102
- }
103
- }
79
+ // Populate the table with rows imported from CSV in a bulk update
80
+ const data = await csvParser.transform({
81
+ ...dataImport,
82
+ existingTable: table,
83
+ })
84
+
85
+ let finalData = []
86
+ for (let i = 0; i < data.length; i++) {
87
+ let row = data[i]
88
+ row._id = generateRowID(table._id)
89
+ row.tableId = table._id
90
+ const processed = inputProcessing(user, table, row, {
91
+ noAutoRelationships: true,
92
+ })
93
+ table = processed.table
94
+ row = processed.row
104
95
 
105
- finalData.push(row)
96
+ for (let [fieldName, schema] of Object.entries(table.schema)) {
97
+ // check whether the options need to be updated for inclusion as part of the data import
98
+ if (
99
+ schema.type === FieldTypes.OPTIONS &&
100
+ (!schema.constraints.inclusion ||
101
+ schema.constraints.inclusion.indexOf(row[fieldName]) === -1)
102
+ ) {
103
+ schema.constraints.inclusion = [
104
+ ...schema.constraints.inclusion,
105
+ row[fieldName],
106
+ ]
107
+ }
106
108
  }
107
109
 
108
- await db.bulkDocs(finalData)
109
- let response = await db.put(table)
110
- table._rev = response._rev
110
+ finalData.push(row)
111
111
  }
112
+
113
+ await db.bulkDocs(finalData)
114
+ let response = await db.put(table)
115
+ table._rev = response._rev
112
116
  return table
113
117
  }
114
118
 
@@ -315,4 +319,41 @@ exports.checkForViewUpdates = async (db, table, rename, deletedColumns) => {
315
319
  }
316
320
  }
317
321
 
322
+ exports.generateForeignKey = (column, relatedTable) => {
323
+ return `fk_${relatedTable.name}_${column.fieldName}`
324
+ }
325
+
326
+ exports.generateJunctionTableName = (column, table, relatedTable) => {
327
+ return `jt_${table.name}_${relatedTable.name}_${column.name}_${column.fieldName}`
328
+ }
329
+
330
+ exports.foreignKeyStructure = (keyName, meta = null) => {
331
+ const structure = {
332
+ type: FieldTypes.NUMBER,
333
+ constraints: {},
334
+ name: keyName,
335
+ }
336
+ if (meta) {
337
+ structure.meta = meta
338
+ }
339
+ return structure
340
+ }
341
+
342
+ exports.hasTypeChanged = (table, oldTable) => {
343
+ if (!oldTable) {
344
+ return false
345
+ }
346
+ for (let [key, field] of Object.entries(oldTable.schema)) {
347
+ const oldType = field.type
348
+ if (!table.schema[key]) {
349
+ continue
350
+ }
351
+ const newType = table.schema[key].type
352
+ if (oldType !== newType && SwitchableTypes.indexOf(oldType) === -1) {
353
+ return true
354
+ }
355
+ }
356
+ return false
357
+ }
358
+
318
359
  exports.TableSaveFunctions = TableSaveFunctions
@@ -4,27 +4,144 @@ const {
4
4
  getUserMetadataParams,
5
5
  } = require("../../db/utils")
6
6
  const { InternalTables } = require("../../db/utils")
7
- const { getGlobalUsers } = require("../../utilities/global")
7
+ const { getGlobalUsers, getRawGlobalUser } = require("../../utilities/global")
8
8
  const { getFullUser } = require("../../utilities/users")
9
+ const { isEqual } = require("lodash")
10
+ const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles")
11
+ const { getDevelopmentAppID, getDeployedAppIDs } = require("@budibase/auth/db")
12
+ const { doesDatabaseExist } = require("../../utilities")
13
+ const { UserStatus } = require("@budibase/auth/constants")
9
14
 
10
- function removeGlobalProps(user) {
11
- // make sure to always remove some of the global user props
15
+ async function rawMetadata(db) {
16
+ return (
17
+ await db.allDocs(
18
+ getUserMetadataParams(null, {
19
+ include_docs: true,
20
+ })
21
+ )
22
+ ).rows.map(row => row.doc)
23
+ }
24
+
25
+ function combineMetadataAndUser(user, metadata) {
26
+ // skip users with no access
27
+ if (user.roleId === BUILTIN_ROLE_IDS.PUBLIC) {
28
+ return null
29
+ }
30
+ delete user._rev
31
+ const metadataId = generateUserMetadataID(user._id)
32
+ const newDoc = {
33
+ ...user,
34
+ _id: metadataId,
35
+ tableId: InternalTables.USER_METADATA,
36
+ }
37
+ const found = Array.isArray(metadata)
38
+ ? metadata.find(doc => doc._id === metadataId)
39
+ : metadata
40
+ // copy rev over for the purposes of equality check
41
+ if (found) {
42
+ newDoc._rev = found._rev
43
+ }
44
+ if (found == null || !isEqual(newDoc, found)) {
45
+ return {
46
+ ...found,
47
+ ...newDoc,
48
+ }
49
+ }
50
+ return null
51
+ }
52
+
53
+ exports.syncGlobalUsers = async appId => {
54
+ // sync user metadata
55
+ const db = new CouchDB(appId)
56
+ const [users, metadata] = await Promise.all([
57
+ getGlobalUsers(appId),
58
+ rawMetadata(db),
59
+ ])
60
+ const toWrite = []
61
+ for (let user of users) {
62
+ const combined = await combineMetadataAndUser(user, metadata)
63
+ if (combined) {
64
+ toWrite.push(combined)
65
+ }
66
+ }
67
+ await db.bulkDocs(toWrite)
68
+ }
69
+
70
+ exports.syncUser = async function (ctx) {
71
+ let deleting = false,
72
+ user
73
+ const userId = ctx.params.id
74
+ try {
75
+ user = await getRawGlobalUser(userId)
76
+ } catch (err) {
77
+ if (err && err.status === 404) {
78
+ user = {}
79
+ deleting = true
80
+ } else {
81
+ throw err
82
+ }
83
+ }
84
+ const roles = user.roles
85
+ // remove props which aren't useful to metadata
12
86
  delete user.password
87
+ delete user.forceResetPassword
13
88
  delete user.roles
14
- delete user.builder
15
- return user
89
+ // run through all production appIDs in the users roles
90
+ let prodAppIds
91
+ // if they are a builder then get all production app IDs
92
+ if ((user.builder && user.builder.global) || deleting) {
93
+ prodAppIds = await getDeployedAppIDs(CouchDB)
94
+ } else {
95
+ prodAppIds = Object.entries(roles)
96
+ .filter(entry => entry[1] !== BUILTIN_ROLE_IDS.PUBLIC)
97
+ .map(([appId]) => appId)
98
+ }
99
+ for (let prodAppId of prodAppIds) {
100
+ const roleId = roles[prodAppId]
101
+ const devAppId = getDevelopmentAppID(prodAppId)
102
+ for (let appId of [prodAppId, devAppId]) {
103
+ if (!(await doesDatabaseExist(appId))) {
104
+ continue
105
+ }
106
+ const db = new CouchDB(appId)
107
+ const metadataId = generateUserMetadataID(userId)
108
+ let metadata
109
+ try {
110
+ metadata = await db.get(metadataId)
111
+ } catch (err) {
112
+ if (deleting) {
113
+ continue
114
+ }
115
+ metadata = {
116
+ tableId: InternalTables.USER_METADATA,
117
+ }
118
+ }
119
+ // assign the roleId for the metadata doc
120
+ if (roleId) {
121
+ metadata.roleId = roleId
122
+ }
123
+ let combined = !deleting
124
+ ? combineMetadataAndUser(user, metadata)
125
+ : {
126
+ ...metadata,
127
+ status: UserStatus.INACTIVE,
128
+ metadata: BUILTIN_ROLE_IDS.PUBLIC,
129
+ }
130
+ // if its null then there was no updates required
131
+ if (combined) {
132
+ await db.put(combined)
133
+ }
134
+ }
135
+ }
136
+ ctx.body = {
137
+ message: "User synced.",
138
+ }
16
139
  }
17
140
 
18
141
  exports.fetchMetadata = async function (ctx) {
19
142
  const database = new CouchDB(ctx.appId)
20
143
  const global = await getGlobalUsers(ctx.appId)
21
- const metadata = (
22
- await database.allDocs(
23
- getUserMetadataParams(null, {
24
- include_docs: true,
25
- })
26
- )
27
- ).rows.map(row => row.doc)
144
+ const metadata = await rawMetadata(database)
28
145
  const users = []
29
146
  for (let user of global) {
30
147
  // find the metadata that matches up to the global ID
@@ -52,7 +169,9 @@ exports.updateSelfMetadata = async function (ctx) {
52
169
  exports.updateMetadata = async function (ctx) {
53
170
  const appId = ctx.appId
54
171
  const db = new CouchDB(appId)
55
- const user = removeGlobalProps(ctx.request.body)
172
+ const user = ctx.request.body
173
+ // this isn't applicable to the user
174
+ delete user.roles
56
175
  const metadata = {
57
176
  tableId: InternalTables.USER_METADATA,
58
177
  ...user,
@@ -16,3 +16,8 @@ exports.csv = function (headers, rows) {
16
16
  exports.json = function (headers, rows) {
17
17
  return JSON.stringify(rows, undefined, 2)
18
18
  }
19
+
20
+ exports.ExportFormats = {
21
+ CSV: "csv",
22
+ JSON: "json",
23
+ }
@@ -4,6 +4,7 @@ const { apiFileReturn } = require("../../../utilities/fileSystem")
4
4
  const exporters = require("./exporters")
5
5
  const { saveView, getView, getViews, deleteView } = require("./utils")
6
6
  const { fetchView } = require("../row")
7
+ const { getTable } = require("../table/utils")
7
8
 
8
9
  exports.fetch = async ctx => {
9
10
  const db = new CouchDB(ctx.appId)
@@ -56,7 +57,7 @@ exports.exportView = async ctx => {
56
57
  const view = await getView(db, viewName)
57
58
 
58
59
  const format = ctx.query.format
59
- if (!format) {
60
+ if (!format || !Object.values(exporters.ExportFormats).includes(format)) {
60
61
  ctx.throw(400, "Format must be specified, either csv or json")
61
62
  }
62
63
 
@@ -80,10 +81,22 @@ exports.exportView = async ctx => {
80
81
  let schema = view && view.meta && view.meta.schema
81
82
  if (!schema) {
82
83
  const tableId = ctx.params.tableId || view.meta.tableId
83
- const table = await db.get(tableId)
84
+ const table = await getTable(ctx.appId, tableId)
84
85
  schema = table.schema
85
86
  }
86
87
 
88
+ // make sure no "undefined" entries appear in the CSV
89
+ if (format === exporters.ExportFormats.CSV) {
90
+ const schemaKeys = Object.keys(schema)
91
+ for (let key of schemaKeys) {
92
+ for (let row of ctx.body) {
93
+ if (row[key] == null) {
94
+ row[key] = ""
95
+ }
96
+ }
97
+ }
98
+ }
99
+
87
100
  // Export part
88
101
  let headers = Object.keys(schema)
89
102
  const exporter = exporters[format]
@@ -66,6 +66,7 @@ router
66
66
  )
67
67
  .get(
68
68
  "/api/queries/:queryId",
69
+ paramResource("queryId"),
69
70
  authorized(PermissionTypes.QUERY, PermissionLevels.READ),
70
71
  queryController.find
71
72
  )
@@ -14,24 +14,130 @@ const {
14
14
  const router = Router()
15
15
 
16
16
  router
17
+ /**
18
+ * @api {get} /api/:tableId/:rowId/enrich Get an enriched row
19
+ * @apiName Get an enriched row
20
+ * @apiGroup rows
21
+ * @apiPermission table read access
22
+ * @apiDescription This API is only useful when dealing with rows that have relationships.
23
+ * Normally when a row is a returned from the API relationships will only have the structure
24
+ * `{ primaryDisplay: "name", _id: ... }` but this call will return the full related rows
25
+ * for each relationship instead.
26
+ *
27
+ * @apiParam {string} rowId The ID of the row which is to be retrieved and enriched.
28
+ *
29
+ * @apiSuccess {object} row The response body will be the enriched row.
30
+ */
17
31
  .get(
18
32
  "/api/:tableId/:rowId/enrich",
19
33
  paramSubResource("tableId", "rowId"),
20
34
  authorized(PermissionTypes.TABLE, PermissionLevels.READ),
21
35
  rowController.fetchEnrichedRow
22
36
  )
37
+ /**
38
+ * @api {get} /api/:tableId/rows Get all rows in a table
39
+ * @apiName Get all rows in a table
40
+ * @apiGroup rows
41
+ * @apiPermission table read access
42
+ * @apiDescription This is a deprecated endpoint that should not be used anymore, instead use the search endpoint.
43
+ * This endpoint gets all of the rows within the specified table - it is not heavily used
44
+ * due to its lack of support for pagination. With SQL tables this will retrieve up to a limit and then
45
+ * will simply stop.
46
+ *
47
+ * @apiParam {string} tableId The ID of the table to retrieve all rows within.
48
+ *
49
+ * @apiSuccess {object[]} rows The response body will be an array of all rows found.
50
+ */
23
51
  .get(
24
52
  "/api/:tableId/rows",
25
53
  paramResource("tableId"),
26
54
  authorized(PermissionTypes.TABLE, PermissionLevels.READ),
27
55
  rowController.fetch
28
56
  )
57
+ /**
58
+ * @api {get} /api/:tableId/rows/:rowId Retrieve a single row
59
+ * @apiName Retrieve a single row
60
+ * @apiGroup rows
61
+ * @apiPermission table read access
62
+ * @apiDescription This endpoint retrieves only the specified row. If you wish to retrieve
63
+ * a row by anything other than its _id field, use the search endpoint.
64
+ *
65
+ * @apiParam {string} tableId The ID of the table to retrieve a row from.
66
+ * @apiParam {string} rowId The ID of the row to retrieve.
67
+ *
68
+ * @apiSuccess {object} body The response body will be the row that was found.
69
+ */
29
70
  .get(
30
71
  "/api/:tableId/rows/:rowId",
31
72
  paramSubResource("tableId", "rowId"),
32
73
  authorized(PermissionTypes.TABLE, PermissionLevels.READ),
33
74
  rowController.find
34
75
  )
76
+ /**
77
+ * @api {post} /api/:tableId/search Search for rows in a table
78
+ * @apiName Search for rows in a table
79
+ * @apiGroup rows
80
+ * @apiPermission table read access
81
+ * @apiDescription This is the primary method of accessing rows in Budibase, the data provider
82
+ * and data UI in the builder are built atop this. All filtering, sorting and pagination is
83
+ * handled through this, for internal and external (datasource plus, e.g. SQL) tables.
84
+ *
85
+ * @apiParam {string} tableId The ID of the table to retrieve rows from.
86
+ *
87
+ * @apiParam (Body) {boolean} [paginate] If pagination is required then this should be set to true,
88
+ * defaults to false.
89
+ * @apiParam (Body) {object} [query] This contains a set of filters which should be applied, if none
90
+ * specified then the request will be unfiltered. An example with all of the possible query
91
+ * options has been supplied below.
92
+ * @apiParam (Body) {number} [limit] This sets a limit for the number of rows that will be returned,
93
+ * this will be implemented at the database level if supported for performance reasons. This
94
+ * is useful when paginating to set exactly how many rows per page.
95
+ * @apiParam (Body) {string} [bookmark] If pagination is enabled then a bookmark will be returned
96
+ * with each successful search request, this should be supplied back to get the next page.
97
+ * @apiParam (Body) {object} [sort] If sort is desired this should contain the name of the column to
98
+ * sort on.
99
+ * @apiParam (Body) {string} [sortOrder] If sort is enabled then this can be either "descending" or
100
+ * "ascending" as required.
101
+ * @apiParam (Body) {string} [sortType] If sort is enabled then you must specify the type of search
102
+ * being used, either "string" or "number". This is only used for internal tables.
103
+ *
104
+ * @apiParamExample {json} Example:
105
+ * {
106
+ * "tableId": "ta_70260ff0b85c467ca74364aefc46f26d",
107
+ * "query": {
108
+ * "string": {},
109
+ * "fuzzy": {},
110
+ * "range": {
111
+ * "columnName": {
112
+ * "high": 20,
113
+ * "low": 10,
114
+ * }
115
+ * },
116
+ * "equal": {
117
+ * "columnName": "someValue"
118
+ * },
119
+ * "notEqual": {},
120
+ * "empty": {},
121
+ * "notEmpty": {},
122
+ * "contains": {},
123
+ * "notContains": {}
124
+ * "oneOf": {
125
+ * "columnName": ["value"]
126
+ * }
127
+ * },
128
+ * "limit": 10,
129
+ * "sort": "name",
130
+ * "sortOrder": "descending",
131
+ * "sortType": "string",
132
+ * "paginate": true
133
+ * }
134
+ *
135
+ * @apiSuccess {object[]} rows An array of rows that was found based on the supplied parameters.
136
+ * @apiSuccess {boolean} hasNextPage If pagination was enabled then this specifies whether or
137
+ * not there is another page after this request.
138
+ * @apiSuccess {string} bookmark The bookmark to be sent with the next request to get the next
139
+ * page.
140
+ */
35
141
  .post(
36
142
  "/api/:tableId/search",
37
143
  paramResource("tableId"),
@@ -46,6 +152,30 @@ router
46
152
  authorized(PermissionTypes.TABLE, PermissionLevels.READ),
47
153
  rowController.search
48
154
  )
155
+ /**
156
+ * @api {post} /api/:tableId/rows Creates a new row
157
+ * @apiName Creates a new row
158
+ * @apiGroup rows
159
+ * @apiPermission table write access
160
+ * @apiDescription This API will create a new row based on the supplied body. If the
161
+ * body includes an "_id" field then it will update an existing row if the field
162
+ * links to one. Please note that "_id", "_rev" and "tableId" are fields that are
163
+ * already used by Budibase tables and cannot be used for columns.
164
+ *
165
+ * @apiParam {string} tableId The ID of the table to save a row to.
166
+ *
167
+ * @apiParam (Body) {string} [_id] If the row exists already then an ID for the row must be provided.
168
+ * @apiParam (Body) {string} [_rev] If working with an existing row for an internal table its revision
169
+ * must also be provided.
170
+ * @apiParam (Body) {string} tableId The ID of the table should also be specified in the row body itself.
171
+ * @apiParam (Body) {any} [any] Any field supplied in the body will be assessed to see if it matches
172
+ * a column in the specified table. All other fields will be dropped and not stored.
173
+ *
174
+ * @apiSuccess {string} _id The ID of the row that was just saved, if it was just created this
175
+ * is the rows new ID.
176
+ * @apiSuccess {string} [_rev] If saving to an internal table a revision will also be returned.
177
+ * @apiSuccess {object} body The contents of the row that was saved will be returned as well.
178
+ */
49
179
  .post(
50
180
  "/api/:tableId/rows",
51
181
  paramResource("tableId"),
@@ -53,18 +183,66 @@ router
53
183
  usage,
54
184
  rowController.save
55
185
  )
186
+ /**
187
+ * @api {patch} /api/:tableId/rows Updates a row
188
+ * @apiName Update a row
189
+ * @apiGroup rows
190
+ * @apiPermission table write access
191
+ * @apiDescription This endpoint is identical to the row creation endpoint but instead it will
192
+ * error if an _id isn't provided, it will only function for existing rows.
193
+ */
56
194
  .patch(
57
195
  "/api/:tableId/rows",
58
196
  paramResource("tableId"),
59
197
  authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
60
198
  rowController.patch
61
199
  )
200
+ /**
201
+ * @api {post} /api/:tableId/rows/validate Validate inputs for a row
202
+ * @apiName Validate inputs for a row
203
+ * @apiGroup rows
204
+ * @apiPermission table write access
205
+ * @apiDescription When attempting to save a row you may want to check if the row is valid
206
+ * given the table schema, this will iterate through all the constraints on the table and
207
+ * check if the request body is valid.
208
+ *
209
+ * @apiParam {string} tableId The ID of the table the row is to be validated for.
210
+ *
211
+ * @apiParam (Body) {any} [any] Any fields provided in the request body will be tested
212
+ * against the table schema and constraints.
213
+ *
214
+ * @apiSuccess {boolean} valid If inputs provided are acceptable within the table schema this
215
+ * will be true, if it is not then then errors property will be populated.
216
+ * @apiSuccess {object} [errors] A key value map of information about fields on the input
217
+ * which do not match the table schema. The key name will be the column names that have breached
218
+ * the schema.
219
+ */
62
220
  .post(
63
221
  "/api/:tableId/rows/validate",
64
222
  paramResource("tableId"),
65
223
  authorized(PermissionTypes.TABLE, PermissionLevels.WRITE),
66
224
  rowController.validate
67
225
  )
226
+ /**
227
+ * @api {delete} /api/:tableId/rows Delete rows
228
+ * @apiName Delete rows
229
+ * @apiGroup rows
230
+ * @apiPermission table write access
231
+ * @apiDescription This endpoint can delete a single row, or delete them in a bulk
232
+ * fashion.
233
+ *
234
+ * @apiParam {string} tableId The ID of the table the row is to be deleted from.
235
+ *
236
+ * @apiParam (Body) {object[]} [rows] If bulk deletion is desired then provide the rows in this
237
+ * key of the request body that are to be deleted.
238
+ * @apiParam (Body) {string} [_id] If deleting a single row then provide its ID in this field.
239
+ * @apiParam (Body) {string} [_rev] If deleting a single row from an internal table then provide its
240
+ * revision here.
241
+ *
242
+ * @apiSuccess {object[]|object} body If deleting bulk then the response body will be an array
243
+ * of the deleted rows, if deleting a single row then the body will contain a "row" property which
244
+ * is the deleted row.
245
+ */
68
246
  .delete(
69
247
  "/api/:tableId/rows",
70
248
  paramResource("tableId"),