@budibase/server 0.9.174 → 0.9.176-alpha.2

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 (203) hide show
  1. package/builder/assets/{index.7469e1e4.css → index.75ed4773.css} +1 -1
  2. package/builder/assets/{index.ba2741f9.js → index.d3780441.js} +34 -34
  3. package/builder/index.html +2 -2
  4. package/coverage/clover.xml +383 -207
  5. package/coverage/coverage-final.json +8 -6
  6. package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
  7. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  8. package/coverage/lcov-report/api/controllers/application.js.html +1 -1
  9. package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
  10. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  11. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  12. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/component.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/datasource.js.html +120 -24
  15. package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
  16. package/coverage/lcov-report/api/controllers/hosting.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/index.html +19 -19
  18. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/query.js.html +9 -3
  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 +14 -14
  27. package/coverage/lcov-report/api/controllers/row/index.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
  29. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
  30. package/coverage/lcov-report/api/controllers/row/utils.js.html +11 -32
  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/external.js.html +908 -0
  34. package/coverage/lcov-report/api/controllers/table/index.html +59 -29
  35. package/coverage/lcov-report/api/controllers/table/index.js.html +52 -376
  36. package/coverage/lcov-report/api/controllers/table/internal.js.html +494 -0
  37. package/coverage/lcov-report/api/controllers/table/utils.js.html +70 -10
  38. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  39. package/coverage/lcov-report/api/controllers/user.js.html +1 -1
  40. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  41. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  42. package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
  43. package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
  44. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
  45. package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
  46. package/coverage/lcov-report/api/index.html +1 -1
  47. package/coverage/lcov-report/api/index.js.html +1 -1
  48. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  49. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  50. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  51. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/index.html +1 -1
  61. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/static.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  78. package/coverage/lcov-report/automations/actions.js.html +1 -1
  79. package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
  80. package/coverage/lcov-report/automations/bullboard.js.html +1 -1
  81. package/coverage/lcov-report/automations/index.html +1 -1
  82. package/coverage/lcov-report/automations/index.js.html +1 -1
  83. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  84. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  85. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  87. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  89. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/index.html +1 -1
  92. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  96. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  98. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  99. package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
  100. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  101. package/coverage/lcov-report/automations/thread.js.html +1 -1
  102. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  103. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  104. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  105. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  106. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  107. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  108. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  109. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  110. package/coverage/lcov-report/automations/triggers.js.html +1 -1
  111. package/coverage/lcov-report/automations/utils.js.html +1 -1
  112. package/coverage/lcov-report/constants/index.html +5 -5
  113. package/coverage/lcov-report/constants/index.js.html +25 -4
  114. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  115. package/coverage/lcov-report/constants/screens.js.html +1 -1
  116. package/coverage/lcov-report/db/client.js.html +1 -1
  117. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  118. package/coverage/lcov-report/db/index.html +1 -1
  119. package/coverage/lcov-report/db/index.js.html +1 -1
  120. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
  121. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  122. package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
  123. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  124. package/coverage/lcov-report/db/newid.js.html +1 -1
  125. package/coverage/lcov-report/db/utils.js.html +1 -1
  126. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  127. package/coverage/lcov-report/events/index.html +1 -1
  128. package/coverage/lcov-report/events/index.js.html +1 -1
  129. package/coverage/lcov-report/events/utils.js.html +1 -1
  130. package/coverage/lcov-report/index.html +38 -38
  131. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  132. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  133. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  134. package/coverage/lcov-report/middleware/authorized.js.html +1 -1
  135. package/coverage/lcov-report/middleware/builder.js.html +1 -1
  136. package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
  137. package/coverage/lcov-report/middleware/index.html +1 -1
  138. package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
  139. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  140. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  141. package/coverage/lcov-report/middleware/usageQuota.js.html +1 -1
  142. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  143. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  144. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  145. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  146. package/coverage/lcov-report/utilities/global.js.html +1 -1
  147. package/coverage/lcov-report/utilities/index.html +1 -1
  148. package/coverage/lcov-report/utilities/index.js.html +1 -1
  149. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +1 -1
  150. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  151. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  152. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  153. package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
  154. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
  155. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
  156. package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
  157. package/coverage/lcov-report/utilities/security.js.html +1 -1
  158. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  159. package/coverage/lcov-report/utilities/users.js.html +1 -1
  160. package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
  161. package/coverage/lcov.info +652 -332
  162. package/dist/api/controllers/datasource.js +41 -14
  163. package/dist/api/controllers/query.js +3 -1
  164. package/dist/api/controllers/row/ExternalRequest.js +12 -10
  165. package/dist/api/controllers/row/utils.js +3 -11
  166. package/dist/api/controllers/table/external.js +222 -0
  167. package/dist/api/controllers/table/index.js +28 -121
  168. package/dist/api/controllers/table/internal.js +138 -0
  169. package/dist/api/controllers/table/utils.js +17 -0
  170. package/dist/constants/index.js +6 -0
  171. package/dist/definitions/datasource.js +3 -0
  172. package/dist/integrations/base/datasourcePlus.js +2 -0
  173. package/dist/integrations/base/sql.js +16 -11
  174. package/dist/integrations/base/sqlTable.js +141 -0
  175. package/dist/integrations/base/utils.js +30 -0
  176. package/dist/integrations/mysql.js +13 -7
  177. package/dist/integrations/postgres.js +18 -8
  178. package/dist/integrations/utils.js +21 -6
  179. package/dist/package.json +4 -4
  180. package/dist/tests/utilities/TestConfiguration.js +2 -1
  181. package/dist/tsconfig.tsbuildinfo +1 -1
  182. package/package.json +5 -5
  183. package/src/api/controllers/datasource.js +46 -14
  184. package/src/api/controllers/query.js +3 -1
  185. package/src/api/controllers/row/ExternalRequest.ts +12 -10
  186. package/src/api/controllers/row/utils.js +3 -10
  187. package/src/api/controllers/table/external.js +276 -0
  188. package/src/api/controllers/table/index.js +27 -135
  189. package/src/api/controllers/table/internal.js +138 -0
  190. package/src/api/controllers/table/utils.js +20 -0
  191. package/src/api/routes/tests/datasource.spec.js +2 -2
  192. package/src/api/routes/tests/query.spec.js +0 -1
  193. package/src/constants/index.js +7 -0
  194. package/src/definitions/common.ts +7 -1
  195. package/src/definitions/datasource.ts +5 -0
  196. package/src/integrations/base/datasourcePlus.ts +8 -0
  197. package/src/integrations/base/sql.ts +15 -16
  198. package/src/integrations/base/sqlTable.ts +167 -0
  199. package/src/integrations/base/utils.ts +19 -0
  200. package/src/integrations/mysql.ts +15 -9
  201. package/src/integrations/postgres.ts +19 -9
  202. package/src/integrations/utils.ts +22 -6
  203. package/src/tests/utilities/TestConfiguration.js +2 -1
@@ -10,8 +10,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
10
10
  };
11
11
  const CouchDB = require("../../db");
12
12
  const { generateDatasourceID, getDatasourceParams, getQueryParams, DocumentTypes, BudibaseInternalDB, getTableParams, } = require("../../db/utils");
13
+ const { BuildSchemaErrors } = require("../../constants");
13
14
  const { integrations } = require("../../integrations");
14
- const { makeExternalQuery } = require("./row/utils");
15
+ const { getDatasourceAndQuery } = require("./row/utils");
15
16
  exports.fetch = function (ctx) {
16
17
  return __awaiter(this, void 0, void 0, function* () {
17
18
  const database = new CouchDB(ctx.appId);
@@ -33,11 +34,15 @@ exports.buildSchemaFromDb = function (ctx) {
33
34
  return __awaiter(this, void 0, void 0, function* () {
34
35
  const db = new CouchDB(ctx.appId);
35
36
  const datasource = yield db.get(ctx.params.datasourceId);
36
- const tables = yield buildSchemaHelper(datasource);
37
+ const { tables, error } = yield buildSchemaHelper(datasource);
37
38
  datasource.entities = tables;
38
- const response = yield db.put(datasource);
39
- datasource._rev = response.rev;
40
- ctx.body = datasource;
39
+ const dbResp = yield db.put(datasource);
40
+ datasource._rev = dbResp.rev;
41
+ const response = { datasource };
42
+ if (error) {
43
+ response.error = error;
44
+ }
45
+ ctx.body = response;
41
46
  });
42
47
  };
43
48
  exports.update = function (ctx) {
@@ -57,7 +62,7 @@ exports.update = function (ctx) {
57
62
  }
58
63
  ctx.status = 200;
59
64
  ctx.message = "Datasource saved successfully.";
60
- ctx.body = datasource;
65
+ ctx.body = { datasource };
61
66
  });
62
67
  };
63
68
  exports.save = function (ctx) {
@@ -66,12 +71,14 @@ exports.save = function (ctx) {
66
71
  const plus = ctx.request.body.datasource.plus;
67
72
  const fetchSchema = ctx.request.body.fetchSchema;
68
73
  const datasource = Object.assign({ _id: generateDatasourceID({ plus }), type: plus ? DocumentTypes.DATASOURCE_PLUS : DocumentTypes.DATASOURCE }, ctx.request.body.datasource);
74
+ let schemaError = null;
69
75
  if (fetchSchema) {
70
- let tables = yield buildSchemaHelper(datasource);
76
+ const { tables, error } = yield buildSchemaHelper(datasource);
77
+ schemaError = error;
71
78
  datasource.entities = tables;
72
79
  }
73
- const response = yield db.put(datasource);
74
- datasource._rev = response.rev;
80
+ const dbResp = yield db.put(datasource);
81
+ datasource._rev = dbResp.rev;
75
82
  // Drain connection pools when configuration is changed
76
83
  if (datasource.source) {
77
84
  const source = integrations[datasource.source];
@@ -79,9 +86,11 @@ exports.save = function (ctx) {
79
86
  yield source.pool.end();
80
87
  }
81
88
  }
82
- ctx.status = 200;
83
- ctx.message = "Datasource saved successfully.";
84
- ctx.body = datasource;
89
+ const response = { datasource };
90
+ if (schemaError) {
91
+ response.error = schemaError;
92
+ }
93
+ ctx.body = response;
85
94
  });
86
95
  };
87
96
  exports.destroy = function (ctx) {
@@ -107,7 +116,7 @@ exports.query = function (ctx) {
107
116
  return __awaiter(this, void 0, void 0, function* () {
108
117
  const queryJson = ctx.request.body;
109
118
  try {
110
- ctx.body = yield makeExternalQuery(ctx.appId, queryJson);
119
+ ctx.body = yield getDatasourceAndQuery(ctx.appId, queryJson);
111
120
  }
112
121
  catch (err) {
113
122
  ctx.throw(400, err);
@@ -120,5 +129,23 @@ const buildSchemaHelper = (datasource) => __awaiter(void 0, void 0, void 0, func
120
129
  const connector = new Connector(datasource.config);
121
130
  yield connector.buildSchema(datasource._id, datasource.entities);
122
131
  datasource.entities = connector.tables;
123
- return connector.tables;
132
+ // make sure they all have a display name selected
133
+ for (let entity of Object.values(datasource.entities)) {
134
+ if (entity.primaryDisplay) {
135
+ continue;
136
+ }
137
+ const notAutoColumn = Object.values(entity.schema).find(schema => !schema.autocolumn);
138
+ if (notAutoColumn) {
139
+ entity.primaryDisplay = notAutoColumn.name;
140
+ }
141
+ }
142
+ const errors = connector.schemaErrors;
143
+ let error = null;
144
+ if (errors && Object.keys(errors).length > 0) {
145
+ const noKeyTables = Object.entries(errors)
146
+ .filter(entry => entry[1] === BuildSchemaErrors.NO_KEY)
147
+ .map(([name]) => name);
148
+ error = `No primary key constraint found for the following: ${noKeyTables.join(", ")}`;
149
+ }
150
+ return { tables: connector.tables, error };
124
151
  });
@@ -95,7 +95,9 @@ function enrichQueryFields(fields, parameters = {}) {
95
95
  }
96
96
  else if (typeof fields[key] === "string") {
97
97
  // enrich string value as normal
98
- enrichedQuery[key] = yield processString(fields[key], parameters);
98
+ enrichedQuery[key] = yield processString(fields[key], parameters, {
99
+ noHelpers: true,
100
+ });
99
101
  }
100
102
  else {
101
103
  enrichedQuery[key] = fields[key];
@@ -24,12 +24,13 @@ const datasource_1 = require("../../../definitions/datasource");
24
24
  const utils_1 = require("../../../integrations/utils");
25
25
  var External;
26
26
  (function (External) {
27
- const { makeExternalQuery } = require("./utils");
27
+ const { getDatasourceAndQuery } = require("./utils");
28
28
  const { DataSourceOperation, FieldTypes, RelationshipTypes, } = require("../../../constants");
29
29
  const { breakExternalTableId, isSQL } = require("../../../integrations/utils");
30
30
  const { processObjectSync } = require("@budibase/string-templates");
31
31
  const { cloneDeep } = require("lodash/fp");
32
32
  const CouchDB = require("../../../db");
33
+ const { processFormulas } = require("../../../utilities/rowProcessor/utils");
33
34
  function buildFilters(id, filters, table) {
34
35
  const primary = table.primary;
35
36
  // if passed in array need to copy for shifting etc
@@ -182,7 +183,7 @@ var External;
182
183
  let newRow = {}, manyRelationships = [];
183
184
  for (let [key, field] of Object.entries(table.schema)) {
184
185
  // if set already, or not set just skip it
185
- if ((!row[key] && row[key] !== "") || newRow[key] || field.autocolumn) {
186
+ if (row[key] == null || newRow[key] || field.autocolumn || field.type === FieldTypes.FORMULA) {
186
187
  continue;
187
188
  }
188
189
  // if its an empty string then it means return the column to null (if possible)
@@ -295,7 +296,7 @@ var External;
295
296
  // do this at end once its been added to the final rows
296
297
  finalRows = this.updateRelationshipColumns(row, finalRows, relationships);
297
298
  }
298
- return Object.values(finalRows);
299
+ return processFormulas(table, Object.values(finalRows));
299
300
  }
300
301
  /**
301
302
  * Gets the list of relationship JSON structures based on the columns in the table,
@@ -359,7 +360,7 @@ var External;
359
360
  const tableId = isMany ? field.through : field.tableId;
360
361
  const manyKey = field.throughFrom || primaryKey;
361
362
  const fieldName = isMany ? manyKey : field.fieldName;
362
- const response = yield makeExternalQuery(this.appId, {
363
+ const response = yield getDatasourceAndQuery(this.appId, {
363
364
  endpoint: getEndpoint(tableId, DataSourceOperation.READ),
364
365
  filters: {
365
366
  equal: {
@@ -403,7 +404,7 @@ var External;
403
404
  ? DataSourceOperation.UPDATE
404
405
  : DataSourceOperation.CREATE;
405
406
  if (!found) {
406
- promises.push(makeExternalQuery(appId, {
407
+ promises.push(getDatasourceAndQuery(appId, {
407
408
  endpoint: getEndpoint(tableId, operation),
408
409
  // if we're doing many relationships then we're writing, only one response
409
410
  body,
@@ -430,7 +431,7 @@ var External;
430
431
  ? DataSourceOperation.DELETE
431
432
  : DataSourceOperation.UPDATE;
432
433
  const body = isMany ? null : { [colName]: null };
433
- promises.push(makeExternalQuery(this.appId, {
434
+ promises.push(getDatasourceAndQuery(this.appId, {
434
435
  endpoint: getEndpoint(tableId, op),
435
436
  body,
436
437
  filters,
@@ -449,13 +450,14 @@ var External;
449
450
  * is more performant and has the added benefit of protecting against this scenario.
450
451
  */
451
452
  buildFields(table, includeRelations = datasource_1.IncludeRelationships.INCLUDE) {
452
- function extractNonLinkFieldNames(table, existing = []) {
453
+ function extractRealFields(table, existing = []) {
453
454
  return Object.entries(table.schema)
454
455
  .filter(column => column[1].type !== FieldTypes.LINK &&
456
+ column[1].type !== FieldTypes.FORMULA &&
455
457
  !existing.find((field) => field === column[0]))
456
458
  .map(column => `${table.name}.${column[0]}`);
457
459
  }
458
- let fields = extractNonLinkFieldNames(table);
460
+ let fields = extractRealFields(table);
459
461
  for (let field of Object.values(table.schema)) {
460
462
  if (field.type !== FieldTypes.LINK || !includeRelations) {
461
463
  continue;
@@ -463,7 +465,7 @@ var External;
463
465
  const { tableName: linkTableName } = breakExternalTableId(field.tableId);
464
466
  const linkTable = this.tables[linkTableName];
465
467
  if (linkTable) {
466
- const linkedFields = extractNonLinkFieldNames(linkTable, fields);
468
+ const linkedFields = extractRealFields(linkTable, fields);
467
469
  fields = fields.concat(linkedFields);
468
470
  }
469
471
  }
@@ -521,7 +523,7 @@ var External;
521
523
  },
522
524
  };
523
525
  // can't really use response right now
524
- const response = yield makeExternalQuery(appId, json);
526
+ const response = yield getDatasourceAndQuery(appId, json);
525
527
  // handle many to many relationships now if we know the ID (could be auto increment)
526
528
  if (operation !== DataSourceOperation.READ &&
527
529
  processed.manyRelationships) {
@@ -14,8 +14,8 @@ const CouchDB = require("../../../db");
14
14
  const { InternalTables } = require("../../../db/utils");
15
15
  const userController = require("../user");
16
16
  const { FieldTypes } = require("../../../constants");
17
- const { integrations } = require("../../../integrations");
18
17
  const { processStringSync } = require("@budibase/string-templates");
18
+ const { makeExternalQuery } = require("../../../integrations/base/utils");
19
19
  validateJs.extend(validateJs.validators.datetime, {
20
20
  parse: function (value) {
21
21
  return new Date(value).getTime();
@@ -25,19 +25,11 @@ validateJs.extend(validateJs.validators.datetime, {
25
25
  return new Date(value).toISOString();
26
26
  },
27
27
  });
28
- exports.makeExternalQuery = (appId, json) => __awaiter(void 0, void 0, void 0, function* () {
28
+ exports.getDatasourceAndQuery = (appId, json) => __awaiter(void 0, void 0, void 0, function* () {
29
29
  const datasourceId = json.endpoint.datasourceId;
30
30
  const db = new CouchDB(appId);
31
31
  const datasource = yield db.get(datasourceId);
32
- const Integration = integrations[datasource.source];
33
- // query is the opinionated function
34
- if (Integration.prototype.query) {
35
- const integration = new Integration(datasource.config);
36
- return integration.query(json);
37
- }
38
- else {
39
- throw "Datasource does not support query.";
40
- }
32
+ return makeExternalQuery(datasource, json);
41
33
  });
42
34
  exports.findRow = (ctx, db, tableId, rowId) => __awaiter(void 0, void 0, void 0, function* () {
43
35
  let row;
@@ -0,0 +1,222 @@
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
+ const CouchDB = require("../../../db");
12
+ const { buildExternalTableId, breakExternalTableId, } = require("../../../integrations/utils");
13
+ const { getTable, generateForeignKey, generateJunctionTableName, foreignKeyStructure, } = require("./utils");
14
+ const { DataSourceOperation, FieldTypes, RelationshipTypes, } = require("../../../constants");
15
+ const { makeExternalQuery } = require("../../../integrations/base/utils");
16
+ const { cloneDeep } = require("lodash/fp");
17
+ function makeTableRequest(datasource, operation, table, tables, oldTable = null) {
18
+ return __awaiter(this, void 0, void 0, function* () {
19
+ const json = {
20
+ endpoint: {
21
+ datasourceId: datasource._id,
22
+ entityId: table._id,
23
+ operation,
24
+ },
25
+ meta: {
26
+ tables,
27
+ },
28
+ table,
29
+ };
30
+ if (oldTable) {
31
+ json.meta.table = oldTable;
32
+ }
33
+ return makeExternalQuery(datasource, json);
34
+ });
35
+ }
36
+ function cleanupRelationships(table, tables, oldTable = null) {
37
+ const tableToIterate = oldTable ? oldTable : table;
38
+ // clean up relationships in couch table schemas
39
+ for (let [key, schema] of Object.entries(tableToIterate.schema)) {
40
+ if (schema.type === FieldTypes.LINK &&
41
+ (!oldTable || table.schema[key] == null)) {
42
+ const relatedTable = Object.values(tables).find(table => table._id === schema.tableId);
43
+ const foreignKey = schema.foreignKey;
44
+ if (!relatedTable || !foreignKey) {
45
+ continue;
46
+ }
47
+ for (let [relatedKey, relatedSchema] of Object.entries(relatedTable.schema)) {
48
+ if (relatedSchema.type === FieldTypes.LINK &&
49
+ relatedSchema.fieldName === foreignKey) {
50
+ delete relatedTable.schema[relatedKey];
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
56
+ function getDatasourceId(table) {
57
+ if (!table) {
58
+ throw "No table supplied";
59
+ }
60
+ if (table.sourceId) {
61
+ return table.sourceId;
62
+ }
63
+ return breakExternalTableId(table._id).datasourceId;
64
+ }
65
+ function otherRelationshipType(type) {
66
+ if (type === RelationshipTypes.MANY_TO_MANY) {
67
+ return RelationshipTypes.MANY_TO_MANY;
68
+ }
69
+ return type === RelationshipTypes.ONE_TO_MANY
70
+ ? RelationshipTypes.MANY_TO_ONE
71
+ : RelationshipTypes.ONE_TO_MANY;
72
+ }
73
+ function generateManyLinkSchema(datasource, column, table, relatedTable) {
74
+ const primary = table.name + table.primary[0];
75
+ const relatedPrimary = relatedTable.name + relatedTable.primary[0];
76
+ const jcTblName = generateJunctionTableName(column, table, relatedTable);
77
+ // first create the new table
78
+ const junctionTable = {
79
+ _id: buildExternalTableId(datasource._id, jcTblName),
80
+ name: jcTblName,
81
+ primary: [primary, relatedPrimary],
82
+ constrained: [primary, relatedPrimary],
83
+ schema: {
84
+ [primary]: foreignKeyStructure(primary, {
85
+ toTable: table.name,
86
+ toKey: table.primary[0],
87
+ }),
88
+ [relatedPrimary]: foreignKeyStructure(relatedPrimary, {
89
+ toTable: relatedTable.name,
90
+ toKey: relatedTable.primary[0],
91
+ }),
92
+ },
93
+ };
94
+ column.through = junctionTable._id;
95
+ column.throughFrom = primary;
96
+ column.throughTo = relatedPrimary;
97
+ column.fieldName = relatedPrimary;
98
+ return junctionTable;
99
+ }
100
+ function generateLinkSchema(column, table, relatedTable, type) {
101
+ const isOneSide = type === RelationshipTypes.ONE_TO_MANY;
102
+ const primary = isOneSide ? relatedTable.primary[0] : table.primary[0];
103
+ // generate a foreign key
104
+ const foreignKey = generateForeignKey(column, relatedTable);
105
+ column.relationshipType = type;
106
+ column.foreignKey = isOneSide ? foreignKey : primary;
107
+ column.fieldName = isOneSide ? primary : foreignKey;
108
+ return foreignKey;
109
+ }
110
+ function generateRelatedSchema(linkColumn, table, relatedTable, columnName) {
111
+ // generate column for other table
112
+ const relatedSchema = cloneDeep(linkColumn);
113
+ // swap them from the main link
114
+ if (linkColumn.foreignKey) {
115
+ relatedSchema.fieldName = linkColumn.foreignKey;
116
+ relatedSchema.foreignKey = linkColumn.fieldName;
117
+ }
118
+ // is many to many
119
+ else {
120
+ // don't need to copy through, already got it
121
+ relatedSchema.fieldName = linkColumn.throughFrom;
122
+ relatedSchema.throughTo = linkColumn.throughFrom;
123
+ relatedSchema.throughFrom = linkColumn.throughTo;
124
+ }
125
+ relatedSchema.relationshipType = otherRelationshipType(linkColumn.relationshipType);
126
+ relatedSchema.tableId = relatedTable._id;
127
+ relatedSchema.name = columnName;
128
+ table.schema[columnName] = relatedSchema;
129
+ }
130
+ function isRelationshipSetup(column) {
131
+ return column.foreignKey || column.through;
132
+ }
133
+ exports.save = function (ctx) {
134
+ return __awaiter(this, void 0, void 0, function* () {
135
+ const appId = ctx.appId;
136
+ const table = ctx.request.body;
137
+ // can't do this
138
+ delete table.dataImport;
139
+ const datasourceId = getDatasourceId(ctx.request.body);
140
+ let tableToSave = Object.assign({ type: "table", _id: buildExternalTableId(datasourceId, table.name) }, table);
141
+ let oldTable;
142
+ if (ctx.request.body && ctx.request.body._id) {
143
+ oldTable = yield getTable(appId, ctx.request.body._id);
144
+ }
145
+ const db = new CouchDB(appId);
146
+ const datasource = yield db.get(datasourceId);
147
+ const oldTables = cloneDeep(datasource.entities);
148
+ const tables = datasource.entities;
149
+ const extraTablesToUpdate = [];
150
+ // check if relations need setup
151
+ for (let schema of Object.values(tableToSave.schema)) {
152
+ if (schema.type !== FieldTypes.LINK || isRelationshipSetup(schema)) {
153
+ continue;
154
+ }
155
+ const relatedTable = Object.values(tables).find(table => table._id === schema.tableId);
156
+ const relatedColumnName = schema.fieldName;
157
+ const relationType = schema.relationshipType;
158
+ if (relationType === RelationshipTypes.MANY_TO_MANY) {
159
+ const junctionTable = generateManyLinkSchema(datasource, schema, table, relatedTable);
160
+ if (tables[junctionTable.name]) {
161
+ throw "Junction table already exists, cannot create another relationship.";
162
+ }
163
+ tables[junctionTable.name] = junctionTable;
164
+ extraTablesToUpdate.push(junctionTable);
165
+ }
166
+ else {
167
+ const fkTable = relationType === RelationshipTypes.ONE_TO_MANY ? table : relatedTable;
168
+ const foreignKey = generateLinkSchema(schema, table, relatedTable, relationType);
169
+ fkTable.schema[foreignKey] = foreignKeyStructure(foreignKey);
170
+ if (fkTable.constrained == null) {
171
+ fkTable.constrained = [];
172
+ }
173
+ if (fkTable.constrained.indexOf(foreignKey) === -1) {
174
+ fkTable.constrained.push(foreignKey);
175
+ }
176
+ // foreign key is in other table, need to save it to external
177
+ if (fkTable._id !== table._id) {
178
+ extraTablesToUpdate.push(fkTable);
179
+ }
180
+ }
181
+ generateRelatedSchema(schema, relatedTable, table, relatedColumnName);
182
+ schema.main = true;
183
+ }
184
+ cleanupRelationships(tableToSave, tables, oldTable);
185
+ const operation = oldTable
186
+ ? DataSourceOperation.UPDATE_TABLE
187
+ : DataSourceOperation.CREATE_TABLE;
188
+ yield makeTableRequest(datasource, operation, tableToSave, tables, oldTable);
189
+ // update any extra tables (like foreign keys in other tables)
190
+ for (let extraTable of extraTablesToUpdate) {
191
+ const oldExtraTable = oldTables[extraTable.name];
192
+ let op = oldExtraTable
193
+ ? DataSourceOperation.UPDATE_TABLE
194
+ : DataSourceOperation.CREATE_TABLE;
195
+ yield makeTableRequest(datasource, op, extraTable, tables, oldExtraTable);
196
+ }
197
+ // make sure the constrained list, all still exist
198
+ if (Array.isArray(tableToSave.constrained)) {
199
+ tableToSave.constrained = tableToSave.constrained.filter(constraint => Object.keys(tableToSave.schema).includes(constraint));
200
+ }
201
+ // store it into couch now for budibase reference
202
+ datasource.entities[tableToSave.name] = tableToSave;
203
+ yield db.put(datasource);
204
+ return tableToSave;
205
+ });
206
+ };
207
+ exports.destroy = function (ctx) {
208
+ return __awaiter(this, void 0, void 0, function* () {
209
+ const appId = ctx.appId;
210
+ const tableToDelete = yield getTable(appId, ctx.params.tableId);
211
+ const datasourceId = getDatasourceId(tableToDelete);
212
+ const db = new CouchDB(appId);
213
+ const datasource = yield db.get(datasourceId);
214
+ const tables = datasource.entities;
215
+ const operation = DataSourceOperation.DELETE_TABLE;
216
+ yield makeTableRequest(datasource, operation, tableToDelete, tables);
217
+ cleanupRelationships(tableToDelete, tables);
218
+ delete datasource.entities[tableToDelete.name];
219
+ yield db.put(datasource);
220
+ return tableToDelete;
221
+ });
222
+ };
@@ -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,120 +53,24 @@ 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.` };
167
74
  });
168
75
  };
169
76
  exports.validateCSVSchema = function (ctx) {