@budibase/server 1.0.27-alpha.2 → 1.0.27-alpha.6

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 (192) hide show
  1. package/__mocks__/node-fetch.ts +18 -0
  2. package/builder/assets/{index.5522fcd5.css → index.431d69df.css} +1 -1
  3. package/builder/assets/{index.e9e2c4d7.js → index.f909f0e2.js} +245 -240
  4. package/builder/index.html +2 -2
  5. package/coverage/clover.xml +429 -339
  6. package/coverage/coverage-final.json +22 -22
  7. package/coverage/lcov-report/api/controllers/analytics.js.html +1 -1
  8. package/coverage/lcov-report/api/controllers/apikeys.js.html +1 -1
  9. package/coverage/lcov-report/api/controllers/application.js.html +54 -54
  10. package/coverage/lcov-report/api/controllers/auth.js.html +1 -1
  11. package/coverage/lcov-report/api/controllers/automation.js.html +1 -1
  12. package/coverage/lcov-report/api/controllers/backup.js.html +1 -1
  13. package/coverage/lcov-report/api/controllers/cloud.js.html +1 -1
  14. package/coverage/lcov-report/api/controllers/component.js.html +1 -1
  15. package/coverage/lcov-report/api/controllers/datasource.js.html +49 -49
  16. package/coverage/lcov-report/api/controllers/dev.js.html +1 -1
  17. package/coverage/lcov-report/api/controllers/hosting.js.html +1 -1
  18. package/coverage/lcov-report/api/controllers/index.html +20 -20
  19. package/coverage/lcov-report/api/controllers/integration.js.html +1 -1
  20. package/coverage/lcov-report/api/controllers/layout.js.html +1 -1
  21. package/coverage/lcov-report/api/controllers/metadata.js.html +1 -1
  22. package/coverage/lcov-report/api/controllers/permission.js.html +1 -1
  23. package/coverage/lcov-report/api/controllers/query/index.html +19 -19
  24. package/coverage/lcov-report/api/controllers/query/index.js.html +66 -150
  25. package/coverage/lcov-report/api/controllers/query/validation.js.html +6 -3
  26. package/coverage/lcov-report/api/controllers/role.js.html +1 -1
  27. package/coverage/lcov-report/api/controllers/routing.js.html +1 -1
  28. package/coverage/lcov-report/api/controllers/row/external.js.html +1 -1
  29. package/coverage/lcov-report/api/controllers/row/index.html +1 -1
  30. package/coverage/lcov-report/api/controllers/row/index.js.html +1 -1
  31. package/coverage/lcov-report/api/controllers/row/internal.js.html +1 -1
  32. package/coverage/lcov-report/api/controllers/row/internalSearch.js.html +1 -1
  33. package/coverage/lcov-report/api/controllers/row/utils.js.html +1 -1
  34. package/coverage/lcov-report/api/controllers/screen.js.html +1 -1
  35. package/coverage/lcov-report/api/controllers/script.js.html +1 -1
  36. package/coverage/lcov-report/api/controllers/table/external.js.html +1 -1
  37. package/coverage/lcov-report/api/controllers/table/index.html +1 -1
  38. package/coverage/lcov-report/api/controllers/table/index.js.html +1 -1
  39. package/coverage/lcov-report/api/controllers/table/internal.js.html +1 -1
  40. package/coverage/lcov-report/api/controllers/table/utils.js.html +1 -1
  41. package/coverage/lcov-report/api/controllers/templates.js.html +1 -1
  42. package/coverage/lcov-report/api/controllers/user.js.html +1 -1
  43. package/coverage/lcov-report/api/controllers/view/exporters.js.html +1 -1
  44. package/coverage/lcov-report/api/controllers/view/index.html +1 -1
  45. package/coverage/lcov-report/api/controllers/view/index.js.html +1 -1
  46. package/coverage/lcov-report/api/controllers/view/utils.js.html +1 -1
  47. package/coverage/lcov-report/api/controllers/view/viewBuilder.js.html +1 -1
  48. package/coverage/lcov-report/api/controllers/webhook.js.html +1 -1
  49. package/coverage/lcov-report/api/index.html +1 -1
  50. package/coverage/lcov-report/api/index.js.html +5 -5
  51. package/coverage/lcov-report/api/routes/analytics.js.html +1 -1
  52. package/coverage/lcov-report/api/routes/apikeys.js.html +1 -1
  53. package/coverage/lcov-report/api/routes/application.js.html +1 -1
  54. package/coverage/lcov-report/api/routes/auth.js.html +1 -1
  55. package/coverage/lcov-report/api/routes/automation.js.html +1 -1
  56. package/coverage/lcov-report/api/routes/backup.js.html +1 -1
  57. package/coverage/lcov-report/api/routes/cloud.js.html +1 -1
  58. package/coverage/lcov-report/api/routes/component.js.html +1 -1
  59. package/coverage/lcov-report/api/routes/datasource.js.html +1 -1
  60. package/coverage/lcov-report/api/routes/deploy.js.html +1 -1
  61. package/coverage/lcov-report/api/routes/dev.js.html +1 -1
  62. package/coverage/lcov-report/api/routes/hosting.js.html +1 -1
  63. package/coverage/lcov-report/api/routes/index.html +1 -1
  64. package/coverage/lcov-report/api/routes/index.js.html +1 -1
  65. package/coverage/lcov-report/api/routes/integration.js.html +1 -1
  66. package/coverage/lcov-report/api/routes/layout.js.html +1 -1
  67. package/coverage/lcov-report/api/routes/metadata.js.html +1 -1
  68. package/coverage/lcov-report/api/routes/permission.js.html +1 -1
  69. package/coverage/lcov-report/api/routes/query.js.html +24 -3
  70. package/coverage/lcov-report/api/routes/role.js.html +1 -1
  71. package/coverage/lcov-report/api/routes/routing.js.html +1 -1
  72. package/coverage/lcov-report/api/routes/row.js.html +1 -1
  73. package/coverage/lcov-report/api/routes/screen.js.html +1 -1
  74. package/coverage/lcov-report/api/routes/script.js.html +1 -1
  75. package/coverage/lcov-report/api/routes/static.js.html +1 -1
  76. package/coverage/lcov-report/api/routes/table.js.html +1 -1
  77. package/coverage/lcov-report/api/routes/templates.js.html +1 -1
  78. package/coverage/lcov-report/api/routes/user.js.html +1 -1
  79. package/coverage/lcov-report/api/routes/view.js.html +1 -1
  80. package/coverage/lcov-report/api/routes/webhook.js.html +1 -1
  81. package/coverage/lcov-report/automations/actions.js.html +1 -1
  82. package/coverage/lcov-report/automations/automationUtils.js.html +1 -1
  83. package/coverage/lcov-report/automations/bullboard.js.html +1 -1
  84. package/coverage/lcov-report/automations/index.html +1 -1
  85. package/coverage/lcov-report/automations/index.js.html +1 -1
  86. package/coverage/lcov-report/automations/steps/bash.js.html +1 -1
  87. package/coverage/lcov-report/automations/steps/createRow.js.html +1 -1
  88. package/coverage/lcov-report/automations/steps/delay.js.html +1 -1
  89. package/coverage/lcov-report/automations/steps/deleteRow.js.html +1 -1
  90. package/coverage/lcov-report/automations/steps/discord.js.html +1 -1
  91. package/coverage/lcov-report/automations/steps/executeQuery.js.html +31 -7
  92. package/coverage/lcov-report/automations/steps/executeScript.js.html +1 -1
  93. package/coverage/lcov-report/automations/steps/filter.js.html +1 -1
  94. package/coverage/lcov-report/automations/steps/index.html +11 -11
  95. package/coverage/lcov-report/automations/steps/integromat.js.html +1 -1
  96. package/coverage/lcov-report/automations/steps/outgoingWebhook.js.html +1 -1
  97. package/coverage/lcov-report/automations/steps/queryRows.js.html +1 -1
  98. package/coverage/lcov-report/automations/steps/sendSmtpEmail.js.html +1 -1
  99. package/coverage/lcov-report/automations/steps/serverLog.js.html +1 -1
  100. package/coverage/lcov-report/automations/steps/slack.js.html +1 -1
  101. package/coverage/lcov-report/automations/steps/updateRow.js.html +1 -1
  102. package/coverage/lcov-report/automations/steps/utils.js.html +1 -1
  103. package/coverage/lcov-report/automations/steps/zapier.js.html +1 -1
  104. package/coverage/lcov-report/automations/triggerInfo/app.js.html +1 -1
  105. package/coverage/lcov-report/automations/triggerInfo/cron.js.html +1 -1
  106. package/coverage/lcov-report/automations/triggerInfo/index.html +1 -1
  107. package/coverage/lcov-report/automations/triggerInfo/index.js.html +1 -1
  108. package/coverage/lcov-report/automations/triggerInfo/rowDeleted.js.html +1 -1
  109. package/coverage/lcov-report/automations/triggerInfo/rowSaved.js.html +1 -1
  110. package/coverage/lcov-report/automations/triggerInfo/rowUpdated.js.html +1 -1
  111. package/coverage/lcov-report/automations/triggerInfo/webhook.js.html +1 -1
  112. package/coverage/lcov-report/automations/triggers.js.html +1 -1
  113. package/coverage/lcov-report/automations/utils.js.html +5 -5
  114. package/coverage/lcov-report/constants/index.html +1 -1
  115. package/coverage/lcov-report/constants/index.js.html +1 -1
  116. package/coverage/lcov-report/constants/layouts.js.html +1 -1
  117. package/coverage/lcov-report/constants/screens.js.html +1 -1
  118. package/coverage/lcov-report/db/client.js.html +1 -1
  119. package/coverage/lcov-report/db/inMemoryView.js.html +1 -1
  120. package/coverage/lcov-report/db/index.html +1 -1
  121. package/coverage/lcov-report/db/index.js.html +1 -1
  122. package/coverage/lcov-report/db/linkedRows/LinkController.js.html +1 -1
  123. package/coverage/lcov-report/db/linkedRows/index.html +1 -1
  124. package/coverage/lcov-report/db/linkedRows/index.js.html +1 -1
  125. package/coverage/lcov-report/db/linkedRows/linkUtils.js.html +1 -1
  126. package/coverage/lcov-report/db/newid.js.html +2 -2
  127. package/coverage/lcov-report/db/utils.js.html +18 -18
  128. package/coverage/lcov-report/events/AutomationEmitter.js.html +1 -1
  129. package/coverage/lcov-report/events/index.html +1 -1
  130. package/coverage/lcov-report/events/index.js.html +1 -1
  131. package/coverage/lcov-report/events/utils.js.html +1 -1
  132. package/coverage/lcov-report/index.html +46 -46
  133. package/coverage/lcov-report/integrations/tests/TestConfiguration.js.html +1 -1
  134. package/coverage/lcov-report/integrations/tests/index.html +1 -1
  135. package/coverage/lcov-report/middleware/appInfo.js.html +1 -1
  136. package/coverage/lcov-report/middleware/authorized.js.html +10 -10
  137. package/coverage/lcov-report/middleware/builder.js.html +8 -8
  138. package/coverage/lcov-report/middleware/currentapp.js.html +28 -28
  139. package/coverage/lcov-report/middleware/index.html +1 -1
  140. package/coverage/lcov-report/middleware/joi-validator.js.html +9 -9
  141. package/coverage/lcov-report/middleware/resourceId.js.html +17 -17
  142. package/coverage/lcov-report/middleware/selfhost.js.html +1 -1
  143. package/coverage/lcov-report/middleware/usageQuota.js.html +1 -1
  144. package/coverage/lcov-report/middleware/utils.js.html +2 -2
  145. package/coverage/lcov-report/threads/automation.js.html +1 -1
  146. package/coverage/lcov-report/threads/index.html +30 -30
  147. package/coverage/lcov-report/threads/index.js.html +11 -11
  148. package/coverage/lcov-report/threads/query.js.html +358 -91
  149. package/coverage/lcov-report/threads/utils.js.html +352 -10
  150. package/coverage/lcov-report/utilities/bcrypt.js.html +1 -1
  151. package/coverage/lcov-report/utilities/budibaseDir.js.html +1 -1
  152. package/coverage/lcov-report/utilities/centralPath.js.html +1 -1
  153. package/coverage/lcov-report/utilities/csvParser.js.html +1 -1
  154. package/coverage/lcov-report/utilities/global.js.html +13 -13
  155. package/coverage/lcov-report/utilities/index.html +1 -1
  156. package/coverage/lcov-report/utilities/index.js.html +9 -9
  157. package/coverage/lcov-report/utilities/queue/inMemoryQueue.js.html +2 -2
  158. package/coverage/lcov-report/utilities/queue/index.html +1 -1
  159. package/coverage/lcov-report/utilities/routing/index.html +1 -1
  160. package/coverage/lcov-report/utilities/routing/index.js.html +1 -1
  161. package/coverage/lcov-report/utilities/rowProcessor/index.html +1 -1
  162. package/coverage/lcov-report/utilities/rowProcessor/index.js.html +1 -1
  163. package/coverage/lcov-report/utilities/rowProcessor/utils.js.html +1 -1
  164. package/coverage/lcov-report/utilities/scriptRunner.js.html +1 -1
  165. package/coverage/lcov-report/utilities/security.js.html +1 -1
  166. package/coverage/lcov-report/utilities/statusCodes.js.html +1 -1
  167. package/coverage/lcov-report/utilities/users.js.html +1 -1
  168. package/coverage/lcov-report/utilities/workerRequests.js.html +1 -1
  169. package/coverage/lcov.info +732 -576
  170. package/dist/api/controllers/query/import/sources/curl.js +5 -1
  171. package/dist/api/controllers/query/index.js +29 -47
  172. package/dist/api/controllers/query/validation.js +2 -1
  173. package/dist/api/routes/query.js +3 -1
  174. package/dist/automations/steps/executeQuery.js +8 -1
  175. package/dist/integrations/rest.js +2 -2
  176. package/dist/package.json +5 -4
  177. package/dist/tests/utilities/TestConfiguration.js +7 -0
  178. package/dist/threads/query.js +145 -52
  179. package/dist/threads/utils.js +104 -0
  180. package/dist/tsconfig.tsbuildinfo +1 -1
  181. package/package.json +6 -5
  182. package/src/api/controllers/query/import/sources/curl.ts +5 -1
  183. package/src/api/controllers/query/index.js +27 -55
  184. package/src/api/controllers/query/validation.js +2 -1
  185. package/src/api/routes/query.js +8 -1
  186. package/src/api/routes/tests/query.spec.js +88 -0
  187. package/src/automations/steps/executeQuery.js +9 -1
  188. package/src/definitions/datasource.ts +10 -0
  189. package/src/integrations/rest.ts +5 -2
  190. package/src/tests/utilities/TestConfiguration.js +10 -0
  191. package/src/threads/query.js +145 -56
  192. package/src/threads/utils.js +114 -0
@@ -26,8 +26,12 @@ const parseBody = (curl) => {
26
26
  if (curl.data) {
27
27
  const keys = Object.keys(curl.data);
28
28
  if (keys.length) {
29
- const key = keys[0];
29
+ let key = keys[0];
30
30
  try {
31
+ // filter out the dollar syntax used by curl for shell support
32
+ if (key.startsWith("$")) {
33
+ key = key.substring(1);
34
+ }
31
35
  return JSON.parse(key);
32
36
  }
33
37
  catch (e) {
@@ -8,7 +8,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- const { processString } = require("@budibase/string-templates");
12
11
  const CouchDB = require("../../../db");
13
12
  const { generateQueryID, getQueryParams, isProdAppID, } = require("../../../db/utils");
14
13
  const { BaseQueryVerbs } = require("../../../constants");
@@ -81,44 +80,6 @@ exports.save = function (ctx) {
81
80
  ctx.message = `Query ${query.name} saved successfully.`;
82
81
  });
83
82
  };
84
- function enrichQueryFields(fields, parameters = {}) {
85
- return __awaiter(this, void 0, void 0, function* () {
86
- const enrichedQuery = {};
87
- // enrich the fields with dynamic parameters
88
- for (let key of Object.keys(fields)) {
89
- if (fields[key] == null) {
90
- continue;
91
- }
92
- if (typeof fields[key] === "object") {
93
- // enrich nested fields object
94
- enrichedQuery[key] = yield enrichQueryFields(fields[key], parameters);
95
- }
96
- else if (typeof fields[key] === "string") {
97
- // enrich string value as normal
98
- enrichedQuery[key] = yield processString(fields[key], parameters, {
99
- noHelpers: true,
100
- });
101
- }
102
- else {
103
- enrichedQuery[key] = fields[key];
104
- }
105
- }
106
- if (enrichedQuery.json ||
107
- enrichedQuery.customData ||
108
- enrichedQuery.requestBody) {
109
- try {
110
- enrichedQuery.json = JSON.parse(enrichedQuery.json ||
111
- enrichedQuery.customData ||
112
- enrichedQuery.requestBody);
113
- }
114
- catch (err) {
115
- // no json found, ignore
116
- }
117
- delete enrichedQuery.customData;
118
- }
119
- return enrichedQuery;
120
- });
121
- }
122
83
  exports.find = function (ctx) {
123
84
  return __awaiter(this, void 0, void 0, function* () {
124
85
  const db = new CouchDB(ctx.appId);
@@ -135,14 +96,18 @@ exports.preview = function (ctx) {
135
96
  return __awaiter(this, void 0, void 0, function* () {
136
97
  const db = new CouchDB(ctx.appId);
137
98
  const datasource = yield db.get(ctx.request.body.datasourceId);
138
- const { fields, parameters, queryVerb, transformer } = ctx.request.body;
139
- const enrichedQuery = yield enrichQueryFields(fields, parameters);
99
+ // preview may not have a queryId as it hasn't been saved, but if it does
100
+ // this stops dynamic variables from calling the same query
101
+ const { fields, parameters, queryVerb, transformer, queryId } = ctx.request.body;
140
102
  try {
141
103
  const { rows, keys, info, extra } = yield Runner.run({
104
+ appId: ctx.appId,
142
105
  datasource,
143
106
  queryVerb,
144
- query: enrichedQuery,
107
+ fields,
108
+ parameters,
145
109
  transformer,
110
+ queryId,
146
111
  });
147
112
  ctx.body = {
148
113
  rows,
@@ -156,26 +121,43 @@ exports.preview = function (ctx) {
156
121
  }
157
122
  });
158
123
  };
159
- exports.execute = function (ctx) {
124
+ function execute(ctx, opts = { rowsOnly: false }) {
160
125
  return __awaiter(this, void 0, void 0, function* () {
161
126
  const db = new CouchDB(ctx.appId);
162
127
  const query = yield db.get(ctx.params.queryId);
163
128
  const datasource = yield db.get(query.datasourceId);
164
- const enrichedQuery = yield enrichQueryFields(query.fields, ctx.request.body.parameters);
165
129
  // call the relevant CRUD method on the integration class
166
130
  try {
167
- const { rows } = yield Runner.run({
131
+ const { rows, extra } = yield Runner.run({
132
+ appId: ctx.appId,
168
133
  datasource,
169
134
  queryVerb: query.queryVerb,
170
- query: enrichedQuery,
135
+ fields: query.fields,
136
+ parameters: ctx.request.body.parameters,
171
137
  transformer: query.transformer,
138
+ queryId: ctx.params.queryId,
172
139
  });
173
- ctx.body = rows;
140
+ if (opts && opts.rowsOnly) {
141
+ ctx.body = rows;
142
+ }
143
+ else {
144
+ ctx.body = Object.assign({ data: rows }, extra);
145
+ }
174
146
  }
175
147
  catch (err) {
176
148
  ctx.throw(400, err);
177
149
  }
178
150
  });
151
+ }
152
+ exports.executeV1 = function (ctx) {
153
+ return __awaiter(this, void 0, void 0, function* () {
154
+ return execute(ctx, { rowsOnly: true });
155
+ });
156
+ };
157
+ exports.executeV2 = function (ctx) {
158
+ return __awaiter(this, void 0, void 0, function* () {
159
+ return execute(ctx, { rowsOnly: false });
160
+ });
179
161
  };
180
162
  exports.destroy = function (ctx) {
181
163
  return __awaiter(this, void 0, void 0, function* () {
@@ -32,6 +32,7 @@ exports.generateQueryPreviewValidation = () => {
32
32
  extra: Joi.object().optional(),
33
33
  datasourceId: Joi.string().required(),
34
34
  transformer: Joi.string().optional(),
35
- parameters: Joi.object({}).required().unknown(true)
35
+ parameters: Joi.object({}).required().unknown(true),
36
+ queryId: Joi.string().optional(),
36
37
  }));
37
38
  };
@@ -12,6 +12,8 @@ router
12
12
  .post("/api/queries/import", authorized(BUILDER), queryController.import)
13
13
  .post("/api/queries/preview", bodyResource("datasourceId"), authorized(BUILDER), generateQueryPreviewValidation(), queryController.preview)
14
14
  .get("/api/queries/:queryId", paramResource("queryId"), authorized(PermissionTypes.QUERY, PermissionLevels.READ), queryController.find)
15
- .post("/api/queries/:queryId", paramResource("queryId"), authorized(PermissionTypes.QUERY, PermissionLevels.WRITE), queryController.execute)
15
+ // DEPRECATED - use new query endpoint for future work
16
+ .post("/api/queries/:queryId", paramResource("queryId"), authorized(PermissionTypes.QUERY, PermissionLevels.WRITE), queryController.executeV1)
17
+ .post("/api/v2/queries/:queryId", paramResource("queryId"), authorized(PermissionTypes.QUERY, PermissionLevels.WRITE), queryController.executeV2)
16
18
  .delete("/api/queries/:queryId/:revId", paramResource("queryId"), authorized(BUILDER), queryController.destroy);
17
19
  module.exports = router;
@@ -55,6 +55,10 @@ exports.definition = {
55
55
  type: "object",
56
56
  description: "The response from the datasource execution",
57
57
  },
58
+ info: {
59
+ type: "object",
60
+ description: "Some query types may return extra data, like headers from a REST query",
61
+ },
58
62
  success: {
59
63
  type: "boolean",
60
64
  description: "Whether the action was successful",
@@ -85,14 +89,17 @@ exports.run = function ({ inputs, appId, emitter }) {
85
89
  });
86
90
  try {
87
91
  yield queryController.execute(ctx);
92
+ const _b = ctx.body, { data } = _b, rest = __rest(_b, ["data"]);
88
93
  return {
89
- response: ctx.body,
94
+ response: data,
95
+ info: rest,
90
96
  success: true,
91
97
  };
92
98
  }
93
99
  catch (err) {
94
100
  return {
95
101
  success: false,
102
+ info: {},
96
103
  response: automationUtils.getError(err),
97
104
  };
98
105
  }
@@ -47,7 +47,7 @@ var RestModule;
47
47
  const { performance } = require("perf_hooks");
48
48
  const FormData = require("form-data");
49
49
  const { URLSearchParams } = require("url");
50
- const { parseStringPromise: xmlParser, Builder: XmlBuilder } = require("xml2js");
50
+ const { parseStringPromise: xmlParser, Builder: XmlBuilder, } = require("xml2js");
51
51
  const SCHEMA = {
52
52
  docs: "https://github.com/node-fetch/node-fetch",
53
53
  description: "With the REST API datasource, you can connect, query and pull data from multiple REST APIs. You can then use the retrieved data to build apps.",
@@ -202,7 +202,7 @@ var RestModule;
202
202
  break;
203
203
  case BodyTypes.XML:
204
204
  if (object != null) {
205
- string = (new XmlBuilder()).buildObject(object);
205
+ string = new XmlBuilder().buildObject(object);
206
206
  }
207
207
  input.body = string;
208
208
  input.headers["Content-Type"] = "application/xml";
package/dist/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.1",
4
+ "version": "1.0.27-alpha.5",
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.1",
74
- "@budibase/client": "^1.0.27-alpha.1",
75
- "@budibase/string-templates": "^1.0.27-alpha.1",
73
+ "@budibase/auth": "^1.0.27-alpha.5",
74
+ "@budibase/client": "^1.0.27-alpha.5",
75
+ "@budibase/string-templates": "^1.0.27-alpha.5",
76
76
  "@bull-board/api": "^3.7.0",
77
77
  "@bull-board/koa": "^3.7.0",
78
78
  "@elastic/elasticsearch": "7.10.0",
@@ -122,6 +122,7 @@
122
122
  "pouchdb-replication-stream": "1.2.9",
123
123
  "server-destroy": "1.0.1",
124
124
  "svelte": "^3.38.2",
125
+ "swagger-parser": "^10.0.3",
125
126
  "to-json-schema": "0.2.5",
126
127
  "uuid": "3.3.2",
127
128
  "validate.js": "0.13.1",
@@ -294,6 +294,13 @@ class TestConfiguration {
294
294
  return this.datasource;
295
295
  });
296
296
  }
297
+ updateDatasource(datasource) {
298
+ return __awaiter(this, void 0, void 0, function* () {
299
+ const response = yield this._req(datasource, { datasourceId: datasource._id }, controllers.datasource.update);
300
+ this.datasource = response.datasource;
301
+ return this.datasource;
302
+ });
303
+ }
297
304
  createQuery(config = null) {
298
305
  return __awaiter(this, void 0, void 0, function* () {
299
306
  if (!this.datasource && !config) {
@@ -8,63 +8,156 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  step((generator = generator.apply(thisArg, _arguments || [])).next());
9
9
  });
10
10
  };
11
- require("./utils").threadSetup();
11
+ const threadUtils = require("./utils");
12
+ threadUtils.threadSetup();
12
13
  const ScriptRunner = require("../utilities/scriptRunner");
13
14
  const { integrations } = require("../integrations");
14
- function formatResponse(resp) {
15
- if (typeof resp === "string") {
16
- try {
17
- resp = JSON.parse(resp);
18
- }
19
- catch (err) {
20
- resp = { response: resp };
21
- }
15
+ const { processStringSync } = require("@budibase/string-templates");
16
+ const CouchDB = require("../db");
17
+ class QueryRunner {
18
+ constructor(input, flags = { noRecursiveQuery: false }) {
19
+ this.appId = input.appId;
20
+ this.datasource = input.datasource;
21
+ this.queryVerb = input.queryVerb;
22
+ this.fields = input.fields;
23
+ this.parameters = input.parameters;
24
+ this.transformer = input.transformer;
25
+ this.queryId = input.queryId;
26
+ this.noRecursiveQuery = flags.noRecursiveQuery;
27
+ this.cachedVariables = [];
28
+ // allows the response from a query to be stored throughout this
29
+ // execution so that if it needs to be re-used for another variable
30
+ // it can be
31
+ this.queryResponse = {};
32
+ this.hasRerun = false;
33
+ }
34
+ execute() {
35
+ return __awaiter(this, void 0, void 0, function* () {
36
+ let { datasource, fields, queryVerb, transformer } = this;
37
+ // pre-query, make sure datasource variables are added to parameters
38
+ const parameters = yield this.addDatasourceVariables();
39
+ const query = threadUtils.enrichQueryFields(fields, parameters);
40
+ const Integration = integrations[datasource.source];
41
+ if (!Integration) {
42
+ throw "Integration type does not exist.";
43
+ }
44
+ const integration = new Integration(datasource.config);
45
+ let output = threadUtils.formatResponse(yield integration[queryVerb](query));
46
+ let rows = output, info = undefined, extra = undefined;
47
+ if (threadUtils.hasExtraData(output)) {
48
+ rows = output.data;
49
+ info = output.info;
50
+ extra = output.extra;
51
+ }
52
+ // transform as required
53
+ if (transformer) {
54
+ const runner = new ScriptRunner(transformer, { data: rows });
55
+ rows = runner.execute();
56
+ }
57
+ // if the request fails we retry once, invalidating the cached value
58
+ if (info &&
59
+ info.code >= 400 &&
60
+ this.cachedVariables.length > 0 &&
61
+ !this.hasRerun) {
62
+ this.hasRerun = true;
63
+ // invalidate the cache value
64
+ yield threadUtils.invalidateDynamicVariables(this.cachedVariables);
65
+ return this.execute();
66
+ }
67
+ // needs to an array for next step
68
+ if (!Array.isArray(rows)) {
69
+ rows = [rows];
70
+ }
71
+ // map into JSON if just raw primitive here
72
+ if (rows.find(row => typeof row !== "object")) {
73
+ rows = rows.map(value => ({ value }));
74
+ }
75
+ // get all the potential fields in the schema
76
+ let keys = rows.flatMap(Object.keys);
77
+ if (integration.end) {
78
+ integration.end();
79
+ }
80
+ return { rows, keys, info, extra };
81
+ });
82
+ }
83
+ runAnotherQuery(queryId, parameters) {
84
+ return __awaiter(this, void 0, void 0, function* () {
85
+ const db = new CouchDB(this.appId);
86
+ const query = yield db.get(queryId);
87
+ const datasource = yield db.get(query.datasourceId);
88
+ return new QueryRunner({
89
+ appId: this.appId,
90
+ datasource,
91
+ queryVerb: query.queryVerb,
92
+ fields: query.fields,
93
+ parameters,
94
+ transformer: query.transformer,
95
+ }, { noRecursiveQuery: true }).execute();
96
+ });
97
+ }
98
+ getDynamicVariable(variable) {
99
+ return __awaiter(this, void 0, void 0, function* () {
100
+ let { parameters } = this;
101
+ const queryId = variable.queryId, name = variable.name;
102
+ let value = yield threadUtils.checkCacheForDynamicVariable(queryId, name);
103
+ if (!value) {
104
+ value = this.queryResponse[queryId]
105
+ ? this.queryResponse[queryId]
106
+ : yield this.runAnotherQuery(queryId, parameters);
107
+ // store incase this query is to be called again
108
+ this.queryResponse[queryId] = value;
109
+ yield threadUtils.storeDynamicVariable(queryId, name, value);
110
+ }
111
+ else {
112
+ this.cachedVariables.push({ queryId, name });
113
+ }
114
+ return value;
115
+ });
116
+ }
117
+ addDatasourceVariables() {
118
+ return __awaiter(this, void 0, void 0, function* () {
119
+ let { datasource, parameters, fields } = this;
120
+ if (!datasource || !datasource.config) {
121
+ return parameters;
122
+ }
123
+ const staticVars = datasource.config.staticVariables || {};
124
+ const dynamicVars = datasource.config.dynamicVariables || [];
125
+ for (let [key, value] of Object.entries(staticVars)) {
126
+ if (!parameters[key]) {
127
+ parameters[key] = value;
128
+ }
129
+ }
130
+ if (!this.noRecursiveQuery) {
131
+ // need to see if this uses any variables
132
+ const stringFields = JSON.stringify(fields);
133
+ const foundVars = dynamicVars.filter(variable => {
134
+ // don't allow a query to use its own dynamic variable (loop)
135
+ if (variable.queryId === this.queryId) {
136
+ return false;
137
+ }
138
+ // look for {{ variable }} but allow spaces between handlebars
139
+ const regex = new RegExp(`{{[ ]*${variable.name}[ ]*}}`);
140
+ return regex.test(stringFields);
141
+ });
142
+ const dynamics = foundVars.map(dynVar => this.getDynamicVariable(dynVar));
143
+ const responses = yield Promise.all(dynamics);
144
+ for (let i = 0; i < foundVars.length; i++) {
145
+ const variable = foundVars[i];
146
+ parameters[variable.name] = processStringSync(variable.value, {
147
+ data: responses[i].rows,
148
+ info: responses[i].extra,
149
+ });
150
+ // make sure its known that this uses dynamic variables in case it fails
151
+ this.hasDynamicVariables = true;
152
+ }
153
+ }
154
+ return parameters;
155
+ });
22
156
  }
23
- return resp;
24
- }
25
- function hasExtraData(response) {
26
- return (typeof response === "object" &&
27
- !Array.isArray(response) &&
28
- response.data != null &&
29
- response.info != null);
30
- }
31
- function runAndTransform(datasource, queryVerb, query, transformer) {
32
- return __awaiter(this, void 0, void 0, function* () {
33
- const Integration = integrations[datasource.source];
34
- if (!Integration) {
35
- throw "Integration type does not exist.";
36
- }
37
- const integration = new Integration(datasource.config);
38
- let output = formatResponse(yield integration[queryVerb](query));
39
- let rows = output, info = undefined, extra = undefined;
40
- if (hasExtraData(output)) {
41
- rows = output.data;
42
- info = output.info;
43
- extra = output.extra;
44
- }
45
- // transform as required
46
- if (transformer) {
47
- const runner = new ScriptRunner(transformer, { data: rows });
48
- rows = runner.execute();
49
- }
50
- // needs to an array for next step
51
- if (!Array.isArray(rows)) {
52
- rows = [rows];
53
- }
54
- // map into JSON if just raw primitive here
55
- if (rows.find(row => typeof row !== "object")) {
56
- rows = rows.map(value => ({ value }));
57
- }
58
- // get all the potential fields in the schema
59
- let keys = rows.flatMap(Object.keys);
60
- if (integration.end) {
61
- integration.end();
62
- }
63
- return { rows, keys, info, extra };
64
- });
65
157
  }
66
158
  module.exports = (input, callback) => {
67
- runAndTransform(input.datasource, input.queryVerb, input.query, input.transformer)
159
+ const Runner = new QueryRunner(input);
160
+ Runner.execute()
68
161
  .then(response => {
69
162
  callback(null, response);
70
163
  })
@@ -1,7 +1,35 @@
1
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
+ };
2
11
  const env = require("../environment");
3
12
  const CouchDB = require("../db");
4
13
  const { init } = require("@budibase/auth");
14
+ const redis = require("@budibase/auth/redis");
15
+ const { SEPARATOR } = require("@budibase/auth/db");
16
+ const { processStringSync } = require("@budibase/string-templates");
17
+ const VARIABLE_TTL_SECONDS = 3600;
18
+ let client;
19
+ const IS_TRIPLE_BRACE = new RegExp(/^{{3}.*}{3}$/);
20
+ const IS_HANDLEBARS = new RegExp(/^{{2}.*}{2}$/);
21
+ function getClient() {
22
+ return __awaiter(this, void 0, void 0, function* () {
23
+ if (!client) {
24
+ client = yield new redis.Client(redis.utils.Databases.QUERY_VARS).init();
25
+ }
26
+ return client;
27
+ });
28
+ }
29
+ process.on("exit", () => __awaiter(void 0, void 0, void 0, function* () {
30
+ if (client)
31
+ yield client.finish();
32
+ }));
5
33
  exports.threadSetup = () => {
6
34
  // don't run this if not threading
7
35
  if (env.isTest() || env.DISABLE_THREADING) {
@@ -11,3 +39,79 @@ exports.threadSetup = () => {
11
39
  env.setInThread();
12
40
  init(CouchDB);
13
41
  };
42
+ function makeVariableKey(queryId, variable) {
43
+ return `${queryId}${SEPARATOR}${variable}`;
44
+ }
45
+ exports.checkCacheForDynamicVariable = (queryId, variable) => __awaiter(void 0, void 0, void 0, function* () {
46
+ const cache = yield getClient();
47
+ return cache.get(makeVariableKey(queryId, variable));
48
+ });
49
+ exports.invalidateDynamicVariables = (cachedVars) => __awaiter(void 0, void 0, void 0, function* () {
50
+ let promises = [];
51
+ for (let variable of cachedVars) {
52
+ promises.push(client.delete(makeVariableKey(variable.queryId, variable.name)));
53
+ }
54
+ yield Promise.all(promises);
55
+ });
56
+ exports.storeDynamicVariable = (queryId, variable, value) => __awaiter(void 0, void 0, void 0, function* () {
57
+ const cache = yield getClient();
58
+ yield cache.store(makeVariableKey(queryId, variable), value, VARIABLE_TTL_SECONDS);
59
+ });
60
+ exports.formatResponse = resp => {
61
+ if (typeof resp === "string") {
62
+ try {
63
+ resp = JSON.parse(resp);
64
+ }
65
+ catch (err) {
66
+ resp = { response: resp };
67
+ }
68
+ }
69
+ return resp;
70
+ };
71
+ exports.hasExtraData = response => {
72
+ return (typeof response === "object" &&
73
+ !Array.isArray(response) &&
74
+ response.data != null &&
75
+ response.info != null);
76
+ };
77
+ exports.enrichQueryFields = (fields, parameters = {}) => {
78
+ const enrichedQuery = {};
79
+ // enrich the fields with dynamic parameters
80
+ for (let key of Object.keys(fields)) {
81
+ if (fields[key] == null) {
82
+ continue;
83
+ }
84
+ if (typeof fields[key] === "object") {
85
+ // enrich nested fields object
86
+ enrichedQuery[key] = this.enrichQueryFields(fields[key], parameters);
87
+ }
88
+ else if (typeof fields[key] === "string") {
89
+ // enrich string value as normal
90
+ let value = fields[key];
91
+ // add triple brace to avoid escaping e.g. '=' in cookie header
92
+ if (IS_HANDLEBARS.test(value) && !IS_TRIPLE_BRACE.test(value)) {
93
+ value = `{${value}}`;
94
+ }
95
+ enrichedQuery[key] = processStringSync(value, parameters, {
96
+ noHelpers: true,
97
+ });
98
+ }
99
+ else {
100
+ enrichedQuery[key] = fields[key];
101
+ }
102
+ }
103
+ if (enrichedQuery.json ||
104
+ enrichedQuery.customData ||
105
+ enrichedQuery.requestBody) {
106
+ try {
107
+ enrichedQuery.json = JSON.parse(enrichedQuery.json ||
108
+ enrichedQuery.customData ||
109
+ enrichedQuery.requestBody);
110
+ }
111
+ catch (err) {
112
+ // no json found, ignore
113
+ }
114
+ delete enrichedQuery.customData;
115
+ }
116
+ return enrichedQuery;
117
+ };