@budibase/server 2.4.40 → 2.4.42-alpha.0

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 (75) hide show
  1. package/__mocks__/node-fetch.ts +6 -1
  2. package/builder/assets/index.3cb1022d.css +6 -0
  3. package/builder/assets/{index.07ed2ead.js → index.4b6c8c0e.js} +384 -383
  4. package/builder/index.html +7 -7
  5. package/dist/api/controllers/application.js +28 -24
  6. package/dist/api/controllers/datasource.js +2 -1
  7. package/dist/api/controllers/public/metrics.js +113 -0
  8. package/dist/api/controllers/row/external.js +16 -8
  9. package/dist/api/controllers/row/index.js +11 -1
  10. package/dist/api/controllers/row/internal.js +1 -10
  11. package/dist/api/controllers/row/utils.js +7 -7
  12. package/dist/api/controllers/static/index.js +84 -24
  13. package/dist/api/controllers/static/templates/BudibaseApp.svelte +33 -10
  14. package/dist/api/controllers/table/external.js +16 -12
  15. package/dist/api/controllers/table/utils.js +15 -1
  16. package/dist/api/routes/public/index.js +8 -0
  17. package/dist/api/routes/public/metrics.js +30 -0
  18. package/dist/app.js +1 -0
  19. package/dist/constants/index.js +2 -1
  20. package/dist/integrations/googlesheets.js +125 -59
  21. package/dist/integrations/redis.js +1 -1
  22. package/dist/integrations/utils.js +17 -2
  23. package/dist/package.json +13 -12
  24. package/dist/sdk/users/utils.js +2 -1
  25. package/dist/tsconfig.build.tsbuildinfo +1 -1
  26. package/dist/utilities/global.js +17 -7
  27. package/jest.config.ts +1 -0
  28. package/package.json +14 -13
  29. package/scripts/test.sh +3 -3
  30. package/specs/openapi.json +39 -0
  31. package/specs/openapi.yaml +169 -0
  32. package/specs/resources/application.ts +11 -0
  33. package/specs/resources/index.ts +2 -0
  34. package/specs/resources/metrics.ts +81 -0
  35. package/src/api/controllers/application.ts +20 -21
  36. package/src/api/controllers/datasource.ts +2 -1
  37. package/src/api/controllers/public/metrics.ts +251 -0
  38. package/src/api/controllers/row/external.ts +26 -16
  39. package/src/api/controllers/row/index.ts +12 -2
  40. package/src/api/controllers/row/internal.ts +0 -7
  41. package/src/api/controllers/row/utils.ts +7 -7
  42. package/src/api/controllers/static/index.ts +69 -26
  43. package/src/api/controllers/static/templates/BudibaseApp.svelte +33 -10
  44. package/src/api/controllers/table/external.ts +24 -17
  45. package/src/api/controllers/table/index.ts +9 -9
  46. package/src/api/controllers/table/utils.ts +18 -2
  47. package/src/api/controllers/view/tests/__snapshots__/viewBuilder.spec.js.snap +48 -48
  48. package/src/api/routes/public/index.ts +10 -1
  49. package/src/api/routes/public/metrics.ts +28 -0
  50. package/src/api/routes/public/tests/metrics.spec.js +34 -0
  51. package/src/api/routes/tests/__snapshots__/datasource.spec.ts.snap +22 -22
  52. package/src/api/routes/tests/__snapshots__/view.spec.js.snap +5 -5
  53. package/src/api/routes/tests/appSync.spec.ts +31 -0
  54. package/src/api/routes/tests/internalSearch.spec.js +8 -7
  55. package/src/app.ts +2 -1
  56. package/src/automations/automationUtils.ts +1 -1
  57. package/src/automations/tests/automation.spec.ts +99 -0
  58. package/src/constants/index.ts +1 -0
  59. package/src/definitions/openapi.ts +15 -0
  60. package/src/integration-test/postgres.spec.ts +46 -52
  61. package/src/integrations/googlesheets.ts +143 -71
  62. package/src/integrations/redis.ts +1 -1
  63. package/src/integrations/tests/googlesheets.spec.ts +13 -13
  64. package/src/integrations/tests/redis.spec.ts +9 -5
  65. package/src/integrations/utils.ts +16 -4
  66. package/src/middleware/currentapp.ts +2 -2
  67. package/src/sdk/users/utils.ts +4 -1
  68. package/src/tests/jestEnv.ts +1 -0
  69. package/src/tests/jestSetup.ts +5 -1
  70. package/src/tests/utilities/TestConfiguration.ts +13 -0
  71. package/src/tests/utilities/structures.ts +13 -1
  72. package/src/utilities/global.ts +21 -9
  73. package/builder/assets/favicon.e7fc7733.png +0 -0
  74. package/builder/assets/index.b0e3aca6.css +0 -6
  75. package/src/automations/tests/automation.spec.js +0 -84
@@ -150,16 +150,16 @@ function isRelationshipSetup(column) {
150
150
  }
151
151
  function save(ctx) {
152
152
  return __awaiter(this, void 0, void 0, function* () {
153
- const table = ctx.request.body;
154
- const renamed = table === null || table === void 0 ? void 0 : table._rename;
153
+ const inputs = ctx.request.body;
154
+ const renamed = inputs === null || inputs === void 0 ? void 0 : inputs._rename;
155
155
  // can't do this right now
156
- delete table.rows;
156
+ delete inputs.rows;
157
157
  const datasourceId = getDatasourceId(ctx.request.body);
158
158
  // table doesn't exist already, note that it is created
159
- if (!table._id) {
160
- table.created = true;
159
+ if (!inputs._id) {
160
+ inputs.created = true;
161
161
  }
162
- let tableToSave = Object.assign({ type: "table", _id: (0, utils_1.buildExternalTableId)(datasourceId, table.name) }, table);
162
+ let tableToSave = Object.assign({ type: "table", _id: (0, utils_1.buildExternalTableId)(datasourceId, inputs.name) }, inputs);
163
163
  let oldTable;
164
164
  if (ctx.request.body && ctx.request.body._id) {
165
165
  oldTable = yield sdk_1.default.tables.getTable(ctx.request.body._id);
@@ -172,6 +172,8 @@ function save(ctx) {
172
172
  if (!datasource.entities) {
173
173
  datasource.entities = {};
174
174
  }
175
+ // GSheets is a specific case - only ever has a static primary key
176
+ tableToSave = (0, utils_2.setStaticSchemas)(datasource, tableToSave);
175
177
  const oldTables = cloneDeep(datasource.entities);
176
178
  const tables = datasource.entities;
177
179
  const extraTablesToUpdate = [];
@@ -187,7 +189,7 @@ function save(ctx) {
187
189
  const relatedColumnName = schema.fieldName;
188
190
  const relationType = schema.relationshipType;
189
191
  if (relationType === types_1.RelationshipTypes.MANY_TO_MANY) {
190
- const junctionTable = generateManyLinkSchema(datasource, schema, table, relatedTable);
192
+ const junctionTable = generateManyLinkSchema(datasource, schema, tableToSave, relatedTable);
191
193
  if (tables[junctionTable.name]) {
192
194
  throw "Junction table already exists, cannot create another relationship.";
193
195
  }
@@ -195,8 +197,10 @@ function save(ctx) {
195
197
  extraTablesToUpdate.push(junctionTable);
196
198
  }
197
199
  else {
198
- const fkTable = relationType === types_1.RelationshipTypes.ONE_TO_MANY ? table : relatedTable;
199
- const foreignKey = generateLinkSchema(schema, table, relatedTable, relationType);
200
+ const fkTable = relationType === types_1.RelationshipTypes.ONE_TO_MANY
201
+ ? tableToSave
202
+ : relatedTable;
203
+ const foreignKey = generateLinkSchema(schema, tableToSave, relatedTable, relationType);
200
204
  fkTable.schema[foreignKey] = (0, utils_2.foreignKeyStructure)(foreignKey);
201
205
  if (fkTable.constrained == null) {
202
206
  fkTable.constrained = [];
@@ -205,11 +209,11 @@ function save(ctx) {
205
209
  fkTable.constrained.push(foreignKey);
206
210
  }
207
211
  // foreign key is in other table, need to save it to external
208
- if (fkTable._id !== table._id) {
212
+ if (fkTable._id !== tableToSave._id) {
209
213
  extraTablesToUpdate.push(fkTable);
210
214
  }
211
215
  }
212
- generateRelatedSchema(schema, relatedTable, table, relatedColumnName);
216
+ generateRelatedSchema(schema, relatedTable, tableToSave, relatedColumnName);
213
217
  schema.main = true;
214
218
  }
215
219
  cleanupRelationships(tableToSave, tables, oldTable);
@@ -264,7 +268,7 @@ function bulkImport(ctx) {
264
268
  if (!rows || !(0, schema_1.isRows)(rows) || !(0, schema_1.isSchema)(schema)) {
265
269
  ctx.throw(400, "Provided data import information is invalid.");
266
270
  }
267
- const parsedRows = yield (0, schema_1.parse)(rows, schema);
271
+ const parsedRows = (0, schema_1.parse)(rows, schema);
268
272
  yield (0, external_1.handleRequest)(types_1.Operation.BULK_CREATE, table._id, {
269
273
  rows: parsedRows,
270
274
  });
@@ -12,7 +12,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.TableSaveFunctions = exports.hasTypeChanged = exports.areSwitchableTypes = exports.foreignKeyStructure = exports.generateJunctionTableName = exports.generateForeignKey = exports.checkForViewUpdates = exports.checkStaticTables = exports.handleSearchIndexes = exports.handleDataImport = exports.importToRows = exports.makeSureTableUpToDate = exports.checkForColumnUpdates = exports.clearColumns = void 0;
15
+ exports.TableSaveFunctions = exports.setStaticSchemas = exports.hasTypeChanged = exports.areSwitchableTypes = exports.foreignKeyStructure = exports.generateJunctionTableName = exports.generateForeignKey = exports.checkForViewUpdates = exports.checkStaticTables = exports.handleSearchIndexes = exports.handleDataImport = exports.importToRows = exports.makeSureTableUpToDate = exports.checkForColumnUpdates = exports.clearColumns = void 0;
16
16
  const schema_1 = require("../../../utilities/schema");
17
17
  const utils_1 = require("../../../db/utils");
18
18
  const lodash_1 = require("lodash");
@@ -24,6 +24,7 @@ const viewBuilder_1 = __importDefault(require("../view/viewBuilder"));
24
24
  const fp_1 = require("lodash/fp");
25
25
  const pro_1 = require("@budibase/pro");
26
26
  const backend_core_1 = require("@budibase/backend-core");
27
+ const types_1 = require("@budibase/types");
27
28
  function clearColumns(table, columnNames) {
28
29
  return __awaiter(this, void 0, void 0, function* () {
29
30
  const db = backend_core_1.context.getAppDB();
@@ -356,5 +357,18 @@ function hasTypeChanged(table, oldTable) {
356
357
  return false;
357
358
  }
358
359
  exports.hasTypeChanged = hasTypeChanged;
360
+ // used for external tables, some of them will have static schemas that need
361
+ // to be hard set
362
+ function setStaticSchemas(datasource, table) {
363
+ var _a;
364
+ // GSheets is a specific case - only ever has a static primary key
365
+ if (table && datasource.source === types_1.SourceName.GOOGLE_SHEETS) {
366
+ table.primary = [constants_1.GOOGLE_SHEETS_PRIMARY_KEY];
367
+ // if there is an id column, remove it, should never exist in GSheets
368
+ (_a = table.schema) === null || _a === void 0 ? true : delete _a.id;
369
+ }
370
+ return table;
371
+ }
372
+ exports.setStaticSchemas = setStaticSchemas;
359
373
  const _TableSaveFunctions = TableSaveFunctions;
360
374
  exports.TableSaveFunctions = _TableSaveFunctions;
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.shutdown = void 0;
7
7
  const applications_1 = __importDefault(require("./applications"));
8
+ const metrics_1 = __importDefault(require("./metrics"));
8
9
  const queries_1 = __importDefault(require("./queries"));
9
10
  const tables_1 = __importDefault(require("./tables"));
10
11
  const rows_1 = __importDefault(require("./rows"));
@@ -86,6 +87,12 @@ function addToRouter(endpoints) {
86
87
  }
87
88
  }
88
89
  }
90
+ function applyAdminRoutes(endpoints) {
91
+ addMiddleware(endpoints.read, backend_core_1.middleware.builderOrAdmin);
92
+ addMiddleware(endpoints.write, backend_core_1.middleware.builderOrAdmin);
93
+ addToRouter(endpoints.read);
94
+ addToRouter(endpoints.write);
95
+ }
89
96
  function applyRoutes(endpoints, permType, resource, subResource) {
90
97
  const paramMiddleware = subResource
91
98
  ? (0, resourceId_1.paramSubResource)(resource, subResource)
@@ -107,6 +114,7 @@ function applyRoutes(endpoints, permType, resource, subResource) {
107
114
  addToRouter(endpoints.read);
108
115
  addToRouter(endpoints.write);
109
116
  }
117
+ applyAdminRoutes(metrics_1.default);
110
118
  applyRoutes(applications_1.default, PermissionType.APP, "appId");
111
119
  applyRoutes(tables_1.default, PermissionType.TABLE, "tableId");
112
120
  applyRoutes(users_1.default, PermissionType.USER, "userId");
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const metrics_1 = __importDefault(require("../../controllers/public/metrics"));
7
+ const Endpoint_1 = __importDefault(require("./utils/Endpoint"));
8
+ const read = [];
9
+ /**
10
+ * @openapi
11
+ * /metrics:
12
+ * get:
13
+ * operationId: metricsGet
14
+ * summary: Retrieve Budibase tenant metrics
15
+ * description: Output metrics in OpenMetrics format compatible with Prometheus
16
+ * tags:
17
+ * - metrics
18
+ * responses:
19
+ * 200:
20
+ * description: Returns tenant metrics.
21
+ * content:
22
+ * text/plain:
23
+ * schema:
24
+ * type: string
25
+ * examples:
26
+ * metrics:
27
+ * $ref: '#/components/examples/metrics'
28
+ */
29
+ read.push(new Endpoint_1.default("get", "/metrics", metrics_1.default.fetch));
30
+ exports.default = { read };
package/dist/app.js CHANGED
@@ -105,6 +105,7 @@ server.on("close", () => __awaiter(void 0, void 0, void 0, function* () {
105
105
  }
106
106
  shuttingDown = true;
107
107
  console.log("Server Closed");
108
+ backend_core_1.timers.cleanup();
108
109
  yield automations.shutdown();
109
110
  yield redis.shutdown();
110
111
  backend_core_1.events.shutdown();
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MAX_AUTOMATION_RECURRING_ERRORS = exports.ObjectStoreBuckets = exports.AutomationErrors = exports.BuildSchemaErrors = exports.InvalidColumns = exports.MetadataTypes = exports.BaseQueryVerbs = exports.OBJ_STORE_DIRECTORY = exports.AutoFieldDefaultNames = exports.AutoFieldSubTypes = exports.USERS_TABLE_SCHEMA = exports.SortDirection = exports.DatasourceAuthTypes = exports.DataSourceOperation = exports.AuthTypes = exports.FormulaTypes = exports.SwitchableTypes = exports.CanSwitchTypes = exports.NoEmptyFilterStrings = exports.FilterTypes = exports.RelationshipTypes = exports.FieldTypes = void 0;
3
+ exports.GOOGLE_SHEETS_PRIMARY_KEY = exports.MAX_AUTOMATION_RECURRING_ERRORS = exports.ObjectStoreBuckets = exports.AutomationErrors = exports.BuildSchemaErrors = exports.InvalidColumns = exports.MetadataTypes = exports.BaseQueryVerbs = exports.OBJ_STORE_DIRECTORY = exports.AutoFieldDefaultNames = exports.AutoFieldSubTypes = exports.USERS_TABLE_SCHEMA = exports.SortDirection = exports.DatasourceAuthTypes = exports.DataSourceOperation = exports.AuthTypes = exports.FormulaTypes = exports.SwitchableTypes = exports.CanSwitchTypes = exports.NoEmptyFilterStrings = exports.FilterTypes = exports.RelationshipTypes = exports.FieldTypes = void 0;
4
4
  const backend_core_1 = require("@budibase/backend-core");
5
5
  var types_1 = require("@budibase/types");
6
6
  Object.defineProperty(exports, "FieldTypes", { enumerable: true, get: function () { return types_1.FieldType; } });
@@ -178,3 +178,4 @@ var AutomationErrors;
178
178
  // pass through the list from the auth/core lib
179
179
  exports.ObjectStoreBuckets = backend_core_1.objectStore.ObjectStoreBuckets;
180
180
  exports.MAX_AUTOMATION_RECURRING_ERRORS = 5;
181
+ exports.GOOGLE_SHEETS_PRIMARY_KEY = "rowNumber";
@@ -15,11 +15,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const types_1 = require("@budibase/types");
16
16
  const google_auth_library_1 = require("google-auth-library");
17
17
  const utils_1 = require("./utils");
18
- const constants_1 = require("../constants");
19
18
  const google_spreadsheet_1 = require("google-spreadsheet");
20
19
  const node_fetch_1 = __importDefault(require("node-fetch"));
21
20
  const backend_core_1 = require("@budibase/backend-core");
22
21
  const shared_core_1 = require("@budibase/shared-core");
22
+ const constants_1 = require("../constants");
23
+ const ALLOWED_TYPES = [
24
+ types_1.FieldType.STRING,
25
+ types_1.FieldType.FORMULA,
26
+ types_1.FieldType.NUMBER,
27
+ types_1.FieldType.LONGFORM,
28
+ types_1.FieldType.DATETIME,
29
+ types_1.FieldType.OPTIONS,
30
+ types_1.FieldType.BOOLEAN,
31
+ types_1.FieldType.BARCODEQR,
32
+ ];
23
33
  const SCHEMA = {
24
34
  plus: true,
25
35
  auth: {
@@ -135,6 +145,7 @@ class GoogleSheetsIntegration {
135
145
  });
136
146
  }
137
147
  connect() {
148
+ var _a;
138
149
  return __awaiter(this, void 0, void 0, function* () {
139
150
  try {
140
151
  // Initialise oAuth client
@@ -159,12 +170,36 @@ class GoogleSheetsIntegration {
159
170
  yield this.client.loadInfo();
160
171
  }
161
172
  catch (err) {
173
+ // this happens for xlsx imports
174
+ if ((_a = err.message) === null || _a === void 0 ? void 0 : _a.includes("operation is not supported")) {
175
+ err.message =
176
+ "This operation is not supported - XLSX sheets must be converted.";
177
+ }
162
178
  console.error("Error connecting to google sheets", err);
163
179
  throw err;
164
180
  }
165
181
  });
166
182
  }
167
- buildSchema(datasourceId) {
183
+ getTableSchema(title, headerValues, id) {
184
+ // base table
185
+ const table = {
186
+ name: title,
187
+ primary: [constants_1.GOOGLE_SHEETS_PRIMARY_KEY],
188
+ schema: {},
189
+ };
190
+ if (id) {
191
+ table._id = id;
192
+ }
193
+ // build schema from headers
194
+ for (let header of headerValues) {
195
+ table.schema[header] = {
196
+ name: header,
197
+ type: types_1.FieldType.STRING,
198
+ };
199
+ }
200
+ return table;
201
+ }
202
+ buildSchema(datasourceId, entities) {
168
203
  return __awaiter(this, void 0, void 0, function* () {
169
204
  yield this.connect();
170
205
  const sheets = this.client.sheetsByIndex;
@@ -172,56 +207,47 @@ class GoogleSheetsIntegration {
172
207
  for (let sheet of sheets) {
173
208
  // must fetch rows to determine schema
174
209
  yield sheet.getRows();
175
- // build schema
176
- const schema = {};
177
- // build schema from headers
178
- for (let header of sheet.headerValues) {
179
- schema[header] = {
180
- name: header,
181
- type: constants_1.FieldTypes.STRING,
182
- };
183
- }
184
- // create tables
185
- tables[sheet.title] = {
186
- _id: (0, utils_1.buildExternalTableId)(datasourceId, sheet.title),
187
- name: sheet.title,
188
- primary: ["rowNumber"],
189
- schema,
190
- };
210
+ const id = (0, utils_1.buildExternalTableId)(datasourceId, sheet.title);
211
+ tables[sheet.title] = this.getTableSchema(sheet.title, sheet.headerValues, id);
191
212
  }
192
- this.tables = tables;
213
+ const final = (0, utils_1.finaliseExternalTables)(tables, entities);
214
+ this.tables = final.tables;
215
+ this.schemaErrors = final.errors;
193
216
  });
194
217
  }
195
218
  query(json) {
219
+ var _a, _b, _c, _d, _e, _f, _g, _h;
196
220
  return __awaiter(this, void 0, void 0, function* () {
197
221
  const sheet = json.endpoint.entityId;
198
- const handlers = {
199
- [constants_1.DataSourceOperation.CREATE]: () => this.create({ sheet, row: json.body }),
200
- [constants_1.DataSourceOperation.READ]: () => this.read(Object.assign(Object.assign({}, json), { sheet })),
201
- [constants_1.DataSourceOperation.UPDATE]: () => {
202
- var _a, _b, _c;
222
+ switch (json.endpoint.operation) {
223
+ case types_1.Operation.CREATE:
224
+ return this.create({ sheet, row: json.body });
225
+ case types_1.Operation.BULK_CREATE:
226
+ return this.createBulk({ sheet, rows: json.body });
227
+ case types_1.Operation.READ:
228
+ return this.read(Object.assign(Object.assign({}, json), { sheet }));
229
+ case types_1.Operation.UPDATE:
203
230
  return this.update({
204
231
  // exclude the header row and zero index
205
232
  rowIndex: ((_c = (_b = (_a = json.extra) === null || _a === void 0 ? void 0 : _a.idFilter) === null || _b === void 0 ? void 0 : _b.equal) === null || _c === void 0 ? void 0 : _c.rowNumber) - 2,
206
233
  sheet,
207
234
  row: json.body,
208
235
  });
209
- },
210
- [constants_1.DataSourceOperation.DELETE]: () => {
211
- var _a, _b, _c;
236
+ case types_1.Operation.DELETE:
212
237
  return this.delete({
213
238
  // exclude the header row and zero index
214
- rowIndex: ((_c = (_b = (_a = json.extra) === null || _a === void 0 ? void 0 : _a.idFilter) === null || _b === void 0 ? void 0 : _b.equal) === null || _c === void 0 ? void 0 : _c.rowNumber) - 2,
239
+ rowIndex: ((_f = (_e = (_d = json.extra) === null || _d === void 0 ? void 0 : _d.idFilter) === null || _e === void 0 ? void 0 : _e.equal) === null || _f === void 0 ? void 0 : _f.rowNumber) - 2,
215
240
  sheet,
216
241
  });
217
- },
218
- [constants_1.DataSourceOperation.CREATE_TABLE]: () => { var _a; return this.createTable((_a = json === null || json === void 0 ? void 0 : json.table) === null || _a === void 0 ? void 0 : _a.name); },
219
- [constants_1.DataSourceOperation.UPDATE_TABLE]: () => this.updateTable(json.table),
220
- [constants_1.DataSourceOperation.DELETE_TABLE]: () => { var _a; return this.deleteTable((_a = json === null || json === void 0 ? void 0 : json.table) === null || _a === void 0 ? void 0 : _a.name); },
221
- };
222
- // @ts-ignore
223
- const internalQueryMethod = handlers[json.endpoint.operation];
224
- return yield internalQueryMethod();
242
+ case types_1.Operation.CREATE_TABLE:
243
+ return this.createTable((_g = json === null || json === void 0 ? void 0 : json.table) === null || _g === void 0 ? void 0 : _g.name);
244
+ case types_1.Operation.UPDATE_TABLE:
245
+ return this.updateTable(json.table);
246
+ case types_1.Operation.DELETE_TABLE:
247
+ return this.deleteTable((_h = json === null || json === void 0 ? void 0 : json.table) === null || _h === void 0 ? void 0 : _h.name);
248
+ default:
249
+ throw new Error(`GSheets integration does not support "${json.endpoint.operation}".`);
250
+ }
225
251
  });
226
252
  }
227
253
  buildRowObject(headers, values, rowNumber) {
@@ -234,9 +260,12 @@ class GoogleSheetsIntegration {
234
260
  }
235
261
  createTable(name) {
236
262
  return __awaiter(this, void 0, void 0, function* () {
263
+ if (!name) {
264
+ throw new Error("Must provide name for new sheet.");
265
+ }
237
266
  try {
238
267
  yield this.connect();
239
- return yield this.client.addSheet({ title: name, headerValues: ["test"] });
268
+ return yield this.client.addSheet({ title: name, headerValues: [name] });
240
269
  }
241
270
  catch (err) {
242
271
  console.error("Error creating new table in google sheets", err);
@@ -246,34 +275,53 @@ class GoogleSheetsIntegration {
246
275
  }
247
276
  updateTable(table) {
248
277
  return __awaiter(this, void 0, void 0, function* () {
249
- try {
250
- yield this.connect();
251
- const sheet = this.client.sheetsByTitle[table.name];
252
- yield sheet.loadHeaderRow();
253
- if (table._rename) {
254
- const headers = [];
255
- for (let header of sheet.headerValues) {
256
- if (header === table._rename.old) {
257
- headers.push(table._rename.updated);
258
- }
259
- else {
260
- headers.push(header);
261
- }
278
+ yield this.connect();
279
+ const sheet = this.client.sheetsByTitle[table.name];
280
+ yield sheet.loadHeaderRow();
281
+ if (table._rename) {
282
+ const headers = [];
283
+ for (let header of sheet.headerValues) {
284
+ if (header === table._rename.old) {
285
+ headers.push(table._rename.updated);
286
+ }
287
+ else {
288
+ headers.push(header);
262
289
  }
290
+ }
291
+ try {
263
292
  yield sheet.setHeaderRow(headers);
264
293
  }
265
- else {
266
- const updatedHeaderValues = [...sheet.headerValues];
267
- const newField = Object.keys(table.schema).find(key => !sheet.headerValues.includes(key));
268
- if (newField) {
269
- updatedHeaderValues.push(newField);
294
+ catch (err) {
295
+ console.error("Error updating column name in google sheets", err);
296
+ throw err;
297
+ }
298
+ }
299
+ else {
300
+ const updatedHeaderValues = [...sheet.headerValues];
301
+ // add new column - doesn't currently exist
302
+ for (let [key, column] of Object.entries(table.schema)) {
303
+ if (!ALLOWED_TYPES.includes(column.type)) {
304
+ throw new Error(`Column type: ${column.type} not allowed for GSheets integration.`);
305
+ }
306
+ if (!sheet.headerValues.includes(key) &&
307
+ column.type !== types_1.FieldType.FORMULA) {
308
+ updatedHeaderValues.push(key);
309
+ }
310
+ }
311
+ // clear out deleted columns
312
+ for (let key of sheet.headerValues) {
313
+ if (!Object.keys(table.schema).includes(key)) {
314
+ const idx = updatedHeaderValues.indexOf(key);
315
+ updatedHeaderValues.splice(idx, 1);
270
316
  }
317
+ }
318
+ try {
271
319
  yield sheet.setHeaderRow(updatedHeaderValues);
272
320
  }
273
- }
274
- catch (err) {
275
- console.error("Error updating table in google sheets", err);
276
- throw err;
321
+ catch (err) {
322
+ console.error("Error updating table in google sheets", err);
323
+ throw err;
324
+ }
277
325
  }
278
326
  });
279
327
  }
@@ -307,6 +355,24 @@ class GoogleSheetsIntegration {
307
355
  }
308
356
  });
309
357
  }
358
+ createBulk(query) {
359
+ return __awaiter(this, void 0, void 0, function* () {
360
+ try {
361
+ yield this.connect();
362
+ const sheet = this.client.sheetsByTitle[query.sheet];
363
+ let rowsToInsert = [];
364
+ for (let row of query.rows) {
365
+ rowsToInsert.push(typeof row === "string" ? JSON.parse(row) : row);
366
+ }
367
+ const rows = yield sheet.addRows(rowsToInsert);
368
+ return rows.map(row => this.buildRowObject(sheet.headerValues, row._rawData, row._rowNumber));
369
+ }
370
+ catch (err) {
371
+ console.error("Error bulk writing to google sheets", err);
372
+ throw err;
373
+ }
374
+ });
375
+ }
310
376
  read(query) {
311
377
  return __awaiter(this, void 0, void 0, function* () {
312
378
  try {
@@ -94,7 +94,7 @@ class RedisIntegration {
94
94
  }
95
95
  disconnect() {
96
96
  return __awaiter(this, void 0, void 0, function* () {
97
- return this.client.disconnect();
97
+ return this.client.quit();
98
98
  });
99
99
  }
100
100
  redisContext(query) {
@@ -1,11 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.finaliseExternalTables = exports.isIsoDateString = exports.isSQL = exports.getSqlQuery = exports.convertSqlType = exports.breakRowIdField = exports.convertRowId = exports.isRowId = exports.generateRowIdField = exports.breakExternalTableId = exports.buildExternalTableId = exports.isExternalTable = exports.SqlClient = void 0;
3
+ exports.finaliseExternalTables = exports.shouldCopySpecialColumn = exports.shouldCopyRelationship = exports.isIsoDateString = exports.isSQL = exports.getSqlQuery = exports.convertSqlType = exports.breakRowIdField = exports.convertRowId = exports.isRowId = exports.generateRowIdField = exports.breakExternalTableId = exports.buildExternalTableId = exports.isExternalTable = exports.SqlClient = void 0;
4
4
  const types_1 = require("@budibase/types");
5
5
  const utils_1 = require("../db/utils");
6
6
  const constants_1 = require("../constants");
7
7
  const DOUBLE_SEPARATOR = `${utils_1.SEPARATOR}${utils_1.SEPARATOR}`;
8
8
  const ROW_ID_REGEX = /^\[.*]$/g;
9
+ const ENCODED_SPACE = encodeURIComponent(" ");
9
10
  const SQL_NUMBER_TYPE_MAP = {
10
11
  integer: constants_1.FieldTypes.NUMBER,
11
12
  int: constants_1.FieldTypes.NUMBER,
@@ -67,6 +68,10 @@ function isExternalTable(tableId) {
67
68
  }
68
69
  exports.isExternalTable = isExternalTable;
69
70
  function buildExternalTableId(datasourceId, tableName) {
71
+ // encode spaces
72
+ if (tableName.includes(" ")) {
73
+ tableName = encodeURIComponent(tableName);
74
+ }
70
75
  return `${datasourceId}${DOUBLE_SEPARATOR}${tableName}`;
71
76
  }
72
77
  exports.buildExternalTableId = buildExternalTableId;
@@ -78,6 +83,10 @@ function breakExternalTableId(tableId) {
78
83
  let datasourceId = parts.shift();
79
84
  // if they need joined
80
85
  let tableName = parts.join(DOUBLE_SEPARATOR);
86
+ // if contains encoded spaces, decode it
87
+ if (tableName.includes(ENCODED_SPACE)) {
88
+ tableName = decodeURIComponent(tableName);
89
+ }
81
90
  return { datasourceId, tableName };
82
91
  }
83
92
  exports.breakExternalTableId = breakExternalTableId;
@@ -193,6 +202,7 @@ function shouldCopyRelationship(column, tableIds) {
193
202
  column.tableId &&
194
203
  tableIds.includes(column.tableId));
195
204
  }
205
+ exports.shouldCopyRelationship = shouldCopyRelationship;
196
206
  /**
197
207
  * Similar function to the shouldCopyRelationship function, but instead this looks for options and boolean
198
208
  * types. It is possible to switch a string -> options and a number -> boolean (and vice versus) need to make
@@ -217,6 +227,7 @@ function shouldCopySpecialColumn(column, fetchedColumn) {
217
227
  return (specialTypes.indexOf(column.type) !== -1 ||
218
228
  (fetchedIsNumber && column.type === constants_1.FieldTypes.BOOLEAN));
219
229
  }
230
+ exports.shouldCopySpecialColumn = shouldCopySpecialColumn;
220
231
  /**
221
232
  * Looks for columns which need to be copied over into the new table definitions, like relationships
222
233
  * and options types.
@@ -226,10 +237,14 @@ function shouldCopySpecialColumn(column, fetchedColumn) {
226
237
  * @param tableIds The IDs of the tables which exist now, to check if anything has been removed.
227
238
  */
228
239
  function copyExistingPropsOver(tableName, table, entities, tableIds) {
240
+ var _a, _b, _c;
229
241
  if (entities && entities[tableName]) {
230
- if (entities[tableName].primaryDisplay) {
242
+ if ((_a = entities[tableName]) === null || _a === void 0 ? void 0 : _a.primaryDisplay) {
231
243
  table.primaryDisplay = entities[tableName].primaryDisplay;
232
244
  }
245
+ if ((_b = entities[tableName]) === null || _b === void 0 ? void 0 : _b.created) {
246
+ table.created = (_c = entities[tableName]) === null || _c === void 0 ? void 0 : _c.created;
247
+ }
233
248
  const existingTableSchema = entities[tableName].schema;
234
249
  for (let key in existingTableSchema) {
235
250
  if (!existingTableSchema.hasOwnProperty(key)) {
package/dist/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@budibase/server",
3
3
  "email": "hi@budibase.com",
4
- "version": "2.4.39",
4
+ "version": "2.4.41",
5
5
  "description": "Budibase Web Server",
6
6
  "main": "src/index.ts",
7
7
  "repository": {
@@ -14,7 +14,8 @@
14
14
  "build:dev": "yarn prebuild && tsc --build --watch --preserveWatchOutput",
15
15
  "debug": "yarn build && node --expose-gc --inspect=9222 dist/index.js",
16
16
  "postbuild": "copyfiles -u 1 src/**/*.svelte dist/ && copyfiles -u 1 src/**/*.hbs dist/ && copyfiles -u 1 src/**/*.json dist/",
17
- "test": "bash scripts/test.sh",
17
+ "test": "NODE_OPTIONS=\"--max-old-space-size=4096\" bash scripts/test.sh",
18
+ "test:memory": "jest --maxWorkers=2 --logHeapUsage --forceExit",
18
19
  "test:watch": "jest --watch",
19
20
  "predocker": "copyfiles -f ../client/dist/budibase-client.js ../client/manifest.json client",
20
21
  "build:docker": "yarn run predocker && docker build . -t app-service --label version=$BUDIBASE_RELEASE_VERSION",
@@ -43,12 +44,12 @@
43
44
  "license": "GPL-3.0",
44
45
  "dependencies": {
45
46
  "@apidevtools/swagger-parser": "10.0.3",
46
- "@budibase/backend-core": "^2.4.39",
47
- "@budibase/client": "^2.4.39",
48
- "@budibase/pro": "2.4.39",
49
- "@budibase/shared-core": "^2.4.39",
50
- "@budibase/string-templates": "^2.4.39",
51
- "@budibase/types": "^2.4.39",
47
+ "@budibase/backend-core": "^2.4.41",
48
+ "@budibase/client": "^2.4.41",
49
+ "@budibase/pro": "2.4.40",
50
+ "@budibase/shared-core": "^2.4.41",
51
+ "@budibase/string-templates": "^2.4.41",
52
+ "@budibase/types": "^2.4.41",
52
53
  "@bull-board/api": "3.7.0",
53
54
  "@bull-board/koa": "3.9.4",
54
55
  "@elastic/elasticsearch": "7.10.0",
@@ -125,7 +126,7 @@
125
126
  "@babel/core": "7.17.4",
126
127
  "@babel/preset-env": "7.16.11",
127
128
  "@budibase/standard-components": "^0.9.139",
128
- "@jest/test-sequencer": "24.9.0",
129
+ "@jest/test-sequencer": "29.5.0",
129
130
  "@swc/core": "^1.3.25",
130
131
  "@swc/jest": "^0.2.24",
131
132
  "@trendyol/jest-testcontainers": "^2.1.1",
@@ -134,7 +135,7 @@
134
135
  "@types/global-agent": "2.1.1",
135
136
  "@types/google-spreadsheet": "3.1.5",
136
137
  "@types/ioredis": "4.28.10",
137
- "@types/jest": "27.5.1",
138
+ "@types/jest": "29.5.0",
138
139
  "@types/koa": "2.13.4",
139
140
  "@types/koa__router": "8.0.8",
140
141
  "@types/lodash": "4.14.180",
@@ -154,7 +155,7 @@
154
155
  "eslint": "6.8.0",
155
156
  "ioredis-mock": "7.2.0",
156
157
  "is-wsl": "2.2.0",
157
- "jest": "28.1.1",
158
+ "jest": "29.5.0",
158
159
  "jest-openapi": "0.14.2",
159
160
  "jest-serial-runner": "^1.2.1",
160
161
  "nodemon": "2.0.15",
@@ -166,7 +167,7 @@
166
167
  "supertest": "6.2.2",
167
168
  "swagger-jsdoc": "6.1.0",
168
169
  "timekeeper": "2.2.0",
169
- "ts-jest": "28.0.4",
170
+ "ts-jest": "29.0.5",
170
171
  "ts-node": "10.8.1",
171
172
  "tsconfig-paths": "4.0.0",
172
173
  "typescript": "4.7.3",
@@ -16,7 +16,8 @@ const utils_1 = require("../../db/utils");
16
16
  const lodash_1 = require("lodash");
17
17
  function combineMetadataAndUser(user, metadata) {
18
18
  // skip users with no access
19
- if (user.roleId === backend_core_1.roles.BUILTIN_ROLE_IDS.PUBLIC) {
19
+ if (user.roleId == null ||
20
+ user.roleId === backend_core_1.roles.BUILTIN_ROLE_IDS.PUBLIC) {
20
21
  return null;
21
22
  }
22
23
  delete user._rev;