@@ -260,6 +258,8 @@
bind:this={iframe}
src="/app/preview"
class:hidden={loading || error}
+ class:tablet={$previewStore.previewDevice === "tablet"}
+ class:mobile={$previewStore.previewDevice === "mobile"}
/>
diff --git a/packages/frontend-core/src/components/index.js b/packages/frontend-core/src/components/index.js
index f71420b12b..f724e1e4d9 100644
--- a/packages/frontend-core/src/components/index.js
+++ b/packages/frontend-core/src/components/index.js
@@ -5,4 +5,3 @@ export { default as UserAvatar } from "./UserAvatar.svelte"
export { default as UserAvatars } from "./UserAvatars.svelte"
export { default as Updating } from "./Updating.svelte"
export { Grid } from "./grid"
-export { default as ClientAppSkeleton } from "./ClientAppSkeleton.svelte"
diff --git a/packages/frontend-core/src/themes/midnight.css b/packages/frontend-core/src/themes/midnight.css
index cf6a4fbd13..e311452262 100644
--- a/packages/frontend-core/src/themes/midnight.css
+++ b/packages/frontend-core/src/themes/midnight.css
@@ -17,8 +17,5 @@
--modal-background: var(--spectrum-global-color-gray-50);
--drop-shadow: rgba(0, 0, 0, 0.25) !important;
--spectrum-global-color-blue-100: rgba(35, 40, 50) !important;
-
- --spectrum-alias-background-color-secondary: var(--spectrum-global-color-gray-75);
- --spectrum-alias-background-color-primary: var(--spectrum-global-color-gray-100);
}
diff --git a/packages/frontend-core/src/themes/nord.css b/packages/frontend-core/src/themes/nord.css
index bc142db0fd..d47dbe8aa8 100644
--- a/packages/frontend-core/src/themes/nord.css
+++ b/packages/frontend-core/src/themes/nord.css
@@ -50,7 +50,4 @@
--modal-background: var(--spectrum-global-color-gray-50);
--drop-shadow: rgba(0, 0, 0, 0.15) !important;
--spectrum-global-color-blue-100: rgb(56, 65, 84) !important;
-
- --spectrum-alias-background-color-secondary: var(--spectrum-global-color-gray-75);
- --spectrum-alias-background-color-primary: var(--spectrum-global-color-gray-100);
}
diff --git a/packages/server/package.json b/packages/server/package.json
index 4f1a9fb3cc..45980a4be6 100644
--- a/packages/server/package.json
+++ b/packages/server/package.json
@@ -52,7 +52,6 @@
"@budibase/pro": "0.0.0",
"@budibase/shared-core": "0.0.0",
"@budibase/string-templates": "0.0.0",
- "@budibase/frontend-core": "0.0.0",
"@budibase/types": "0.0.0",
"@bull-board/api": "5.10.2",
"@bull-board/koa": "5.10.2",
diff --git a/packages/server/scripts/integrations/postgres/reset.sh b/packages/server/scripts/integrations/postgres/reset.sh
index 32778bd11f..8deb01cdf8 100755
--- a/packages/server/scripts/integrations/postgres/reset.sh
+++ b/packages/server/scripts/integrations/postgres/reset.sh
@@ -1,3 +1,3 @@
#!/bin/bash
-docker-compose down
+docker-compose down -v
docker volume prune -f
diff --git a/packages/server/src/api/controllers/automation.ts b/packages/server/src/api/controllers/automation.ts
index 186b68f3b7..b986b5232b 100644
--- a/packages/server/src/api/controllers/automation.ts
+++ b/packages/server/src/api/controllers/automation.ts
@@ -20,6 +20,7 @@ import {
AutomationActionStepId,
AutomationResults,
UserCtx,
+ DeleteAutomationResponse,
} from "@budibase/types"
import { getActionDefinitions as actionDefs } from "../../automations/actions"
import sdk from "../../sdk"
@@ -72,7 +73,9 @@ function cleanAutomationInputs(automation: Automation) {
return automation
}
-export async function create(ctx: UserCtx) {
+export async function create(
+ ctx: UserCtx
+) {
const db = context.getAppDB()
let automation = ctx.request.body
automation.appId = ctx.appId
@@ -207,7 +210,7 @@ export async function find(ctx: UserCtx) {
ctx.body = await db.get(ctx.params.id)
}
-export async function destroy(ctx: UserCtx) {
+export async function destroy(ctx: UserCtx) {
const db = context.getAppDB()
const automationId = ctx.params.id
const oldAutomation = await db.get(automationId)
diff --git a/packages/server/src/api/controllers/datasource.ts b/packages/server/src/api/controllers/datasource.ts
index d70c13f800..0f17c5a2f5 100644
--- a/packages/server/src/api/controllers/datasource.ts
+++ b/packages/server/src/api/controllers/datasource.ts
@@ -15,10 +15,14 @@ import {
FieldType,
RelationshipFieldMetadata,
SourceName,
+ UpdateDatasourceRequest,
UpdateDatasourceResponse,
UserCtx,
VerifyDatasourceRequest,
VerifyDatasourceResponse,
+ Table,
+ RowValue,
+ DynamicVariable,
} from "@budibase/types"
import sdk from "../../sdk"
import { builderSocket } from "../../websockets"
@@ -90,8 +94,10 @@ async function invalidateVariables(
existingDatasource: Datasource,
updatedDatasource: Datasource
) {
- const existingVariables: any = existingDatasource.config?.dynamicVariables
- const updatedVariables: any = updatedDatasource.config?.dynamicVariables
+ const existingVariables: DynamicVariable[] =
+ existingDatasource.config?.dynamicVariables || []
+ const updatedVariables: DynamicVariable[] =
+ updatedDatasource.config?.dynamicVariables || []
const toInvalidate = []
if (!existingVariables) {
@@ -103,9 +109,9 @@ async function invalidateVariables(
toInvalidate.push(...existingVariables)
} else {
// invaldate changed / removed
- existingVariables.forEach((existing: any) => {
+ existingVariables.forEach(existing => {
const unchanged = updatedVariables.find(
- (updated: any) =>
+ updated =>
existing.name === updated.name &&
existing.queryId === updated.queryId &&
existing.value === updated.value
@@ -118,24 +124,32 @@ async function invalidateVariables(
await invalidateDynamicVariables(toInvalidate)
}
-export async function update(ctx: UserCtx) {
+export async function update(
+ ctx: UserCtx
+) {
const db = context.getAppDB()
const datasourceId = ctx.params.datasourceId
const baseDatasource = await sdk.datasources.get(datasourceId)
- const auth = baseDatasource.config?.auth
await invalidateVariables(baseDatasource, ctx.request.body)
const isBudibaseSource =
baseDatasource.type === dbCore.BUDIBASE_DATASOURCE_TYPE
- const dataSourceBody = isBudibaseSource
- ? { name: ctx.request.body?.name }
+ const dataSourceBody: Datasource = isBudibaseSource
+ ? {
+ name: ctx.request.body?.name,
+ type: dbCore.BUDIBASE_DATASOURCE_TYPE,
+ source: SourceName.BUDIBASE,
+ }
: ctx.request.body
let datasource: Datasource = {
...baseDatasource,
...sdk.datasources.mergeConfigs(dataSourceBody, baseDatasource),
}
+
+ // this block is specific to GSheets, if no auth set, set it back
+ const auth = baseDatasource.config?.auth
if (auth && !ctx.request.body.auth) {
// don't strip auth config from DB
datasource.config!.auth = auth
@@ -204,7 +218,7 @@ async function destroyInternalTablesBySourceId(datasourceId: string) {
const db = context.getAppDB()
// Get all internal tables
- const internalTables = await db.allDocs(
+ const internalTables = await db.allDocs(
getTableParams(null, {
include_docs: true,
})
@@ -212,8 +226,8 @@ async function destroyInternalTablesBySourceId(datasourceId: string) {
// Filter by datasource and return the docs.
const datasourceTableDocs = internalTables.rows.reduce(
- (acc: any, table: any) => {
- if (table.doc.sourceId == datasourceId) {
+ (acc: Table[], table) => {
+ if (table.doc?.sourceId == datasourceId) {
acc.push(table.doc)
}
return acc
@@ -254,9 +268,9 @@ export async function destroy(ctx: UserCtx) {
if (datasource.type === dbCore.BUDIBASE_DATASOURCE_TYPE) {
await destroyInternalTablesBySourceId(datasourceId)
} else {
- const queries = await db.allDocs(getQueryParams(datasourceId))
+ const queries = await db.allDocs(getQueryParams(datasourceId))
await db.bulkDocs(
- queries.rows.map((row: any) => ({
+ queries.rows.map(row => ({
_id: row.id,
_rev: row.value.rev,
_deleted: true,
diff --git a/packages/server/src/api/controllers/integration.ts b/packages/server/src/api/controllers/integration.ts
index 9cfde31e4c..57038f8401 100644
--- a/packages/server/src/api/controllers/integration.ts
+++ b/packages/server/src/api/controllers/integration.ts
@@ -1,7 +1,10 @@
import { getDefinition, getDefinitions } from "../../integrations"
import { SourceName, UserCtx } from "@budibase/types"
-const DISABLED_EXTERNAL_INTEGRATIONS = [SourceName.AIRTABLE]
+const DISABLED_EXTERNAL_INTEGRATIONS = [
+ SourceName.AIRTABLE,
+ SourceName.BUDIBASE,
+]
export async function fetch(ctx: UserCtx) {
const definitions = await getDefinitions()
diff --git a/packages/server/src/api/controllers/layout.ts b/packages/server/src/api/controllers/layout.ts
index 69e4ad91ed..c0406f50ac 100644
--- a/packages/server/src/api/controllers/layout.ts
+++ b/packages/server/src/api/controllers/layout.ts
@@ -1,9 +1,17 @@
import { EMPTY_LAYOUT } from "../../constants/layouts"
import { generateLayoutID, getScreenParams } from "../../db/utils"
import { events, context } from "@budibase/backend-core"
-import { BBContext, Layout } from "@budibase/types"
+import {
+ BBContext,
+ Layout,
+ SaveLayoutRequest,
+ SaveLayoutResponse,
+ UserCtx,
+} from "@budibase/types"
-export async function save(ctx: BBContext) {
+export async function save(
+ ctx: UserCtx
+) {
const db = context.getAppDB()
let layout = ctx.request.body
diff --git a/packages/server/src/api/controllers/query/index.ts b/packages/server/src/api/controllers/query/index.ts
index 768c921150..973718ba48 100644
--- a/packages/server/src/api/controllers/query/index.ts
+++ b/packages/server/src/api/controllers/query/index.ts
@@ -73,7 +73,7 @@ const _import = async (ctx: UserCtx) => {
}
export { _import as import }
-export async function save(ctx: UserCtx) {
+export async function save(ctx: UserCtx) {
const db = context.getAppDB()
const query: Query = ctx.request.body
diff --git a/packages/server/src/api/controllers/row/ExternalRequest.ts b/packages/server/src/api/controllers/row/ExternalRequest.ts
index b7dc02c0db..685af4e98e 100644
--- a/packages/server/src/api/controllers/row/ExternalRequest.ts
+++ b/packages/server/src/api/controllers/row/ExternalRequest.ts
@@ -7,6 +7,7 @@ import {
FilterType,
IncludeRelationship,
ManyToManyRelationshipFieldMetadata,
+ ManyToOneRelationshipFieldMetadata,
OneToManyRelationshipFieldMetadata,
Operation,
PaginationJson,
@@ -18,6 +19,7 @@ import {
SortJson,
SortType,
Table,
+ isManyToOne,
} from "@budibase/types"
import {
breakExternalTableId,
@@ -32,7 +34,9 @@ import { processObjectSync } from "@budibase/string-templates"
import { cloneDeep } from "lodash/fp"
import { processDates, processFormulas } from "../../../utilities/rowProcessor"
import { db as dbCore } from "@budibase/backend-core"
+import AliasTables from "./alias"
import sdk from "../../../sdk"
+import env from "../../../environment"
export interface ManyRelationship {
tableId?: string
@@ -101,6 +105,39 @@ function buildFilters(
}
}
+async function removeManyToManyRelationships(
+ rowId: string,
+ table: Table,
+ colName: string
+) {
+ const tableId = table._id!
+ const filters = buildFilters(rowId, {}, table)
+ // safety check, if there are no filters on deletion bad things happen
+ if (Object.keys(filters).length !== 0) {
+ return getDatasourceAndQuery({
+ endpoint: getEndpoint(tableId, Operation.DELETE),
+ body: { [colName]: null },
+ filters,
+ })
+ } else {
+ return []
+ }
+}
+
+async function removeOneToManyRelationships(rowId: string, table: Table) {
+ const tableId = table._id!
+ const filters = buildFilters(rowId, {}, table)
+ // safety check, if there are no filters on deletion bad things happen
+ if (Object.keys(filters).length !== 0) {
+ return getDatasourceAndQuery({
+ endpoint: getEndpoint(tableId, Operation.UPDATE),
+ filters,
+ })
+ } else {
+ return []
+ }
+}
+
/**
* This function checks the incoming parameters to make sure all the inputs are
* valid based on on the table schema. The main thing this is looking for is when a
@@ -178,13 +215,13 @@ function generateIdForRow(
function getEndpoint(tableId: string | undefined, operation: string) {
if (!tableId) {
- return {}
+ throw new Error("Cannot get endpoint information - no table ID specified")
}
const { datasourceId, tableName } = breakExternalTableId(tableId)
return {
- datasourceId,
- entityId: tableName,
- operation,
+ datasourceId: datasourceId!,
+ entityId: tableName!,
+ operation: operation as Operation,
}
}
@@ -304,6 +341,18 @@ export class ExternalRequest {
}
}
+ async getRow(table: Table, rowId: string): Promise {
+ const response = await getDatasourceAndQuery({
+ endpoint: getEndpoint(table._id!, Operation.READ),
+ filters: buildFilters(rowId, {}, table),
+ })
+ if (Array.isArray(response) && response.length > 0) {
+ return response[0]
+ } else {
+ throw new Error(`Cannot fetch row by ID "${rowId}"`)
+ }
+ }
+
inputProcessing(row: Row | undefined, table: Table) {
if (!row) {
return { row, manyRelationships: [] }
@@ -571,7 +620,9 @@ export class ExternalRequest {
* information.
*/
async lookupRelations(tableId: string, row: Row) {
- const related: { [key: string]: any } = {}
+ const related: {
+ [key: string]: { rows: Row[]; isMany: boolean; tableId: string }
+ } = {}
const { tableName } = breakExternalTableId(tableId)
if (!tableName) {
return related
@@ -589,14 +640,26 @@ export class ExternalRequest {
) {
continue
}
- const isMany = field.relationshipType === RelationshipType.MANY_TO_MANY
- const tableId = isMany ? field.through : field.tableId
+ let tableId: string | undefined,
+ lookupField: string | undefined,
+ fieldName: string | undefined
+ if (isManyToMany(field)) {
+ tableId = field.through
+ lookupField = primaryKey
+ fieldName = field.throughTo || primaryKey
+ } else if (isManyToOne(field)) {
+ tableId = field.tableId
+ lookupField = field.foreignKey
+ fieldName = field.fieldName
+ }
+ if (!tableId || !lookupField || !fieldName) {
+ throw new Error(
+ "Unable to lookup relationships - undefined column properties."
+ )
+ }
const { tableName: relatedTableName } = breakExternalTableId(tableId)
// @ts-ignore
const linkPrimaryKey = this.tables[relatedTableName].primary[0]
-
- const lookupField = isMany ? primaryKey : field.foreignKey
- const fieldName = isMany ? field.throughTo || primaryKey : field.fieldName
if (!lookupField || !row[lookupField]) {
continue
}
@@ -609,9 +672,12 @@ export class ExternalRequest {
},
})
// this is the response from knex if no rows found
- const rows = !response[0].read ? response : []
- const storeTo = isMany ? field.throughFrom || linkPrimaryKey : fieldName
- related[storeTo] = { rows, isMany, tableId }
+ const rows: Row[] =
+ !Array.isArray(response) || response?.[0].read ? [] : response
+ const storeTo = isManyToMany(field)
+ ? field.throughFrom || linkPrimaryKey
+ : fieldName
+ related[storeTo] = { rows, isMany: isManyToMany(field), tableId }
}
return related
}
@@ -697,24 +763,43 @@ export class ExternalRequest {
continue
}
for (let row of rows) {
- const filters = buildFilters(generateIdForRow(row, table), {}, table)
- // safety check, if there are no filters on deletion bad things happen
- if (Object.keys(filters).length !== 0) {
- const op = isMany ? Operation.DELETE : Operation.UPDATE
- const body = isMany ? null : { [colName]: null }
- promises.push(
- getDatasourceAndQuery({
- endpoint: getEndpoint(tableId, op),
- body,
- filters,
- })
- )
+ const rowId = generateIdForRow(row, table)
+ const promise: Promise = isMany
+ ? removeManyToManyRelationships(rowId, table, colName)
+ : removeOneToManyRelationships(rowId, table)
+ if (promise) {
+ promises.push(promise)
}
}
}
await Promise.all(promises)
}
+ async removeRelationshipsToRow(table: Table, rowId: string) {
+ const row = await this.getRow(table, rowId)
+ const related = await this.lookupRelations(table._id!, row)
+ for (let column of Object.values(table.schema)) {
+ const relationshipColumn = column as RelationshipFieldMetadata
+ if (!isManyToOne(relationshipColumn)) {
+ continue
+ }
+ const { rows, isMany, tableId } = related[relationshipColumn.fieldName]
+ const table = this.getTable(tableId)!
+ await Promise.all(
+ rows.map(row => {
+ const rowId = generateIdForRow(row, table)
+ return isMany
+ ? removeManyToManyRelationships(
+ rowId,
+ table,
+ relationshipColumn.fieldName
+ )
+ : removeOneToManyRelationships(rowId, table)
+ })
+ )
+ }
+ }
+
/**
* This function is a bit crazy, but the exact purpose of it is to protect against the scenario in which
* you have column overlap in relationships, e.g. we join a few different tables and they all have the
@@ -804,7 +889,7 @@ export class ExternalRequest {
}
let json = {
endpoint: {
- datasourceId,
+ datasourceId: datasourceId!,
entityId: tableName,
operation,
},
@@ -826,17 +911,30 @@ export class ExternalRequest {
},
}
- // can't really use response right now
- const response = await getDatasourceAndQuery(json)
- // handle many to many relationships now if we know the ID (could be auto increment)
+ // remove any relationships that could block deletion
+ if (operation === Operation.DELETE && id) {
+ await this.removeRelationshipsToRow(table, generateRowIdField(id))
+ }
+
+ // aliasing can be disabled fully if desired
+ let response
+ if (env.SQL_ALIASING_DISABLE) {
+ response = await getDatasourceAndQuery(json)
+ } else {
+ const aliasing = new AliasTables(Object.keys(this.tables))
+ response = await aliasing.queryWithAliasing(json)
+ }
+
+ const responseRows = Array.isArray(response) ? response : []
+ // handle many-to-many relationships now if we know the ID (could be auto increment)
if (operation !== Operation.READ) {
await this.handleManyRelationships(
table._id || "",
- response[0],
+ responseRows[0],
processed.manyRelationships
)
}
- const output = this.outputProcessing(response, table, relationships)
+ const output = this.outputProcessing(responseRows, table, relationships)
// if reading it'll just be an array of rows, return whole thing
if (operation === Operation.READ) {
return (
diff --git a/packages/server/src/api/controllers/row/alias.ts b/packages/server/src/api/controllers/row/alias.ts
new file mode 100644
index 0000000000..9658a0d638
--- /dev/null
+++ b/packages/server/src/api/controllers/row/alias.ts
@@ -0,0 +1,166 @@
+import {
+ QueryJson,
+ SearchFilters,
+ Table,
+ Row,
+ DatasourcePlusQueryResponse,
+} from "@budibase/types"
+import { getDatasourceAndQuery } from "../../../sdk/app/rows/utils"
+import { cloneDeep } from "lodash"
+
+class CharSequence {
+ static alphabet = "abcdefghijklmnopqrstuvwxyz"
+ counters: number[]
+
+ constructor() {
+ this.counters = [0]
+ }
+
+ getCharacter(): string {
+ const char = this.counters.map(i => CharSequence.alphabet[i]).join("")
+ for (let i = this.counters.length - 1; i >= 0; i--) {
+ if (this.counters[i] < CharSequence.alphabet.length - 1) {
+ this.counters[i]++
+ return char
+ }
+ this.counters[i] = 0
+ }
+ this.counters.unshift(0)
+ return char
+ }
+}
+
+export default class AliasTables {
+ aliases: Record
+ tableAliases: Record
+ tableNames: string[]
+ charSeq: CharSequence
+
+ constructor(tableNames: string[]) {
+ this.tableNames = tableNames
+ this.aliases = {}
+ this.tableAliases = {}
+ this.charSeq = new CharSequence()
+ }
+
+ getAlias(tableName: string) {
+ if (this.aliases[tableName]) {
+ return this.aliases[tableName]
+ }
+ const char = this.charSeq.getCharacter()
+ this.aliases[tableName] = char
+ this.tableAliases[char] = tableName
+ return char
+ }
+
+ aliasField(field: string) {
+ const tableNames = this.tableNames
+ if (field.includes(".")) {
+ const [tableName, column] = field.split(".")
+ const foundTableName = tableNames.find(name => {
+ const idx = tableName.indexOf(name)
+ if (idx === -1 || idx > 1) {
+ return
+ }
+ return Math.abs(tableName.length - name.length) <= 2
+ })
+ if (foundTableName) {
+ const aliasedTableName = tableName.replace(
+ foundTableName,
+ this.getAlias(foundTableName)
+ )
+ field = `${aliasedTableName}.${column}`
+ }
+ }
+ return field
+ }
+
+ reverse(rows: T): T {
+ const process = (row: Row) => {
+ const final: Row = {}
+ for (let [key, value] of Object.entries(row)) {
+ if (!key.includes(".")) {
+ final[key] = value
+ } else {
+ const [alias, column] = key.split(".")
+ const tableName = this.tableAliases[alias] || alias
+ final[`${tableName}.${column}`] = value
+ }
+ }
+ return final
+ }
+ if (Array.isArray(rows)) {
+ return rows.map(row => process(row)) as T
+ } else {
+ return process(rows) as T
+ }
+ }
+
+ aliasMap(tableNames: (string | undefined)[]) {
+ const map: Record = {}
+ for (let tableName of tableNames) {
+ if (tableName) {
+ map[tableName] = this.getAlias(tableName)
+ }
+ }
+ return map
+ }
+
+ async queryWithAliasing(json: QueryJson): DatasourcePlusQueryResponse {
+ json = cloneDeep(json)
+ const aliasTable = (table: Table) => ({
+ ...table,
+ name: this.getAlias(table.name),
+ })
+ // run through the query json to update anywhere a table may be used
+ if (json.resource?.fields) {
+ json.resource.fields = json.resource.fields.map(field =>
+ this.aliasField(field)
+ )
+ }
+ if (json.filters) {
+ for (let [filterKey, filter] of Object.entries(json.filters)) {
+ if (typeof filter !== "object") {
+ continue
+ }
+ const aliasedFilters: typeof filter = {}
+ for (let key of Object.keys(filter)) {
+ aliasedFilters[this.aliasField(key)] = filter[key]
+ }
+ json.filters[filterKey as keyof SearchFilters] = aliasedFilters
+ }
+ }
+ if (json.relationships) {
+ json.relationships = json.relationships.map(relationship => ({
+ ...relationship,
+ aliases: this.aliasMap([
+ relationship.through,
+ relationship.tableName,
+ json.endpoint.entityId,
+ ]),
+ }))
+ }
+ if (json.meta?.table) {
+ json.meta.table = aliasTable(json.meta.table)
+ }
+ if (json.meta?.tables) {
+ const aliasedTables: Record = {}
+ for (let [tableName, table] of Object.entries(json.meta.tables)) {
+ aliasedTables[this.getAlias(tableName)] = aliasTable(table)
+ }
+ json.meta.tables = aliasedTables
+ }
+ // invert and return
+ const invertedTableAliases: Record = {}
+ for (let [key, value] of Object.entries(this.tableAliases)) {
+ invertedTableAliases[value] = key
+ }
+ json.tableAliases = invertedTableAliases
+ const response = await getDatasourceAndQuery(json)
+ if (Array.isArray(response)) {
+ return this.reverse(response)
+ } else {
+ return response
+ }
+ }
+}
diff --git a/packages/server/src/api/controllers/row/internal.ts b/packages/server/src/api/controllers/row/internal.ts
index 3ee08fff2e..cc903bd74a 100644
--- a/packages/server/src/api/controllers/row/internal.ts
+++ b/packages/server/src/api/controllers/row/internal.ts
@@ -189,11 +189,12 @@ export async function fetchEnrichedRow(ctx: UserCtx) {
const tableId = utils.getTableId(ctx)
const rowId = ctx.params.rowId as string
// need table to work out where links go in row, as well as the link docs
- const [table, row, links] = await Promise.all([
+ const [table, links] = await Promise.all([
sdk.tables.getTable(tableId),
- utils.findRow(ctx, tableId, rowId),
linkRows.getLinkDocuments({ tableId, rowId, fieldName }),
])
+ let row = await utils.findRow(ctx, tableId, rowId)
+ row = await outputProcessing(table, row)
const linkVals = links as LinkDocumentValue[]
// look up the actual rows based on the ids
diff --git a/packages/server/src/api/controllers/screen.ts b/packages/server/src/api/controllers/screen.ts
index 446fe2e5fa..ee8e0ff892 100644
--- a/packages/server/src/api/controllers/screen.ts
+++ b/packages/server/src/api/controllers/screen.ts
@@ -7,7 +7,13 @@ import {
roles,
} from "@budibase/backend-core"
import { updateAppPackage } from "./application"
-import { Plugin, ScreenProps, BBContext, Screen } from "@budibase/types"
+import {
+ Plugin,
+ ScreenProps,
+ BBContext,
+ Screen,
+ UserCtx,
+} from "@budibase/types"
import { builderSocket } from "../../websockets"
export async function fetch(ctx: BBContext) {
@@ -31,7 +37,7 @@ export async function fetch(ctx: BBContext) {
)
}
-export async function save(ctx: BBContext) {
+export async function save(ctx: UserCtx) {
const db = context.getAppDB()
let screen = ctx.request.body
diff --git a/packages/server/src/api/controllers/static/index.ts b/packages/server/src/api/controllers/static/index.ts
index 367934445a..5a3803e6d5 100644
--- a/packages/server/src/api/controllers/static/index.ts
+++ b/packages/server/src/api/controllers/static/index.ts
@@ -1,5 +1,7 @@
import { InvalidFileExtensions } from "@budibase/shared-core"
+
import AppComponent from "./templates/BudibaseApp.svelte"
+
import { join } from "../../../utilities/centralPath"
import * as uuid from "uuid"
import { ObjectStoreBuckets } from "../../../constants"
@@ -22,13 +24,7 @@ import AWS from "aws-sdk"
import fs from "fs"
import sdk from "../../../sdk"
import * as pro from "@budibase/pro"
-import {
- UserCtx,
- App,
- Ctx,
- ProcessAttachmentResponse,
- Feature,
-} from "@budibase/types"
+import { App, Ctx, ProcessAttachmentResponse } from "@budibase/types"
import {
getAppMigrationVersion,
getLatestMigrationId,
@@ -36,61 +32,6 @@ import {
import send from "koa-send"
-const getThemeVariables = (theme: string) => {
- if (theme === "spectrum--lightest") {
- return `
- --spectrum-global-color-gray-50: rgb(255, 255, 255);
- --spectrum-global-color-gray-200: rgb(244, 244, 244);
- --spectrum-global-color-gray-300: rgb(234, 234, 234);
- --spectrum-alias-background-color-primary: var(--spectrum-global-color-gray-50);
- `
- }
- if (theme === "spectrum--light") {
- return `
- --spectrum-global-color-gray-50: rgb(255, 255, 255);
- --spectrum-global-color-gray-200: rgb(234, 234, 234);
- --spectrum-global-color-gray-300: rgb(225, 225, 225);
- --spectrum-alias-background-color-primary: var(--spectrum-global-color-gray-50);
-
- `
- }
- if (theme === "spectrum--dark") {
- return `
- --spectrum-global-color-gray-100: rgb(50, 50, 50);
- --spectrum-global-color-gray-200: rgb(62, 62, 62);
- --spectrum-global-color-gray-300: rgb(74, 74, 74);
- --spectrum-alias-background-color-primary: var(--spectrum-global-color-gray-100);
- `
- }
- if (theme === "spectrum--darkest") {
- return `
- --spectrum-global-color-gray-100: rgb(30, 30, 30);
- --spectrum-global-color-gray-200: rgb(44, 44, 44);
- --spectrum-global-color-gray-300: rgb(57, 57, 57);
- --spectrum-alias-background-color-primary: var(--spectrum-global-color-gray-100);
- `
- }
- if (theme === "spectrum--nord") {
- return `
- --spectrum-global-color-gray-100: #3b4252;
-
- --spectrum-global-color-gray-200: #424a5c;
- --spectrum-global-color-gray-300: #4c566a;
- --spectrum-alias-background-color-primary: var(--spectrum-global-color-gray-100);
- `
- }
- if (theme === "spectrum--midnight") {
- return `
- --hue: 220;
- --sat: 10%;
- --spectrum-global-color-gray-100: hsl(var(--hue), var(--sat), 17%);
- --spectrum-global-color-gray-200: hsl(var(--hue), var(--sat), 20%);
- --spectrum-global-color-gray-300: hsl(var(--hue), var(--sat), 24%);
- --spectrum-alias-background-color-primary: var(--spectrum-global-color-gray-100);
- `
- }
-}
-
export const toggleBetaUiFeature = async function (ctx: Ctx) {
const cookieName = `beta:${ctx.params.feature}`
@@ -205,7 +146,7 @@ const requiresMigration = async (ctx: Ctx) => {
return requiresMigrations
}
-export const serveApp = async function (ctx: UserCtx) {
+export const serveApp = async function (ctx: Ctx) {
const needMigrations = await requiresMigration(ctx)
const bbHeaderEmbed =
@@ -226,19 +167,9 @@ export const serveApp = async function (ctx: UserCtx) {
const appInfo = await db.get(DocumentType.APP_METADATA)
let appId = context.getAppId()
- const hideDevTools = !!ctx.params.appUrl
- const sideNav = appInfo.navigation.navigation === "Left"
- const hideFooter =
- ctx?.user?.license?.features?.includes(Feature.BRANDING) || false
- const themeVariables = getThemeVariables(appInfo?.theme)
-
if (!env.isJest()) {
const plugins = objectStore.enrichPluginURLs(appInfo.usedPlugins)
-
const { head, html, css } = AppComponent.render({
- hideDevTools,
- sideNav,
- hideFooter,
metaImage:
branding?.metaImageUrl ||
"https://res.cloudinary.com/daog6scxm/image/upload/v1698759482/meta-images/plain-branded-meta-image-coral_ocxmgu.png",
@@ -263,7 +194,7 @@ export const serveApp = async function (ctx: UserCtx) {
ctx.body = await processString(appHbs, {
head,
body: html,
- css: `:root{${themeVariables}} ${css.code}`,
+ style: css.code,
appId,
embedded: bbHeaderEmbed,
})
diff --git a/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte b/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte
index 63b293b4ca..7819368fc0 100644
--- a/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte
+++ b/packages/server/src/api/controllers/static/templates/BudibaseApp.svelte
@@ -1,6 +1,4 @@
@@ -102,7 +96,6 @@
-
{#if clientLibPath}
There was an error loading your app
diff --git a/packages/server/src/api/controllers/static/templates/app.hbs b/packages/server/src/api/controllers/static/templates/app.hbs
index b01b723c3e..8c445158a0 100644
--- a/packages/server/src/api/controllers/static/templates/app.hbs
+++ b/packages/server/src/api/controllers/static/templates/app.hbs
@@ -1,12 +1,8 @@
-
+
{{{head}}}
-
+