@budibase/server 1.0.27-alpha.4 → 1.0.27-alpha.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/builder/assets/{index.431d69df.css → index.0dce895b.css} +1 -1
  2. package/builder/assets/{index.d849c3ee.js → index.d5347573.js} +155 -155
  3. package/builder/index.html +2 -2
  4. package/coverage/clover.xml +399 -349
  5. package/coverage/coverage-final.json +4 -4
  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 +1 -1
  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 +1 -1
  18. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  19. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/query/index.html +19 -19
  23. package/coverage/lcov-report/api/controllers/query/index.js.html +73 -10
  24. package/coverage/lcov-report/api/controllers/query/validation.js.html +1 -1
  25. package/coverage/lcov-report/api/controllers/role.js.html +1 -1
  26. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/index.html +11 -11
  29. package/coverage/lcov-report/api/controllers/row/index.js.html +1 -1
  30. package/coverage/lcov-report/api/controllers/row/internal.js.html +38 -11
  31. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
  32. package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
  33. package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
  34. package/coverage/lcov-report/api/controllers/script.js.html +1 -1
  35. package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
  36. package/coverage/lcov-report/api/controllers/table/index.html +1 -1
  37. package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
  38. package/coverage/lcov-report/api/controllers/table/internal.js.html +1 -1
  39. package/coverage/lcov-report/api/controllers/table/utils.js.html +1 -1
  40. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  41. package/coverage/lcov-report/api/controllers/user.js.html +1 -1
  42. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  43. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  44. package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
  45. package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
  46. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
  47. package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
  48. package/coverage/lcov-report/api/index.html +1 -1
  49. package/coverage/lcov-report/api/index.js.html +1 -1
  50. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  51. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/index.html +1 -1
  63. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  64. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/query.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  70. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/static.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  80. package/coverage/lcov-report/automations/actions.js.html +1 -1
  81. package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
  82. package/coverage/lcov-report/automations/bullboard.js.html +1 -1
  83. package/coverage/lcov-report/automations/index.html +1 -1
  84. package/coverage/lcov-report/automations/index.js.html +1 -1
  85. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  87. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  89. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/executeQuery.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  92. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/index.html +1 -1
  94. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  95. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  96. package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  98. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  99. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  100. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  101. package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
  102. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  103. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  104. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  105. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  106. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  107. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  108. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  109. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  110. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  111. package/coverage/lcov-report/automations/triggers.js.html +1 -1
  112. package/coverage/lcov-report/automations/utils.js.html +1 -1
  113. package/coverage/lcov-report/constants/index.html +1 -1
  114. package/coverage/lcov-report/constants/index.js.html +1 -1
  115. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  116. package/coverage/lcov-report/constants/screens.js.html +1 -1
  117. package/coverage/lcov-report/db/client.js.html +1 -1
  118. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  119. package/coverage/lcov-report/db/index.html +1 -1
  120. package/coverage/lcov-report/db/index.js.html +1 -1
  121. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
  122. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  123. package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
  124. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  125. package/coverage/lcov-report/db/newid.js.html +1 -1
  126. package/coverage/lcov-report/db/utils.js.html +1 -1
  127. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  128. package/coverage/lcov-report/events/index.html +1 -1
  129. package/coverage/lcov-report/events/index.js.html +1 -1
  130. package/coverage/lcov-report/events/utils.js.html +1 -1
  131. package/coverage/lcov-report/index.html +39 -39
  132. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  133. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  134. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  135. package/coverage/lcov-report/middleware/authorized.js.html +1 -1
  136. package/coverage/lcov-report/middleware/builder.js.html +1 -1
  137. package/coverage/lcov-report/middleware/currentapp.js.html +1 -1
  138. package/coverage/lcov-report/middleware/index.html +1 -1
  139. package/coverage/lcov-report/middleware/joi-validator.js.html +1 -1
  140. package/coverage/lcov-report/middleware/resourceId.js.html +1 -1
  141. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  142. package/coverage/lcov-report/middleware/usageQuota.js.html +1 -1
  143. package/coverage/lcov-report/middleware/utils.js.html +1 -1
  144. package/coverage/lcov-report/threads/automation.js.html +1 -1
  145. package/coverage/lcov-report/threads/index.html +11 -11
  146. package/coverage/lcov-report/threads/index.js.html +1 -1
  147. package/coverage/lcov-report/threads/query.js.html +1 -1
  148. package/coverage/lcov-report/threads/utils.js.html +28 -25
  149. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  150. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  151. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  152. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  153. package/coverage/lcov-report/utilities/global.js.html +1 -1
  154. package/coverage/lcov-report/utilities/index.html +1 -1
  155. package/coverage/lcov-report/utilities/index.js.html +1 -1
  156. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +1 -1
  157. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  158. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  159. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  160. package/coverage/lcov-report/utilities/rowProcessor/index.html +19 -19
  161. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +199 -10
  162. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
  163. package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
  164. package/coverage/lcov-report/utilities/security.js.html +1 -1
  165. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  166. package/coverage/lcov-report/utilities/users.js.html +1 -1
  167. package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
  168. package/coverage/lcov.info +668 -572
  169. package/dist/api/controllers/query/import/sources/base/index.js +2 -1
  170. package/dist/api/controllers/query/import/sources/curl.js +6 -4
  171. package/dist/api/controllers/query/import/sources/openapi2.js +8 -5
  172. package/dist/api/controllers/query/index.js +16 -0
  173. package/dist/api/controllers/row/internal.js +14 -6
  174. package/dist/integrations/base/sqlTable.js +1 -1
  175. package/dist/integrations/rest.js +1 -1
  176. package/dist/package.json +4 -4
  177. package/dist/threads/utils.js +2 -1
  178. package/dist/tsconfig.tsbuildinfo +1 -1
  179. package/dist/utilities/fileSystem/utilities.js +2 -1
  180. package/dist/utilities/rowProcessor/index.js +63 -0
  181. package/package.json +5 -5
  182. package/src/api/controllers/query/import/sources/base/index.ts +3 -1
  183. package/src/api/controllers/query/import/sources/curl.ts +7 -3
  184. package/src/api/controllers/query/import/sources/openapi2.ts +9 -5
  185. package/src/api/controllers/query/import/sources/tests/curl/curl.spec.js +2 -3
  186. package/src/api/controllers/query/import/sources/tests/openapi2/openapi2.spec.js +6 -7
  187. package/src/api/controllers/query/import/tests/index.spec.js +0 -6
  188. package/src/api/controllers/query/index.js +21 -0
  189. package/src/api/controllers/row/internal.js +14 -5
  190. package/src/integrations/base/sqlTable.ts +1 -1
  191. package/src/integrations/rest.ts +1 -1
  192. package/src/threads/utils.js +2 -1
  193. package/src/utilities/fileSystem/utilities.js +2 -0
  194. package/src/utilities/rowProcessor/index.js +63 -0
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- const { ObjectStore, makeSureBucketExists, upload, streamUpload, retrieve, retrieveToTmp, deleteFolder, uploadDirectory, downloadTarball, } = require("@budibase/auth").objectStore;
2
+ const { ObjectStore, makeSureBucketExists, upload, deleteFiles, streamUpload, retrieve, retrieveToTmp, deleteFolder, uploadDirectory, downloadTarball, } = require("@budibase/auth").objectStore;
3
3
  /***********************************
4
4
  * NOTE *
5
5
  * This file purely exists so that *
@@ -17,3 +17,4 @@ exports.retrieveToTmp = retrieveToTmp;
17
17
  exports.deleteFolder = deleteFolder;
18
18
  exports.uploadDirectory = uploadDirectory;
19
19
  exports.downloadTarball = downloadTarball;
20
+ exports.deleteFiles = deleteFiles;
@@ -13,6 +13,10 @@ const { cloneDeep } = require("lodash/fp");
13
13
  const { FieldTypes, AutoFieldSubTypes } = require("../../constants");
14
14
  const { attachmentsRelativeURL } = require("../index");
15
15
  const { processFormulas } = require("./utils");
16
+ const { deleteFiles } = require("../../utilities/fileSystem/utilities");
17
+ const { ObjectStoreBuckets } = require("../../constants");
18
+ const { isProdAppID, getDeployedAppID, dbExists } = require("@budibase/auth/db");
19
+ const CouchDB = require("../../db");
16
20
  const BASE_AUTO_ID = 1;
17
21
  /**
18
22
  * A map of how we convert various properties in rows to each other based on the row type.
@@ -103,6 +107,22 @@ const TYPE_TRANSFORM_MAP = {
103
107
  },
104
108
  },
105
109
  };
110
+ /**
111
+ * Given the old state of the row and the new one after an update, this will
112
+ * find the keys that have been removed in the updated row.
113
+ */
114
+ function getRemovedAttachmentKeys(oldRow, row, attachmentKey) {
115
+ if (!oldRow[attachmentKey]) {
116
+ return [];
117
+ }
118
+ const oldKeys = oldRow[attachmentKey].map(attachment => attachment.key);
119
+ // no attachments in new row, all removed
120
+ if (!row[attachmentKey]) {
121
+ return oldKeys;
122
+ }
123
+ const newKeys = row[attachmentKey].map(attachment => attachment.key);
124
+ return oldKeys.filter(key => newKeys.indexOf(key) === -1);
125
+ }
106
126
  /**
107
127
  * This will update any auto columns that are found on the row/table with the correct information based on
108
128
  * time now and the current logged in user making the request.
@@ -253,3 +273,46 @@ exports.outputProcessing = (ctx, table, rows, opts = { squash: true }) => __awai
253
273
  }
254
274
  return wasArray ? enriched : enriched[0];
255
275
  });
276
+ /**
277
+ * Clean up any attachments that were attached to a row.
278
+ * @param {string} appId The ID of the app from which a row is being deleted.
279
+ * @param {object} table The table from which a row is being removed.
280
+ * @param {any} row optional - the row being removed.
281
+ * @param {any} rows optional - if multiple rows being deleted can do this in bulk.
282
+ * @param {any} oldRow optional - if updating a row this will determine the difference.
283
+ * @return {Promise<void>} When all attachments have been removed this will return.
284
+ */
285
+ exports.cleanupAttachments = (appId, table, { row, rows, oldRow }) => __awaiter(void 0, void 0, void 0, function* () {
286
+ if (!isProdAppID(appId)) {
287
+ const prodAppId = getDeployedAppID(appId);
288
+ // if prod exists, then don't allow deleting
289
+ const exists = yield dbExists(CouchDB, prodAppId);
290
+ if (exists) {
291
+ return;
292
+ }
293
+ }
294
+ let files = [];
295
+ function addFiles(row, key) {
296
+ if (row[key]) {
297
+ files = files.concat(row[key].map(attachment => attachment.key));
298
+ }
299
+ }
300
+ for (let [key, schema] of Object.entries(table.schema)) {
301
+ if (schema.type !== FieldTypes.ATTACHMENT) {
302
+ continue;
303
+ }
304
+ // if updating, need to manage the differences
305
+ if (oldRow && row) {
306
+ files = files.concat(getRemovedAttachmentKeys(oldRow, row, key));
307
+ }
308
+ else if (row) {
309
+ addFiles(row, key);
310
+ }
311
+ else if (rows) {
312
+ rows.forEach(row => addFiles(row, key));
313
+ }
314
+ }
315
+ if (files.length > 0) {
316
+ return deleteFiles(ObjectStoreBuckets.APPS, files);
317
+ }
318
+ });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/server",
3
3
  "email": "hi@budibase.com",
4
- "version": "1.0.27-alpha.4",
4
+ "version": "1.0.27-alpha.8",
5
5
  "description": "Budibase Web Server",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -70,9 +70,9 @@
70
70
  "license": "GPL-3.0",
71
71
  "dependencies": {
72
72
  "@apidevtools/swagger-parser": "^10.0.3",
73
- "@budibase/auth": "^1.0.27-alpha.4",
74
- "@budibase/client": "^1.0.27-alpha.4",
75
- "@budibase/string-templates": "^1.0.27-alpha.4",
73
+ "@budibase/auth": "^1.0.27-alpha.8",
74
+ "@budibase/client": "^1.0.27-alpha.8",
75
+ "@budibase/string-templates": "^1.0.27-alpha.8",
76
76
  "@bull-board/api": "^3.7.0",
77
77
  "@bull-board/koa": "^3.7.0",
78
78
  "@elastic/elasticsearch": "7.10.0",
@@ -165,5 +165,5 @@
165
165
  "optionalDependencies": {
166
166
  "oracledb": "^5.3.0"
167
167
  },
168
- "gitHead": "23ca7a9750e188e0c392dc56dcdb1842eaa6d3a9"
168
+ "gitHead": "f74090d2eb0a4fefcd40e99aca1c53e45063511c"
169
169
  }
@@ -1,7 +1,7 @@
1
1
  import { Query, QueryParameter } from "../../../../../../definitions/datasource"
2
+ import { URL } from "url"
2
3
 
3
4
  export interface ImportInfo {
4
- url: string
5
5
  name: string
6
6
  }
7
7
 
@@ -23,6 +23,7 @@ export abstract class ImportSource {
23
23
  name: string,
24
24
  method: string,
25
25
  path: string,
26
+ url: URL,
26
27
  queryString: string,
27
28
  headers: object = {},
28
29
  parameters: QueryParameter[] = [],
@@ -33,6 +34,7 @@ export abstract class ImportSource {
33
34
  const transformer = "return data"
34
35
  const schema = {}
35
36
  path = this.processPath(path)
37
+ path = `${url.origin}/${path}`
36
38
  queryString = this.processQuery(queryString)
37
39
  const requestBody = JSON.stringify(body, null, 2)
38
40
 
@@ -60,16 +60,19 @@ export class Curl extends ImportSource {
60
60
  return true
61
61
  }
62
62
 
63
+ getUrl = (): URL => {
64
+ return new URL(this.curl.raw_url)
65
+ }
66
+
63
67
  getInfo = async (): Promise<ImportInfo> => {
64
- const url = new URL(this.curl.url)
68
+ const url = this.getUrl()
65
69
  return {
66
- url: url.origin,
67
70
  name: url.hostname,
68
71
  }
69
72
  }
70
73
 
71
74
  getQueries = async (datasourceId: string): Promise<Query[]> => {
72
- const url = new URL(this.curl.raw_url)
75
+ const url = this.getUrl()
73
76
  const name = url.pathname
74
77
  const path = url.pathname
75
78
  const method = this.curl.method
@@ -87,6 +90,7 @@ export class Curl extends ImportSource {
87
90
  name,
88
91
  method,
89
92
  path,
93
+ url,
90
94
  queryString,
91
95
  headers,
92
96
  [],
@@ -2,6 +2,7 @@ import { ImportInfo } from "./base"
2
2
  import { Query, QueryParameter } from "../../../../../definitions/datasource"
3
3
  import { OpenAPIV2 } from "openapi-types"
4
4
  import { OpenAPISource } from "./base/openapi"
5
+ import { URL } from "url"
5
6
 
6
7
  const parameterNotRef = (
7
8
  param: OpenAPIV2.Parameter | OpenAPIV2.ReferenceObject
@@ -55,20 +56,22 @@ export class OpenAPI2 extends OpenAPISource {
55
56
  }
56
57
  }
57
58
 
58
- getInfo = async (): Promise<ImportInfo> => {
59
+ getUrl = (): URL => {
59
60
  const scheme = this.document.schemes?.includes("https") ? "https" : "http"
60
61
  const basePath = this.document.basePath || ""
61
62
  const host = this.document.host || "<host>"
62
- const url = `${scheme}://${host}${basePath}`
63
- const name = this.document.info.title || "Swagger Import"
63
+ return new URL(`${scheme}://${host}${basePath}`)
64
+ }
64
65
 
66
+ getInfo = async (): Promise<ImportInfo> => {
67
+ const name = this.document.info.title || "Swagger Import"
65
68
  return {
66
- url: url,
67
- name: name,
69
+ name,
68
70
  }
69
71
  }
70
72
 
71
73
  getQueries = async (datasourceId: string): Promise<Query[]> => {
74
+ const url = this.getUrl()
72
75
  const queries = []
73
76
 
74
77
  for (let [path, pathItem] of Object.entries(this.document.paths)) {
@@ -145,6 +148,7 @@ export class OpenAPI2 extends OpenAPISource {
145
148
  name,
146
149
  methodName,
147
150
  path,
151
+ url,
148
152
  queryString,
149
153
  headers,
150
154
  parameters,
@@ -35,7 +35,6 @@ describe("Curl Import", () => {
35
35
  it("returns import info", async () => {
36
36
  await init("get")
37
37
  const info = await curl.getInfo()
38
- expect(info.url).toBe("http://example.com")
39
38
  expect(info.name).toBe("example.com")
40
39
  })
41
40
 
@@ -67,8 +66,8 @@ describe("Curl Import", () => {
67
66
  }
68
67
 
69
68
  it("populates path", async () => {
70
- await testPath("get", "")
71
- await testPath("path", "paths/abc")
69
+ await testPath("get", "http://example.com/")
70
+ await testPath("path", "http://example.com/paths/abc")
72
71
  })
73
72
 
74
73
  const testHeaders = async (file, headers) => {
@@ -41,7 +41,6 @@ describe("OpenAPI2 Import", () => {
41
41
  const testImportInfo = async (file, extension) => {
42
42
  await init(file, extension)
43
43
  const info = await openapi2.getInfo()
44
- expect(info.url).toBe("https://petstore.swagger.io/v2")
45
44
  expect(info.name).toBe("Swagger Petstore")
46
45
  }
47
46
 
@@ -92,12 +91,12 @@ describe("OpenAPI2 Import", () => {
92
91
 
93
92
  it("populates path", async () => {
94
93
  const assertions = {
95
- "createEntity" : "entities",
96
- "getEntities" : "entities",
97
- "getEntity" : "entities/{{entityId}}",
98
- "updateEntity" : "entities/{{entityId}}",
99
- "patchEntity" : "entities/{{entityId}}",
100
- "deleteEntity" : "entities/{{entityId}}"
94
+ "createEntity" : "http://example.com/entities",
95
+ "getEntities" : "http://example.com/entities",
96
+ "getEntity" : "http://example.com/entities/{{entityId}}",
97
+ "updateEntity" : "http://example.com/entities/{{entityId}}",
98
+ "patchEntity" : "http://example.com/entities/{{entityId}}",
99
+ "deleteEntity" : "http://example.com/entities/{{entityId}}"
101
100
  }
102
101
  await runTests("crud", testPath, assertions)
103
102
  })
@@ -51,30 +51,24 @@ describe("Rest Importer", () => {
51
51
  await init(data)
52
52
  const info = await restImporter.getInfo()
53
53
  expect(info.name).toBe(assertions[key].name)
54
- expect(info.url).toBe(assertions[key].url)
55
54
  }
56
55
 
57
56
  it("gets info", async () => {
58
57
  const assertions = {
59
58
  "oapi2CrudJson" : {
60
59
  name: "CRUD",
61
- url: "http://example.com"
62
60
  },
63
61
  "oapi2CrudYaml" : {
64
62
  name: "CRUD",
65
- url: "http://example.com"
66
63
  },
67
64
  "oapi2PetstoreJson" : {
68
65
  name: "Swagger Petstore",
69
- url: "https://petstore.swagger.io/v2"
70
66
  },
71
67
  "oapi2PetstoreYaml" :{
72
68
  name: "Swagger Petstore",
73
- url: "https://petstore.swagger.io/v2"
74
69
  },
75
70
  "curl": {
76
71
  name: "example.com",
77
- url: "http://example.com"
78
72
  }
79
73
  }
80
74
  await runTest(testGetInfo, assertions)
@@ -8,6 +8,7 @@ const { BaseQueryVerbs } = require("../../../constants")
8
8
  const { Thread, ThreadType } = require("../../../threads")
9
9
  const { save: saveDatasource } = require("../datasource")
10
10
  const { RestImporter } = require("./import")
11
+ const { invalidateDynamicVariables } = require("../../../threads/utils")
11
12
 
12
13
  const Runner = new Thread(ThreadType.QUERY, { timeoutMs: 10000 })
13
14
 
@@ -166,8 +167,28 @@ exports.executeV2 = async function (ctx) {
166
167
  return execute(ctx, { rowsOnly: false })
167
168
  }
168
169
 
170
+ const removeDynamicVariables = async (db, queryId) => {
171
+ const query = await db.get(queryId)
172
+ const datasource = await db.get(query.datasourceId)
173
+ const dynamicVariables = datasource.config.dynamicVariables
174
+
175
+ if (dynamicVariables) {
176
+ // delete dynamic variables from the datasource
177
+ const newVariables = dynamicVariables.filter(dv => dv.queryId !== queryId)
178
+ datasource.config.dynamicVariables = newVariables
179
+ await db.put(datasource)
180
+
181
+ // invalidate the deleted variables
182
+ const variablesToDelete = dynamicVariables.filter(
183
+ dv => dv.queryId === queryId
184
+ )
185
+ await invalidateDynamicVariables(variablesToDelete)
186
+ }
187
+ }
188
+
169
189
  exports.destroy = async function (ctx) {
170
190
  const db = new CouchDB(ctx.appId)
191
+ await removeDynamicVariables(db, ctx.params.queryId)
171
192
  await db.remove(ctx.params.queryId, ctx.params.revId)
172
193
  ctx.message = `Query deleted.`
173
194
  ctx.status = 200
@@ -11,6 +11,7 @@ const {
11
11
  inputProcessing,
12
12
  outputProcessing,
13
13
  processAutoColumn,
14
+ cleanupAttachments,
14
15
  } = require("../../../utilities/rowProcessor")
15
16
  const { FieldTypes } = require("../../../constants")
16
17
  const { isEqual } = require("lodash")
@@ -25,6 +26,7 @@ const {
25
26
  getFromDesignDoc,
26
27
  getFromMemoryDoc,
27
28
  } = require("../view/utils")
29
+ const { cloneDeep } = require("lodash/fp")
28
30
 
29
31
  const CALCULATION_TYPES = {
30
32
  SUM: "sum",
@@ -109,14 +111,14 @@ exports.patch = async ctx => {
109
111
  const inputs = ctx.request.body
110
112
  const tableId = inputs.tableId
111
113
  const isUserTable = tableId === InternalTables.USER_METADATA
112
- let dbRow
114
+ let oldRow
113
115
  try {
114
- dbRow = await db.get(inputs._id)
116
+ oldRow = await db.get(inputs._id)
115
117
  } catch (err) {
116
118
  if (isUserTable) {
117
119
  // don't include the rev, it'll be the global rev
118
120
  // this time
119
- dbRow = {
121
+ oldRow = {
120
122
  _id: inputs._id,
121
123
  }
122
124
  } else {
@@ -125,13 +127,14 @@ exports.patch = async ctx => {
125
127
  }
126
128
  let dbTable = await db.get(tableId)
127
129
  // need to build up full patch fields before coerce
130
+ let combinedRow = cloneDeep(oldRow)
128
131
  for (let key of Object.keys(inputs)) {
129
132
  if (!dbTable.schema[key]) continue
130
- dbRow[key] = inputs[key]
133
+ combinedRow[key] = inputs[key]
131
134
  }
132
135
 
133
136
  // this returns the table and row incase they have been updated
134
- let { table, row } = inputProcessing(ctx.user, dbTable, dbRow)
137
+ let { table, row } = inputProcessing(ctx.user, dbTable, combinedRow)
135
138
  const validateResult = await validate({
136
139
  row,
137
140
  table,
@@ -149,6 +152,8 @@ exports.patch = async ctx => {
149
152
  tableId: row.tableId,
150
153
  table,
151
154
  })
155
+ // check if any attachments removed
156
+ await cleanupAttachments(appId, table, { oldRow, row })
152
157
 
153
158
  if (isUserTable) {
154
159
  // the row has been updated, need to put it into the ctx
@@ -295,6 +300,8 @@ exports.destroy = async function (ctx) {
295
300
  row,
296
301
  tableId: row.tableId,
297
302
  })
303
+ // remove any attachments that were on the row from object storage
304
+ await cleanupAttachments(appId, table, { row })
298
305
 
299
306
  let response
300
307
  if (ctx.params.tableId === InternalTables.USER_METADATA) {
@@ -341,6 +348,8 @@ exports.bulkDestroy = async ctx => {
341
348
  } else {
342
349
  await db.bulkDocs(rows.map(row => ({ ...row, _deleted: true })))
343
350
  }
351
+ // remove any attachments that were on the rows from object storage
352
+ await cleanupAttachments(appId, table, { rows })
344
353
  await Promise.all(updates)
345
354
  return { response: { ok: true }, rows }
346
355
  }
@@ -36,7 +36,7 @@ function generateSchema(
36
36
  case FieldTypes.STRING:
37
37
  case FieldTypes.OPTIONS:
38
38
  case FieldTypes.LONGFORM:
39
- schema.string(key)
39
+ schema.text(key)
40
40
  break
41
41
  case FieldTypes.NUMBER:
42
42
  // if meta is specified then this is a junction table entry
@@ -171,7 +171,7 @@ module RestModule {
171
171
  getUrl(path: string, queryString: string): string {
172
172
  const main = `${path}?${queryString}`
173
173
  let complete = main
174
- if (this.config.url && !main.startsWith(this.config.url)) {
174
+ if (this.config.url && !main.startsWith("http")) {
175
175
  complete = !this.config.url ? main : `${this.config.url}/${main}`
176
176
  }
177
177
  if (!complete.startsWith("http")) {
@@ -42,10 +42,11 @@ exports.checkCacheForDynamicVariable = async (queryId, variable) => {
42
42
  }
43
43
 
44
44
  exports.invalidateDynamicVariables = async cachedVars => {
45
+ const cache = await getClient()
45
46
  let promises = []
46
47
  for (let variable of cachedVars) {
47
48
  promises.push(
48
- client.delete(makeVariableKey(variable.queryId, variable.name))
49
+ cache.delete(makeVariableKey(variable.queryId, variable.name))
49
50
  )
50
51
  }
51
52
  await Promise.all(promises)
@@ -2,6 +2,7 @@ const {
2
2
  ObjectStore,
3
3
  makeSureBucketExists,
4
4
  upload,
5
+ deleteFiles,
5
6
  streamUpload,
6
7
  retrieve,
7
8
  retrieveToTmp,
@@ -28,3 +29,4 @@ exports.retrieveToTmp = retrieveToTmp
28
29
  exports.deleteFolder = deleteFolder
29
30
  exports.uploadDirectory = uploadDirectory
30
31
  exports.downloadTarball = downloadTarball
32
+ exports.deleteFiles = deleteFiles
@@ -3,6 +3,10 @@ const { cloneDeep } = require("lodash/fp")
3
3
  const { FieldTypes, AutoFieldSubTypes } = require("../../constants")
4
4
  const { attachmentsRelativeURL } = require("../index")
5
5
  const { processFormulas } = require("./utils")
6
+ const { deleteFiles } = require("../../utilities/fileSystem/utilities")
7
+ const { ObjectStoreBuckets } = require("../../constants")
8
+ const { isProdAppID, getDeployedAppID, dbExists } = require("@budibase/auth/db")
9
+ const CouchDB = require("../../db")
6
10
 
7
11
  const BASE_AUTO_ID = 1
8
12
 
@@ -95,6 +99,23 @@ const TYPE_TRANSFORM_MAP = {
95
99
  },
96
100
  }
97
101
 
102
+ /**
103
+ * Given the old state of the row and the new one after an update, this will
104
+ * find the keys that have been removed in the updated row.
105
+ */
106
+ function getRemovedAttachmentKeys(oldRow, row, attachmentKey) {
107
+ if (!oldRow[attachmentKey]) {
108
+ return []
109
+ }
110
+ const oldKeys = oldRow[attachmentKey].map(attachment => attachment.key)
111
+ // no attachments in new row, all removed
112
+ if (!row[attachmentKey]) {
113
+ return oldKeys
114
+ }
115
+ const newKeys = row[attachmentKey].map(attachment => attachment.key)
116
+ return oldKeys.filter(key => newKeys.indexOf(key) === -1)
117
+ }
118
+
98
119
  /**
99
120
  * This will update any auto columns that are found on the row/table with the correct information based on
100
121
  * time now and the current logged in user making the request.
@@ -272,3 +293,45 @@ exports.outputProcessing = async (
272
293
  }
273
294
  return wasArray ? enriched : enriched[0]
274
295
  }
296
+
297
+ /**
298
+ * Clean up any attachments that were attached to a row.
299
+ * @param {string} appId The ID of the app from which a row is being deleted.
300
+ * @param {object} table The table from which a row is being removed.
301
+ * @param {any} row optional - the row being removed.
302
+ * @param {any} rows optional - if multiple rows being deleted can do this in bulk.
303
+ * @param {any} oldRow optional - if updating a row this will determine the difference.
304
+ * @return {Promise<void>} When all attachments have been removed this will return.
305
+ */
306
+ exports.cleanupAttachments = async (appId, table, { row, rows, oldRow }) => {
307
+ if (!isProdAppID(appId)) {
308
+ const prodAppId = getDeployedAppID(appId)
309
+ // if prod exists, then don't allow deleting
310
+ const exists = await dbExists(CouchDB, prodAppId)
311
+ if (exists) {
312
+ return
313
+ }
314
+ }
315
+ let files = []
316
+ function addFiles(row, key) {
317
+ if (row[key]) {
318
+ files = files.concat(row[key].map(attachment => attachment.key))
319
+ }
320
+ }
321
+ for (let [key, schema] of Object.entries(table.schema)) {
322
+ if (schema.type !== FieldTypes.ATTACHMENT) {
323
+ continue
324
+ }
325
+ // if updating, need to manage the differences
326
+ if (oldRow && row) {
327
+ files = files.concat(getRemovedAttachmentKeys(oldRow, row, key))
328
+ } else if (row) {
329
+ addFiles(row, key)
330
+ } else if (rows) {
331
+ rows.forEach(row => addFiles(row, key))
332
+ }
333
+ }
334
+ if (files.length > 0) {
335
+ return deleteFiles(ObjectStoreBuckets.APPS, files)
336
+ }
337
+ }