Merge branch 'master' of github.com:Budibase/budibase into develop
This commit is contained in:
commit
3d3efadb37
|
@ -1,5 +1,5 @@
|
||||||
{
|
{
|
||||||
"version": "2.7.26-alpha.5",
|
"version": "2.7.33",
|
||||||
"npmClient": "yarn",
|
"npmClient": "yarn",
|
||||||
"packages": [
|
"packages": [
|
||||||
"packages/*"
|
"packages/*"
|
||||||
|
|
|
@ -87,7 +87,7 @@
|
||||||
border-color: var(--spectrum-global-color-gray-400);
|
border-color: var(--spectrum-global-color-gray-400);
|
||||||
}
|
}
|
||||||
/* Toolbar button color */
|
/* Toolbar button color */
|
||||||
:global(.EasyMDEContainer .editor-toolbar button i) {
|
:global(.EasyMDEContainer .editor-toolbar button) {
|
||||||
color: var(--spectrum-global-color-gray-800);
|
color: var(--spectrum-global-color-gray-800);
|
||||||
}
|
}
|
||||||
/* Separator between toolbar buttons*/
|
/* Separator between toolbar buttons*/
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<script>
|
<script>
|
||||||
import { tables } from "stores/backend"
|
import { datasources, tables } from "stores/backend"
|
||||||
import EditRolesButton from "./buttons/EditRolesButton.svelte"
|
import EditRolesButton from "./buttons/EditRolesButton.svelte"
|
||||||
import { TableNames } from "constants"
|
import { TableNames } from "constants"
|
||||||
import { Grid } from "@budibase/frontend-core"
|
import { Grid } from "@budibase/frontend-core"
|
||||||
|
@ -26,6 +26,18 @@
|
||||||
$: id = $tables.selected?._id
|
$: id = $tables.selected?._id
|
||||||
$: isUsersTable = id === TableNames.USERS
|
$: isUsersTable = id === TableNames.USERS
|
||||||
$: isInternal = $tables.selected?.type !== "external"
|
$: isInternal = $tables.selected?.type !== "external"
|
||||||
|
|
||||||
|
const handleGridTableUpdate = async e => {
|
||||||
|
tables.replaceTable(id, e.detail)
|
||||||
|
|
||||||
|
// We need to refresh datasources when an external table changes.
|
||||||
|
// Type "external" may exist - sometimes type is "table" and sometimes it
|
||||||
|
// is "external" - it has different meanings in different endpoints.
|
||||||
|
// If we check both these then we hopefully catch all external tables.
|
||||||
|
if (e.detail?.type === "external" || e.detail?.sql) {
|
||||||
|
await datasources.fetch()
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="wrapper">
|
<div class="wrapper">
|
||||||
|
@ -37,7 +49,7 @@
|
||||||
allowDeleteRows={!isUsersTable}
|
allowDeleteRows={!isUsersTable}
|
||||||
schemaOverrides={isUsersTable ? userSchemaOverrides : null}
|
schemaOverrides={isUsersTable ? userSchemaOverrides : null}
|
||||||
showAvatars={false}
|
showAvatars={false}
|
||||||
on:updatetable={e => tables.replaceTable(id, e.detail)}
|
on:updatetable={handleGridTableUpdate}
|
||||||
>
|
>
|
||||||
<svelte:fragment slot="controls">
|
<svelte:fragment slot="controls">
|
||||||
{#if isInternal}
|
{#if isInternal}
|
||||||
|
|
|
@ -59,7 +59,6 @@
|
||||||
$: valid = getErrorCount(errors) === 0 && allRequiredAttributesSet()
|
$: valid = getErrorCount(errors) === 0 && allRequiredAttributesSet()
|
||||||
$: isManyToMany = relationshipType === RelationshipTypes.MANY_TO_MANY
|
$: isManyToMany = relationshipType === RelationshipTypes.MANY_TO_MANY
|
||||||
$: isManyToOne = relationshipType === RelationshipTypes.MANY_TO_ONE
|
$: isManyToOne = relationshipType === RelationshipTypes.MANY_TO_ONE
|
||||||
$: toRelationship.relationshipType = fromRelationship?.relationshipType
|
|
||||||
|
|
||||||
function getTable(id) {
|
function getTable(id) {
|
||||||
return plusTables.find(table => table._id === id)
|
return plusTables.find(table => table._id === id)
|
||||||
|
@ -180,6 +179,16 @@
|
||||||
return getErrorCount(errors) === 0
|
return getErrorCount(errors) === 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function otherRelationshipType(type) {
|
||||||
|
if (type === RelationshipTypes.MANY_TO_ONE) {
|
||||||
|
return RelationshipTypes.ONE_TO_MANY
|
||||||
|
} else if (type === RelationshipTypes.ONE_TO_MANY) {
|
||||||
|
return RelationshipTypes.MANY_TO_ONE
|
||||||
|
} else if (type === RelationshipTypes.MANY_TO_MANY) {
|
||||||
|
return RelationshipTypes.MANY_TO_MANY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function buildRelationships() {
|
function buildRelationships() {
|
||||||
const id = Helpers.uuid()
|
const id = Helpers.uuid()
|
||||||
//Map temporary variables
|
//Map temporary variables
|
||||||
|
@ -200,6 +209,7 @@
|
||||||
...toRelationship,
|
...toRelationship,
|
||||||
tableId: fromId,
|
tableId: fromId,
|
||||||
name: fromColumn,
|
name: fromColumn,
|
||||||
|
relationshipType: otherRelationshipType(relationshipType),
|
||||||
through: throughId,
|
through: throughId,
|
||||||
type: "link",
|
type: "link",
|
||||||
_id: id,
|
_id: id,
|
||||||
|
|
|
@ -93,6 +93,7 @@
|
||||||
try {
|
try {
|
||||||
await beforeSave()
|
await beforeSave()
|
||||||
table = await tables.save(newTable)
|
table = await tables.save(newTable)
|
||||||
|
await datasources.fetch()
|
||||||
await afterSave(table)
|
await afterSave(table)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
notifications.error(e)
|
notifications.error(e)
|
||||||
|
|
|
@ -65,6 +65,7 @@
|
||||||
const updatedTable = cloneDeep(table)
|
const updatedTable = cloneDeep(table)
|
||||||
updatedTable.name = updatedName
|
updatedTable.name = updatedName
|
||||||
await tables.save(updatedTable)
|
await tables.save(updatedTable)
|
||||||
|
await datasources.fetch()
|
||||||
notifications.success("Table renamed successfully")
|
notifications.success("Table renamed successfully")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,18 @@
|
||||||
faFileArrowUp,
|
faFileArrowUp,
|
||||||
faChevronLeft,
|
faChevronLeft,
|
||||||
faCircleInfo,
|
faCircleInfo,
|
||||||
|
faBold,
|
||||||
|
faItalic,
|
||||||
|
faHeading,
|
||||||
|
faQuoteLeft,
|
||||||
|
faListUl,
|
||||||
|
faListOl,
|
||||||
|
faLink,
|
||||||
|
faImage,
|
||||||
|
faEye,
|
||||||
|
faColumns,
|
||||||
|
faArrowsAlt,
|
||||||
|
faQuestionCircle,
|
||||||
} from "@fortawesome/free-solid-svg-icons"
|
} from "@fortawesome/free-solid-svg-icons"
|
||||||
import { faGithub, faDiscord } from "@fortawesome/free-brands-svg-icons"
|
import { faGithub, faDiscord } from "@fortawesome/free-brands-svg-icons"
|
||||||
|
|
||||||
|
@ -22,7 +34,22 @@
|
||||||
faEnvelope,
|
faEnvelope,
|
||||||
faFileArrowUp,
|
faFileArrowUp,
|
||||||
faChevronLeft,
|
faChevronLeft,
|
||||||
faCircleInfo
|
faCircleInfo,
|
||||||
|
|
||||||
|
// -- Required for easyMDE use in the builder.
|
||||||
|
faBold,
|
||||||
|
faItalic,
|
||||||
|
faHeading,
|
||||||
|
faQuoteLeft,
|
||||||
|
faListUl,
|
||||||
|
faListOl,
|
||||||
|
faLink,
|
||||||
|
faImage,
|
||||||
|
faEye,
|
||||||
|
faColumns,
|
||||||
|
faArrowsAlt,
|
||||||
|
faQuestionCircle
|
||||||
|
// --
|
||||||
)
|
)
|
||||||
dom.watch()
|
dom.watch()
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -117,6 +117,10 @@ export function createDatasourcesStore() {
|
||||||
...state,
|
...state,
|
||||||
list: [...state.list, datasource],
|
list: [...state.list, datasource],
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
// If this is a new datasource then we should refresh the tables list,
|
||||||
|
// because otherwise we'll never see the new tables
|
||||||
|
tables.fetch()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update existing datasource
|
// Update existing datasource
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { get, writable, derived } from "svelte/store"
|
import { get, writable, derived } from "svelte/store"
|
||||||
import { datasources } from "./"
|
|
||||||
import { cloneDeep } from "lodash/fp"
|
import { cloneDeep } from "lodash/fp"
|
||||||
import { API } from "api"
|
import { API } from "api"
|
||||||
import { SWITCHABLE_TYPES } from "constants/backend"
|
import { SWITCHABLE_TYPES } from "constants/backend"
|
||||||
|
@ -63,7 +62,6 @@ export function createTablesStore() {
|
||||||
|
|
||||||
const savedTable = await API.saveTable(updatedTable)
|
const savedTable = await API.saveTable(updatedTable)
|
||||||
replaceTable(savedTable._id, savedTable)
|
replaceTable(savedTable._id, savedTable)
|
||||||
await datasources.fetch()
|
|
||||||
select(savedTable._id)
|
select(savedTable._id)
|
||||||
return savedTable
|
return savedTable
|
||||||
}
|
}
|
||||||
|
|
|
@ -283,7 +283,7 @@
|
||||||
|
|
||||||
// Skip if the value is the same
|
// Skip if the value is the same
|
||||||
if (!skipCheck && fieldState.value === value) {
|
if (!skipCheck && fieldState.value === value) {
|
||||||
return true
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update field state
|
// Update field state
|
||||||
|
@ -295,7 +295,7 @@
|
||||||
return state
|
return state
|
||||||
})
|
})
|
||||||
|
|
||||||
return !error
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clears the value of a certain field back to the default value
|
// Clears the value of a certain field back to the default value
|
||||||
|
@ -376,8 +376,9 @@
|
||||||
deregister,
|
deregister,
|
||||||
validate: () => {
|
validate: () => {
|
||||||
// Validate the field by force setting the same value again
|
// Validate the field by force setting the same value again
|
||||||
const { fieldState } = get(getField(field))
|
const fieldInfo = getField(field)
|
||||||
return setValue(fieldState.value, true)
|
setValue(get(fieldInfo).fieldState.value, true)
|
||||||
|
return !get(fieldInfo).fieldState.error
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,12 +90,12 @@ export const deriveStores = context => {
|
||||||
// Update local state
|
// Update local state
|
||||||
table.set(newTable)
|
table.set(newTable)
|
||||||
|
|
||||||
|
// Update server
|
||||||
|
await API.saveTable(newTable)
|
||||||
|
|
||||||
// Broadcast change to external state can be updated, as this change
|
// Broadcast change to external state can be updated, as this change
|
||||||
// will not be received by the builder websocket because we caused it ourselves
|
// will not be received by the builder websocket because we caused it ourselves
|
||||||
dispatch("updatetable", newTable)
|
dispatch("updatetable", newTable)
|
||||||
|
|
||||||
// Update server
|
|
||||||
await API.saveTable(newTable)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import { writable, derived, get } from "svelte/store"
|
||||||
import { fetchData } from "../../../fetch/fetchData"
|
import { fetchData } from "../../../fetch/fetchData"
|
||||||
import { notifications } from "@budibase/bbui"
|
import { notifications } from "@budibase/bbui"
|
||||||
import { NewRowID, RowPageSize } from "../lib/constants"
|
import { NewRowID, RowPageSize } from "../lib/constants"
|
||||||
|
import { tick } from "svelte"
|
||||||
|
|
||||||
const initialSortState = {
|
const initialSortState = {
|
||||||
column: null,
|
column: null,
|
||||||
|
@ -124,13 +125,22 @@ export const deriveStores = context => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// Subscribe to changes of this fetch model
|
// Subscribe to changes of this fetch model
|
||||||
unsubscribe = newFetch.subscribe($fetch => {
|
unsubscribe = newFetch.subscribe(async $fetch => {
|
||||||
if ($fetch.loaded && !$fetch.loading) {
|
if ($fetch.loaded && !$fetch.loading) {
|
||||||
hasNextPage.set($fetch.hasNextPage)
|
hasNextPage.set($fetch.hasNextPage)
|
||||||
const $instanceLoaded = get(instanceLoaded)
|
const $instanceLoaded = get(instanceLoaded)
|
||||||
const resetRows = $fetch.resetKey !== lastResetKey
|
const resetRows = $fetch.resetKey !== lastResetKey
|
||||||
|
const previousResetKey = lastResetKey
|
||||||
lastResetKey = $fetch.resetKey
|
lastResetKey = $fetch.resetKey
|
||||||
|
|
||||||
|
// If resetting rows due to a table change, wipe data and wait for
|
||||||
|
// derived stores to compute. This prevents stale data being passed
|
||||||
|
// to cells when we save the new schema.
|
||||||
|
if (!$instanceLoaded && previousResetKey) {
|
||||||
|
rows.set([])
|
||||||
|
await tick()
|
||||||
|
}
|
||||||
|
|
||||||
// Reset state properties when dataset changes
|
// Reset state properties when dataset changes
|
||||||
if (!$instanceLoaded || resetRows) {
|
if (!$instanceLoaded || resetRows) {
|
||||||
table.set($fetch.definition)
|
table.set($fetch.definition)
|
||||||
|
|
|
@ -3,10 +3,10 @@ import * as userController from "../user"
|
||||||
import { FieldTypes } from "../../../constants"
|
import { FieldTypes } from "../../../constants"
|
||||||
import { context } from "@budibase/backend-core"
|
import { context } from "@budibase/backend-core"
|
||||||
import { makeExternalQuery } from "../../../integrations/base/query"
|
import { makeExternalQuery } from "../../../integrations/base/query"
|
||||||
import { Row, Table } from "@budibase/types"
|
import { FieldType, Row, Table, UserCtx } from "@budibase/types"
|
||||||
import { Format } from "../view/exporters"
|
import { Format } from "../view/exporters"
|
||||||
import { UserCtx } from "@budibase/types"
|
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
|
|
||||||
const validateJs = require("validate.js")
|
const validateJs = require("validate.js")
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
|
|
||||||
|
@ -20,6 +20,13 @@ validateJs.extend(validateJs.validators.datetime, {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
function isForeignKey(key: string, table: Table) {
|
||||||
|
const relationships = Object.values(table.schema).filter(
|
||||||
|
column => column.type === FieldType.LINK
|
||||||
|
)
|
||||||
|
return relationships.some(relationship => relationship.foreignKey === key)
|
||||||
|
}
|
||||||
|
|
||||||
export async function getDatasourceAndQuery(json: any) {
|
export async function getDatasourceAndQuery(json: any) {
|
||||||
const datasourceId = json.endpoint.datasourceId
|
const datasourceId = json.endpoint.datasourceId
|
||||||
const datasource = await sdk.datasources.get(datasourceId)
|
const datasource = await sdk.datasources.get(datasourceId)
|
||||||
|
@ -65,6 +72,10 @@ export async function validate({
|
||||||
const column = fetchedTable.schema[fieldName]
|
const column = fetchedTable.schema[fieldName]
|
||||||
const constraints = cloneDeep(column.constraints)
|
const constraints = cloneDeep(column.constraints)
|
||||||
const type = column.type
|
const type = column.type
|
||||||
|
// foreign keys are likely to be enriched
|
||||||
|
if (isForeignKey(fieldName, fetchedTable)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
// formulas shouldn't validated, data will be deleted anyway
|
// formulas shouldn't validated, data will be deleted anyway
|
||||||
if (type === FieldTypes.FORMULA || column.autocolumn) {
|
if (type === FieldTypes.FORMULA || column.autocolumn) {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -26,6 +26,7 @@ import {
|
||||||
RelationshipTypes,
|
RelationshipTypes,
|
||||||
} from "@budibase/types"
|
} from "@budibase/types"
|
||||||
import sdk from "../../../sdk"
|
import sdk from "../../../sdk"
|
||||||
|
import { builderSocket } from "../../../websockets"
|
||||||
const { cloneDeep } = require("lodash/fp")
|
const { cloneDeep } = require("lodash/fp")
|
||||||
|
|
||||||
async function makeTableRequest(
|
async function makeTableRequest(
|
||||||
|
@ -318,6 +319,11 @@ export async function save(ctx: UserCtx) {
|
||||||
datasource.entities[tableToSave.name] = tableToSave
|
datasource.entities[tableToSave.name] = tableToSave
|
||||||
await db.put(datasource)
|
await db.put(datasource)
|
||||||
|
|
||||||
|
// Since tables are stored inside datasources, we need to notify clients
|
||||||
|
// that the datasource definition changed
|
||||||
|
const updatedDatasource = await db.get(datasource._id)
|
||||||
|
builderSocket?.emitDatasourceUpdate(ctx, updatedDatasource)
|
||||||
|
|
||||||
return tableToSave
|
return tableToSave
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -344,6 +350,11 @@ export async function destroy(ctx: UserCtx) {
|
||||||
|
|
||||||
await db.put(datasource)
|
await db.put(datasource)
|
||||||
|
|
||||||
|
// Since tables are stored inside datasources, we need to notify clients
|
||||||
|
// that the datasource definition changed
|
||||||
|
const updatedDatasource = await db.get(datasource._id)
|
||||||
|
builderSocket?.emitDatasourceUpdate(ctx, updatedDatasource)
|
||||||
|
|
||||||
return tableToDelete
|
return tableToDelete
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -385,7 +385,7 @@ class MongoIntegration implements IntegrationBase {
|
||||||
createObjectIds(json: any) {
|
createObjectIds(json: any) {
|
||||||
const self = this
|
const self = this
|
||||||
function interpolateObjectIds(json: any) {
|
function interpolateObjectIds(json: any) {
|
||||||
for (let field of Object.keys(json)) {
|
for (let field of Object.keys(json || {})) {
|
||||||
if (json[field] instanceof Object) {
|
if (json[field] instanceof Object) {
|
||||||
json[field] = self.createObjectIds(json[field])
|
json[field] = self.createObjectIds(json[field])
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,11 +36,14 @@ export function checkDatasourceTypes(schema: Integration, config: any) {
|
||||||
async function enrichDatasourceWithValues(datasource: Datasource) {
|
async function enrichDatasourceWithValues(datasource: Datasource) {
|
||||||
const cloned = cloneDeep(datasource)
|
const cloned = cloneDeep(datasource)
|
||||||
const env = await getEnvironmentVariables()
|
const env = await getEnvironmentVariables()
|
||||||
|
//Do not process entities, as we do not want to process formulas
|
||||||
|
const { entities, ...clonedWithoutEntities } = cloned
|
||||||
const processed = processObjectSync(
|
const processed = processObjectSync(
|
||||||
cloned,
|
clonedWithoutEntities,
|
||||||
{ env },
|
{ env },
|
||||||
{ onlyFound: true }
|
{ onlyFound: true }
|
||||||
) as Datasource
|
) as Datasource
|
||||||
|
processed.entities = entities
|
||||||
const definition = await getDefinition(processed.source)
|
const definition = await getDefinition(processed.source)
|
||||||
processed.config = checkDatasourceTypes(definition!, processed.config)
|
processed.config = checkDatasourceTypes(definition!, processed.config)
|
||||||
return {
|
return {
|
||||||
|
|
Loading…
Reference in New Issue