From feb7e2756fe47690b39ac7bea5381a7107442554 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 10 Feb 2021 16:10:39 +0000 Subject: [PATCH 01/27] WIP - start of auto columns like autonumber, createdBy, createdAt, updatedBy etc. --- .../modals/CreateTableModal.svelte | 56 ++++++++- packages/server/src/api/controllers/row.js | 17 +-- packages/server/src/api/routes/table.js | 23 ++++ packages/server/src/automations/triggers.js | 6 +- packages/server/src/utilities/index.js | 119 ------------------ packages/server/src/utilities/rowProcessor.js | 119 ++++++++++++++++++ 6 files changed, 209 insertions(+), 131 deletions(-) create mode 100644 packages/server/src/utilities/rowProcessor.js diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte index faadbdeb49..b6bf9ec8d7 100644 --- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte @@ -6,8 +6,6 @@ Input, Label, ModalContent, - Button, - Spacer, Toggle, } from "@budibase/bbui" import TableDataImport from "../TableDataImport.svelte" @@ -28,6 +26,13 @@ let dataImport let error = "" let createAutoscreens = true + let autoColumns = { + createdBy: false, + createdAt: false, + updatedBy: false, + updatedAt: false, + autoNumber: false, + } function checkValid(evt) { const tableName = evt.target.value @@ -42,6 +47,7 @@ let newTable = { name, schema: dataImport.schema || {}, + autoColumns, dataImport, } @@ -93,6 +99,30 @@ on:input={checkValid} bind:value={name} {error} /> +
+ +
+
+ + + +
+
+ + +
+
+
@@ -101,3 +131,25 @@ + + diff --git a/packages/server/src/api/controllers/row.js b/packages/server/src/api/controllers/row.js index 857d1dd2ad..03af91a2b6 100644 --- a/packages/server/src/api/controllers/row.js +++ b/packages/server/src/api/controllers/row.js @@ -9,7 +9,10 @@ const { ViewNames, } = require("../../db/utils") const usersController = require("./user") -const { coerceRowValues, enrichRows } = require("../../utilities") +const { + inputProcessing, + outputProcessing, +} = require("../../utilities/rowProcessor") const TABLE_VIEW_BEGINS_WITH = `all${SEPARATOR}${DocumentTypes.TABLE}${SEPARATOR}` @@ -64,7 +67,7 @@ exports.patch = async function(ctx) { row[key] = patchfields[key] } - row = coerceRowValues(row, table) + row = inputProcessing(ctx.user, table, row) const validateResult = await validate({ row, @@ -134,7 +137,7 @@ exports.save = async function(ctx) { const table = await db.get(row.tableId) - row = coerceRowValues(row, table) + row = inputProcessing(ctx.user, table, row) const validateResult = await validate({ row, @@ -204,7 +207,7 @@ exports.fetchView = async function(ctx) { schema: {}, } } - ctx.body = await enrichRows(appId, table, response.rows) + ctx.body = await outputProcessing(appId, table, response.rows) } if (calculation === CALCULATION_TYPES.STATS) { @@ -247,7 +250,7 @@ exports.fetchTableRows = async function(ctx) { ) rows = response.rows.map(row => row.doc) } - ctx.body = await enrichRows(appId, table, rows) + ctx.body = await outputProcessing(appId, table, rows) } exports.find = async function(ctx) { @@ -256,7 +259,7 @@ exports.find = async function(ctx) { try { const table = await db.get(ctx.params.tableId) const row = await findRow(db, appId, ctx.params.tableId, ctx.params.rowId) - ctx.body = await enrichRows(appId, table, row) + ctx.body = await outputProcessing(appId, table, row) } catch (err) { ctx.throw(400, err) } @@ -341,7 +344,7 @@ exports.fetchEnrichedRow = async function(ctx) { keys: linkVals.map(linkVal => linkVal.id), }) // need to include the IDs in these rows for any links they may have - let linkedRows = await enrichRows( + let linkedRows = await outputProcessing( appId, table, response.rows.map(row => row.doc) diff --git a/packages/server/src/api/routes/table.js b/packages/server/src/api/routes/table.js index da5c753b83..0e6b916c24 100644 --- a/packages/server/src/api/routes/table.js +++ b/packages/server/src/api/routes/table.js @@ -7,9 +7,31 @@ const { PermissionLevels, PermissionTypes, } = require("../../utilities/security/permissions") +const joiValidator = require("../../middleware/joi-validator") +const Joi = require("joi") const router = Router() +function generateSaveValidator() { + // prettier-ignore + return joiValidator.body(Joi.object({ + _id: Joi.string(), + _rev: Joi.string(), + type: Joi.string().valid("table"), + primaryDisplay: Joi.string(), + schema: Joi.object().required(), + name: Joi.string().required(), + views: Joi.object(), + autoColumns: Joi.object({ + createdBy: Joi.boolean(), + createdAt: Joi.boolean(), + updatedBy: Joi.boolean(), + updatedAt: Joi.boolean(), + }), + dataImport: Joi.object(), + }).unknown(true)) +} + router .get("/api/tables", authorized(BUILDER), tableController.fetch) .get( @@ -23,6 +45,7 @@ router // allows control over updating a table bodyResource("_id"), authorized(BUILDER), + generateSaveValidator(), tableController.save ) .post( diff --git a/packages/server/src/automations/triggers.js b/packages/server/src/automations/triggers.js index 6634016e3f..e4c91e5610 100644 --- a/packages/server/src/automations/triggers.js +++ b/packages/server/src/automations/triggers.js @@ -2,7 +2,7 @@ const CouchDB = require("../db") const emitter = require("../events/index") const InMemoryQueue = require("../utilities/queue/inMemoryQueue") const { getAutomationParams } = require("../db/utils") -const { coerceValue } = require("../utilities") +const { coerce } = require("../utilities/rowProcessor") let automationQueue = new InMemoryQueue("automationQueue") @@ -240,8 +240,8 @@ module.exports.externalTrigger = async function(automation, params) { // values are likely to be submitted as strings, so we shall convert to correct type const coercedFields = {} const fields = automation.definition.trigger.inputs.fields - for (let key in fields) { - coercedFields[key] = coerceValue(params.fields[key], fields[key]) + for (let key of Object.keys(fields)) { + coercedFields[key] = coerce(params.fields[key], fields[key]) } params.fields = coercedFields } diff --git a/packages/server/src/utilities/index.js b/packages/server/src/utilities/index.js index 31cc74b5e6..4cf01dc836 100644 --- a/packages/server/src/utilities/index.js +++ b/packages/server/src/utilities/index.js @@ -3,60 +3,9 @@ const { DocumentTypes, SEPARATOR } = require("../db/utils") const fs = require("fs") const { cloneDeep } = require("lodash/fp") const CouchDB = require("../db") -const { OBJ_STORE_DIRECTORY } = require("../constants") -const linkRows = require("../db/linkedRows") const APP_PREFIX = DocumentTypes.APP + SEPARATOR -/** - * A map of how we convert various properties in rows to each other based on the row type. - */ -const TYPE_TRANSFORM_MAP = { - link: { - "": [], - [null]: [], - [undefined]: undefined, - }, - options: { - "": "", - [null]: "", - [undefined]: undefined, - }, - string: { - "": "", - [null]: "", - [undefined]: undefined, - }, - longform: { - "": "", - [null]: "", - [undefined]: undefined, - }, - number: { - "": null, - [null]: null, - [undefined]: undefined, - parse: n => parseFloat(n), - }, - datetime: { - "": null, - [undefined]: undefined, - [null]: null, - }, - attachment: { - "": [], - [null]: [], - [undefined]: undefined, - }, - boolean: { - "": null, - [null]: null, - [undefined]: undefined, - true: true, - false: false, - }, -} - function confirmAppId(possibleAppId) { return possibleAppId && possibleAppId.startsWith(APP_PREFIX) ? possibleAppId @@ -159,43 +108,6 @@ exports.walkDir = (dirPath, callback) => { } } -/** - * This will coerce a value to the correct types based on the type transform map - * @param {object} row The value to coerce - * @param {object} type The type fo coerce to - * @returns {object} The coerced value - */ -exports.coerceValue = (value, type) => { - // eslint-disable-next-line no-prototype-builtins - if (TYPE_TRANSFORM_MAP[type].hasOwnProperty(value)) { - return TYPE_TRANSFORM_MAP[type][value] - } else if (TYPE_TRANSFORM_MAP[type].parse) { - return TYPE_TRANSFORM_MAP[type].parse(value) - } - - return value -} - -/** - * This will coerce the values in a row to the correct types based on the type transform map and the - * table schema. - * @param {object} row The row which is to be coerced to correct values based on schema, this input - * row will not be updated. - * @param {object} table The table that has been retrieved from DB, this must contain the expected - * schema for the rows. - * @returns {object} The updated row will be returned with all values coerced. - */ -exports.coerceRowValues = (row, table) => { - const clonedRow = cloneDeep(row) - for (let [key, value] of Object.entries(clonedRow)) { - const field = table.schema[key] - if (!field) continue - - clonedRow[key] = exports.coerceValue(value, field.type) - } - return clonedRow -} - exports.getLogoUrl = () => { return "https://d33wubrfki0l68.cloudfront.net/aac32159d7207b5085e74a7ef67afbb7027786c5/2b1fd/img/logo/bb-emblem.svg" } @@ -213,34 +125,3 @@ exports.getAllApps = async () => { .map(({ value }) => value) } } - -/** - * This function "enriches" the input rows with anything they are supposed to contain, for example - * link records or attachment links. - * @param {string} appId the ID of the application for which rows are being enriched. - * @param {object} table the table from which these rows came from originally, this is used to determine - * the schema of the rows and then enrich. - * @param {object[]} rows the rows which are to be enriched. - * @returns {object[]} the enriched rows will be returned. - */ -exports.enrichRows = async (appId, table, rows) => { - // attach any linked row information - const enriched = await linkRows.attachLinkInfo(appId, rows) - // update the attachments URL depending on hosting - if (env.CLOUD && env.SELF_HOSTED) { - for (let [property, column] of Object.entries(table.schema)) { - if (column.type === "attachment") { - for (let row of enriched) { - if (row[property] == null || row[property].length === 0) { - continue - } - row[property].forEach(attachment => { - attachment.url = `${OBJ_STORE_DIRECTORY}/${appId}/${attachment.url}` - attachment.url = attachment.url.replace("//", "/") - }) - } - } - } - } - return enriched -} diff --git a/packages/server/src/utilities/rowProcessor.js b/packages/server/src/utilities/rowProcessor.js new file mode 100644 index 0000000000..270f636aae --- /dev/null +++ b/packages/server/src/utilities/rowProcessor.js @@ -0,0 +1,119 @@ +const env = require("../environment") +const { OBJ_STORE_DIRECTORY } = require("../constants") +const linkRows = require("../db/linkedRows") +const { cloneDeep } = require("lodash/fp") + +/** + * A map of how we convert various properties in rows to each other based on the row type. + */ +const TYPE_TRANSFORM_MAP = { + link: { + "": [], + [null]: [], + [undefined]: undefined, + }, + options: { + "": "", + [null]: "", + [undefined]: undefined, + }, + string: { + "": "", + [null]: "", + [undefined]: undefined, + }, + longform: { + "": "", + [null]: "", + [undefined]: undefined, + }, + number: { + "": null, + [null]: null, + [undefined]: undefined, + parse: n => parseFloat(n), + }, + datetime: { + "": null, + [undefined]: undefined, + [null]: null, + }, + attachment: { + "": [], + [null]: [], + [undefined]: undefined, + }, + boolean: { + "": null, + [null]: null, + [undefined]: undefined, + true: true, + false: false, + }, +} + +/** + * This will coerce a value to the correct types based on the type transform map + * @param {any} value The value to coerce + * @param {string} type The type fo coerce to + * @returns {any} The coerced value + */ +exports.coerce = (value, type) => { + // eslint-disable-next-line no-prototype-builtins + if (TYPE_TRANSFORM_MAP[type].hasOwnProperty(value)) { + return TYPE_TRANSFORM_MAP[type][value] + } else if (TYPE_TRANSFORM_MAP[type].parse) { + return TYPE_TRANSFORM_MAP[type].parse(value) + } + return value +} + +/** + * Given an input route this function will apply all the necessary pre-processing to it, such as coercion + * of column values or adding auto-column values. + * @param {object} user the user which is performing the input. + * @param {object} row the row which is being created/updated. + * @param {object} table the table which the row is being saved to. + * @returns {object} the row which has been prepared to be written to the DB. + */ +exports.inputProcessing = (user, table, row) => { + const clonedRow = cloneDeep(row) + for (let [key, value] of Object.entries(clonedRow)) { + const field = table.schema[key] + if (!field) continue + + clonedRow[key] = exports.coerce(value, field.type) + } + return clonedRow +} + +/** + * This function enriches the input rows with anything they are supposed to contain, for example + * link records or attachment links. + * @param {string} appId the ID of the application for which rows are being enriched. + * @param {object} table the table from which these rows came from originally, this is used to determine + * the schema of the rows and then enrich. + * @param {object[]} rows the rows which are to be enriched. + * @returns {object[]} the enriched rows will be returned. + */ +exports.outputProcessing = async (appId, table, rows) => { + // attach any linked row information + const outputRows = await linkRows.attachLinkInfo(appId, rows) + // update the attachments URL depending on hosting + if (env.CLOUD && env.SELF_HOSTED) { + for (let [property, column] of Object.entries(table.schema)) { + if (column.type === "attachment") { + for (let row of outputRows) { + if (row[property] == null || row[property].length === 0) { + continue + } + row[property].forEach(attachment => { + attachment.url = `${OBJ_STORE_DIRECTORY}/${appId}/${attachment.url}` + attachment.url = attachment.url.replace("//", "/") + }) + } + } + } + } + return outputRows +} From ca20cbeecac510597b27ea331c6da5753d948e81 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 10 Feb 2021 17:55:19 +0000 Subject: [PATCH 02/27] First lot of work to update the auto fields into schema. --- .../modals/CreateTableModal.svelte | 40 ++++++++++++------- .../builder/src/constants/backend/index.js | 7 ++++ packages/server/src/api/controllers/row.js | 5 ++- packages/server/src/api/controllers/table.js | 3 +- packages/server/src/api/routes/table.js | 6 --- packages/server/src/constants/index.js | 12 ++++++ .../src/db/linkedRows/LinkController.js | 18 +++++---- .../server/src/db/linkedRows/linkUtils.js | 3 +- packages/server/src/utilities/rowProcessor.js | 19 ++++----- 9 files changed, 73 insertions(+), 40 deletions(-) diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte index b6bf9ec8d7..b9a2307433 100644 --- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte @@ -14,6 +14,8 @@ import { NEW_ROW_TEMPLATE } from "builderStore/store/screenTemplates/newRowScreen" import { ROW_DETAIL_TEMPLATE } from "builderStore/store/screenTemplates/rowDetailScreen" import { ROW_LIST_TEMPLATE } from "builderStore/store/screenTemplates/rowListScreen" + import { FIELDS } from "constants/backend" + import { cloneDeep } from "lodash/fp" const defaultScreens = [ NEW_ROW_TEMPLATE, @@ -27,11 +29,22 @@ let error = "" let createAutoscreens = true let autoColumns = { - createdBy: false, - createdAt: false, - updatedBy: false, - updatedAt: false, - autoNumber: false, + createdBy: true, + createdAt: true, + updatedBy: true, + updatedAt: true, + autoID: true, + } + + function addAutoColumns(schema) { + for (let [property, enabled] of Object.entries(autoColumns)) { + if (!enabled) { + continue + } + const autoColDef = cloneDeep(FIELDS.AUTO) + autoColDef.subtype = property + schema[property] = autoColDef + } } function checkValid(evt) { @@ -46,8 +59,7 @@ async function saveTable() { let newTable = { name, - schema: dataImport.schema || {}, - autoColumns, + schema: addAutoColumns(dataImport.schema || {}), dataImport, } @@ -100,7 +112,7 @@ bind:value={name} {error} />
- +
+ text="Auto ID" + bind:checked={autoColumns.autoID} />
*) { - margin-top: 10px; + margin-bottom: 10px; } + .toggle-2 :global(> *) { - margin-top: 10px; - margin-left: 10px; + margin-bottom: 10px; + margin-left: 20px; } diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index 80eaf613f8..9ef95f3c61 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -80,6 +80,13 @@ export const FIELDS = { presence: false, }, }, + AUTO: { + name: "Auto Column", + icon: "ri-magic-line", + type: "auto", + // no constraints for auto-columns + // these are fully created serverside + } } export const FILE_TYPES = { diff --git a/packages/server/src/api/controllers/row.js b/packages/server/src/api/controllers/row.js index 0c4163aa22..34f027002c 100644 --- a/packages/server/src/api/controllers/row.js +++ b/packages/server/src/api/controllers/row.js @@ -13,6 +13,7 @@ const { inputProcessing, outputProcessing, } = require("../../utilities/rowProcessor") +const { FieldTypes } = require("../../constants") const TABLE_VIEW_BEGINS_WITH = `all${SEPARATOR}${DocumentTypes.TABLE}${SEPARATOR}` @@ -261,7 +262,7 @@ exports.search = async function(ctx) { const table = await db.get(ctx.params.tableId) - ctx.body = await enrichRows(appId, table, rows) + ctx.body = await outputProcessing(appId, table, rows) } exports.fetchTableRows = async function(ctx) { @@ -384,7 +385,7 @@ exports.fetchEnrichedRow = async function(ctx) { // insert the link rows in the correct place throughout the main row for (let fieldName of Object.keys(table.schema)) { let field = table.schema[fieldName] - if (field.type === "link") { + if (field.type === FieldTypes.LINK) { row[fieldName] = linkedRows.filter( linkRow => linkRow.tableId === field.tableId ) diff --git a/packages/server/src/api/controllers/table.js b/packages/server/src/api/controllers/table.js index 1fa77b0b2b..d3e7213fdb 100644 --- a/packages/server/src/api/controllers/table.js +++ b/packages/server/src/api/controllers/table.js @@ -8,6 +8,7 @@ const { generateRowID, } = require("../../db/utils") const { isEqual } = require("lodash/fp") +const { FieldTypes } = require("../../constants") async function checkForColumnUpdates(db, oldTable, updatedTable) { let updatedRows @@ -91,7 +92,7 @@ exports.save = async function(ctx) { } // rename row fields when table column is renamed - if (_rename && tableToSave.schema[_rename.updated].type === "link") { + if (_rename && tableToSave.schema[_rename.updated].type === FieldTypes.LINK) { ctx.throw(400, "Cannot rename a linked column.") } else if (_rename && tableToSave.primaryDisplay === _rename.old) { ctx.throw(400, "Cannot rename the display column.") diff --git a/packages/server/src/api/routes/table.js b/packages/server/src/api/routes/table.js index 0e6b916c24..7c7f45afeb 100644 --- a/packages/server/src/api/routes/table.js +++ b/packages/server/src/api/routes/table.js @@ -22,12 +22,6 @@ function generateSaveValidator() { schema: Joi.object().required(), name: Joi.string().required(), views: Joi.object(), - autoColumns: Joi.object({ - createdBy: Joi.boolean(), - createdAt: Joi.boolean(), - updatedBy: Joi.boolean(), - updatedAt: Joi.boolean(), - }), dataImport: Joi.object(), }).unknown(true)) } diff --git a/packages/server/src/constants/index.js b/packages/server/src/constants/index.js index 2e18de98af..54dedbcf11 100644 --- a/packages/server/src/constants/index.js +++ b/packages/server/src/constants/index.js @@ -39,6 +39,18 @@ const USERS_TABLE_SCHEMA = { primaryDisplay: "email", } +exports.FieldTypes = { + STRING: "string", + LONGFORM: "longform", + OPTIONS: "options", + NUMBER: "number", + BOOLEAN: "boolean", + DATETIME: "datetime", + ATTACHMENT: "attachment", + LINK: "link", + AUTO: "auto", +} + exports.AuthTypes = AuthTypes exports.USERS_TABLE_SCHEMA = USERS_TABLE_SCHEMA exports.BUILDER_CONFIG_DB = "builder-config-db" diff --git a/packages/server/src/db/linkedRows/LinkController.js b/packages/server/src/db/linkedRows/LinkController.js index 061a9ac1ae..ca64cb3430 100644 --- a/packages/server/src/db/linkedRows/LinkController.js +++ b/packages/server/src/db/linkedRows/LinkController.js @@ -2,6 +2,7 @@ const CouchDB = require("../index") const { IncludeDocs, getLinkDocuments } = require("./linkUtils") const { generateLinkID } = require("../utils") const Sentry = require("@sentry/node") +const { FieldTypes } = require("../../constants") /** * Creates a new link document structure which can be put to the database. It is important to @@ -26,7 +27,7 @@ function LinkDocument( // build the ID out of unique references to this link document this._id = generateLinkID(tableId1, tableId2, rowId1, rowId2) // required for referencing in view - this.type = "link" + this.type = FieldTypes.LINK this.doc1 = { tableId: tableId1, fieldName: fieldName1, @@ -75,7 +76,7 @@ class LinkController { } for (let fieldName of Object.keys(table.schema)) { const { type } = table.schema[fieldName] - if (type === "link") { + if (type === FieldTypes.LINK) { return true } } @@ -123,7 +124,7 @@ class LinkController { // get the links this row wants to make const rowField = row[fieldName] const field = table.schema[fieldName] - if (field.type === "link" && rowField != null) { + if (field.type === FieldTypes.LINK && rowField != null) { // check which links actual pertain to the update in this row const thisFieldLinkDocs = linkDocs.filter( linkDoc => @@ -234,7 +235,7 @@ class LinkController { const schema = table.schema for (let fieldName of Object.keys(schema)) { const field = schema[fieldName] - if (field.type === "link") { + if (field.type === FieldTypes.LINK) { // handle this in a separate try catch, want // the put to bubble up as an error, if can't update // table for some reason @@ -247,7 +248,7 @@ class LinkController { // create the link field in the other table linkedTable.schema[field.fieldName] = { name: field.fieldName, - type: "link", + type: FieldTypes.LINK, // these are the props of the table that initiated the link tableId: table._id, fieldName: fieldName, @@ -274,7 +275,10 @@ class LinkController { for (let fieldName of Object.keys(oldTable.schema)) { const field = oldTable.schema[fieldName] // this field has been removed from the table schema - if (field.type === "link" && newTable.schema[fieldName] == null) { + if ( + field.type === FieldTypes.LINK && + newTable.schema[fieldName] == null + ) { await this.removeFieldFromTable(fieldName) } } @@ -295,7 +299,7 @@ class LinkController { for (let fieldName of Object.keys(schema)) { const field = schema[fieldName] try { - if (field.type === "link") { + if (field.type === FieldTypes.LINK) { const linkedTable = await this._db.get(field.tableId) delete linkedTable.schema[field.fieldName] await this._db.put(linkedTable) diff --git a/packages/server/src/db/linkedRows/linkUtils.js b/packages/server/src/db/linkedRows/linkUtils.js index cb669cf5c7..ee22a87410 100644 --- a/packages/server/src/db/linkedRows/linkUtils.js +++ b/packages/server/src/db/linkedRows/linkUtils.js @@ -1,6 +1,7 @@ const CouchDB = require("../index") const Sentry = require("@sentry/node") const { ViewNames, getQueryIndex } = require("../utils") +const { FieldTypes } = require("../../constants") /** * Only needed so that boolean parameters are being used for includeDocs @@ -23,7 +24,7 @@ exports.createLinkView = async appId => { const designDoc = await db.get("_design/database") const view = { map: function(doc) { - if (doc.type === "link") { + if (doc.type === FieldTypes.LINK) { let doc1 = doc.doc1 let doc2 = doc.doc2 emit([doc1.tableId, doc1.rowId], { diff --git a/packages/server/src/utilities/rowProcessor.js b/packages/server/src/utilities/rowProcessor.js index 270f636aae..7ce080cf9f 100644 --- a/packages/server/src/utilities/rowProcessor.js +++ b/packages/server/src/utilities/rowProcessor.js @@ -2,48 +2,49 @@ const env = require("../environment") const { OBJ_STORE_DIRECTORY } = require("../constants") const linkRows = require("../db/linkedRows") const { cloneDeep } = require("lodash/fp") +const { FieldTypes } = require("../constants") /** * A map of how we convert various properties in rows to each other based on the row type. */ const TYPE_TRANSFORM_MAP = { - link: { + [FieldTypes.LINK]: { "": [], [null]: [], [undefined]: undefined, }, - options: { + [FieldTypes.OPTIONS]: { "": "", [null]: "", [undefined]: undefined, }, - string: { + [FieldTypes.STRING]: { "": "", [null]: "", [undefined]: undefined, }, - longform: { + [FieldTypes.LONGFORM]: { "": "", [null]: "", [undefined]: undefined, }, - number: { + [FieldTypes.NUMBER]: { "": null, [null]: null, [undefined]: undefined, parse: n => parseFloat(n), }, - datetime: { + [FieldTypes.DATETIME]: { "": null, [undefined]: undefined, [null]: null, }, - attachment: { + [FieldTypes.ATTACHMENT]: { "": [], [null]: [], [undefined]: undefined, }, - boolean: { + [FieldTypes.BOOLEAN]: { "": null, [null]: null, [undefined]: undefined, @@ -102,7 +103,7 @@ exports.outputProcessing = async (appId, table, rows) => { // update the attachments URL depending on hosting if (env.CLOUD && env.SELF_HOSTED) { for (let [property, column] of Object.entries(table.schema)) { - if (column.type === "attachment") { + if (column.type === FieldTypes.ATTACHMENT) { for (let row of outputRows) { if (row[property] == null || row[property].length === 0) { continue From 4b1855974c61e42aed5d93b56f3757d2c927df7a Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 15 Feb 2021 17:47:14 +0000 Subject: [PATCH 03/27] Work in progress, getting the server backend mostly ready for this work. --- .../modals/CreateTableModal.svelte | 30 +++--- .../builder/src/constants/backend/index.js | 55 +++++++++-- packages/server/src/api/controllers/row.js | 37 ++++---- packages/server/src/constants/index.js | 8 ++ .../src/db/linkedRows/LinkController.js | 9 +- packages/server/src/db/linkedRows/index.js | 4 +- packages/server/src/db/utils.js | 16 +++- packages/server/src/utilities/linkedRows.js | 0 packages/server/src/utilities/rowProcessor.js | 95 ++++++++++++++++--- 9 files changed, 194 insertions(+), 60 deletions(-) delete mode 100644 packages/server/src/utilities/linkedRows.js diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte index b9a2307433..5ae060254b 100644 --- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte @@ -14,8 +14,7 @@ import { NEW_ROW_TEMPLATE } from "builderStore/store/screenTemplates/newRowScreen" import { ROW_DETAIL_TEMPLATE } from "builderStore/store/screenTemplates/rowDetailScreen" import { ROW_LIST_TEMPLATE } from "builderStore/store/screenTemplates/rowListScreen" - import { FIELDS } from "constants/backend" - import { cloneDeep } from "lodash/fp" + import { AUTO_COLUMN_SUB_TYPES, buildAutoColumn } from "constants/backend" const defaultScreens = [ NEW_ROW_TEMPLATE, @@ -23,33 +22,34 @@ ROW_LIST_TEMPLATE, ] + $: tableNames = $backendUiStore.tables.map(table => table.name) + let modal let name let dataImport let error = "" let createAutoscreens = true let autoColumns = { - createdBy: true, - createdAt: true, - updatedBy: true, - updatedAt: true, - autoID: true, + [AUTO_COLUMN_SUB_TYPES.AUTO_ID]: {enabled: true, name: "Auto ID"}, + [AUTO_COLUMN_SUB_TYPES.CREATED_BY]: {enabled: true, name: "Created By"}, + [AUTO_COLUMN_SUB_TYPES.CREATED_AT]: {enabled: true, name: "Created At"}, + [AUTO_COLUMN_SUB_TYPES.UPDATED_BY]: {enabled: true, name: "Updated By"}, + [AUTO_COLUMN_SUB_TYPES.UPDATED_AT]: {enabled: true, name: "Updated At"}, } - function addAutoColumns(schema) { - for (let [property, enabled] of Object.entries(autoColumns)) { - if (!enabled) { + function addAutoColumns(tableName, schema) { + for (let [subtype, col] of Object.entries(autoColumns)) { + if (!col.enabled) { continue } - const autoColDef = cloneDeep(FIELDS.AUTO) - autoColDef.subtype = property - schema[property] = autoColDef + schema[col.name] = buildAutoColumn(tableName, col.name, subtype) } + return schema } function checkValid(evt) { const tableName = evt.target.value - if ($backendUiStore.models?.some(model => model.name === tableName)) { + if (tableNames.includes(tableName)) { error = `Table with name ${tableName} already exists. Please choose another name.` return } @@ -59,7 +59,7 @@ async function saveTable() { let newTable = { name, - schema: addAutoColumns(dataImport.schema || {}), + schema: addAutoColumns(name, dataImport.schema || {}), dataImport, } diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index a3076b18d5..62fe0cb5ab 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -80,13 +80,14 @@ export const FIELDS = { presence: false, }, }, - AUTO: { - name: "Auto Column", - icon: "ri-magic-line", - type: "auto", - // no constraints for auto-columns - // these are fully created serverside - } +} + +export const AUTO_COLUMN_SUB_TYPES = { + CREATED_BY: "createdBy", + CREATED_AT: "createdAt", + UPDATED_BY: "updatedBy", + UPDATED_AT: "updatedAt", + AUTO_ID: "autoID", } export const FILE_TYPES = { @@ -107,3 +108,43 @@ export const Roles = { PUBLIC: "PUBLIC", BUILDER: "BUILDER", } + +export const USER_TABLE_ID = "ta_users" + +export function isAutoColumnUserRelationship(subtype) { + return subtype === AUTO_COLUMN_SUB_TYPES.CREATED_BY || + subtype === AUTO_COLUMN_SUB_TYPES.UPDATED_BY +} + +export function buildAutoColumn(tableName, name, subtype) { + let type + switch (subtype) { + case AUTO_COLUMN_SUB_TYPES.UPDATED_BY: + case AUTO_COLUMN_SUB_TYPES.CREATED_BY: + type = FIELDS.LINK.type + break + case AUTO_COLUMN_SUB_TYPES.AUTO_ID: + type = FIELDS.NUMBER.type + break + default: + type = FIELDS.STRING.type + break + } + if (Object.values(AUTO_COLUMN_SUB_TYPES).indexOf(subtype) === -1) { + throw "Cannot build auto column with supplied subtype" + } + const base = { + name, + type, + subtype, + icon: "ri-magic-line", + autocolumn: true, + // no constraints, this should never have valid inputs + constraints: {}, + } + if (isAutoColumnUserRelationship(subtype)) { + base.tableId = USER_TABLE_ID + base.fieldName = `${tableName}-${name}` + } + return base +} diff --git a/packages/server/src/api/controllers/row.js b/packages/server/src/api/controllers/row.js index 34f027002c..078e4b722f 100644 --- a/packages/server/src/api/controllers/row.js +++ b/packages/server/src/api/controllers/row.js @@ -58,18 +58,17 @@ async function findRow(db, appId, tableId, rowId) { exports.patch = async function(ctx) { const appId = ctx.user.appId const db = new CouchDB(appId) - let row = await db.get(ctx.params.rowId) - const table = await db.get(row.tableId) + let dbRow = await db.get(ctx.params.rowId) + let dbTable = await db.get(dbRow.tableId) const patchfields = ctx.request.body - // need to build up full patch fields before coerce for (let key of Object.keys(patchfields)) { - if (!table.schema[key]) continue - row[key] = patchfields[key] + if (!dbTable.schema[key]) continue + dbRow[key] = patchfields[key] } - row = inputProcessing(ctx.user, table, row) - + // this returns the table and row incase they have been updated + let { table, row } = await inputProcessing(ctx.user, dbTable, dbRow) const validateResult = await validate({ row, table, @@ -114,32 +113,34 @@ exports.patch = async function(ctx) { exports.save = async function(ctx) { const appId = ctx.user.appId const db = new CouchDB(appId) - let row = ctx.request.body - row.tableId = ctx.params.tableId + let inputs = ctx.request.body + inputs.tableId = ctx.params.tableId // TODO: find usage of this and break out into own endpoint - if (ctx.request.body.type === "delete") { + if (inputs.type === "delete") { await bulkDelete(ctx) - ctx.body = ctx.request.body.rows + ctx.body = inputs.rows return } // if the row obj had an _id then it will have been retrieved const existingRow = ctx.preExisting if (existingRow) { - ctx.params.rowId = row._id + ctx.params.rowId = inputs._id await exports.patch(ctx) return } - if (!row._rev && !row._id) { - row._id = generateRowID(row.tableId) + if (!inputs._rev && !inputs._id) { + inputs._id = generateRowID(inputs.tableId) } - const table = await db.get(row.tableId) - - row = inputProcessing(ctx.user, table, row) - + // this returns the table and row incase they have been updated + let { table, row } = await inputProcessing( + ctx.user, + await db.get(inputs.tableId), + inputs + ) const validateResult = await validate({ row, table, diff --git a/packages/server/src/constants/index.js b/packages/server/src/constants/index.js index d8038e2a9e..314e220a97 100644 --- a/packages/server/src/constants/index.js +++ b/packages/server/src/constants/index.js @@ -51,6 +51,14 @@ exports.FieldTypes = { AUTO: "auto", } +exports.AutoFieldSubTypes = { + CREATED_BY: "createdBy", + CREATED_AT: "createdAt", + UPDATED_BY: "updatedBy", + UPDATED_AT: "updatedAt", + AUTO_ID: "autoID", +} + exports.AuthTypes = AuthTypes exports.USERS_TABLE_SCHEMA = USERS_TABLE_SCHEMA exports.BUILDER_CONFIG_DB = "builder-config-db" diff --git a/packages/server/src/db/linkedRows/LinkController.js b/packages/server/src/db/linkedRows/LinkController.js index d69566d67c..54a0bfdd58 100644 --- a/packages/server/src/db/linkedRows/LinkController.js +++ b/packages/server/src/db/linkedRows/LinkController.js @@ -25,7 +25,14 @@ function LinkDocument( rowId2 ) { // build the ID out of unique references to this link document - this._id = generateLinkID(tableId1, tableId2, rowId1, rowId2) + this._id = generateLinkID( + tableId1, + tableId2, + rowId1, + rowId2, + fieldName1, + fieldName2 + ) // required for referencing in view this.type = FieldTypes.LINK this.doc1 = { diff --git a/packages/server/src/db/linkedRows/index.js b/packages/server/src/db/linkedRows/index.js index 02af98eef9..fb536c5c44 100644 --- a/packages/server/src/db/linkedRows/index.js +++ b/packages/server/src/db/linkedRows/index.js @@ -5,7 +5,7 @@ const { createLinkView, getUniqueByProp, } = require("./linkUtils") -const _ = require("lodash") +const { flatten } = require("lodash") /** * This functionality makes sure that when rows with links are created, updated or deleted they are processed @@ -101,7 +101,7 @@ exports.attachLinkInfo = async (appId, rows) => { } let tableIds = [...new Set(rows.map(el => el.tableId))] // start by getting all the link values for performance reasons - let responses = _.flatten( + let responses = flatten( await Promise.all( tableIds.map(tableId => getLinkDocuments({ diff --git a/packages/server/src/db/utils.js b/packages/server/src/db/utils.js index 5e7e74d711..6ca55b6336 100644 --- a/packages/server/src/db/utils.js +++ b/packages/server/src/db/utils.js @@ -138,10 +138,22 @@ exports.generateAutomationID = () => { * @param {string} tableId2 The ID of the linked table. * @param {string} rowId1 The ID of the linker row. * @param {string} rowId2 The ID of the linked row. + * @param {string} fieldName1 The name of the field in the linker row. + * @param {string} fieldName2 the name of the field in the linked row. * @returns {string} The new link doc ID which the automation doc can be stored under. */ -exports.generateLinkID = (tableId1, tableId2, rowId1, rowId2) => { - return `${DocumentTypes.LINK}${SEPARATOR}${tableId1}${SEPARATOR}${tableId2}${SEPARATOR}${rowId1}${SEPARATOR}${rowId2}` +exports.generateLinkID = ( + tableId1, + tableId2, + rowId1, + rowId2, + fieldName1, + fieldName2 +) => { + const tables = `${SEPARATOR}${tableId1}${SEPARATOR}${tableId2}` + const rows = `${SEPARATOR}${rowId1}${SEPARATOR}${rowId2}` + const fields = `${SEPARATOR}${fieldName1}${SEPARATOR}${fieldName2}` + return `${DocumentTypes.LINK}${tables}${rows}${fields}` } /** diff --git a/packages/server/src/utilities/linkedRows.js b/packages/server/src/utilities/linkedRows.js deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/packages/server/src/utilities/rowProcessor.js b/packages/server/src/utilities/rowProcessor.js index fe18eefdc6..c2885d8440 100644 --- a/packages/server/src/utilities/rowProcessor.js +++ b/packages/server/src/utilities/rowProcessor.js @@ -2,13 +2,18 @@ const env = require("../environment") const { OBJ_STORE_DIRECTORY } = require("../constants") const linkRows = require("../db/linkedRows") const { cloneDeep } = require("lodash/fp") -const { FieldTypes } = require("../constants") +const { FieldTypes, AutoFieldSubTypes } = require("../constants") +const CouchDB = require("../db") +const { ViewNames } = require("../db/utils") + +const BASE_AUTO_ID = 1 +const USER_TABLE_ID = ViewNames.USERS /** * A map of how we convert various properties in rows to each other based on the row type. */ const TYPE_TRANSFORM_MAP = { - link: { + [FieldTypes.LINK]: { "": [], [null]: [], [undefined]: undefined, @@ -19,44 +24,102 @@ const TYPE_TRANSFORM_MAP = { return link }, }, - options: { + [FieldTypes.OPTIONS]: { "": "", [null]: "", [undefined]: undefined, }, - string: { + [FieldTypes.STRING]: { "": "", [null]: "", [undefined]: undefined, }, - longform: { + [FieldTypes.LONGFORM]: { "": "", [null]: "", [undefined]: undefined, }, - number: { + [FieldTypes.NUMBER]: { "": null, [null]: null, [undefined]: undefined, parse: n => parseFloat(n), }, - datetime: { + [FieldTypes.DATETIME]: { "": null, [undefined]: undefined, [null]: null, }, - attachment: { + [FieldTypes.ATTACHMENT]: { "": [], [null]: [], [undefined]: undefined, }, - boolean: { + [FieldTypes.BOOLEAN]: { "": null, [null]: null, [undefined]: undefined, true: true, false: false, }, + [FieldTypes.AUTO]: { + parse: () => undefined, + }, +} + +function getAutoRelationshipName(table, columnName) { + return `${table.name}-${columnName}` +} + +/** + * This will update any auto columns that are found on the row/table with the correct information based on + * time now and the current logged in user making the request. + * @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. + * @returns {Promise<{row: Object, table: Object}>} The updated row and table, the table may need to be updated + * for automatic ID purposes. + */ +async function processAutoColumn(user, table, row) { + let now = new Date().toISOString() + // if a row doesn't have a revision then it doesn't exist yet + const creating = !row._rev + let tableUpdated = false + for (let [key, schema] of Object.entries(table.schema)) { + if (!schema.autocolumn) { + continue + } + switch (schema.subtype) { + case AutoFieldSubTypes.CREATED_BY: + if (creating) { + row[key] = [user.userId] + } + break + case AutoFieldSubTypes.CREATED_AT: + if (creating) { + row[key] = now + } + break + case AutoFieldSubTypes.UPDATED_BY: + row[key] = [user.userId] + break + case AutoFieldSubTypes.UPDATED_AT: + row[key] = now + break + case AutoFieldSubTypes.AUTO_ID: + schema.lastID = !schema.lastID ? BASE_AUTO_ID : schema.lastID + 1 + row[key] = schema.lastID + tableUpdated = true + break + } + } + if (tableUpdated) { + const db = new CouchDB(user.appId) + const response = await db.put(table) + // update the revision + table._rev = response._rev + } + return { table, row } } /** @@ -65,7 +128,7 @@ const TYPE_TRANSFORM_MAP = { * @param {object} type The type fo coerce to * @returns {object} The coerced value */ -exports.coerceValue = (row, type) => { +exports.coerce = (row, type) => { // eslint-disable-next-line no-prototype-builtins if (TYPE_TRANSFORM_MAP[type].hasOwnProperty(row)) { return TYPE_TRANSFORM_MAP[type][row] @@ -84,15 +147,17 @@ exports.coerceValue = (row, type) => { * @param {object} table the table which the row is being saved to. * @returns {object} the row which has been prepared to be written to the DB. */ -exports.inputProcessing = (user, table, row) => { - const clonedRow = cloneDeep(row) +exports.inputProcessing = async (user, table, row) => { + let clonedRow = cloneDeep(row) for (let [key, value] of Object.entries(clonedRow)) { const field = table.schema[key] - if (!field) continue - + if (!field) { + continue + } clonedRow[key] = exports.coerce(value, field.type) } - return clonedRow + // handle auto columns - this returns an object like {table, row} + return processAutoColumn(user, table, clonedRow) } /** From 1a2b9bbef4d44f6358de9b3c3ddcae36efdc717b Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 15 Feb 2021 18:41:26 +0000 Subject: [PATCH 04/27] Removing unused stuff. --- packages/server/src/utilities/rowProcessor.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/packages/server/src/utilities/rowProcessor.js b/packages/server/src/utilities/rowProcessor.js index c2885d8440..ceb31daa3b 100644 --- a/packages/server/src/utilities/rowProcessor.js +++ b/packages/server/src/utilities/rowProcessor.js @@ -4,10 +4,8 @@ const linkRows = require("../db/linkedRows") const { cloneDeep } = require("lodash/fp") const { FieldTypes, AutoFieldSubTypes } = require("../constants") const CouchDB = require("../db") -const { ViewNames } = require("../db/utils") const BASE_AUTO_ID = 1 -const USER_TABLE_ID = ViewNames.USERS /** * A map of how we convert various properties in rows to each other based on the row type. @@ -67,10 +65,6 @@ const TYPE_TRANSFORM_MAP = { }, } -function getAutoRelationshipName(table, columnName) { - return `${table.name}-${columnName}` -} - /** * This will update any auto columns that are found on the row/table with the correct information based on * time now and the current logged in user making the request. From 245cd0a791ece2ef31865994ca82f393b55892f2 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 15 Feb 2021 18:53:20 +0000 Subject: [PATCH 05/27] Fixing issue with relationships. --- packages/server/src/db/linkedRows/linkUtils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/server/src/db/linkedRows/linkUtils.js b/packages/server/src/db/linkedRows/linkUtils.js index ee22a87410..c07c17eda7 100644 --- a/packages/server/src/db/linkedRows/linkUtils.js +++ b/packages/server/src/db/linkedRows/linkUtils.js @@ -24,7 +24,8 @@ exports.createLinkView = async appId => { const designDoc = await db.get("_design/database") const view = { map: function(doc) { - if (doc.type === FieldTypes.LINK) { + // everything in this must remain constant as its going to Pouch, no external variables + if (doc.type === "link") { let doc1 = doc.doc1 let doc2 = doc.doc2 emit([doc1.tableId, doc1.rowId], { From 23cac6a9acc3dabff0b8619cc3cc919847c8a255 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 15 Feb 2021 19:01:15 +0000 Subject: [PATCH 06/27] Fixing issue with linked rows not handling uniqueness correctly when links between tables are using fieldnames for uniqueness. --- packages/server/src/db/linkedRows/index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/server/src/db/linkedRows/index.js b/packages/server/src/db/linkedRows/index.js index fb536c5c44..5ea758a976 100644 --- a/packages/server/src/db/linkedRows/index.js +++ b/packages/server/src/db/linkedRows/index.js @@ -118,8 +118,12 @@ exports.attachLinkInfo = async (appId, rows) => { // have to get unique as the previous table query can // return duplicates, could be querying for both tables in a relation const linkVals = getUniqueByProp( - responses.filter(el => el.thisId === row._id), - "id" + responses + // find anything that matches the row's ID we are searching for + .filter(el => el.thisId === row._id) + // create a unique ID which we can use for getting only unique ones + .map(el => ({ ...el, unique: el.id + el.fieldName })), + "unique" ) for (let linkVal of linkVals) { // work out which link pertains to this row From 846772bfebb33f2b76340cc2b93ad38c65d9317d Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 15 Feb 2021 19:59:30 +0000 Subject: [PATCH 07/27] Adding the ability to create/control auto-columns from the create/edit column modal. --- .../screenTemplates/utils/commonComponents.js | 4 ++ .../DataTable/modals/CreateEditColumn.svelte | 38 +++++++++++++++---- .../DataTable/modals/CreateEditRow.svelte | 8 ++-- .../modals/CreateTableModal.svelte | 10 +---- .../components/integration/QueryViewer.svelte | 1 - .../builder/src/constants/backend/index.js | 28 +++++++++++--- 6 files changed, 65 insertions(+), 24 deletions(-) diff --git a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js index 4c127fbe0b..ee7a4da0fd 100644 --- a/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js +++ b/packages/builder/src/builderStore/store/screenTemplates/utils/commonComponents.js @@ -179,6 +179,10 @@ export function makeDatasourceFormComponents(datasource) { let fields = Object.keys(schema || {}) fields.forEach(field => { const fieldSchema = schema[field] + // skip autocolumns + if (fieldSchema.autocolumn) { + return + } const fieldType = typeof fieldSchema === "object" ? fieldSchema.type : fieldSchema const componentType = fieldTypeToComponentMap[fieldType] diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index bc18b7559c..0045d2261c 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -10,12 +10,13 @@ import { cloneDeep } from "lodash/fp" import { backendUiStore } from "builderStore" import { TableNames, UNEDITABLE_USER_FIELDS } from "constants" - import { FIELDS } from "constants/backend" + import { FIELDS, getAutoColumnInformation, buildAutoColumn, AUTO_COLUMN_SUB_TYPES } from "constants/backend" import { notifier } from "builderStore/store/notifications" import ValuesList from "components/common/ValuesList.svelte" import DatePicker from "components/common/DatePicker.svelte" import ConfirmDialog from "components/common/ConfirmDialog.svelte" + const AUTO_COL = "auto" let fieldDefinitions = cloneDeep(FIELDS) export let onClosed @@ -43,7 +44,17 @@ $backendUiStore.selectedTable?._id === TableNames.USERS && UNEDITABLE_USER_FIELDS.includes(field.name) + // used to select what different options can be displayed for column type + $: canBeSearched = field.type !== 'link' && + field.subtype !== AUTO_COLUMN_SUB_TYPES.CREATED_BY && + field.subtype !== AUTO_COLUMN_SUB_TYPES.UPDATED_BY + $: canBeDisplay = field.type !== 'link' && field.type !== AUTO_COL + $: canBeRequired = field.type !== 'link' && !uneditable && field.type !== AUTO_COL + async function saveColumn() { + if (field.type === AUTO_COL) { + field = buildAutoColumn($backendUiStore.draftTable.name, field.name, field.subtype) + } backendUiStore.update(state => { backendUiStore.actions.tables.saveField({ originalName, @@ -67,11 +78,14 @@ } function handleFieldConstraints(event) { - const { type, constraints } = fieldDefinitions[ + const definition = fieldDefinitions[ event.target.value.toUpperCase() ] - field.type = type - field.constraints = constraints + if (!definition) { + return + } + field.type = definition.type + field.constraints = definition.constraints } function onChangeRequired(e) { @@ -124,9 +138,10 @@ {#each Object.values(fieldDefinitions) as field} {/each} + - {#if field.type !== 'link' && !uneditable} + {#if canBeRequired} {/if} - {#if field.type !== 'link'} + {#if canBeDisplay} + {/if} + {#if canBeSearched} + {:else if field.type === AUTO_COL} + {/if}
- {#if !uneditable && originalName} + {#if !uneditable && originalName != null} Delete Column {/if} diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditRow.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditRow.svelte index 1bff069b95..5929b2db96 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditRow.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditRow.svelte @@ -40,9 +40,11 @@ onConfirm={saveRow}> {#each tableSchema as [key, meta]} -
- -
+ {#if !meta.autocolumn} +
+ +
+ {/if} {/each} diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte index 5ae060254b..bcf7edbed0 100644 --- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte @@ -14,7 +14,7 @@ import { NEW_ROW_TEMPLATE } from "builderStore/store/screenTemplates/newRowScreen" import { ROW_DETAIL_TEMPLATE } from "builderStore/store/screenTemplates/rowDetailScreen" import { ROW_LIST_TEMPLATE } from "builderStore/store/screenTemplates/rowListScreen" - import { AUTO_COLUMN_SUB_TYPES, buildAutoColumn } from "constants/backend" + import { buildAutoColumn, getAutoColumnInformation } from "constants/backend" const defaultScreens = [ NEW_ROW_TEMPLATE, @@ -29,13 +29,7 @@ let dataImport let error = "" let createAutoscreens = true - let autoColumns = { - [AUTO_COLUMN_SUB_TYPES.AUTO_ID]: {enabled: true, name: "Auto ID"}, - [AUTO_COLUMN_SUB_TYPES.CREATED_BY]: {enabled: true, name: "Created By"}, - [AUTO_COLUMN_SUB_TYPES.CREATED_AT]: {enabled: true, name: "Created At"}, - [AUTO_COLUMN_SUB_TYPES.UPDATED_BY]: {enabled: true, name: "Updated By"}, - [AUTO_COLUMN_SUB_TYPES.UPDATED_AT]: {enabled: true, name: "Updated At"}, - } + let autoColumns = getAutoColumnInformation() function addAutoColumns(tableName, schema) { for (let [subtype, col] of Object.entries(autoColumns)) { diff --git a/packages/builder/src/components/integration/QueryViewer.svelte b/packages/builder/src/components/integration/QueryViewer.svelte index 0f38f7650f..1c3ea2ae4b 100644 --- a/packages/builder/src/components/integration/QueryViewer.svelte +++ b/packages/builder/src/components/integration/QueryViewer.svelte @@ -13,7 +13,6 @@ } from "@budibase/bbui" import { notifier } from "builderStore/store/notifications" import api from "builderStore/api" - import { FIELDS } from "constants/backend" import IntegrationQueryEditor from "components/integration/index.svelte" import ExternalDataSourceTable from "components/backend/DataTable/ExternalDataSourceTable.svelte" import EditQueryParamsPopover from "components/backend/DatasourceNavigator/popovers/EditQueryParamsPopover.svelte" diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index 62fe0cb5ab..ba4ad09d9f 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -79,15 +79,23 @@ export const FIELDS = { type: "array", presence: false, }, - }, + } } export const AUTO_COLUMN_SUB_TYPES = { + AUTO_ID: "autoID", CREATED_BY: "createdBy", CREATED_AT: "createdAt", UPDATED_BY: "updatedBy", UPDATED_AT: "updatedAt", - AUTO_ID: "autoID", +} + +export const AUTO_COLUMN_DISPLAY_NAMES = { + AUTO_ID: "Auto ID", + CREATED_BY: "Created By", + CREATED_AT: "Created At", + UPDATED_BY: "Updated By", + UPDATED_AT: "Updated At", } export const FILE_TYPES = { @@ -116,18 +124,29 @@ export function isAutoColumnUserRelationship(subtype) { subtype === AUTO_COLUMN_SUB_TYPES.UPDATED_BY } +export function getAutoColumnInformation(enabled = true) { + let info = {} + for (let [key, subtype] of Object.entries(AUTO_COLUMN_SUB_TYPES)) { + info[subtype] = {enabled, name: AUTO_COLUMN_DISPLAY_NAMES[key]} + } + return info +} + export function buildAutoColumn(tableName, name, subtype) { - let type + let type, constraints switch (subtype) { case AUTO_COLUMN_SUB_TYPES.UPDATED_BY: case AUTO_COLUMN_SUB_TYPES.CREATED_BY: type = FIELDS.LINK.type + constraints = FIELDS.LINK.constraints break case AUTO_COLUMN_SUB_TYPES.AUTO_ID: type = FIELDS.NUMBER.type + constraints = FIELDS.NUMBER.constraints break default: type = FIELDS.STRING.type + constraints = FIELDS.STRING.constraints break } if (Object.values(AUTO_COLUMN_SUB_TYPES).indexOf(subtype) === -1) { @@ -139,8 +158,7 @@ export function buildAutoColumn(tableName, name, subtype) { subtype, icon: "ri-magic-line", autocolumn: true, - // no constraints, this should never have valid inputs - constraints: {}, + constraints, } if (isAutoColumnUserRelationship(subtype)) { base.tableId = USER_TABLE_ID From 3a106c937cf4ac7f298bfe0a61883f77fc625aa5 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Mon, 15 Feb 2021 19:59:49 +0000 Subject: [PATCH 08/27] Formating. --- .../DataTable/modals/CreateEditColumn.svelte | 29 ++++++++++++------- .../popovers/ManageAccessPopover.svelte | 4 ++- .../modals/CreateTableModal.svelte | 27 ++++------------- .../builder/src/constants/backend/index.js | 8 +++-- 4 files changed, 33 insertions(+), 35 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 0045d2261c..86a321865d 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -10,7 +10,12 @@ import { cloneDeep } from "lodash/fp" import { backendUiStore } from "builderStore" import { TableNames, UNEDITABLE_USER_FIELDS } from "constants" - import { FIELDS, getAutoColumnInformation, buildAutoColumn, AUTO_COLUMN_SUB_TYPES } from "constants/backend" + import { + FIELDS, + getAutoColumnInformation, + buildAutoColumn, + AUTO_COLUMN_SUB_TYPES, + } from "constants/backend" import { notifier } from "builderStore/store/notifications" import ValuesList from "components/common/ValuesList.svelte" import DatePicker from "components/common/DatePicker.svelte" @@ -45,15 +50,21 @@ UNEDITABLE_USER_FIELDS.includes(field.name) // used to select what different options can be displayed for column type - $: canBeSearched = field.type !== 'link' && - field.subtype !== AUTO_COLUMN_SUB_TYPES.CREATED_BY && - field.subtype !== AUTO_COLUMN_SUB_TYPES.UPDATED_BY - $: canBeDisplay = field.type !== 'link' && field.type !== AUTO_COL - $: canBeRequired = field.type !== 'link' && !uneditable && field.type !== AUTO_COL + $: canBeSearched = + field.type !== "link" && + field.subtype !== AUTO_COLUMN_SUB_TYPES.CREATED_BY && + field.subtype !== AUTO_COLUMN_SUB_TYPES.UPDATED_BY + $: canBeDisplay = field.type !== "link" && field.type !== AUTO_COL + $: canBeRequired = + field.type !== "link" && !uneditable && field.type !== AUTO_COL async function saveColumn() { if (field.type === AUTO_COL) { - field = buildAutoColumn($backendUiStore.draftTable.name, field.name, field.subtype) + field = buildAutoColumn( + $backendUiStore.draftTable.name, + field.name, + field.subtype + ) } backendUiStore.update(state => { backendUiStore.actions.tables.saveField({ @@ -78,9 +89,7 @@ } function handleFieldConstraints(event) { - const definition = fieldDefinitions[ - event.target.value.toUpperCase() - ] + const definition = fieldDefinitions[event.target.value.toUpperCase()] if (!definition) { return } diff --git a/packages/builder/src/components/backend/DataTable/popovers/ManageAccessPopover.svelte b/packages/builder/src/components/backend/DataTable/popovers/ManageAccessPopover.svelte index e8f6f6df0b..ee8162c8d4 100644 --- a/packages/builder/src/components/backend/DataTable/popovers/ManageAccessPopover.svelte +++ b/packages/builder/src/components/backend/DataTable/popovers/ManageAccessPopover.svelte @@ -30,7 +30,9 @@
Who Can Access This Data?
- +
diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte index bcf7edbed0..7365f3e64e 100644 --- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte @@ -2,12 +2,7 @@ import { goto } from "@sveltech/routify" import { backendUiStore, store } from "builderStore" import { notifier } from "builderStore/store/notifications" - import { - Input, - Label, - ModalContent, - Toggle, - } from "@budibase/bbui" + import { Input, Label, ModalContent, Toggle } from "@budibase/bbui" import TableDataImport from "../TableDataImport.svelte" import analytics from "analytics" import screenTemplates from "builderStore/store/screenTemplates" @@ -109,23 +104,13 @@
- - - + + +
- - + +
diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index ba4ad09d9f..37eb0f82f7 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -79,7 +79,7 @@ export const FIELDS = { type: "array", presence: false, }, - } + }, } export const AUTO_COLUMN_SUB_TYPES = { @@ -120,14 +120,16 @@ export const Roles = { export const USER_TABLE_ID = "ta_users" export function isAutoColumnUserRelationship(subtype) { - return subtype === AUTO_COLUMN_SUB_TYPES.CREATED_BY || + return ( + subtype === AUTO_COLUMN_SUB_TYPES.CREATED_BY || subtype === AUTO_COLUMN_SUB_TYPES.UPDATED_BY + ) } export function getAutoColumnInformation(enabled = true) { let info = {} for (let [key, subtype] of Object.entries(AUTO_COLUMN_SUB_TYPES)) { - info[subtype] = {enabled, name: AUTO_COLUMN_DISPLAY_NAMES[key]} + info[subtype] = { enabled, name: AUTO_COLUMN_DISPLAY_NAMES[key] } } return info } From fd763694672de097bd27d84f96da960841874c0a Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 16 Feb 2021 10:07:15 +0000 Subject: [PATCH 09/27] Removing repeated constant. --- packages/builder/src/constants/backend/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index 37eb0f82f7..02e62e7e27 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -1,3 +1,5 @@ +import { TableNames } from "../index" + export const FIELDS = { STRING: { name: "Text", @@ -117,8 +119,6 @@ export const Roles = { BUILDER: "BUILDER", } -export const USER_TABLE_ID = "ta_users" - export function isAutoColumnUserRelationship(subtype) { return ( subtype === AUTO_COLUMN_SUB_TYPES.CREATED_BY || @@ -163,7 +163,7 @@ export function buildAutoColumn(tableName, name, subtype) { constraints, } if (isAutoColumnUserRelationship(subtype)) { - base.tableId = USER_TABLE_ID + base.tableId = TableNames.USERS base.fieldName = `${tableName}-${name}` } return base From 113de648334ca86cf36607801a358c57652a8253 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 16 Feb 2021 13:56:40 +0000 Subject: [PATCH 10/27] Some further UI work for auto-columns. --- .../backend/DataTable/DataTable.svelte | 4 ++ .../components/backend/DataTable/Table.svelte | 25 ++++++--- .../buttons/HideAutocolumnButton.svelte | 29 ++++++++++ .../DataTable/modals/CreateEditColumn.svelte | 6 +- .../modals/CreateTableModal.svelte | 2 +- .../builder/src/constants/backend/index.js | 43 --------------- .../builder/src/utilities/backend/index.js | 55 +++++++++++++++++++ packages/server/src/utilities/rowProcessor.js | 8 ++- 8 files changed, 114 insertions(+), 58 deletions(-) create mode 100644 packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte create mode 100644 packages/builder/src/utilities/backend/index.js diff --git a/packages/builder/src/components/backend/DataTable/DataTable.svelte b/packages/builder/src/components/backend/DataTable/DataTable.svelte index 577fda62a8..b0a1c0fdfe 100644 --- a/packages/builder/src/components/backend/DataTable/DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/DataTable.svelte @@ -6,6 +6,7 @@ import ExportButton from "./buttons/ExportButton.svelte" import EditRolesButton from "./buttons/EditRolesButton.svelte" import ManageAccessButton from "./buttons/ManageAccessButton.svelte" + import HideAutocolumnButton from "./buttons/HideAutocolumnButton.svelte" import * as api from "./api" import Table from "./Table.svelte" import { TableNames } from "constants" @@ -14,6 +15,7 @@ let data = [] let loading = false + let hideAutocolumns $: isUsersTable = $backendUiStore.selectedTable?._id === TableNames.USERS $: title = $backendUiStore.selectedTable.name @@ -41,6 +43,7 @@ tableId={$backendUiStore.selectedTable?._id} {data} allowEditing={true} + bind:hideAutocolumns {loading}> {#if schema && Object.keys(schema).length > 0} @@ -50,6 +53,7 @@ + {/if} {#if isUsersTable} diff --git a/packages/builder/src/components/backend/DataTable/Table.svelte b/packages/builder/src/components/backend/DataTable/Table.svelte index 070ebec080..825ed6af7b 100644 --- a/packages/builder/src/components/backend/DataTable/Table.svelte +++ b/packages/builder/src/components/backend/DataTable/Table.svelte @@ -24,6 +24,7 @@ export let allowEditing = false export let loading = false export let theme = "alpine" + export let hideAutocolumns = false let columnDefs = [] let selectedRows = [] @@ -85,7 +86,11 @@ return !(isUsersTable && ["email", "roleId"].includes(key)) } - Object.entries(schema || {}).forEach(([key, value]) => { + for (let [key, value] of Object.entries(schema || {})) { + // skip autocolumns if hiding + if (hideAutocolumns && value.autocolumn) { + continue + } result.push({ headerCheckboxSelection: false, headerComponent: TableHeader, @@ -108,7 +113,7 @@ resizable: true, minWidth: 200, }) - }) + } columnDefs = result } @@ -150,13 +155,15 @@
- (selectedRows = detail)} /> + {#key columnDefs.length} + (selectedRows = detail)} /> + {/key}
diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 86a321865d..eba5a99e87 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -12,10 +12,12 @@ import { TableNames, UNEDITABLE_USER_FIELDS } from "constants" import { FIELDS, - getAutoColumnInformation, - buildAutoColumn, AUTO_COLUMN_SUB_TYPES, } from "constants/backend" + import { + getAutoColumnInformation, + buildAutoColumn, + } from "utilities/backend" import { notifier } from "builderStore/store/notifications" import ValuesList from "components/common/ValuesList.svelte" import DatePicker from "components/common/DatePicker.svelte" diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte index 7365f3e64e..cba0cb02cd 100644 --- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte @@ -9,7 +9,7 @@ import { NEW_ROW_TEMPLATE } from "builderStore/store/screenTemplates/newRowScreen" import { ROW_DETAIL_TEMPLATE } from "builderStore/store/screenTemplates/rowDetailScreen" import { ROW_LIST_TEMPLATE } from "builderStore/store/screenTemplates/rowListScreen" - import { buildAutoColumn, getAutoColumnInformation } from "constants/backend" + import { buildAutoColumn, getAutoColumnInformation } from "utilities/backend" const defaultScreens = [ NEW_ROW_TEMPLATE, diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index 02e62e7e27..9fa9900a2f 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -125,46 +125,3 @@ export function isAutoColumnUserRelationship(subtype) { subtype === AUTO_COLUMN_SUB_TYPES.UPDATED_BY ) } - -export function getAutoColumnInformation(enabled = true) { - let info = {} - for (let [key, subtype] of Object.entries(AUTO_COLUMN_SUB_TYPES)) { - info[subtype] = { enabled, name: AUTO_COLUMN_DISPLAY_NAMES[key] } - } - return info -} - -export function buildAutoColumn(tableName, name, subtype) { - let type, constraints - switch (subtype) { - case AUTO_COLUMN_SUB_TYPES.UPDATED_BY: - case AUTO_COLUMN_SUB_TYPES.CREATED_BY: - type = FIELDS.LINK.type - constraints = FIELDS.LINK.constraints - break - case AUTO_COLUMN_SUB_TYPES.AUTO_ID: - type = FIELDS.NUMBER.type - constraints = FIELDS.NUMBER.constraints - break - default: - type = FIELDS.STRING.type - constraints = FIELDS.STRING.constraints - break - } - if (Object.values(AUTO_COLUMN_SUB_TYPES).indexOf(subtype) === -1) { - throw "Cannot build auto column with supplied subtype" - } - const base = { - name, - type, - subtype, - icon: "ri-magic-line", - autocolumn: true, - constraints, - } - if (isAutoColumnUserRelationship(subtype)) { - base.tableId = TableNames.USERS - base.fieldName = `${tableName}-${name}` - } - return base -} diff --git a/packages/builder/src/utilities/backend/index.js b/packages/builder/src/utilities/backend/index.js new file mode 100644 index 0000000000..464b25f5d3 --- /dev/null +++ b/packages/builder/src/utilities/backend/index.js @@ -0,0 +1,55 @@ +import { TableNames } from "../../constants" +import { + AUTO_COLUMN_DISPLAY_NAMES, + AUTO_COLUMN_SUB_TYPES, + FIELDS, + isAutoColumnUserRelationship +} from "../../constants/backend" + +export function getAutoColumnInformation(enabled = true) { + let info = {} + for (let [key, subtype] of Object.entries(AUTO_COLUMN_SUB_TYPES)) { + info[subtype] = { enabled, name: AUTO_COLUMN_DISPLAY_NAMES[key] } + } + return info +} + +export function buildAutoColumn(tableName, name, subtype) { + let type, constraints + switch (subtype) { + case AUTO_COLUMN_SUB_TYPES.UPDATED_BY: + case AUTO_COLUMN_SUB_TYPES.CREATED_BY: + type = FIELDS.LINK.type + constraints = FIELDS.LINK.constraints + break + case AUTO_COLUMN_SUB_TYPES.AUTO_ID: + type = FIELDS.NUMBER.type + constraints = FIELDS.NUMBER.constraints + break + case AUTO_COLUMN_SUB_TYPES.UPDATED_AT: + case AUTO_COLUMN_SUB_TYPES.CREATED_AT: + type = FIELDS.DATETIME.type + constraints = FIELDS.DATETIME.constraints + break + default: + type = FIELDS.STRING.type + constraints = FIELDS.STRING.constraints + break + } + if (Object.values(AUTO_COLUMN_SUB_TYPES).indexOf(subtype) === -1) { + throw "Cannot build auto column with supplied subtype" + } + const base = { + name, + type, + subtype, + icon: "ri-magic-line", + autocolumn: true, + constraints, + } + if (isAutoColumnUserRelationship(subtype)) { + base.tableId = TableNames.USERS + base.fieldName = `${tableName}-${name}` + } + return base +} \ No newline at end of file diff --git a/packages/server/src/utilities/rowProcessor.js b/packages/server/src/utilities/rowProcessor.js index ceb31daa3b..5374adf808 100644 --- a/packages/server/src/utilities/rowProcessor.js +++ b/packages/server/src/utilities/rowProcessor.js @@ -101,9 +101,11 @@ async function processAutoColumn(user, table, row) { row[key] = now break case AutoFieldSubTypes.AUTO_ID: - schema.lastID = !schema.lastID ? BASE_AUTO_ID : schema.lastID + 1 - row[key] = schema.lastID - tableUpdated = true + if (creating) { + schema.lastID = !schema.lastID ? BASE_AUTO_ID : schema.lastID + 1 + row[key] = schema.lastID + tableUpdated = true + } break } } From 1a9d16c8c7fe380bb7859430c10dfedc0b478d0c Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 16 Feb 2021 15:41:50 +0000 Subject: [PATCH 11/27] Updating where utilities are held in builder and then making user table relationships autocolumn aware too. --- .../backend/index.js => builderStore/utils.js} | 6 +++--- .../components/backend/DataTable/DataTable.svelte | 13 +++++++------ .../DataTable/buttons/HideAutocolumnButton.svelte | 3 ++- .../DataTable/modals/CreateEditColumn.svelte | 2 +- .../TableNavigator/modals/CreateTableModal.svelte | 2 +- packages/server/src/db/linkedRows/LinkController.js | 8 ++++++-- packages/server/src/db/linkedRows/linkUtils.js | 1 - 7 files changed, 20 insertions(+), 15 deletions(-) rename packages/builder/src/{utilities/backend/index.js => builderStore/utils.js} (94%) diff --git a/packages/builder/src/utilities/backend/index.js b/packages/builder/src/builderStore/utils.js similarity index 94% rename from packages/builder/src/utilities/backend/index.js rename to packages/builder/src/builderStore/utils.js index 464b25f5d3..a95ba3a601 100644 --- a/packages/builder/src/utilities/backend/index.js +++ b/packages/builder/src/builderStore/utils.js @@ -1,10 +1,10 @@ -import { TableNames } from "../../constants" +import { TableNames } from "../constants" import { AUTO_COLUMN_DISPLAY_NAMES, AUTO_COLUMN_SUB_TYPES, FIELDS, isAutoColumnUserRelationship -} from "../../constants/backend" +} from "../constants/backend" export function getAutoColumnInformation(enabled = true) { let info = {} @@ -52,4 +52,4 @@ export function buildAutoColumn(tableName, name, subtype) { base.fieldName = `${tableName}-${name}` } return base -} \ No newline at end of file +} diff --git a/packages/builder/src/components/backend/DataTable/DataTable.svelte b/packages/builder/src/components/backend/DataTable/DataTable.svelte index b0a1c0fdfe..505bfeb6d9 100644 --- a/packages/builder/src/components/backend/DataTable/DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/DataTable.svelte @@ -13,10 +13,9 @@ import CreateEditUser from "./modals/CreateEditUser.svelte" import CreateEditRow from "./modals/CreateEditRow.svelte" + let hideAutocolumns let data = [] let loading = false - let hideAutocolumns - $: isUsersTable = $backendUiStore.selectedTable?._id === TableNames.USERS $: title = $backendUiStore.selectedTable.name $: schema = $backendUiStore.selectedTable.schema @@ -52,10 +51,12 @@ modalContentComponent={isUsersTable ? CreateEditUser : CreateEditRow} /> - + {#if isUsersTable} + + {/if} + + {/if} - {#if isUsersTable} - - {/if} + diff --git a/packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte index ec8c85da62..79fbb15b54 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte @@ -13,10 +13,11 @@
- {#if hideAutocolumns} + Show Auto Columns {:else} + Hide Auto Columns {/if} diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index eba5a99e87..811c005fca 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -17,7 +17,7 @@ import { getAutoColumnInformation, buildAutoColumn, - } from "utilities/backend" + } from "builderStore/utils" import { notifier } from "builderStore/store/notifications" import ValuesList from "components/common/ValuesList.svelte" import DatePicker from "components/common/DatePicker.svelte" diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte index cba0cb02cd..abc53d9813 100644 --- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte @@ -6,10 +6,10 @@ import TableDataImport from "../TableDataImport.svelte" import analytics from "analytics" import screenTemplates from "builderStore/store/screenTemplates" + import { buildAutoColumn, getAutoColumnInformation } from "builderStore/utils" import { NEW_ROW_TEMPLATE } from "builderStore/store/screenTemplates/newRowScreen" import { ROW_DETAIL_TEMPLATE } from "builderStore/store/screenTemplates/rowDetailScreen" import { ROW_LIST_TEMPLATE } from "builderStore/store/screenTemplates/rowListScreen" - import { buildAutoColumn, getAutoColumnInformation } from "utilities/backend" const defaultScreens = [ NEW_ROW_TEMPLATE, diff --git a/packages/server/src/db/linkedRows/LinkController.js b/packages/server/src/db/linkedRows/LinkController.js index 54a0bfdd58..d202c07d63 100644 --- a/packages/server/src/db/linkedRows/LinkController.js +++ b/packages/server/src/db/linkedRows/LinkController.js @@ -259,14 +259,18 @@ class LinkController { } catch (err) { continue } - // create the link field in the other table - linkedTable.schema[field.fieldName] = { + const linkConfig = { name: field.fieldName, type: FieldTypes.LINK, // these are the props of the table that initiated the link tableId: table._id, fieldName: fieldName, } + if (field.autocolumn) { + linkConfig.autocolumn = field.autocolumn + } + // create the link field in the other table + linkedTable.schema[field.fieldName] = linkConfig const response = await this._db.put(linkedTable) // special case for when linking back to self, make sure rev updated if (linkedTable._id === table._id) { diff --git a/packages/server/src/db/linkedRows/linkUtils.js b/packages/server/src/db/linkedRows/linkUtils.js index c07c17eda7..fba12aec0b 100644 --- a/packages/server/src/db/linkedRows/linkUtils.js +++ b/packages/server/src/db/linkedRows/linkUtils.js @@ -1,7 +1,6 @@ const CouchDB = require("../index") const Sentry = require("@sentry/node") const { ViewNames, getQueryIndex } = require("../utils") -const { FieldTypes } = require("../../constants") /** * Only needed so that boolean parameters are being used for includeDocs From cafb50bce6d0099e134ec1c86e16ae8aa0da7ccc Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 16 Feb 2021 16:22:25 +0000 Subject: [PATCH 12/27] Fixing some UI stuff after discussing with Andrew K - making sure auto-columns always appear at end of table. --- .../components/backend/DataTable/Table.svelte | 5 +-- .../DataTable/TableHeader/TableHeader.svelte | 32 ++++++++++++++----- .../buttons/HideAutocolumnButton.svelte | 4 +-- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/Table.svelte b/packages/builder/src/components/backend/DataTable/Table.svelte index 825ed6af7b..959f343546 100644 --- a/packages/builder/src/components/backend/DataTable/Table.svelte +++ b/packages/builder/src/components/backend/DataTable/Table.svelte @@ -100,6 +100,7 @@ }, headerName: value.displayFieldName || key, field: key, + autocolumn: !!value.autocolumn, sortable: true, cellRenderer: getRenderer({ schema: schema[key], @@ -114,8 +115,8 @@ minWidth: 200, }) } - - columnDefs = result + // sort auto-columns to the end if they are present + columnDefs = result.filter(col => !col.autocolumn).concat(result.filter(col => col.autocolumn)) } function selectRelationship(row, fieldName) { diff --git a/packages/builder/src/components/backend/DataTable/TableHeader/TableHeader.svelte b/packages/builder/src/components/backend/DataTable/TableHeader/TableHeader.svelte index f6ef036f40..49d342dd71 100644 --- a/packages/builder/src/components/backend/DataTable/TableHeader/TableHeader.svelte +++ b/packages/builder/src/components/backend/DataTable/TableHeader/TableHeader.svelte @@ -59,8 +59,13 @@ on:mouseover={() => (hovered = true)} on:mouseleave={() => (hovered = false)}>
- {displayName} - +
+ {#if field.autocolumn} + + {/if} + {displayName} +
+
{#if editable && hovered} - + {/if} - + @@ -117,18 +122,29 @@ top: 2px; } - i { + .icon { transition: 0.2s all; font-size: var(--font-size-m); font-weight: 500; } - i:hover { + .col-icon { + display: flex; + } + + .auto { + font-size: var(--font-size-xs); + transition: none; + margin-right: 6px; + margin-top: 2px; + } + + .icon:hover { color: var(--blue); } - i.active, - i:hover { + .icon.active, + .icon:hover { color: var(--blue); } diff --git a/packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte index 79fbb15b54..9c114ae436 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte @@ -14,10 +14,10 @@
{#if hideAutocolumns} - + Show Auto Columns {:else} - + Hide Auto Columns {/if} From 79bb9f3822db21a7145edc8843295cf8b1816a9b Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 16 Feb 2021 16:34:54 +0000 Subject: [PATCH 13/27] Updating BBUI after Andrew K fixed an issue with buttons and borders transitioning (causing buttons to expand/contract etc). --- packages/builder/package.json | 2 +- packages/builder/yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/builder/package.json b/packages/builder/package.json index 85580fa40b..a3249e9d9f 100644 --- a/packages/builder/package.json +++ b/packages/builder/package.json @@ -63,7 +63,7 @@ } }, "dependencies": { - "@budibase/bbui": "^1.58.5", + "@budibase/bbui": "^1.58.8", "@budibase/client": "^0.7.7", "@budibase/colorpicker": "1.0.1", "@budibase/string-templates": "^0.7.7", diff --git a/packages/builder/yarn.lock b/packages/builder/yarn.lock index d44a9e63c0..e6cd11a1a2 100644 --- a/packages/builder/yarn.lock +++ b/packages/builder/yarn.lock @@ -842,10 +842,10 @@ lodash "^4.17.19" to-fast-properties "^2.0.0" -"@budibase/bbui@^1.58.5": - version "1.58.5" - resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.58.5.tgz#c9ce712941760825c7774a1de77594e989db4561" - integrity sha512-0j1I7BetJ2GzB1BXKyvvlkuFphLmADJh2U/Ihubwxx5qUDY8REoVzLgAB4c24zt0CGVTF9VMmOoMLd0zD0QwdQ== +"@budibase/bbui@^1.58.8": + version "1.58.8" + resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.58.8.tgz#c50c54a9baa4fd6d013401586162d4c6354af1a7" + integrity sha512-4qQ6ijMk3XeUOUZwS6dwT51H0T/vfEHhHhNI5JXCQ1TzEX6CUeLNAklY0E/iIMtNteOgdipEkvXGBxh72yX6FQ== dependencies: markdown-it "^12.0.2" quill "^1.3.7" From 258b2138c3a3092c24454031a4327ce1dd65cb62 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 16 Feb 2021 16:38:36 +0000 Subject: [PATCH 14/27] Formatting. --- packages/builder/src/builderStore/utils.js | 2 +- .../src/components/backend/DataTable/DataTable.svelte | 1 - .../src/components/backend/DataTable/Table.svelte | 4 +++- .../backend/DataTable/TableHeader/TableHeader.svelte | 4 +--- .../DataTable/buttons/HideAutocolumnButton.svelte | 7 ++----- .../backend/DataTable/modals/CreateEditColumn.svelte | 10 ++-------- 6 files changed, 9 insertions(+), 19 deletions(-) diff --git a/packages/builder/src/builderStore/utils.js b/packages/builder/src/builderStore/utils.js index a95ba3a601..3ddf6fb667 100644 --- a/packages/builder/src/builderStore/utils.js +++ b/packages/builder/src/builderStore/utils.js @@ -3,7 +3,7 @@ import { AUTO_COLUMN_DISPLAY_NAMES, AUTO_COLUMN_SUB_TYPES, FIELDS, - isAutoColumnUserRelationship + isAutoColumnUserRelationship, } from "../constants/backend" export function getAutoColumnInformation(enabled = true) { diff --git a/packages/builder/src/components/backend/DataTable/DataTable.svelte b/packages/builder/src/components/backend/DataTable/DataTable.svelte index 505bfeb6d9..9197026f4a 100644 --- a/packages/builder/src/components/backend/DataTable/DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/DataTable.svelte @@ -58,5 +58,4 @@ {/if} - diff --git a/packages/builder/src/components/backend/DataTable/Table.svelte b/packages/builder/src/components/backend/DataTable/Table.svelte index 959f343546..702a1d80dc 100644 --- a/packages/builder/src/components/backend/DataTable/Table.svelte +++ b/packages/builder/src/components/backend/DataTable/Table.svelte @@ -116,7 +116,9 @@ }) } // sort auto-columns to the end if they are present - columnDefs = result.filter(col => !col.autocolumn).concat(result.filter(col => col.autocolumn)) + columnDefs = result + .filter(col => !col.autocolumn) + .concat(result.filter(col => col.autocolumn)) } function selectRelationship(row, fieldName) { diff --git a/packages/builder/src/components/backend/DataTable/TableHeader/TableHeader.svelte b/packages/builder/src/components/backend/DataTable/TableHeader/TableHeader.svelte index 49d342dd71..2d65010a62 100644 --- a/packages/builder/src/components/backend/DataTable/TableHeader/TableHeader.svelte +++ b/packages/builder/src/components/backend/DataTable/TableHeader/TableHeader.svelte @@ -60,9 +60,7 @@ on:mouseleave={() => (hovered = false)}>
- {#if field.autocolumn} - - {/if} + {#if field.autocolumn}{/if} {displayName}
diff --git a/packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte index 9c114ae436..1af93dd4f8 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte @@ -14,12 +14,9 @@
{#if hideAutocolumns} - + Show Auto Columns - {:else} - - Hide Auto Columns - {/if} + {:else} Hide Auto Columns{/if}
diff --git a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte index 811c005fca..8ae492226f 100644 --- a/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte +++ b/packages/builder/src/components/backend/DataTable/modals/CreateEditColumn.svelte @@ -10,14 +10,8 @@ import { cloneDeep } from "lodash/fp" import { backendUiStore } from "builderStore" import { TableNames, UNEDITABLE_USER_FIELDS } from "constants" - import { - FIELDS, - AUTO_COLUMN_SUB_TYPES, - } from "constants/backend" - import { - getAutoColumnInformation, - buildAutoColumn, - } from "builderStore/utils" + import { FIELDS, AUTO_COLUMN_SUB_TYPES } from "constants/backend" + import { getAutoColumnInformation, buildAutoColumn } from "builderStore/utils" import { notifier } from "builderStore/store/notifications" import ValuesList from "components/common/ValuesList.svelte" import DatePicker from "components/common/DatePicker.svelte" From b7b1b1714847954f9c67126b1edb71dc018afff9 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 16 Feb 2021 16:46:18 +0000 Subject: [PATCH 15/27] Fixing test case. --- packages/server/src/api/routes/tests/row.spec.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/server/src/api/routes/tests/row.spec.js b/packages/server/src/api/routes/tests/row.spec.js index c411016b71..b8f74c103e 100644 --- a/packages/server/src/api/routes/tests/row.spec.js +++ b/packages/server/src/api/routes/tests/row.spec.js @@ -7,7 +7,7 @@ const { createAttachmentTable, makeBasicRow, } = require("./couchTestUtils"); -const { enrichRows } = require("../../../utilities") +const { outputProcessing } = require("../../../utilities/rowProcessor") const env = require("../../../environment") describe("/rows", () => { @@ -285,7 +285,7 @@ describe("/rows", () => { link: [firstRow._id], tableId: table._id, })).body - const enriched = await enrichRows(appId, table, [secondRow]) + const enriched = await outputProcessing(appId, table, [secondRow]) expect(enriched[0].link.length).toBe(1) expect(enriched[0].link[0]).toBe(firstRow._id) }) @@ -304,7 +304,7 @@ describe("/rows", () => { // the environment needs configured for this env.CLOUD = 1 env.SELF_HOSTED = 1 - const enriched = await enrichRows(appId, table, [row]) + const enriched = await outputProcessing(appId, table, [row]) expect(enriched[0].attachment[0].url).toBe(`/app-assets/assets/${appId}/test/thing`) // remove env config env.CLOUD = undefined From 811aa5ced372d3b9cf63f25efa91a0daaaa03db3 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 16 Feb 2021 16:52:30 +0000 Subject: [PATCH 16/27] Fixing minor issue with auto-column toggles. --- .../modals/CreateTableModal.svelte | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte index abc53d9813..1bd1a29eb7 100644 --- a/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte +++ b/packages/builder/src/components/backend/TableNavigator/modals/CreateTableModal.svelte @@ -104,13 +104,21 @@
- - - + + +
- - + +
From 7b389180c7a16d537142cb214332be07617de076 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 16 Feb 2021 17:21:34 +0000 Subject: [PATCH 17/27] Fixing an issue with complex relationships that are heavily intra-related. --- packages/server/src/api/controllers/row.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/server/src/api/controllers/row.js b/packages/server/src/api/controllers/row.js index 078e4b722f..4f195ad4e8 100644 --- a/packages/server/src/api/controllers/row.js +++ b/packages/server/src/api/controllers/row.js @@ -387,8 +387,13 @@ exports.fetchEnrichedRow = async function(ctx) { for (let fieldName of Object.keys(table.schema)) { let field = table.schema[fieldName] if (field.type === FieldTypes.LINK) { - row[fieldName] = linkedRows.filter( - linkRow => linkRow.tableId === field.tableId + // find the links that pertain to this field, get their indexes + const linkIndexes = linkVals + .filter(link => link.fieldName === fieldName) + .map(link => linkVals.indexOf(link)) + // find the rows that the links state are linked to this field + row[fieldName] = linkedRows.filter((linkRow, index) => + linkIndexes.includes(index) ) } } From 905f913da386650fbabc2963ec79c718818e76b2 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 16 Feb 2021 17:35:16 +0000 Subject: [PATCH 18/27] Fixing linting. --- packages/builder/src/constants/backend/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js index 9fa9900a2f..3249215918 100644 --- a/packages/builder/src/constants/backend/index.js +++ b/packages/builder/src/constants/backend/index.js @@ -1,5 +1,3 @@ -import { TableNames } from "../index" - export const FIELDS = { STRING: { name: "Text", From ad49a428b5138367eaf3903758ec408aaab376a0 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 16 Feb 2021 17:55:21 +0000 Subject: [PATCH 19/27] Fixing an issue with Cypress tests. --- .../src/components/backend/DataTable/Table.svelte | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/Table.svelte b/packages/builder/src/components/backend/DataTable/Table.svelte index 702a1d80dc..51dedf1a4d 100644 --- a/packages/builder/src/components/backend/DataTable/Table.svelte +++ b/packages/builder/src/components/backend/DataTable/Table.svelte @@ -91,7 +91,7 @@ if (hideAutocolumns && value.autocolumn) { continue } - result.push({ + let config = { headerCheckboxSelection: false, headerComponent: TableHeader, headerComponentParams: { @@ -100,7 +100,6 @@ }, headerName: value.displayFieldName || key, field: key, - autocolumn: !!value.autocolumn, sortable: true, cellRenderer: getRenderer({ schema: schema[key], @@ -113,12 +112,15 @@ autoHeight: true, resizable: true, minWidth: 200, - }) + } + // sort auto-columns to the end if they are present + if (value.autocolumn) { + result.push(config) + } else { + result.unshift(config) + } } - // sort auto-columns to the end if they are present columnDefs = result - .filter(col => !col.autocolumn) - .concat(result.filter(col => col.autocolumn)) } function selectRelationship(row, fieldName) { From 1e39a873b23c651d5a38bc16ec970a7a4049c9be Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 16 Feb 2021 18:29:38 +0000 Subject: [PATCH 20/27] Fixing issue with cypress test, fixing a 409 conflict that could occur in table saving - shouldn't happen now. --- .../builder/src/components/backend/DataTable/DataTable.svelte | 2 +- packages/builder/src/components/backend/DataTable/Table.svelte | 2 +- .../backend/DataTable/buttons/HideAutocolumnButton.svelte | 2 +- packages/server/src/api/controllers/table.js | 2 ++ 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/DataTable.svelte b/packages/builder/src/components/backend/DataTable/DataTable.svelte index 9197026f4a..5f6f230210 100644 --- a/packages/builder/src/components/backend/DataTable/DataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/DataTable.svelte @@ -13,7 +13,7 @@ import CreateEditUser from "./modals/CreateEditUser.svelte" import CreateEditRow from "./modals/CreateEditRow.svelte" - let hideAutocolumns + let hideAutocolumns = true let data = [] let loading = false $: isUsersTable = $backendUiStore.selectedTable?._id === TableNames.USERS diff --git a/packages/builder/src/components/backend/DataTable/Table.svelte b/packages/builder/src/components/backend/DataTable/Table.svelte index 51dedf1a4d..195876cf05 100644 --- a/packages/builder/src/components/backend/DataTable/Table.svelte +++ b/packages/builder/src/components/backend/DataTable/Table.svelte @@ -24,7 +24,7 @@ export let allowEditing = false export let loading = false export let theme = "alpine" - export let hideAutocolumns = false + export let hideAutocolumns let columnDefs = [] let selectedRows = [] diff --git a/packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte b/packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte index 1af93dd4f8..fc50bfbfef 100644 --- a/packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte +++ b/packages/builder/src/components/backend/DataTable/buttons/HideAutocolumnButton.svelte @@ -1,7 +1,7 @@ - +
{#if view.calculation} {/if} +
From d700cc254c088eb37664b7b251e75322359140f7 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 16 Feb 2021 18:47:25 +0000 Subject: [PATCH 22/27] Formatting. --- .../components/backend/DataTable/ViewDataTable.svelte | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte b/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte index c1a75e4517..4ac5ec80a8 100644 --- a/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte +++ b/packages/builder/src/components/backend/DataTable/ViewDataTable.svelte @@ -50,13 +50,18 @@ } - +
{#if view.calculation} {/if} - +
From 6e198a8ce1f5b9c7501d3853ecacf6c8678c04c3 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 16 Feb 2021 18:58:59 +0000 Subject: [PATCH 23/27] Fixing automation issue - now autocolumns hidden there too. --- .../automation/SetupPanel/RowSelector.svelte | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte b/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte index 43fa4b8bf1..7b010f2ecb 100644 --- a/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte +++ b/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte @@ -30,20 +30,22 @@ {#if schemaFields.length}
{#each schemaFields as [field, schema]} - {#if schemaHasOptions(schema)} - - {:else if schema.type === 'string' || schema.type === 'number'} - + {#if !schema.autocolumn} + {#if schemaHasOptions(schema)} + + {:else if schema.type === 'string' || schema.type === 'number'} + + {/if} {/if} {/each}
From 6d519e1ad92a536c66227a0bf8c0bb79196941de Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 16 Feb 2021 21:40:35 +0000 Subject: [PATCH 24/27] Updating cypress test to fix in CI. --- packages/builder/cypress/integration/createView.spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/builder/cypress/integration/createView.spec.js b/packages/builder/cypress/integration/createView.spec.js index ba18368ecf..1c13635ebd 100644 --- a/packages/builder/cypress/integration/createView.spec.js +++ b/packages/builder/cypress/integration/createView.spec.js @@ -62,9 +62,10 @@ context("Create a View", () => { expect(headers).to.deep.eq([ 'avg', 'sumsqr', 'count', 'max', 'min', 'sum', 'field' ]) }) cy.get(".ag-cell").then($values => { - const values = Array.from($values).map(header => + let values = Array.from($values).map(header => header.textContent.trim() ) + values = values.filter(value => value !== "") expect(values).to.deep.eq([ '31', '5347', '5', '49', '20', '155', 'age' ]) }) }) From 91926d17d995c71a5d72f4a2c9d32824d45e79f5 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Tue, 16 Feb 2021 22:12:58 +0000 Subject: [PATCH 25/27] Adding delay to cypress test to allow time for statistics to calculate. --- packages/builder/cypress/integration/createView.spec.js | 2 +- .../backend/DataTable/popovers/CalculatePopover.svelte | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/builder/cypress/integration/createView.spec.js b/packages/builder/cypress/integration/createView.spec.js index 1c13635ebd..bdc89bec53 100644 --- a/packages/builder/cypress/integration/createView.spec.js +++ b/packages/builder/cypress/integration/createView.spec.js @@ -53,6 +53,7 @@ context("Create a View", () => { cy.wait(50) cy.get(".menu-container").find("select").eq(1).select("age") cy.contains("Save").click() + cy.wait(100) cy.get(".ag-center-cols-viewport").scrollTo("100%") cy.get("[data-cy=table-header]").then($headers => { expect($headers).to.have.length(7) @@ -65,7 +66,6 @@ context("Create a View", () => { let values = Array.from($values).map(header => header.textContent.trim() ) - values = values.filter(value => value !== "") expect(values).to.deep.eq([ '31', '5347', '5', '49', '20', '155', 'age' ]) }) }) diff --git a/packages/builder/src/components/backend/DataTable/popovers/CalculatePopover.svelte b/packages/builder/src/components/backend/DataTable/popovers/CalculatePopover.svelte index 23965a63e0..8d98e22806 100644 --- a/packages/builder/src/components/backend/DataTable/popovers/CalculatePopover.svelte +++ b/packages/builder/src/components/backend/DataTable/popovers/CalculatePopover.svelte @@ -30,7 +30,9 @@ Object.keys(viewTable.schema).filter( field => view.calculation === "count" || - viewTable.schema[field].type === "number" + // don't want to perform calculations based on auto ID + (viewTable.schema[field].type === "number" && + !viewTable.schema[field].autocolumn) ) function saveView() { From 28e244fa1669dd02fe64a0f4836f6e17b7fb6478 Mon Sep 17 00:00:00 2001 From: mike12345567 Date: Wed, 17 Feb 2021 10:36:36 +0000 Subject: [PATCH 26/27] Fixing an edge case, create table with auto ID, create some rows, then update a column and try to create another row, ID generation would have reset, this makes sure internal changes to the table are kept. --- packages/server/src/api/controllers/table.js | 28 +++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/packages/server/src/api/controllers/table.js b/packages/server/src/api/controllers/table.js index d0bc4c0bee..88f06f14ce 100644 --- a/packages/server/src/api/controllers/table.js +++ b/packages/server/src/api/controllers/table.js @@ -8,7 +8,7 @@ const { generateRowID, } = require("../../db/utils") const { isEqual } = require("lodash/fp") -const { FieldTypes } = require("../../constants") +const { FieldTypes, AutoFieldSubTypes } = require("../../constants") async function checkForColumnUpdates(db, oldTable, updatedTable) { let updatedRows @@ -40,6 +40,27 @@ async function checkForColumnUpdates(db, oldTable, updatedTable) { return updatedRows } +// makes sure the passed in table isn't going to reset the auto ID +function makeSureTableUpToDate(table, tableToSave) { + if (!table) { + return tableToSave + } + // sure sure rev is up to date + tableToSave._rev = table._rev + // make sure auto IDs are always updated - these are internal + // so the client may not know they have changed + for (let [field, column] of Object.entries(table.schema)) { + if ( + column.autocolumn && + column.subtype === AutoFieldSubTypes.AUTO_ID && + tableToSave.schema[field] + ) { + tableToSave.schema[field].lastID = column.lastID + } + } + return tableToSave +} + exports.fetch = async function(ctx) { const db = new CouchDB(ctx.user.appId) const body = await db.allDocs( @@ -59,7 +80,7 @@ exports.save = async function(ctx) { const appId = ctx.user.appId const db = new CouchDB(appId) const { dataImport, ...rest } = ctx.request.body - const tableToSave = { + let tableToSave = { type: "table", _id: generateTableID(), views: {}, @@ -70,8 +91,7 @@ exports.save = async function(ctx) { let oldTable if (ctx.request.body && ctx.request.body._id) { oldTable = await db.get(ctx.request.body._id) - // update _rev just to make sure its always accurate - tableToSave._rev = oldTable._rev + tableToSave = makeSureTableUpToDate(oldTable, tableToSave) } // make sure that types don't change of a column, have to remove From a35c34fd82ad8bfdee80a6d2bf27b0a578007178 Mon Sep 17 00:00:00 2001 From: Andrew Kingston Date: Wed, 17 Feb 2021 15:16:44 +0000 Subject: [PATCH 27/27] Add disabled setting for forms and fields, and force autocolumns in forms to be disabled --- packages/standard-components/manifest.json | 54 +++++++++++++++++++ .../src/forms/AttachmentField.svelte | 13 ++++- .../src/forms/BooleanField.svelte | 3 ++ .../src/forms/DateTimeField.svelte | 9 +++- .../src/forms/Field.svelte | 3 +- .../standard-components/src/forms/Form.svelte | 24 +++++++-- .../src/forms/LongFormField.svelte | 12 ++++- .../src/forms/OptionsField.svelte | 2 + .../src/forms/Picker.svelte | 1 + .../src/forms/RelationshipField.svelte | 2 + .../src/forms/StringField.svelte | 8 ++- 11 files changed, 122 insertions(+), 9 deletions(-) diff --git a/packages/standard-components/manifest.json b/packages/standard-components/manifest.json index 4d80ee7b2b..229fd0242e 100644 --- a/packages/standard-components/manifest.json +++ b/packages/standard-components/manifest.json @@ -1135,6 +1135,12 @@ "value": "spectrum--large" } ] + }, + { + "type": "boolean", + "label": "Disabled", + "key": "disabled", + "defaultValue": false } ] }, @@ -1185,6 +1191,12 @@ "type": "text", "label": "Placeholder", "key": "placeholder" + }, + { + "type": "boolean", + "label": "Disabled", + "key": "disabled", + "defaultValue": false } ] }, @@ -1207,6 +1219,12 @@ "type": "text", "label": "Placeholder", "key": "placeholder" + }, + { + "type": "boolean", + "label": "Disabled", + "key": "disabled", + "defaultValue": false } ] }, @@ -1230,6 +1248,12 @@ "label": "Placeholder", "key": "placeholder", "placeholder": "Choose an option" + }, + { + "type": "boolean", + "label": "Disabled", + "key": "disabled", + "defaultValue": false } ] }, @@ -1252,6 +1276,12 @@ "type": "text", "label": "Text", "key": "text" + }, + { + "type": "boolean", + "label": "Disabled", + "key": "disabled", + "defaultValue": false } ] }, @@ -1275,6 +1305,12 @@ "label": "Placeholder", "key": "placeholder", "placeholder": "Type something..." + }, + { + "type": "boolean", + "label": "Disabled", + "key": "disabled", + "defaultValue": false } ] }, @@ -1303,6 +1339,12 @@ "label": "Show Time", "key": "enableTime", "defaultValue": true + }, + { + "type": "boolean", + "label": "Disabled", + "key": "disabled", + "defaultValue": false } ] }, @@ -1320,6 +1362,12 @@ "type": "text", "label": "Label", "key": "label" + }, + { + "type": "boolean", + "label": "Disabled", + "key": "disabled", + "defaultValue": false } ] }, @@ -1337,6 +1385,12 @@ "type": "text", "label": "Label", "key": "label" + }, + { + "type": "boolean", + "label": "Disabled", + "key": "disabled", + "defaultValue": false } ] } diff --git a/packages/standard-components/src/forms/AttachmentField.svelte b/packages/standard-components/src/forms/AttachmentField.svelte index 6cf16c109e..5df1d40eb9 100644 --- a/packages/standard-components/src/forms/AttachmentField.svelte +++ b/packages/standard-components/src/forms/AttachmentField.svelte @@ -5,6 +5,7 @@ export let field export let label + export let disabled = false let fieldState let fieldApi @@ -24,11 +25,21 @@ {#if mounted} - +
+ +
{/if}
+ + diff --git a/packages/standard-components/src/forms/BooleanField.svelte b/packages/standard-components/src/forms/BooleanField.svelte index d7c43ea63c..6e38c22be4 100644 --- a/packages/standard-components/src/forms/BooleanField.svelte +++ b/packages/standard-components/src/forms/BooleanField.svelte @@ -5,6 +5,7 @@ export let field export let label export let text + export let disabled = false let fieldState let fieldApi @@ -17,6 +18,7 @@ - + {#if fieldState} {#if !$fieldState.valid} { + registerField: (field, defaultValue = null, fieldDisabled = false) => { if (!field) { return } + + // Auto columns are always disabled + const isAutoColumn = !!schema?.[field]?.autocolumn + if (fieldMap[field] != null) { + // Update disabled property just so that toggling the disabled field + // state in the builder makes updates in real time. + // We only need this because of optimisations which prevent fully + // remounting when settings change. + fieldMap[field].fieldState.update(state => { + state.disabled = disabled || fieldDisabled || isAutoColumn + return state + }) return fieldMap[field] } @@ -47,7 +60,11 @@ const validate = createValidatorFromConstraints(constraints, field, table) fieldMap[field] = { - fieldState: makeFieldState(field, defaultValue), + fieldState: makeFieldState( + field, + defaultValue, + disabled || fieldDisabled || isAutoColumn + ), fieldApi: makeFieldApi(field, defaultValue, validate), fieldSchema: schema?.[field] ?? {}, } @@ -115,13 +132,14 @@ } // Creates observable state data about a specific field - const makeFieldState = (field, defaultValue) => { + const makeFieldState = (field, defaultValue, fieldDisabled) => { return writable({ field, fieldId: `id-${generateID()}`, value: initialValues[field] ?? defaultValue, error: null, valid: true, + disabled: fieldDisabled, }) } diff --git a/packages/standard-components/src/forms/LongFormField.svelte b/packages/standard-components/src/forms/LongFormField.svelte index d22be1b6a9..a5ef5d94ed 100644 --- a/packages/standard-components/src/forms/LongFormField.svelte +++ b/packages/standard-components/src/forms/LongFormField.svelte @@ -6,6 +6,7 @@ export let field export let label export let placeholder + export let disabled = false let fieldState let fieldApi @@ -41,12 +42,13 @@ {#if mounted} -
+
{/if} @@ -68,4 +70,12 @@ div :global(.ql-editor p) { word-break: break-all; } + + div.disabled { + pointer-events: none !important; + background-color: rgb(244, 244, 244); + } + div.disabled :global(.ql-container *) { + color: var(--spectrum-alias-text-color-disabled) !important; + } diff --git a/packages/standard-components/src/forms/OptionsField.svelte b/packages/standard-components/src/forms/OptionsField.svelte index 48feb77b54..82f213686b 100644 --- a/packages/standard-components/src/forms/OptionsField.svelte +++ b/packages/standard-components/src/forms/OptionsField.svelte @@ -5,6 +5,7 @@ export let field export let label export let placeholder + export let disabled = false let fieldState let fieldApi @@ -26,6 +27,7 @@ {#if fieldState} -
+
{#if !$fieldState.valid}