From cf5b7b1a996175e66c40eaaa01dc9c091174477a Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 20 Sep 2021 19:21:04 +0100 Subject: [PATCH] Fixing issues with automation steps found during testing. --- .../src/api/controllers/row/internal.js | 22 +++++++++++++--- .../server/src/automations/steps/createRow.js | 13 ++++------ .../server/src/automations/steps/deleteRow.js | 18 ++++++------- .../server/src/automations/steps/queryRows.js | 25 ++++++++----------- .../server/src/automations/steps/updateRow.js | 17 ++++++------- packages/server/src/db/inMemoryView.js | 11 +++++--- .../src/utilities/rowProcessor/index.js | 10 +++++--- 7 files changed, 65 insertions(+), 51 deletions(-) diff --git a/packages/server/src/api/controllers/row/internal.js b/packages/server/src/api/controllers/row/internal.js index 9cfca69fa8..77e3069239 100644 --- a/packages/server/src/api/controllers/row/internal.js +++ b/packages/server/src/api/controllers/row/internal.js @@ -11,6 +11,7 @@ const userController = require("../user") const { inputProcessing, outputProcessing, + processAutoColumn, } = require("../../../utilities/rowProcessor") const { FieldTypes } = require("../../../constants") const { isEqual } = require("lodash") @@ -28,11 +29,26 @@ const CALCULATION_TYPES = { async function storeResponse(ctx, db, row, oldTable, table) { row.type = "row" - const response = await db.put(row) // don't worry about rev, tables handle rev/lastID updates + // if another row has been written since processing this will + // handle the auto ID clash if (!isEqual(oldTable, table)) { - await db.put(table) + try { + await db.put(table) + } catch (err) { + if (err.status === 409) { + const updatedTable = await db.get(table._id) + let response = processAutoColumn(null, updatedTable, row, { + reprocessing: true, + }) + await db.put(response.table) + row = response.row + } else { + throw err + } + } } + const response = await db.put(row) row._rev = response.rev // process the row before return, to include relationships row = await outputProcessing(ctx, table, row, { squash: false }) @@ -182,7 +198,7 @@ exports.fetchView = async ctx => { } let response // TODO: make sure not self hosted in Cloud - if (!env.SELF_HOSTED) { + if (env.SELF_HOSTED) { response = await db.query(`database/${viewName}`, { include_docs: !calculation, group: !!group, diff --git a/packages/server/src/automations/steps/createRow.js b/packages/server/src/automations/steps/createRow.js index 9706126438..9033004578 100644 --- a/packages/server/src/automations/steps/createRow.js +++ b/packages/server/src/automations/steps/createRow.js @@ -2,6 +2,7 @@ const rowController = require("../../api/controllers/row") const automationUtils = require("../automationUtils") const env = require("../../environment") const usage = require("../../utilities/usageQuota") +const { buildCtx } = require("./utils") exports.definition = { name: "Create Row", @@ -69,16 +70,12 @@ exports.run = async function ({ inputs, appId, apiKey, emitter }) { } } // have to clean up the row, remove the table from it - const ctx = { + const ctx = buildCtx(appId, emitter, { + body: inputs.row, params: { tableId: inputs.row.tableId, }, - request: { - body: inputs.row, - }, - appId, - eventEmitter: emitter, - } + }) try { inputs.row = await automationUtils.cleanUpRow( @@ -86,7 +83,7 @@ exports.run = async function ({ inputs, appId, apiKey, emitter }) { inputs.row.tableId, inputs.row ) - if (env.isProd()) { + if (env.USE_QUOTAS) { await usage.update(apiKey, usage.Properties.ROW, 1) } await rowController.save(ctx) diff --git a/packages/server/src/automations/steps/deleteRow.js b/packages/server/src/automations/steps/deleteRow.js index 26623d628b..0f9648cc51 100644 --- a/packages/server/src/automations/steps/deleteRow.js +++ b/packages/server/src/automations/steps/deleteRow.js @@ -1,6 +1,7 @@ const rowController = require("../../api/controllers/row") const env = require("../../environment") const usage = require("../../utilities/usageQuota") +const { buildCtx } = require("./utils") exports.definition = { description: "Delete a row from your database", @@ -60,19 +61,16 @@ exports.run = async function ({ inputs, appId, apiKey, emitter }) { }, } } - let ctx = { + + let ctx = buildCtx(appId, emitter, { + body: { + _id: inputs.id, + _rev: inputs.revision, + }, params: { tableId: inputs.tableId, }, - request: { - body: { - _id: inputs.id, - _rev: inputs.revision, - }, - }, - appId, - eventEmitter: emitter, - } + }) try { if (env.isProd()) { diff --git a/packages/server/src/automations/steps/queryRows.js b/packages/server/src/automations/steps/queryRows.js index 64b757418e..3c4bb422a0 100644 --- a/packages/server/src/automations/steps/queryRows.js +++ b/packages/server/src/automations/steps/queryRows.js @@ -1,6 +1,7 @@ const rowController = require("../../api/controllers/row") const tableController = require("../../api/controllers/table") const { FieldTypes } = require("../../constants") +const { buildCtx } = require("./utils") const SortOrders = { ASCENDING: "ascending", @@ -70,12 +71,11 @@ exports.definition = { } async function getTable(appId, tableId) { - const ctx = { + const ctx = buildCtx(appId, null, { params: { id: tableId, }, - appId, - } + }) await tableController.find(ctx) return ctx.body } @@ -89,21 +89,18 @@ exports.run = async function ({ inputs, appId }) { sortType = fieldType === FieldTypes.NUMBER ? FieldTypes.NUMBER : FieldTypes.STRING } - const ctx = { + const ctx = buildCtx(appId, null, { params: { tableId, }, - request: { - body: { - sortOrder, - sortType, - sort: sortColumn, - query: filters || {}, - limit, - }, + body: { + sortOrder, + sortType, + sort: sortColumn, + query: filters || {}, + limit, }, - appId, - } + }) try { await rowController.search(ctx) return { diff --git a/packages/server/src/automations/steps/updateRow.js b/packages/server/src/automations/steps/updateRow.js index ac5eb16fcd..94f77bc801 100644 --- a/packages/server/src/automations/steps/updateRow.js +++ b/packages/server/src/automations/steps/updateRow.js @@ -1,5 +1,6 @@ const rowController = require("../../api/controllers/row") const automationUtils = require("../automationUtils") +const { buildCtx } = require("./utils") exports.definition = { name: "Update Row", @@ -72,19 +73,15 @@ exports.run = async function ({ inputs, appId, emitter }) { } // have to clean up the row, remove the table from it - const ctx = { + const ctx = buildCtx(appId, emitter, { + body: { + ...inputs.row, + _id: inputs.rowId, + }, params: { rowId: inputs.rowId, }, - request: { - body: { - ...inputs.row, - _id: inputs.rowId, - }, - }, - appId, - eventEmitter: emitter, - } + }) try { inputs.row = await automationUtils.cleanUpRowById( diff --git a/packages/server/src/db/inMemoryView.js b/packages/server/src/db/inMemoryView.js index f82b418b7f..d2dd22161e 100644 --- a/packages/server/src/db/inMemoryView.js +++ b/packages/server/src/db/inMemoryView.js @@ -17,9 +17,14 @@ exports.runView = async (appId, view, calculation, group, data) => { runner: view, }, }) - // write all the docs to the in memory Pouch - await db.bulkDocs(data) - const response = await db.query(`database/runner`, { + // write all the docs to the in memory Pouch (remove revs) + await db.bulkDocs( + data.map(row => ({ + ...row, + _rev: undefined, + })) + ) + const response = await db.query("database/runner", { include_docs: !calculation, group: !!group, }) diff --git a/packages/server/src/utilities/rowProcessor/index.js b/packages/server/src/utilities/rowProcessor/index.js index bb4ac98bb7..4e1571892d 100644 --- a/packages/server/src/utilities/rowProcessor/index.js +++ b/packages/server/src/utilities/rowProcessor/index.js @@ -89,10 +89,11 @@ const TYPE_TRANSFORM_MAP = { * @param {Object} user The user to be used for an appId as well as the createdBy and createdAt fields. * @param {Object} table The table which is to be used for the schema, as well as handling auto IDs incrementing. * @param {Object} row The row which is to be updated with information for the auto columns. + * @param {Object} opts specific options for function to carry out optional features. * @returns {{row: Object, table: Object}} The updated row and table, the table may need to be updated * for automatic ID purposes. */ -function processAutoColumn(user, table, row) { +function processAutoColumn(user, table, row, opts = { reprocessing: false }) { let now = new Date().toISOString() // if a row doesn't have a revision then it doesn't exist yet const creating = !row._rev @@ -102,7 +103,7 @@ function processAutoColumn(user, table, row) { } switch (schema.subtype) { case AutoFieldSubTypes.CREATED_BY: - if (creating) { + if (creating && !opts.reprocessing) { row[key] = [user.userId] } break @@ -112,7 +113,9 @@ function processAutoColumn(user, table, row) { } break case AutoFieldSubTypes.UPDATED_BY: - row[key] = [user.userId] + if (!opts.reprocessing) { + row[key] = [user.userId] + } break case AutoFieldSubTypes.UPDATED_AT: row[key] = now @@ -127,6 +130,7 @@ function processAutoColumn(user, table, row) { } return { table, row } } +exports.processAutoColumn = processAutoColumn /** * This will coerce a value to the correct types based on the type transform map