@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,23 +8,26 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- var __rest = (this && this.__rest) || function (s, e) {
12
- var t = {};
13
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
- t[p] = s[p];
15
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
- t[p[i]] = s[p[i]];
19
- }
20
- return t;
21
- };
22
11
  const CouchDB = require("../../../db");
23
- const linkRows = require("../../../db/linkedRows");
12
+ const internal = require("./internal");
13
+ const external = require("./external");
24
14
  const csvParser = require("../../../utilities/csvParser");
25
- const { getRowParams, getTableParams, generateTableID, getDatasourceParams, BudibaseInternalDB, } = require("../../../db/utils");
26
- const { FieldTypes } = require("../../../constants");
27
- const { TableSaveFunctions, getTable } = require("./utils");
15
+ const { isExternalTable } = require("../../../integrations/utils");
16
+ const { getTableParams, getDatasourceParams, BudibaseInternalDB, } = require("../../../db/utils");
17
+ const { getTable } = require("./utils");
18
+ function pickApi({ tableId, table }) {
19
+ if (table && !tableId) {
20
+ tableId = table._id;
21
+ }
22
+ if (table && table.type === "external") {
23
+ return external;
24
+ }
25
+ else if (tableId && isExternalTable(tableId)) {
26
+ return external;
27
+ }
28
+ return internal;
29
+ }
30
+ // covers both internal and external
28
31
  exports.fetch = function (ctx) {
29
32
  return __awaiter(this, void 0, void 0, function* () {
30
33
  const db = new CouchDB(ctx.appId);
@@ -50,126 +53,49 @@ exports.find = function (ctx) {
50
53
  exports.save = function (ctx) {
51
54
  return __awaiter(this, void 0, void 0, function* () {
52
55
  const appId = ctx.appId;
53
- const db = new CouchDB(appId);
54
- const _a = ctx.request.body, { dataImport } = _a, rest = __rest(_a, ["dataImport"]);
55
- let tableToSave = Object.assign({ type: "table", _id: generateTableID(), views: {} }, rest);
56
- // if the table obj had an _id then it will have been retrieved
57
- let oldTable;
58
- if (ctx.request.body && ctx.request.body._id) {
59
- oldTable = yield db.get(ctx.request.body._id);
60
- }
61
- // saving a table is a complex operation, involving many different steps, this
62
- // has been broken out into a utility to make it more obvious/easier to manipulate
63
- const tableSaveFunctions = new TableSaveFunctions({
64
- db,
65
- ctx,
66
- oldTable,
67
- dataImport,
68
- });
69
- tableToSave = yield tableSaveFunctions.before(tableToSave);
70
- // make sure that types don't change of a column, have to remove
71
- // the column if you want to change the type
72
- if (oldTable && oldTable.schema) {
73
- for (let propKey of Object.keys(tableToSave.schema)) {
74
- let column = tableToSave.schema[propKey];
75
- let oldColumn = oldTable.schema[propKey];
76
- if (oldColumn && oldColumn.type === "internal") {
77
- oldColumn.type = "auto";
78
- }
79
- if (oldColumn && oldColumn.type !== column.type) {
80
- ctx.throw(400, "Cannot change the type of a column");
81
- }
82
- }
83
- }
84
- // Don't rename if the name is the same
85
- let { _rename } = tableToSave;
86
- /* istanbul ignore next */
87
- if (_rename && _rename.old === _rename.updated) {
88
- _rename = null;
89
- delete tableToSave._rename;
90
- }
91
- // rename row fields when table column is renamed
92
- /* istanbul ignore next */
93
- if (_rename && tableToSave.schema[_rename.updated].type === FieldTypes.LINK) {
94
- ctx.throw(400, "Cannot rename a linked column.");
95
- }
96
- tableToSave = yield tableSaveFunctions.mid(tableToSave);
97
- // update schema of non-statistics views when new columns are added
98
- for (let view in tableToSave.views) {
99
- const tableView = tableToSave.views[view];
100
- if (!tableView)
101
- continue;
102
- if (tableView.schema.group || tableView.schema.field)
103
- continue;
104
- tableView.schema = tableToSave.schema;
105
- }
106
- // update linked rows
107
- try {
108
- const linkResp = yield linkRows.updateLinks({
109
- appId,
110
- eventType: oldTable
111
- ? linkRows.EventType.TABLE_UPDATED
112
- : linkRows.EventType.TABLE_SAVE,
113
- table: tableToSave,
114
- oldTable: oldTable,
115
- });
116
- if (linkResp != null && linkResp._rev) {
117
- tableToSave._rev = linkResp._rev;
118
- }
119
- }
120
- catch (err) {
121
- ctx.throw(400, err);
122
- }
123
- // don't perform any updates until relationships have been
124
- // checked by the updateLinks function
125
- const updatedRows = tableSaveFunctions.getUpdatedRows();
126
- if (updatedRows && updatedRows.length !== 0) {
127
- yield db.bulkDocs(updatedRows);
128
- }
129
- const result = yield db.put(tableToSave);
130
- tableToSave._rev = result.rev;
131
- tableToSave = yield tableSaveFunctions.after(tableToSave);
132
- ctx.eventEmitter &&
133
- ctx.eventEmitter.emitTable(`table:save`, appId, tableToSave);
56
+ const table = ctx.request.body;
57
+ const savedTable = yield pickApi({ table }).save(ctx);
134
58
  ctx.status = 200;
135
- ctx.message = `Table ${ctx.request.body.name} saved successfully.`;
136
- ctx.body = tableToSave;
59
+ ctx.message = `Table ${table.name} saved successfully.`;
60
+ ctx.eventEmitter &&
61
+ ctx.eventEmitter.emitTable(`table:save`, appId, savedTable);
62
+ ctx.body = savedTable;
137
63
  });
138
64
  };
139
65
  exports.destroy = function (ctx) {
140
66
  return __awaiter(this, void 0, void 0, function* () {
141
67
  const appId = ctx.appId;
142
- const db = new CouchDB(appId);
143
- const tableToDelete = yield db.get(ctx.params.tableId);
144
- // Delete all rows for that table
145
- const rows = yield db.allDocs(getRowParams(ctx.params.tableId, null, {
146
- include_docs: true,
147
- }));
148
- yield db.bulkDocs(rows.rows.map(row => (Object.assign(Object.assign({}, row.doc), { _deleted: true }))));
149
- // update linked rows
150
- yield linkRows.updateLinks({
151
- appId,
152
- eventType: linkRows.EventType.TABLE_DELETE,
153
- table: tableToDelete,
154
- });
155
- // don't remove the table itself until very end
156
- yield db.remove(tableToDelete);
157
- // remove table search index
158
- const currentIndexes = yield db.getIndexes();
159
- const existingIndex = currentIndexes.indexes.find(existing => existing.name === `search:${ctx.params.tableId}`);
160
- if (existingIndex) {
161
- yield db.deleteIndex(existingIndex);
162
- }
68
+ const tableId = ctx.params.tableId;
69
+ const deletedTable = yield pickApi({ tableId }).destroy(ctx);
163
70
  ctx.eventEmitter &&
164
- ctx.eventEmitter.emitTable(`table:delete`, appId, tableToDelete);
71
+ ctx.eventEmitter.emitTable(`table:delete`, appId, deletedTable);
165
72
  ctx.status = 200;
166
- ctx.body = { message: `Table ${ctx.params.tableId} deleted.` };
73
+ ctx.body = { message: `Table ${tableId} deleted.` };
74
+ });
75
+ };
76
+ exports.bulkImport = function (ctx) {
77
+ return __awaiter(this, void 0, void 0, function* () {
78
+ const tableId = ctx.params.tableId;
79
+ yield pickApi({ tableId }).bulkImport(ctx);
80
+ // right now we don't trigger anything for bulk import because it
81
+ // can only be done in the builder, but in the future we may need to
82
+ // think about events for bulk items
83
+ ctx.status = 200;
84
+ ctx.body = { message: `Bulk rows created.` };
167
85
  });
168
86
  };
169
87
  exports.validateCSVSchema = function (ctx) {
170
88
  return __awaiter(this, void 0, void 0, function* () {
171
- const { csvString, schema = {} } = ctx.request.body;
172
- const result = yield csvParser.parse(csvString, schema);
89
+ // tableId being specified means its an import to an existing table
90
+ const { csvString, schema = {}, tableId } = ctx.request.body;
91
+ let existingTable;
92
+ if (tableId) {
93
+ existingTable = yield getTable(ctx.appId, tableId);
94
+ }
95
+ let result = yield csvParser.parse(csvString, schema);
96
+ if (existingTable) {
97
+ result = csvParser.updateSchema({ schema: result, existingTable });
98
+ }
173
99
  ctx.body = { schema: result };
174
100
  });
175
101
  };
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __rest = (this && this.__rest) || function (s, e) {
12
+ var t = {};
13
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
14
+ t[p] = s[p];
15
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
16
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
17
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
18
+ t[p[i]] = s[p[i]];
19
+ }
20
+ return t;
21
+ };
22
+ const CouchDB = require("../../../db");
23
+ const linkRows = require("../../../db/linkedRows");
24
+ const { getRowParams, generateTableID } = require("../../../db/utils");
25
+ const { FieldTypes } = require("../../../constants");
26
+ const { TableSaveFunctions, hasTypeChanged, getTable, handleDataImport, } = require("./utils");
27
+ exports.save = function (ctx) {
28
+ return __awaiter(this, void 0, void 0, function* () {
29
+ const appId = ctx.appId;
30
+ const db = new CouchDB(appId);
31
+ const _a = ctx.request.body, { dataImport } = _a, rest = __rest(_a, ["dataImport"]);
32
+ let tableToSave = Object.assign({ type: "table", _id: generateTableID(), views: {} }, rest);
33
+ // if the table obj had an _id then it will have been retrieved
34
+ let oldTable;
35
+ if (ctx.request.body && ctx.request.body._id) {
36
+ oldTable = yield db.get(ctx.request.body._id);
37
+ }
38
+ if (hasTypeChanged(tableToSave, oldTable)) {
39
+ ctx.throw(400, "A column type has changed.");
40
+ }
41
+ // saving a table is a complex operation, involving many different steps, this
42
+ // has been broken out into a utility to make it more obvious/easier to manipulate
43
+ const tableSaveFunctions = new TableSaveFunctions({
44
+ db,
45
+ ctx,
46
+ oldTable,
47
+ dataImport,
48
+ });
49
+ tableToSave = yield tableSaveFunctions.before(tableToSave);
50
+ // make sure that types don't change of a column, have to remove
51
+ // the column if you want to change the type
52
+ if (oldTable && oldTable.schema) {
53
+ for (let propKey of Object.keys(tableToSave.schema)) {
54
+ let oldColumn = oldTable.schema[propKey];
55
+ if (oldColumn && oldColumn.type === "internal") {
56
+ oldColumn.type = "auto";
57
+ }
58
+ }
59
+ }
60
+ // Don't rename if the name is the same
61
+ let { _rename } = tableToSave;
62
+ /* istanbul ignore next */
63
+ if (_rename && _rename.old === _rename.updated) {
64
+ _rename = null;
65
+ delete tableToSave._rename;
66
+ }
67
+ // rename row fields when table column is renamed
68
+ /* istanbul ignore next */
69
+ if (_rename && tableToSave.schema[_rename.updated].type === FieldTypes.LINK) {
70
+ ctx.throw(400, "Cannot rename a linked column.");
71
+ }
72
+ tableToSave = yield tableSaveFunctions.mid(tableToSave);
73
+ // update schema of non-statistics views when new columns are added
74
+ for (let view in tableToSave.views) {
75
+ const tableView = tableToSave.views[view];
76
+ if (!tableView)
77
+ continue;
78
+ if (tableView.schema.group || tableView.schema.field)
79
+ continue;
80
+ tableView.schema = tableToSave.schema;
81
+ }
82
+ // update linked rows
83
+ try {
84
+ const linkResp = yield linkRows.updateLinks({
85
+ appId,
86
+ eventType: oldTable
87
+ ? linkRows.EventType.TABLE_UPDATED
88
+ : linkRows.EventType.TABLE_SAVE,
89
+ table: tableToSave,
90
+ oldTable: oldTable,
91
+ });
92
+ if (linkResp != null && linkResp._rev) {
93
+ tableToSave._rev = linkResp._rev;
94
+ }
95
+ }
96
+ catch (err) {
97
+ ctx.throw(400, err);
98
+ }
99
+ // don't perform any updates until relationships have been
100
+ // checked by the updateLinks function
101
+ const updatedRows = tableSaveFunctions.getUpdatedRows();
102
+ if (updatedRows && updatedRows.length !== 0) {
103
+ yield db.bulkDocs(updatedRows);
104
+ }
105
+ const result = yield db.put(tableToSave);
106
+ tableToSave._rev = result.rev;
107
+ tableToSave = yield tableSaveFunctions.after(tableToSave);
108
+ return tableToSave;
109
+ });
110
+ };
111
+ exports.destroy = function (ctx) {
112
+ return __awaiter(this, void 0, void 0, function* () {
113
+ const appId = ctx.appId;
114
+ const db = new CouchDB(appId);
115
+ const tableToDelete = yield db.get(ctx.params.tableId);
116
+ // Delete all rows for that table
117
+ const rows = yield db.allDocs(getRowParams(ctx.params.tableId, null, {
118
+ include_docs: true,
119
+ }));
120
+ yield db.bulkDocs(rows.rows.map(row => (Object.assign(Object.assign({}, row.doc), { _deleted: true }))));
121
+ // update linked rows
122
+ yield linkRows.updateLinks({
123
+ appId,
124
+ eventType: linkRows.EventType.TABLE_DELETE,
125
+ table: tableToDelete,
126
+ });
127
+ // don't remove the table itself until very end
128
+ yield db.remove(tableToDelete);
129
+ // remove table search index
130
+ const currentIndexes = yield db.getIndexes();
131
+ const existingIndex = currentIndexes.indexes.find(existing => existing.name === `search:${ctx.params.tableId}`);
132
+ if (existingIndex) {
133
+ yield db.deleteIndex(existingIndex);
134
+ }
135
+ return tableToDelete;
136
+ });
137
+ };
138
+ exports.bulkImport = function (ctx) {
139
+ return __awaiter(this, void 0, void 0, function* () {
140
+ const appId = ctx.appId;
141
+ const table = yield getTable(appId, ctx.params.tableId);
142
+ const { dataImport } = ctx.request.body;
143
+ yield handleDataImport(appId, ctx.user, table, dataImport);
144
+ return table;
145
+ });
146
+ };
@@ -14,7 +14,7 @@ const { getRowParams, generateRowID, InternalTables, } = require("../../../db/ut
14
14
  const { isEqual } = require("lodash/fp");
15
15
  const { AutoFieldSubTypes, FieldTypes } = require("../../../constants");
16
16
  const { inputProcessing } = require("../../../utilities/rowProcessor");
17
- const { USERS_TABLE_SCHEMA } = require("../../../constants");
17
+ const { USERS_TABLE_SCHEMA, SwitchableTypes } = require("../../../constants");
18
18
  const { isExternalTable, breakExternalTableId, } = require("../../../integrations/utils");
19
19
  const { getViews, saveView } = require("../view/utils");
20
20
  const viewTemplate = require("../view/viewBuilder");
@@ -66,37 +66,38 @@ exports.makeSureTableUpToDate = (table, tableToSave) => {
66
66
  return tableToSave;
67
67
  };
68
68
  exports.handleDataImport = (appId, user, table, dataImport) => __awaiter(void 0, void 0, void 0, function* () {
69
+ if (!dataImport || !dataImport.csvString) {
70
+ return table;
71
+ }
69
72
  const db = new CouchDB(appId);
70
- if (dataImport && dataImport.csvString) {
71
- // Populate the table with rows imported from CSV in a bulk update
72
- const data = yield csvParser.transform(dataImport);
73
- let finalData = [];
74
- for (let i = 0; i < data.length; i++) {
75
- let row = data[i];
76
- row._id = generateRowID(table._id);
77
- row.tableId = table._id;
78
- const processed = inputProcessing(user, table, row, {
79
- noAutoRelationships: true,
80
- });
81
- table = processed.table;
82
- row = processed.row;
83
- for (let [fieldName, schema] of Object.entries(table.schema)) {
84
- // check whether the options need to be updated for inclusion as part of the data import
85
- if (schema.type === FieldTypes.OPTIONS &&
86
- (!schema.constraints.inclusion ||
87
- schema.constraints.inclusion.indexOf(row[fieldName]) === -1)) {
88
- schema.constraints.inclusion = [
89
- ...schema.constraints.inclusion,
90
- row[fieldName],
91
- ];
92
- }
73
+ // Populate the table with rows imported from CSV in a bulk update
74
+ const data = yield csvParser.transform(Object.assign(Object.assign({}, dataImport), { existingTable: table }));
75
+ let finalData = [];
76
+ for (let i = 0; i < data.length; i++) {
77
+ let row = data[i];
78
+ row._id = generateRowID(table._id);
79
+ row.tableId = table._id;
80
+ const processed = inputProcessing(user, table, row, {
81
+ noAutoRelationships: true,
82
+ });
83
+ table = processed.table;
84
+ row = processed.row;
85
+ for (let [fieldName, schema] of Object.entries(table.schema)) {
86
+ // check whether the options need to be updated for inclusion as part of the data import
87
+ if (schema.type === FieldTypes.OPTIONS &&
88
+ (!schema.constraints.inclusion ||
89
+ schema.constraints.inclusion.indexOf(row[fieldName]) === -1)) {
90
+ schema.constraints.inclusion = [
91
+ ...schema.constraints.inclusion,
92
+ row[fieldName],
93
+ ];
93
94
  }
94
- finalData.push(row);
95
95
  }
96
- yield db.bulkDocs(finalData);
97
- let response = yield db.put(table);
98
- table._rev = response._rev;
96
+ finalData.push(row);
99
97
  }
98
+ yield db.bulkDocs(finalData);
99
+ let response = yield db.put(table);
100
+ table._rev = response._rev;
100
101
  return table;
101
102
  });
102
103
  exports.handleSearchIndexes = (appId, table) => __awaiter(void 0, void 0, void 0, function* () {
@@ -277,4 +278,37 @@ exports.checkForViewUpdates = (db, table, rename, deletedColumns) => __awaiter(v
277
278
  }
278
279
  }
279
280
  });
281
+ exports.generateForeignKey = (column, relatedTable) => {
282
+ return `fk_${relatedTable.name}_${column.fieldName}`;
283
+ };
284
+ exports.generateJunctionTableName = (column, table, relatedTable) => {
285
+ return `jt_${table.name}_${relatedTable.name}_${column.name}_${column.fieldName}`;
286
+ };
287
+ exports.foreignKeyStructure = (keyName, meta = null) => {
288
+ const structure = {
289
+ type: FieldTypes.NUMBER,
290
+ constraints: {},
291
+ name: keyName,
292
+ };
293
+ if (meta) {
294
+ structure.meta = meta;
295
+ }
296
+ return structure;
297
+ };
298
+ exports.hasTypeChanged = (table, oldTable) => {
299
+ if (!oldTable) {
300
+ return false;
301
+ }
302
+ for (let [key, field] of Object.entries(oldTable.schema)) {
303
+ const oldType = field.type;
304
+ if (!table.schema[key]) {
305
+ continue;
306
+ }
307
+ const newType = table.schema[key].type;
308
+ if (oldType !== newType && SwitchableTypes.indexOf(oldType) === -1) {
309
+ return true;
310
+ }
311
+ }
312
+ return false;
313
+ };
280
314
  exports.TableSaveFunctions = TableSaveFunctions;
@@ -11,22 +11,132 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  const CouchDB = require("../../db");
12
12
  const { generateUserMetadataID, getUserMetadataParams, } = require("../../db/utils");
13
13
  const { InternalTables } = require("../../db/utils");
14
- const { getGlobalUsers } = require("../../utilities/global");
14
+ const { getGlobalUsers, getRawGlobalUser } = require("../../utilities/global");
15
15
  const { getFullUser } = require("../../utilities/users");
16
- function removeGlobalProps(user) {
17
- // make sure to always remove some of the global user props
18
- delete user.password;
19
- delete user.roles;
20
- delete user.builder;
21
- return user;
16
+ const { isEqual } = require("lodash");
17
+ const { BUILTIN_ROLE_IDS } = require("@budibase/auth/roles");
18
+ const { getDevelopmentAppID, getDeployedAppIDs } = require("@budibase/auth/db");
19
+ const { doesDatabaseExist } = require("../../utilities");
20
+ const { UserStatus } = require("@budibase/auth/constants");
21
+ function rawMetadata(db) {
22
+ return __awaiter(this, void 0, void 0, function* () {
23
+ return (yield db.allDocs(getUserMetadataParams(null, {
24
+ include_docs: true,
25
+ }))).rows.map(row => row.doc);
26
+ });
27
+ }
28
+ function combineMetadataAndUser(user, metadata) {
29
+ // skip users with no access
30
+ if (user.roleId === BUILTIN_ROLE_IDS.PUBLIC) {
31
+ return null;
32
+ }
33
+ delete user._rev;
34
+ const metadataId = generateUserMetadataID(user._id);
35
+ const newDoc = Object.assign(Object.assign({}, user), { _id: metadataId, tableId: InternalTables.USER_METADATA });
36
+ const found = Array.isArray(metadata)
37
+ ? metadata.find(doc => doc._id === metadataId)
38
+ : metadata;
39
+ // copy rev over for the purposes of equality check
40
+ if (found) {
41
+ newDoc._rev = found._rev;
42
+ }
43
+ if (found == null || !isEqual(newDoc, found)) {
44
+ return Object.assign(Object.assign({}, found), newDoc);
45
+ }
46
+ return null;
22
47
  }
48
+ exports.syncGlobalUsers = (appId) => __awaiter(void 0, void 0, void 0, function* () {
49
+ // sync user metadata
50
+ const db = new CouchDB(appId);
51
+ const [users, metadata] = yield Promise.all([
52
+ getGlobalUsers(appId),
53
+ rawMetadata(db),
54
+ ]);
55
+ const toWrite = [];
56
+ for (let user of users) {
57
+ const combined = yield combineMetadataAndUser(user, metadata);
58
+ if (combined) {
59
+ toWrite.push(combined);
60
+ }
61
+ }
62
+ yield db.bulkDocs(toWrite);
63
+ });
64
+ exports.syncUser = function (ctx) {
65
+ return __awaiter(this, void 0, void 0, function* () {
66
+ let deleting = false, user;
67
+ const userId = ctx.params.id;
68
+ try {
69
+ user = yield getRawGlobalUser(userId);
70
+ }
71
+ catch (err) {
72
+ if (err && err.status === 404) {
73
+ user = {};
74
+ deleting = true;
75
+ }
76
+ else {
77
+ throw err;
78
+ }
79
+ }
80
+ const roles = user.roles;
81
+ // remove props which aren't useful to metadata
82
+ delete user.password;
83
+ delete user.forceResetPassword;
84
+ delete user.roles;
85
+ // run through all production appIDs in the users roles
86
+ let prodAppIds;
87
+ // if they are a builder then get all production app IDs
88
+ if ((user.builder && user.builder.global) || deleting) {
89
+ prodAppIds = yield getDeployedAppIDs(CouchDB);
90
+ }
91
+ else {
92
+ prodAppIds = Object.entries(roles)
93
+ .filter(entry => entry[1] !== BUILTIN_ROLE_IDS.PUBLIC)
94
+ .map(([appId]) => appId);
95
+ }
96
+ for (let prodAppId of prodAppIds) {
97
+ const roleId = roles[prodAppId];
98
+ const devAppId = getDevelopmentAppID(prodAppId);
99
+ for (let appId of [prodAppId, devAppId]) {
100
+ if (!(yield doesDatabaseExist(appId))) {
101
+ continue;
102
+ }
103
+ const db = new CouchDB(appId);
104
+ const metadataId = generateUserMetadataID(userId);
105
+ let metadata;
106
+ try {
107
+ metadata = yield db.get(metadataId);
108
+ }
109
+ catch (err) {
110
+ if (deleting) {
111
+ continue;
112
+ }
113
+ metadata = {
114
+ tableId: InternalTables.USER_METADATA,
115
+ };
116
+ }
117
+ // assign the roleId for the metadata doc
118
+ if (roleId) {
119
+ metadata.roleId = roleId;
120
+ }
121
+ let combined = !deleting
122
+ ? combineMetadataAndUser(user, metadata)
123
+ : Object.assign(Object.assign({}, metadata), { status: UserStatus.INACTIVE, metadata: BUILTIN_ROLE_IDS.PUBLIC });
124
+ // if its null then there was no updates required
125
+ if (combined) {
126
+ yield db.put(combined);
127
+ }
128
+ }
129
+ }
130
+ ctx.body = {
131
+ message: "User synced.",
132
+ };
133
+ });
134
+ };
23
135
  exports.fetchMetadata = function (ctx) {
24
136
  return __awaiter(this, void 0, void 0, function* () {
25
137
  const database = new CouchDB(ctx.appId);
26
138
  const global = yield getGlobalUsers(ctx.appId);
27
- const metadata = (yield database.allDocs(getUserMetadataParams(null, {
28
- include_docs: true,
29
- }))).rows.map(row => row.doc);
139
+ const metadata = yield rawMetadata(database);
30
140
  const users = [];
31
141
  for (let user of global) {
32
142
  // find the metadata that matches up to the global ID
@@ -52,7 +162,9 @@ exports.updateMetadata = function (ctx) {
52
162
  return __awaiter(this, void 0, void 0, function* () {
53
163
  const appId = ctx.appId;
54
164
  const db = new CouchDB(appId);
55
- const user = removeGlobalProps(ctx.request.body);
165
+ const user = ctx.request.body;
166
+ // this isn't applicable to the user
167
+ delete user.roles;
56
168
  const metadata = Object.assign({ tableId: InternalTables.USER_METADATA }, user);
57
169
  ctx.body = yield db.put(metadata);
58
170
  });
@@ -15,3 +15,7 @@ exports.csv = function (headers, rows) {
15
15
  exports.json = function (headers, rows) {
16
16
  return JSON.stringify(rows, undefined, 2);
17
17
  };
18
+ exports.ExportFormats = {
19
+ CSV: "csv",
20
+ JSON: "json",
21
+ };
@@ -25,6 +25,7 @@ const { apiFileReturn } = require("../../../utilities/fileSystem");
25
25
  const exporters = require("./exporters");
26
26
  const { saveView, getView, getViews, deleteView } = require("./utils");
27
27
  const { fetchView } = require("../row");
28
+ const { getTable } = require("../table/utils");
28
29
  exports.fetch = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
29
30
  const db = new CouchDB(ctx.appId);
30
31
  ctx.body = yield getViews(db);
@@ -65,7 +66,7 @@ exports.exportView = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
65
66
  const viewName = decodeURI(ctx.query.view);
66
67
  const view = yield getView(db, viewName);
67
68
  const format = ctx.query.format;
68
- if (!format) {
69
+ if (!format || !Object.values(exporters.ExportFormats).includes(format)) {
69
70
  ctx.throw(400, "Format must be specified, either csv or json");
70
71
  }
71
72
  if (view) {
@@ -87,9 +88,20 @@ exports.exportView = (ctx) => __awaiter(void 0, void 0, void 0, function* () {
87
88
  let schema = view && view.meta && view.meta.schema;
88
89
  if (!schema) {
89
90
  const tableId = ctx.params.tableId || view.meta.tableId;
90
- const table = yield db.get(tableId);
91
+ const table = yield getTable(ctx.appId, tableId);
91
92
  schema = table.schema;
92
93
  }
94
+ // make sure no "undefined" entries appear in the CSV
95
+ if (format === exporters.ExportFormats.CSV) {
96
+ const schemaKeys = Object.keys(schema);
97
+ for (let key of schemaKeys) {
98
+ for (let row of ctx.body) {
99
+ if (row[key] == null) {
100
+ row[key] = "";
101
+ }
102
+ }
103
+ }
104
+ }
93
105
  // Export part
94
106
  let headers = Object.keys(schema);
95
107
  const exporter = exporters[format];
@@ -41,7 +41,7 @@ router
41
41
  .get("/api/queries", authorized(BUILDER), queryController.fetch)
42
42
  .post("/api/queries", bodySubResource("datasourceId", "_id"), authorized(BUILDER), generateQueryValidation(), queryController.save)
43
43
  .post("/api/queries/preview", bodyResource("datasourceId"), authorized(BUILDER), generateQueryPreviewValidation(), queryController.preview)
44
- .get("/api/queries/:queryId", authorized(PermissionTypes.QUERY, PermissionLevels.READ), queryController.find)
44
+ .get("/api/queries/:queryId", paramResource("queryId"), authorized(PermissionTypes.QUERY, PermissionLevels.READ), queryController.find)
45
45
  .post("/api/queries/:queryId", paramResource("queryId"), authorized(PermissionTypes.QUERY, PermissionLevels.WRITE), queryController.execute)
46
46
  .delete("/api/queries/:queryId/:revId", paramResource("queryId"), authorized(BUILDER), queryController.destroy);
47
47
  module.exports = router;