Merge branch 'develop' of github.com:Budibase/budibase into grid-all-datasources

This commit is contained in:
Andrew Kingston 2023-10-05 08:23:26 +01:00
commit 922a851710
24 changed files with 199 additions and 123 deletions

View File

@ -14,7 +14,7 @@ jobs:
- uses: passeidireto/trigger-external-workflow-action@main
env:
PAYLOAD_BRANCH: ${{ github.head_ref }}
PAYLOAD_PR_NUMBER: ${{ github.ref }}
PAYLOAD_PR_NUMBER: ${{ github.event.pull_request.number }}
with:
repository: budibase/budibase-deploys
event: featurebranch-qa-close

View File

@ -13,7 +13,7 @@ jobs:
- uses: passeidireto/trigger-external-workflow-action@main
env:
PAYLOAD_BRANCH: ${{ github.head_ref }}
PAYLOAD_PR_NUMBER: ${{ github.ref }}
PAYLOAD_PR_NUMBER: ${{ github.event.pull_request.number }}
with:
repository: budibase/budibase-deploys
event: featurebranch-qa-deploy

View File

@ -1,5 +1,5 @@
{
"version": "2.10.16-alpha.19",
"version": "2.11.5-alpha.4",
"npmClient": "yarn",
"packages": [
"packages/*"

View File

@ -6,6 +6,7 @@ import {
AutomationStepIdArray,
AutomationIOType,
AutomationCustomIOType,
DatasourceFeature,
} from "@budibase/types"
import joi from "joi"
@ -67,9 +68,27 @@ function validateDatasource(schema: any) {
version: joi.string().optional(),
schema: joi.object({
docs: joi.string(),
plus: joi.boolean().optional(),
isSQL: joi.boolean().optional(),
auth: joi
.object({
type: joi.string().required(),
})
.optional(),
features: joi
.object(
Object.fromEntries(
Object.values(DatasourceFeature).map(key => [
key,
joi.boolean().optional(),
])
)
)
.optional(),
relationships: joi.boolean().optional(),
description: joi.string().required(),
friendlyName: joi.string().required(),
type: joi.string().allow(...DATASOURCE_TYPES),
description: joi.string().required(),
datasource: joi.object().pattern(joi.string(), fieldValidator).required(),
query: joi
.object()

View File

@ -3,6 +3,8 @@
import { Select, Checkbox } from "@budibase/bbui"
import { createEventDispatcher } from "svelte"
import RowSelectorTypes from "./RowSelectorTypes.svelte"
import DrawerBindableSlot from "../../common/bindings/DrawerBindableSlot.svelte"
import AutomationBindingPanel from "../../common/bindings/ServerBindingPanel.svelte"
const dispatch = createEventDispatcher()
@ -108,14 +110,29 @@
<div class="schema-fields">
{#each schemaFields as [field, schema]}
{#if !schema.autocolumn && schema.type !== "attachment"}
<RowSelectorTypes
{isTestModal}
{field}
<DrawerBindableSlot
fillWidth
title={value.title}
label={field}
panel={AutomationBindingPanel}
type={schema.type}
{schema}
bindings={parsedBindings}
{value}
{onChange}
/>
value={value[field]}
on:change={e => onChange(e, field)}
{bindings}
allowJS={true}
updateOnChange={false}
drawerLeft="260px"
>
<RowSelectorTypes
{isTestModal}
{field}
{schema}
bindings={parsedBindings}
{value}
{onChange}
/>
</DrawerBindableSlot>
{/if}
{#if isUpdateRow && schema.type === "link"}
<div class="checkbox-field">

View File

@ -8,7 +8,6 @@
} from "@budibase/bbui"
import LinkedRowSelector from "components/common/LinkedRowSelector.svelte"
import DrawerBindableInput from "../../common/bindings/DrawerBindableInput.svelte"
import DrawerBindableSlot from "../../common/bindings/DrawerBindableSlot.svelte"
import ModalBindableInput from "../../common/bindings/ModalBindableInput.svelte"
import AutomationBindingPanel from "../../common/bindings/ServerBindingPanel.svelte"
import Editor from "components/integration/QueryEditor.svelte"
@ -31,88 +30,73 @@
}
</script>
<DrawerBindableSlot
fillWidth
title={value.title}
label={field}
panel={AutomationBindingPanel}
type={schema.type}
{schema}
value={value[field]}
on:change={e => onChange(e, field)}
{bindings}
allowJS={true}
updateOnChange={false}
drawerLeft="260px"
>
{#if schemaHasOptions(schema) && schema.type !== "array"}
<Select
on:change={e => onChange(e, field)}
label={field}
{#if schemaHasOptions(schema) && schema.type !== "array"}
<Select
on:change={e => onChange(e, field)}
label={field}
value={value[field]}
options={schema.constraints.inclusion}
/>
{:else if schema.type === "datetime"}
<DatePicker
label={field}
value={value[field]}
on:change={e => onChange(e, field)}
/>
{:else if schema.type === "boolean"}
<Select
on:change={e => onChange(e, field)}
label={field}
value={value[field]}
options={[
{ label: "True", value: "true" },
{ label: "False", value: "false" },
]}
/>
{:else if schema.type === "array"}
<Multiselect
bind:value={value[field]}
label={field}
options={schema.constraints.inclusion}
on:change={e => onChange(e, field)}
/>
{:else if schema.type === "longform"}
<TextArea
label={field}
bind:value={value[field]}
on:change={e => onChange(e, field)}
/>
{:else if schema.type === "json"}
<span>
<Label>{field}</Label>
<Editor
editorHeight="150"
mode="json"
on:change={e => {
if (e.detail?.value !== value[field]) {
onChange(e, field, schema.type)
}
}}
value={value[field]}
options={schema.constraints.inclusion}
/>
{:else if schema.type === "datetime"}
<DatePicker
label={field}
value={value[field]}
on:change={e => onChange(e, field)}
/>
{:else if schema.type === "boolean"}
<Select
on:change={e => onChange(e, field)}
label={field}
value={value[field]}
options={[
{ label: "True", value: "true" },
{ label: "False", value: "false" },
]}
/>
{:else if schema.type === "array"}
<Multiselect
bind:value={value[field]}
label={field}
options={schema.constraints.inclusion}
on:change={e => onChange(e, field)}
/>
{:else if schema.type === "longform"}
<TextArea
label={field}
bind:value={value[field]}
on:change={e => onChange(e, field)}
/>
{:else if schema.type === "json"}
<span>
<Label>{field}</Label>
<Editor
editorHeight="150"
mode="json"
on:change={e => {
if (e.detail?.value !== value[field]) {
onChange(e, field, schema.type)
}
}}
value={value[field]}
/>
</span>
{:else if schema.type === "link"}
<LinkedRowSelector
bind:linkedRows={value[field]}
{schema}
on:change={e => onChange(e, field)}
/>
{:else if schema.type === "string" || schema.type === "number"}
<svelte:component
this={isTestModal ? ModalBindableInput : DrawerBindableInput}
panel={AutomationBindingPanel}
value={value[field]}
on:change={e => onChange(e, field)}
label={field}
type="string"
bindings={parsedBindings}
fillWidth={true}
allowJS={true}
updateOnChange={false}
/>
{/if}
</DrawerBindableSlot>
</span>
{:else if schema.type === "link"}
<LinkedRowSelector
bind:linkedRows={value[field]}
{schema}
on:change={e => onChange(e, field)}
/>
{:else if schema.type === "string" || schema.type === "number"}
<svelte:component
this={isTestModal ? ModalBindableInput : DrawerBindableInput}
panel={AutomationBindingPanel}
value={value[field]}
on:change={e => onChange(e, field)}
label={field}
type="string"
bindings={parsedBindings}
fillWidth={true}
allowJS={true}
updateOnChange={false}
/>
{/if}

View File

@ -277,10 +277,7 @@
dispatch("updatecolumns")
gridDispatch("close-edit-column")
if (
saveColumn.type === LINK_TYPE &&
saveColumn.relationshipType === RelationshipType.MANY_TO_MANY
) {
if (saveColumn.type === LINK_TYPE) {
// Fetching the new tables
tables.fetch()
// Fetching the new relationships
@ -312,6 +309,11 @@
confirmDeleteDialog.hide()
dispatch("updatecolumns")
gridDispatch("close-edit-column")
if (editableColumn.type === LINK_TYPE) {
// Updating the relationships
datasources.fetch()
}
}
} catch (error) {
notifications.error(`Error deleting column: ${error.message}`)

View File

@ -57,7 +57,8 @@
label: table.name,
value: table._id,
}))
$: valid = getErrorCount(errors) === 0 && allRequiredAttributesSet()
$: valid =
getErrorCount(errors) === 0 && allRequiredAttributesSet(relationshipType)
$: isManyToMany = relationshipType === RelationshipType.MANY_TO_MANY
$: isManyToOne = relationshipType === RelationshipType.MANY_TO_ONE
@ -114,7 +115,7 @@
return Object.entries(errors).filter(entry => !!entry[1]).length
}
function allRequiredAttributesSet() {
function allRequiredAttributesSet(relationshipType) {
const base = getTable(fromId) && getTable(toId) && fromColumn && toColumn
if (relationshipType === RelationshipType.MANY_TO_ONE) {
return base && fromPrimary && fromForeign
@ -124,9 +125,10 @@
}
function validate() {
if (!allRequiredAttributesSet() && !hasValidated) {
if (!allRequiredAttributesSet(relationshipType) && !hasValidated) {
return
}
hasValidated = true
errorChecker.setType(relationshipType)
const fromTable = getTable(fromId),

View File

@ -20,7 +20,9 @@
const getSortableFields = schema => {
return Object.entries(schema || {})
.filter(entry => !UNSORTABLE_TYPES.includes(entry[1].type))
.filter(
entry => !UNSORTABLE_TYPES.includes(entry[1].type) && entry[1].sortable
)
.map(entry => entry[0])
}

View File

@ -62,7 +62,14 @@
</div>
{/if}
<div class="truncate">
<Body>{getSubtitle(datasource)}</Body>
<Body>
{@const subtitle = getSubtitle(datasource)}
{#if subtitle}
{subtitle}
{:else}
{Object.values(datasource.config).join(" / ")}
{/if}
</Body>
</div>
</div>
<div class="right">

View File

@ -21,15 +21,22 @@
function getRelationships(tables) {
const relatedColumns = {}
tables.forEach(({ name: tableName, schema }) => {
tables.forEach(({ name: tableName, schema, _id: tableId }) => {
Object.values(schema).forEach(column => {
if (column.type !== "link") return
relatedColumns[column._id] ??= {}
relatedColumns[column._id].through =
relatedColumns[column._id].through || column.through
const columnId =
column.through ||
column._id ||
(column.main
? `${tableId}_${column.fieldName}__${column.tableId}_${column.foreignKey}`
: `${column.tableId}_${column.foreignKey}__${tableId}_${column.fieldName}`)
relatedColumns[column._id][column.main ? "from" : "to"] = {
relatedColumns[columnId] ??= {}
relatedColumns[columnId].through =
relatedColumns[columnId].through || column.through
relatedColumns[columnId][column.main ? "from" : "to"] = {
...column,
tableName,
}

View File

@ -136,6 +136,7 @@ export function createDatasourcesStore() {
config,
name: `${integration.friendlyName}${nameModifier}`,
plus: integration.plus && integration.name !== IntegrationTypes.REST,
isSQL: integration.isSQL,
}
if (await checkDatasourceValidity(integration, datasource)) {

View File

@ -5597,6 +5597,21 @@
}
]
},
{
"type": "event",
"label": "On row click",
"key": "onRowClick",
"context": [
{
"label": "Clicked row",
"key": "row"
}
],
"dependsOn": {
"setting": "allowEditRows",
"value": false
}
},
{
"type": "boolean",
"label": "Add rows",

View File

@ -14,6 +14,7 @@
export let initialSortOrder = null
export let fixedRowHeight = null
export let columns = null
export let onRowClick = null
// Legacy settings
export let table
@ -23,6 +24,7 @@
$: columnWhitelist = columns?.map(col => col.name)
$: schemaOverrides = getSchemaOverrides(columns)
$: handleRowClick = allowEditRows ? undefined : onRowClick
const getSchemaOverrides = columns => {
let overrides = {}
@ -59,6 +61,7 @@
showControls={false}
notifySuccess={notificationStore.actions.success}
notifyError={notificationStore.actions.error}
on:rowclick={e => handleRowClick?.({ row: e.detail })}
/>
</div>

View File

@ -3,6 +3,8 @@
import RelationshipCell from "./RelationshipCell.svelte"
import { FieldSubtype } from "@budibase/types"
export let api
const { API } = getContext("grid")
const { subtype } = $$props.schema
@ -17,8 +19,11 @@
throw `Search for '${subtype}' not implemented`
}
// As we are overriding the search function from RelationshipCell, we want to map one shape to the expected one for the specific API
const email = Object.values(searchParams.query.string)[0]
const results = await API.searchUsers({
...searchParams,
email,
})
// Mapping to the expected data within RelationshipCell
@ -31,6 +36,7 @@
</script>
<RelationshipCell
bind:api
{...$$props}
{schema}
{searchFunction}

View File

@ -17,13 +17,24 @@
const { config, dispatch, selectedRows } = getContext("grid")
const svelteDispatch = createEventDispatcher()
const select = () => {
const select = e => {
e.stopPropagation()
svelteDispatch("select")
const id = row?._id
if (id) {
selectedRows.actions.toggleRow(id)
}
}
const bulkDelete = e => {
e.stopPropagation()
dispatch("request-bulk-delete")
}
const expand = e => {
e.stopPropagation()
svelteDispatch("expand")
}
</script>
<GridCell
@ -56,7 +67,7 @@
{/if}
{/if}
{#if rowSelected && $config.canDeleteRows}
<div class="delete" on:click={() => dispatch("request-bulk-delete")}>
<div class="delete" on:click={bulkDelete}>
<Icon
name="Delete"
size="S"
@ -65,12 +76,7 @@
</div>
{:else}
<div class="expand" class:visible={$config.canExpandRows && expandable}>
<Icon
size="S"
name="Maximize"
hoverable
on:click={() => svelteDispatch("expand")}
/>
<Icon size="S" name="Maximize" hoverable on:click={expand} />
</div>
{/if}
</div>

View File

@ -17,6 +17,7 @@
columnHorizontalInversionIndex,
contentLines,
isDragging,
dispatch,
} = getContext("grid")
$: rowSelected = !!$selectedRows[row._id]
@ -30,6 +31,7 @@
on:focus
on:mouseenter={$isDragging ? null : () => ($hoveredRowId = row._id)}
on:mouseleave={$isDragging ? null : () => ($hoveredRowId = null)}
on:click={() => dispatch("rowclick", row)}
>
{#each $renderedColumns as column, columnIdx (column.name)}
{@const cellId = `${row._id}-${column.name}`}

View File

@ -74,6 +74,7 @@
class="row"
on:mouseenter={$isDragging ? null : () => ($hoveredRowId = row._id)}
on:mouseleave={$isDragging ? null : () => ($hoveredRowId = null)}
on:click={() => dispatch("rowclick", row)}
>
<GutterCell {row} {rowFocused} {rowHovered} {rowSelected} />
{#if $stickyColumn}

@ -1 +1 @@
Subproject commit 7040ae5282cc23d7ae56ac1be8a369d1c32aab2f
Subproject commit 30385682141e5ba9d98de7d71d5be1672109cd15

View File

@ -19,7 +19,7 @@ docker run --rm \
-v ${PWD}/generated:/generated \
-v ${PWD}/config.json:/config.json \
-u $(id -u):$(id -g) \
swaggerapi/swagger-codegen-cli-v3 generate \
swaggerapi/swagger-codegen-cli-v3:3.0.46 generate \
-i /openapi.yml \
-l javascript \
-o /generated \
@ -34,4 +34,4 @@ if [[ -f "openapi.yaml" ]]; then
fi
if [[ -d "generated" ]]; then
rm -r generated
fi
fi

View File

@ -14,5 +14,5 @@ export function isSQL(datasource: Datasource): boolean {
SourceName.MYSQL,
SourceName.ORACLE,
]
return SQL.indexOf(datasource.source) !== -1
return SQL.indexOf(datasource.source) !== -1 || datasource.isSQL === true
}

View File

@ -9,6 +9,7 @@ export interface Datasource extends Document {
// the config is defined by the schema
config?: Record<string, any>
plus?: boolean
isSQL?: boolean
entities?: {
[key: string]: Table
}

View File

@ -140,6 +140,7 @@ export interface DatasourceConfig {
export interface Integration {
docs: string
plus?: boolean
isSQL?: boolean
auth?: { type: string }
features?: Partial<Record<DatasourceFeature, boolean>>
relationships?: boolean

View File

@ -11,7 +11,7 @@ import { TestConfiguration } from "../../../../tests"
import { events } from "@budibase/backend-core"
// this test can 409 - retries reduce issues with this
jest.retryTimes(2)
jest.retryTimes(2, { logErrorsBeforeRetry: true })
jest.setTimeout(30000)
mocks.licenses.useScimIntegration()