Merge branch 'develop' of github.com:Budibase/budibase into more-grid-tweaks

This commit is contained in:
Andrew Kingston 2023-04-26 15:52:34 +01:00
commit defcc7a273
50 changed files with 790 additions and 163 deletions

View File

@ -1,5 +1,5 @@
{ {
"version": "2.5.6-alpha.26", "version": "2.5.6-alpha.32",
"npmClient": "yarn", "npmClient": "yarn",
"useWorkspaces": true, "useWorkspaces": true,
"packages": ["packages/*"], "packages": ["packages/*"],

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/backend-core", "name": "@budibase/backend-core",
"version": "2.5.6-alpha.26", "version": "2.5.6-alpha.32",
"description": "Budibase backend core libraries used in server and worker", "description": "Budibase backend core libraries used in server and worker",
"main": "dist/src/index.js", "main": "dist/src/index.js",
"types": "dist/src/index.d.ts", "types": "dist/src/index.d.ts",
@ -24,7 +24,7 @@
"dependencies": { "dependencies": {
"@budibase/nano": "10.1.2", "@budibase/nano": "10.1.2",
"@budibase/pouchdb-replication-stream": "1.2.10", "@budibase/pouchdb-replication-stream": "1.2.10",
"@budibase/types": "2.5.6-alpha.26", "@budibase/types": "2.5.6-alpha.32",
"@shopify/jest-koa-mocks": "5.0.1", "@shopify/jest-koa-mocks": "5.0.1",
"@techpass/passport-openidconnect": "0.3.2", "@techpass/passport-openidconnect": "0.3.2",
"aws-cloudfront-sign": "2.2.0", "aws-cloudfront-sign": "2.2.0",
@ -47,6 +47,8 @@
"passport-jwt": "4.0.0", "passport-jwt": "4.0.0",
"passport-local": "1.0.0", "passport-local": "1.0.0",
"passport-oauth2-refresh": "^2.1.0", "passport-oauth2-refresh": "^2.1.0",
"pino": "8.11.0",
"pino-http": "8.3.3",
"posthog-node": "1.3.0", "posthog-node": "1.3.0",
"pouchdb": "7.3.0", "pouchdb": "7.3.0",
"pouchdb-find": "7.2.2", "pouchdb-find": "7.2.2",
@ -80,7 +82,6 @@
"jest-serial-runner": "^1.2.1", "jest-serial-runner": "^1.2.1",
"koa": "2.13.4", "koa": "2.13.4",
"nodemon": "2.0.16", "nodemon": "2.0.16",
"pino": "7.11.0",
"pino-pretty": "10.0.0", "pino-pretty": "10.0.0",
"pouchdb-adapter-memory": "7.2.2", "pouchdb-adapter-memory": "7.2.2",
"timekeeper": "2.2.0", "timekeeper": "2.2.0",

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/bbui", "name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.", "description": "A UI solution used in the different Budibase projects.",
"version": "2.5.6-alpha.26", "version": "2.5.6-alpha.32",
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"module": "dist/bbui.es.js", "module": "dist/bbui.es.js",
@ -38,8 +38,8 @@
], ],
"dependencies": { "dependencies": {
"@adobe/spectrum-css-workflow-icons": "1.2.1", "@adobe/spectrum-css-workflow-icons": "1.2.1",
"@budibase/shared-core": "2.5.6-alpha.26", "@budibase/shared-core": "2.5.6-alpha.32",
"@budibase/string-templates": "2.5.6-alpha.26", "@budibase/string-templates": "2.5.6-alpha.32",
"@spectrum-css/accordion": "3.0.24", "@spectrum-css/accordion": "3.0.24",
"@spectrum-css/actionbutton": "1.0.1", "@spectrum-css/actionbutton": "1.0.1",
"@spectrum-css/actiongroup": "1.0.1", "@spectrum-css/actiongroup": "1.0.1",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "2.5.6-alpha.26", "version": "2.5.6-alpha.32",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -58,11 +58,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "2.5.6-alpha.26", "@budibase/bbui": "2.5.6-alpha.32",
"@budibase/client": "2.5.6-alpha.26", "@budibase/frontend-core": "2.5.6-alpha.32",
"@budibase/frontend-core": "2.5.6-alpha.26", "@budibase/shared-core": "2.5.6-alpha.32",
"@budibase/shared-core": "2.5.6-alpha.26", "@budibase/string-templates": "2.5.6-alpha.32",
"@budibase/string-templates": "2.5.6-alpha.26",
"@fortawesome/fontawesome-svg-core": "^6.2.1", "@fortawesome/fontawesome-svg-core": "^6.2.1",
"@fortawesome/free-brands-svg-icons": "^6.2.1", "@fortawesome/free-brands-svg-icons": "^6.2.1",
"@fortawesome/free-solid-svg-icons": "^6.2.1", "@fortawesome/free-solid-svg-icons": "^6.2.1",

View File

@ -10,7 +10,10 @@ import { auth } from "./stores/portal"
export const API = createAPIClient({ export const API = createAPIClient({
attachHeaders: headers => { attachHeaders: headers => {
// Attach app ID header from store // Attach app ID header from store
headers["x-budibase-app-id"] = get(store).appId let appId = get(store).appId
if (appId) {
headers["x-budibase-app-id"] = appId
}
// Add csrf token if authenticated // Add csrf token if authenticated
const user = get(auth).user const user = get(auth).user

View File

@ -134,6 +134,7 @@ export const getFrontendStore = () => {
previousTopNavPath: {}, previousTopNavPath: {},
version: application.version, version: application.version,
revertableVersion: application.revertableVersion, revertableVersion: application.revertableVersion,
upgradableVersion: application.upgradableVersion,
navigation: application.navigation || {}, navigation: application.navigation || {},
usedPlugins: application.usedPlugins || [], usedPlugins: application.usedPlugins || [],
})) }))

View File

@ -9,7 +9,6 @@
} from "@budibase/bbui" } from "@budibase/bbui"
import { store } from "builderStore" import { store } from "builderStore"
import { API } from "api" import { API } from "api"
import clientPackage from "@budibase/client/package.json"
export function show() { export function show() {
updateModal.show() updateModal.show()
@ -25,9 +24,9 @@
$: appId = $store.appId $: appId = $store.appId
$: updateAvailable = $: updateAvailable =
clientPackage.version && $store.upgradableVersion &&
$store.version && $store.version &&
clientPackage.version !== $store.version $store.upgradableVersion !== $store.version
$: revertAvailable = $store.revertableVersion != null $: revertAvailable = $store.revertableVersion != null
const refreshAppPackage = async () => { const refreshAppPackage = async () => {
@ -46,7 +45,7 @@
// Don't wait for the async refresh, since this causes modal flashing // Don't wait for the async refresh, since this causes modal flashing
refreshAppPackage() refreshAppPackage()
notifications.success( notifications.success(
`App updated successfully to version ${clientPackage.version}` `App updated successfully to version ${$store.upgradableVersion}`
) )
} catch (err) { } catch (err) {
notifications.error(`Error updating app: ${err}`) notifications.error(`Error updating app: ${err}`)
@ -91,7 +90,7 @@
{#if updateAvailable} {#if updateAvailable}
<Body size="S"> <Body size="S">
This app is currently using version <b>{$store.version}</b>, but version This app is currently using version <b>{$store.version}</b>, but version
<b>{clientPackage.version}</b> is available. Updates can contain new features, <b>{$store.upgradableVersion}</b> is available. Updates can contain new features,
performance improvements and bug fixes. performance improvements and bug fixes.
</Body> </Body>
{:else} {:else}

View File

@ -21,6 +21,7 @@ import DrawerBindableCombobox from "components/common/bindings/DrawerBindableCom
import ColumnEditor from "./controls/ColumnEditor/ColumnEditor.svelte" import ColumnEditor from "./controls/ColumnEditor/ColumnEditor.svelte"
import BasicColumnEditor from "./controls/ColumnEditor/BasicColumnEditor.svelte" import BasicColumnEditor from "./controls/ColumnEditor/BasicColumnEditor.svelte"
import BarButtonList from "./controls/BarButtonList.svelte" import BarButtonList from "./controls/BarButtonList.svelte"
import FieldConfiguration from "./controls/FieldConfiguration/FieldConfiguration.svelte"
const componentMap = { const componentMap = {
text: DrawerBindableCombobox, text: DrawerBindableCombobox,
@ -43,6 +44,7 @@ const componentMap = {
section: SectionSelect, section: SectionSelect,
filter: FilterEditor, filter: FilterEditor,
url: URLSelect, url: URLSelect,
fieldConfiguration: FieldConfiguration,
columns: ColumnEditor, columns: ColumnEditor,
"columns/basic": BasicColumnEditor, "columns/basic": BasicColumnEditor,
"field/sortable": SortableFieldSelect, "field/sortable": SortableFieldSelect,

View File

@ -0,0 +1,91 @@
<script>
import { Button, ActionButton, Drawer } from "@budibase/bbui"
import { createEventDispatcher } from "svelte"
import ColumnDrawer from "./ColumnDrawer.svelte"
import { cloneDeep } from "lodash/fp"
import {
getDatasourceForProvider,
getSchemaForDatasource,
} from "builderStore/dataBinding"
import { currentAsset } from "builderStore"
import { getFields } from "helpers/searchFields"
export let componentInstance
export let value = []
export let allowCellEditing = true
export let subject = "Table"
const dispatch = createEventDispatcher()
let drawer
let boundValue
$: datasource = getDatasourceForProvider($currentAsset, componentInstance)
$: schema = getSchema($currentAsset, datasource)
$: options = allowCellEditing
? Object.keys(schema || {})
: enrichedSchemaFields?.map(field => field.name)
$: sanitisedValue = getValidColumns(value, options)
$: updateBoundValue(sanitisedValue)
$: enrichedSchemaFields = getFields(Object.values(schema || {}), {
allowLinks: true,
})
const getSchema = (asset, datasource) => {
const schema = getSchemaForDatasource(asset, datasource).schema
// Don't show ID and rev in tables
if (schema) {
delete schema._id
delete schema._rev
}
return schema
}
const updateBoundValue = value => {
boundValue = cloneDeep(value)
}
const getValidColumns = (columns, options) => {
if (!Array.isArray(columns) || !columns.length) {
return []
}
// We need to account for legacy configs which would just be an array
// of strings
if (typeof columns[0] === "string") {
columns = columns.map(col => ({
name: col,
displayName: col,
}))
}
return columns.filter(column => {
return options.includes(column.name)
})
}
const open = () => {
updateBoundValue(sanitisedValue)
drawer.show()
}
const save = () => {
dispatch("change", getValidColumns(boundValue, options))
drawer.hide()
}
</script>
<ActionButton on:click={open}>Configure columns</ActionButton>
<Drawer bind:this={drawer} title="{subject} Columns">
<svelte:fragment slot="description">
Configure the columns in your {subject.toLowerCase()}.
</svelte:fragment>
<Button cta slot="buttons" on:click={save}>Save</Button>
<ColumnDrawer
slot="body"
bind:columns={boundValue}
{options}
{schema}
{allowCellEditing}
/>
</Drawer>

View File

@ -0,0 +1,26 @@
<script>
import { DrawerContent, Drawer, Button, Icon } from "@budibase/bbui"
import ValidationDrawer from "components/design/settings/controls/ValidationEditor/ValidationDrawer.svelte"
export let column
export let type
let drawer
</script>
<Icon name="Settings" hoverable size="S" on:click={drawer.show} />
<Drawer bind:this={drawer} title="Field Validation">
<svelte:fragment slot="description">
"{column.name}" field validation
</svelte:fragment>
<Button cta slot="buttons" on:click={drawer.hide}>Save</Button>
<DrawerContent slot="body">
<div class="container">
<ValidationDrawer
slot="body"
bind:rules={column.validation}
fieldName={column.name}
{type}
/>
</div>
</DrawerContent>
</Drawer>

View File

@ -0,0 +1,202 @@
<script>
import {
Button,
Icon,
DrawerContent,
Layout,
Select,
Label,
Body,
Input,
} from "@budibase/bbui"
import { flip } from "svelte/animate"
import { dndzone } from "svelte-dnd-action"
import { generate } from "shortid"
import CellEditor from "./CellEditor.svelte"
export let columns = []
export let options = []
export let schema = {}
const flipDurationMs = 150
let dragDisabled = true
$: unselectedColumns = getUnselectedColumns(options, columns)
$: columns.forEach(column => {
if (!column.id) {
column.id = generate()
}
})
const getUnselectedColumns = (allColumns, selectedColumns) => {
let optionsObj = {}
allColumns.forEach(option => {
optionsObj[option] = true
})
selectedColumns?.forEach(column => {
delete optionsObj[column.name]
})
return Object.keys(optionsObj)
}
const getRemainingColumnOptions = selectedColumn => {
if (!selectedColumn || unselectedColumns.includes(selectedColumn)) {
return unselectedColumns
}
return [selectedColumn, ...unselectedColumns]
}
const addColumn = () => {
columns = [...columns, {}]
}
const removeColumn = id => {
columns = columns.filter(column => column.id !== id)
}
const updateColumnOrder = e => {
columns = e.detail.items
}
const handleFinalize = e => {
updateColumnOrder(e)
dragDisabled = true
}
const addAllColumns = () => {
let newColumns = columns || []
options.forEach(field => {
const fieldSchema = schema[field]
const hasCol = columns && columns.findIndex(x => x.name === field) !== -1
if (!fieldSchema?.autocolumn && !hasCol) {
newColumns.push({
name: field,
displayName: field,
})
}
})
columns = newColumns
}
const reset = () => {
columns = []
}
const getFieldType = column => {
return `validation/${schema[column.name]?.type}`
}
</script>
<DrawerContent>
<div class="container">
<Layout noPadding gap="S">
{#if columns?.length}
<Layout noPadding gap="XS">
<div class="column">
<div />
<Label size="L">Column</Label>
<Label size="L">Label</Label>
<div />
<div />
</div>
<div
class="columns"
use:dndzone={{
items: columns,
flipDurationMs,
dropTargetStyle: { outline: "none" },
dragDisabled,
}}
on:finalize={handleFinalize}
on:consider={updateColumnOrder}
>
{#each columns as column (column.id)}
<div class="column" animate:flip={{ duration: flipDurationMs }}>
<div
class="handle"
aria-label="drag-handle"
style={dragDisabled ? "cursor: grab" : "cursor: grabbing"}
on:mousedown={() => (dragDisabled = false)}
>
<Icon name="DragHandle" size="XL" />
</div>
<Select
bind:value={column.name}
placeholder="Column"
options={getRemainingColumnOptions(column.name)}
on:change={e => (column.displayName = e.detail)}
/>
<Input bind:value={column.displayName} placeholder="Label" />
<CellEditor type={getFieldType(column)} bind:column />
<Icon
name="Close"
hoverable
size="S"
on:click={() => removeColumn(column.id)}
disabled={columns.length === 1}
/>
</div>
{/each}
</div>
</Layout>
{:else}
<div class="column">
<div class="wide">
<Body size="S">Add columns to be included in your form below.</Body>
</div>
</div>
{/if}
<div class="column">
<div class="buttons wide">
<Button secondary icon="Add" on:click={addColumn}>Add column</Button>
<Button secondary quiet on:click={addAllColumns}>
Add all columns
</Button>
{#if columns?.length}
<Button secondary quiet on:click={reset}>Reset columns</Button>
{/if}
</div>
</div>
</Layout>
</div>
</DrawerContent>
<style>
.container {
width: 100%;
max-width: 600px;
margin: 0 auto;
}
.columns {
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
gap: var(--spacing-m);
}
.column {
gap: var(--spacing-l);
display: grid;
grid-template-columns: 20px 1fr 1fr 16px 16px;
align-items: center;
border-radius: var(--border-radius-s);
transition: background-color ease-in-out 130ms;
}
.column:hover {
background-color: var(--spectrum-global-color-gray-100);
}
.handle {
display: grid;
place-items: center;
}
.wide {
grid-column: 2 / -1;
}
.buttons {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: var(--spacing-m);
}
</style>

View File

@ -0,0 +1,89 @@
<script>
import { Button, ActionButton, Drawer } from "@budibase/bbui"
import { createEventDispatcher } from "svelte"
import ColumnDrawer from "./ColumnDrawer.svelte"
import { cloneDeep } from "lodash/fp"
import {
getDatasourceForProvider,
getSchemaForDatasource,
} from "builderStore/dataBinding"
import { currentAsset } from "builderStore"
import { getFields } from "helpers/searchFields"
export let componentInstance
export let value = []
const convertOldColumnFormat = oldColumns => {
if (typeof oldColumns?.[0] === "string") {
value = oldColumns.map(field => ({ name: field, displayName: field }))
}
}
$: convertOldColumnFormat(value)
const dispatch = createEventDispatcher()
let drawer
let boundValue
$: datasource = getDatasourceForProvider($currentAsset, componentInstance)
$: schema = getSchema($currentAsset, datasource)
$: options = Object.keys(schema || {})
$: sanitisedValue = getValidColumns(value, options)
$: updateBoundValue(sanitisedValue)
$: enrichedSchemaFields = getFields(Object.values(schema || {}), {
allowLinks: true,
})
const getSchema = (asset, datasource) => {
const schema = getSchemaForDatasource(asset, datasource).schema
// Don't show ID and rev in tables
if (schema) {
delete schema._id
delete schema._rev
}
return schema
}
const updateBoundValue = value => {
boundValue = cloneDeep(value)
}
const getValidColumns = (columns, options) => {
if (!Array.isArray(columns) || !columns.length) {
return []
}
// We need to account for legacy configs which would just be an array
// of strings
if (typeof columns[0] === "string") {
columns = columns.map(col => ({
name: col,
displayName: col,
}))
}
return columns.filter(column => {
return options.includes(column.name)
})
}
const open = () => {
updateBoundValue(sanitisedValue)
drawer.show()
}
const save = () => {
dispatch("change", getValidColumns(boundValue, options))
drawer.hide()
}
</script>
<ActionButton on:click={open}>Configure fields</ActionButton>
<Drawer bind:this={drawer} title="Form Fields">
<svelte:fragment slot="description">
Configure the fields in your form.
</svelte:fragment>
<Button cta slot="buttons" on:click={save}>Save</Button>
<ColumnDrawer slot="body" bind:columns={boundValue} {options} {schema} />
</Drawer>

View File

@ -16,6 +16,7 @@
import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte" import DrawerBindableInput from "components/common/bindings/DrawerBindableInput.svelte"
import { generate } from "shortid" import { generate } from "shortid"
export let fieldName = null
export let rules = [] export let rules = []
export let bindings = [] export let bindings = []
export let type export let type
@ -124,7 +125,7 @@
} }
$: dataSourceSchema = getDataSourceSchema($currentAsset, $selectedComponent) $: dataSourceSchema = getDataSourceSchema($currentAsset, $selectedComponent)
$: field = $selectedComponent?.field $: field = fieldName || $selectedComponent?.field
$: schemaRules = parseRulesFromSchema(field, dataSourceSchema || {}) $: schemaRules = parseRulesFromSchema(field, dataSourceSchema || {})
$: fieldType = type?.split("/")[1] || "string" $: fieldType = type?.split("/")[1] || "string"
$: constraintOptions = getConstraintsForType(fieldType) $: constraintOptions = getConstraintsForType(fieldType)
@ -140,8 +141,12 @@
const formParent = findClosestMatchingComponent( const formParent = findClosestMatchingComponent(
asset.props, asset.props,
component._id, component._id,
component => component._component.endsWith("/form") component =>
component._component.endsWith("/form") ||
component._component.endsWith("/formblock") ||
component._component.endsWith("/tableblock")
) )
return getSchemaForDatasource(asset, formParent?.dataSource) return getSchemaForDatasource(asset, formParent?.dataSource)
} }

View File

@ -35,7 +35,7 @@
} }
</script> </script>
<Panel title={$selectedLayout?.name} icon="Experience" borderLeft> <Panel title={$selectedLayout?.name} icon="Experience" borderLeft wide>
<Layout paddingX="L" paddingY="XL" gap="S"> <Layout paddingX="L" paddingY="XL" gap="S">
<Banner type="warning" showCloseButton={false}> <Banner type="warning" showCloseButton={false}>
Custom layouts are being deprecated. They will be removed in a future Custom layouts are being deprecated. They will be removed in a future

View File

@ -9,7 +9,7 @@
} }
</script> </script>
<Panel borderLeft title="Navigation" icon="InfoOutline"> <Panel borderLeft title="Navigation" icon="InfoOutline" wide>
<Layout paddingX="L" paddingY="XL" gap="S"> <Layout paddingX="L" paddingY="XL" gap="S">
{#if $selectedScreen.layoutId} {#if $selectedScreen.layoutId}
<Banner <Banner

View File

@ -149,6 +149,7 @@
title={$selectedScreen.routing.route} title={$selectedScreen.routing.route}
icon={$selectedScreen.routing.route === "/" ? "Home" : "WebPage"} icon={$selectedScreen.routing.route === "/" ? "Home" : "WebPage"}
borderLeft borderLeft
wide
> >
<Layout gap="S" paddingX="L" paddingY="XL"> <Layout gap="S" paddingX="L" paddingY="XL">
{#if $selectedScreen.layoutId} {#if $selectedScreen.layoutId}

View File

@ -3,7 +3,7 @@
import { Body, Layout } from "@budibase/bbui" import { Body, Layout } from "@budibase/bbui"
</script> </script>
<Panel borderLeft title="Theme" icon="InfoOutline"> <Panel borderLeft title="Theme" icon="InfoOutline" wide>
<Layout paddingX="L" paddingY="XL"> <Layout paddingX="L" paddingY="XL">
<Body size="S"> <Body size="S">
Your theme is set across all the screens within your app. Your theme is set across all the screens within your app.

View File

@ -33,7 +33,7 @@
import * as routify from "@roxi/routify" import * as routify from "@roxi/routify"
import { onDestroy } from "svelte" import { onDestroy } from "svelte"
// Keep URL and state in sync for selected screen ID // Keep URL and state in sync for selected app ID
const stopSyncing = syncURLToState({ const stopSyncing = syncURLToState({
urlParam: "appId", urlParam: "appId",
stateKey: "selectedAppId", stateKey: "selectedAppId",
@ -47,6 +47,7 @@
let deletionModal let deletionModal
let exportPublishedVersion = false let exportPublishedVersion = false
let deletionConfirmationAppName let deletionConfirmationAppName
let loaded = false
$: app = $overview.selectedApp $: app = $overview.selectedApp
$: appId = $overview.selectedAppId $: appId = $overview.selectedAppId
@ -56,10 +57,12 @@
$: lockedByYou = $auth.user.email === app?.lockedBy?.email $: lockedByYou = $auth.user.email === app?.lockedBy?.email
const initialiseApp = async appId => { const initialiseApp = async appId => {
loaded = false
try { try {
const pkg = await API.fetchAppPackage(appId) const pkg = await API.fetchAppPackage(appId)
await store.actions.initialise(pkg) await store.actions.initialise(pkg)
await API.syncApp(appId) await API.syncApp(appId)
loaded = true
} catch (error) { } catch (error) {
notifications.error("Error initialising app overview") notifications.error("Error initialising app overview")
$goto("../../") $goto("../../")
@ -228,7 +231,9 @@
active={$isActive("./version")} active={$isActive("./version")}
/> />
</SideNav> </SideNav>
<slot /> {#if loaded}
<slot />
{/if}
</Content> </Content>
</Layout> </Layout>
</Page> </Page>

View File

@ -13,7 +13,6 @@
notifications, notifications,
} from "@budibase/bbui" } from "@budibase/bbui"
import { store } from "builderStore" import { store } from "builderStore"
import clientPackage from "@budibase/client/package.json"
import { processStringSync } from "@budibase/string-templates" import { processStringSync } from "@budibase/string-templates"
import { users, auth, apps, groups, overview } from "stores/portal" import { users, auth, apps, groups, overview } from "stores/portal"
import { fetchData } from "@budibase/frontend-core" import { fetchData } from "@budibase/frontend-core"
@ -40,7 +39,7 @@
}, },
}, },
}) })
$: updateAvailable = clientPackage.version !== $store.version $: updateAvailable = $store.upgradableVersion !== $store.version
$: isPublished = app?.status === AppStatus.DEPLOYED $: isPublished = app?.status === AppStatus.DEPLOYED
$: appEditorId = !app?.updatedBy ? $auth.user._id : app?.updatedBy $: appEditorId = !app?.updatedBy ? $auth.user._id : app?.updatedBy
$: appEditorText = appEditor?.firstName || appEditor?.email $: appEditorText = appEditor?.firstName || appEditor?.email
@ -172,8 +171,8 @@
<Heading size="XS">{$store.version}</Heading> <Heading size="XS">{$store.version}</Heading>
{#if updateAvailable} {#if updateAvailable}
<div class="version-status"> <div class="version-status">
New version <strong>{clientPackage.version}</strong> is available New version <strong>{$store.upgradableVersion}</strong> is
- available -
<Link <Link
on:click={() => { on:click={() => {
$goto("./version") $goto("./version")

View File

@ -1,12 +1,11 @@
<script> <script>
import { Layout, Heading, Body, Divider, Button } from "@budibase/bbui" import { Layout, Heading, Body, Divider, Button } from "@budibase/bbui"
import { store } from "builderStore" import { store } from "builderStore"
import clientPackage from "@budibase/client/package.json"
import VersionModal from "components/deploy/VersionModal.svelte" import VersionModal from "components/deploy/VersionModal.svelte"
let versionModal let versionModal
$: updateAvailable = clientPackage.version !== $store.version $: updateAvailable = $store.upgradableVersion !== $store.version
</script> </script>
<Layout noPadding> <Layout noPadding>
@ -18,7 +17,7 @@
{#if updateAvailable} {#if updateAvailable}
<Body> <Body>
The app is currently using version <strong>{$store.version}</strong> The app is currently using version <strong>{$store.version}</strong>
but version <strong>{clientPackage.version}</strong> is available. but version <strong>{$store.upgradableVersion}</strong> is available.
<br /> <br />
Updates can contain new features, performance improvements and bug fixes. Updates can contain new features, performance improvements and bug fixes.
</Body> </Body>

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "2.5.6-alpha.26", "version": "2.5.6-alpha.32",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "dist/index.js", "main": "dist/index.js",
"bin": { "bin": {
@ -29,9 +29,9 @@
"outputPath": "build" "outputPath": "build"
}, },
"dependencies": { "dependencies": {
"@budibase/backend-core": "2.5.6-alpha.26", "@budibase/backend-core": "2.5.6-alpha.32",
"@budibase/string-templates": "2.5.6-alpha.26", "@budibase/string-templates": "2.5.6-alpha.32",
"@budibase/types": "2.5.6-alpha.26", "@budibase/types": "2.5.6-alpha.32",
"axios": "0.21.2", "axios": "0.21.2",
"chalk": "4.1.0", "chalk": "4.1.0",
"cli-progress": "3.11.2", "cli-progress": "3.11.2",

View File

@ -4435,6 +4435,48 @@
"key": "row" "key": "row"
} }
] ]
},
{
"label": "Fields",
"type": "fieldConfiguration",
"key": "sidePanelFields",
"nested": true,
"dependsOn": {
"setting": "clickBehaviour",
"value": "details"
}
},
{
"label": "Show delete",
"type": "boolean",
"key": "sidePanelShowDelete",
"nested": true,
"dependsOn": {
"setting": "clickBehaviour",
"value": "details"
}
},
{
"label": "Save label",
"type": "text",
"key": "sidePanelSaveLabel",
"defaultValue": "Save",
"nested": true,
"dependsOn": {
"setting": "clickBehaviour",
"value": "details"
}
},
{
"label": "Delete label",
"type": "text",
"key": "sidePanelDeleteLabel",
"defaultValue": "Delete",
"nested": true,
"dependsOn": {
"setting": "clickBehaviour",
"value": "details"
}
} }
] ]
}, },
@ -4979,7 +5021,7 @@
"name": "Fields", "name": "Fields",
"settings": [ "settings": [
{ {
"type": "multifield", "type": "fieldConfiguration",
"label": "Fields", "label": "Fields",
"key": "fields", "key": "fields",
"selectAllFields": true "selectAllFields": true
@ -5028,6 +5070,17 @@
"invert": true "invert": true
} }
}, },
{
"type": "text",
"key": "saveButtonLabel",
"label": "Save button label",
"nested": true,
"defaultValue": "Save",
"dependsOn": {
"setting": "showSaveButton",
"value": true
}
},
{ {
"type": "boolean", "type": "boolean",
"label": "Allow delete", "label": "Allow delete",
@ -5038,6 +5091,17 @@
"value": "Update" "value": "Update"
} }
}, },
{
"type": "text",
"key": "deleteButtonLabel",
"label": "Delete button label",
"nested": true,
"defaultValue": "Delete",
"dependsOn": {
"setting": "showDeleteButton",
"value": true
}
},
{ {
"type": "url", "type": "url",
"label": "Navigate after button press", "label": "Navigate after button press",
@ -5161,36 +5225,5 @@
"type": "schema", "type": "schema",
"suffix": "repeater" "suffix": "repeater"
} }
},
"spreadsheet": {
"name": "Spreadsheet",
"icon": "ViewGrid",
"settings": [
{
"key": "table",
"type": "table",
"label": "Table"
},
{
"type": "filter",
"label": "Filtering",
"key": "filter"
},
{
"type": "field/sortable",
"label": "Sort Column",
"key": "sortColumn"
},
{
"type": "select",
"label": "Sort Order",
"key": "sortOrder",
"options": [
"Ascending",
"Descending"
],
"defaultValue": "Ascending"
}
]
} }
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "2.5.6-alpha.26", "version": "2.5.6-alpha.32",
"license": "MPL-2.0", "license": "MPL-2.0",
"module": "dist/budibase-client.js", "module": "dist/budibase-client.js",
"main": "dist/budibase-client.js", "main": "dist/budibase-client.js",
@ -19,11 +19,11 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "2.5.6-alpha.26", "@budibase/bbui": "2.5.6-alpha.32",
"@budibase/frontend-core": "2.5.6-alpha.26", "@budibase/frontend-core": "2.5.6-alpha.32",
"@budibase/shared-core": "2.5.6-alpha.26", "@budibase/shared-core": "2.5.6-alpha.32",
"@budibase/string-templates": "2.5.6-alpha.26", "@budibase/string-templates": "2.5.6-alpha.32",
"@budibase/types": "2.5.6-alpha.26", "@budibase/types": "2.5.6-alpha.32",
"@spectrum-css/button": "^3.0.3", "@spectrum-css/button": "^3.0.3",
"@spectrum-css/card": "^3.0.3", "@spectrum-css/card": "^3.0.3",
"@spectrum-css/divider": "^1.0.3", "@spectrum-css/divider": "^1.0.3",

View File

@ -26,6 +26,10 @@
export let titleButtonClickBehaviour export let titleButtonClickBehaviour
export let onClickTitleButton export let onClickTitleButton
export let noRowsMessage export let noRowsMessage
export let sidePanelFields
export let sidePanelShowDelete
export let sidePanelSaveLabel
export let sidePanelDeleteLabel
const { fetchDatasourceSchema, API } = getContext("sdk") const { fetchDatasourceSchema, API } = getContext("sdk")
const stateKey = `ID_${generate()}` const stateKey = `ID_${generate()}`
@ -241,10 +245,12 @@
props={{ props={{
dataSource, dataSource,
showSaveButton: true, showSaveButton: true,
showDeleteButton: true, showDeleteButton: sidePanelShowDelete,
saveButtonLabel: sidePanelSaveLabel,
deleteButtonLabel: sidePanelDeleteLabel,
actionType: "Update", actionType: "Update",
rowId: `{{ ${safe("state")}.${safe(stateKey)} }}`, rowId: `{{ ${safe("state")}.${safe(stateKey)} }}`,
fields: normalFields, fields: sidePanelFields || normalFields,
title: editTitle, title: editTitle,
labelPosition: "left", labelPosition: "left",
}} }}
@ -266,8 +272,9 @@
dataSource, dataSource,
showSaveButton: true, showSaveButton: true,
showDeleteButton: false, showDeleteButton: false,
saveButtonLabel: sidePanelSaveLabel,
actionType: "Create", actionType: "Create",
fields: normalFields, fields: sidePanelFields || normalFields,
title: "Create Row", title: "Create Row",
labelPosition: "left", labelPosition: "left",
}} }}

View File

@ -12,6 +12,8 @@
export let fields export let fields
export let labelPosition export let labelPosition
export let title export let title
export let saveButtonLabel
export let deleteButtonLabel
export let showSaveButton export let showSaveButton
export let showDeleteButton export let showDeleteButton
export let rowId export let rowId
@ -20,10 +22,40 @@
const { fetchDatasourceSchema } = getContext("sdk") const { fetchDatasourceSchema } = getContext("sdk")
const convertOldFieldFormat = fields => {
if (typeof fields?.[0] === "string") {
return fields.map(field => ({ name: field, displayName: field }))
}
return fields
}
const getDefaultFields = (fields, schema) => {
if (schema && (!fields || fields.length === 0)) {
const defaultFields = []
Object.values(schema).forEach(field => {
if (field.autocolumn) return
defaultFields.push({
name: field.name,
displayName: field.name,
})
})
return defaultFields
}
return fields
}
let schema let schema
let providerId let providerId
let repeaterId let repeaterId
$: formattedFields = convertOldFieldFormat(fields)
$: fieldsOrDefault = getDefaultFields(formattedFields, schema)
$: fetchSchema(dataSource) $: fetchSchema(dataSource)
$: dataProvider = `{{ literal ${safe(providerId)} }}` $: dataProvider = `{{ literal ${safe(providerId)} }}`
$: filter = [ $: filter = [
@ -46,9 +78,11 @@
actionType, actionType,
size, size,
disabled, disabled,
fields, fields: fieldsOrDefault,
labelPosition, labelPosition,
title, title,
saveButtonLabel,
deleteButtonLabel,
showSaveButton, showSaveButton,
showDeleteButton, showDeleteButton,
schema, schema,

View File

@ -11,6 +11,8 @@
export let fields export let fields
export let labelPosition export let labelPosition
export let title export let title
export let saveButtonLabel
export let deleteButtonLabel
export let showSaveButton export let showSaveButton
export let showDeleteButton export let showDeleteButton
export let schema export let schema
@ -33,6 +35,12 @@
let formId let formId
$: onSave = [ $: onSave = [
{
"##eventHandlerType": "Validate Form",
parameters: {
componentId: formId,
},
},
{ {
"##eventHandlerType": "Save Row", "##eventHandlerType": "Save Row",
parameters: { parameters: {
@ -163,7 +171,7 @@
<BlockComponent <BlockComponent
type="button" type="button"
props={{ props={{
text: "Delete", text: deleteButtonLabel || "Delete",
onClick: onDelete, onClick: onDelete,
quiet: true, quiet: true,
type: "secondary", type: "secondary",
@ -175,7 +183,7 @@
<BlockComponent <BlockComponent
type="button" type="button"
props={{ props={{
text: "Save", text: saveButtonLabel || "Save",
onClick: onSave, onClick: onSave,
type: "cta", type: "cta",
}} }}
@ -188,14 +196,14 @@
{/if} {/if}
<BlockComponent type="fieldgroup" props={{ labelPosition }} order={1}> <BlockComponent type="fieldgroup" props={{ labelPosition }} order={1}>
{#each fields as field, idx} {#each fields as field, idx}
{#if getComponentForField(field)} {#if getComponentForField(field.name)}
<BlockComponent <BlockComponent
type={getComponentForField(field)} type={getComponentForField(field.name)}
props={{ props={{
field, validation: field.validation,
label: field, field: field.name,
placeholder: field, label: field.displayName,
disabled, placeholder: field.displayName,
}} }}
order={idx} order={idx}
/> />

View File

@ -1,13 +1,13 @@
{ {
"name": "@budibase/frontend-core", "name": "@budibase/frontend-core",
"version": "2.5.6-alpha.26", "version": "2.5.6-alpha.32",
"description": "Budibase frontend core libraries used in builder and client", "description": "Budibase frontend core libraries used in builder and client",
"author": "Budibase", "author": "Budibase",
"license": "MPL-2.0", "license": "MPL-2.0",
"svelte": "src/index.js", "svelte": "src/index.js",
"dependencies": { "dependencies": {
"@budibase/bbui": "2.5.6-alpha.26", "@budibase/bbui": "2.5.6-alpha.32",
"@budibase/shared-core": "2.5.6-alpha.26", "@budibase/shared-core": "2.5.6-alpha.32",
"dayjs": "^1.11.7", "dayjs": "^1.11.7",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"socket.io-client": "^4.6.1", "socket.io-client": "^4.6.1",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/sdk", "name": "@budibase/sdk",
"version": "2.5.6-alpha.26", "version": "2.5.6-alpha.32",
"description": "Budibase Public API SDK", "description": "Budibase Public API SDK",
"author": "Budibase", "author": "Budibase",
"license": "MPL-2.0", "license": "MPL-2.0",

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "2.5.6-alpha.26", "version": "2.5.6-alpha.32",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -45,12 +45,12 @@
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@apidevtools/swagger-parser": "10.0.3", "@apidevtools/swagger-parser": "10.0.3",
"@budibase/backend-core": "2.5.6-alpha.26", "@budibase/backend-core": "2.5.6-alpha.32",
"@budibase/client": "2.5.6-alpha.26", "@budibase/client": "2.5.6-alpha.32",
"@budibase/pro": "2.5.6-alpha.26", "@budibase/pro": "2.5.6-alpha.32",
"@budibase/shared-core": "2.5.6-alpha.26", "@budibase/shared-core": "2.5.6-alpha.32",
"@budibase/string-templates": "2.5.6-alpha.26", "@budibase/string-templates": "2.5.6-alpha.32",
"@budibase/types": "2.5.6-alpha.26", "@budibase/types": "2.5.6-alpha.32",
"@bull-board/api": "3.7.0", "@bull-board/api": "3.7.0",
"@bull-board/koa": "3.9.4", "@bull-board/koa": "3.9.4",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",

View File

@ -223,7 +223,7 @@ export async function fetchAppPackage(ctx: UserCtx) {
) )
ctx.body = { ctx.body = {
application, application: { ...application, upgradableVersion: envCore.VERSION },
screens, screens,
layouts, layouts,
clientLibPath, clientLibPath,

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/shared-core", "name": "@budibase/shared-core",
"version": "2.5.6-alpha.26", "version": "2.5.6-alpha.32",
"description": "Shared data utils", "description": "Shared data utils",
"main": "dist/cjs/src/index.js", "main": "dist/cjs/src/index.js",
"types": "dist/mjs/src/index.d.ts", "types": "dist/mjs/src/index.d.ts",
@ -20,7 +20,7 @@
"dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\"" "dev:builder": "yarn prebuild && concurrently \"tsc -p tsconfig.build.json --watch\" \"tsc -p tsconfig-cjs.build.json --watch\""
}, },
"dependencies": { "dependencies": {
"@budibase/types": "2.5.6-alpha.26" "@budibase/types": "2.5.6-alpha.32"
}, },
"devDependencies": { "devDependencies": {
"concurrently": "^7.6.0", "concurrently": "^7.6.0",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "2.5.6-alpha.26", "version": "2.5.6-alpha.32",
"description": "Handlebars wrapper for Budibase templating.", "description": "Handlebars wrapper for Budibase templating.",
"main": "src/index.cjs", "main": "src/index.cjs",
"module": "dist/bundle.mjs", "module": "dist/bundle.mjs",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/types", "name": "@budibase/types",
"version": "2.5.6-alpha.26", "version": "2.5.6-alpha.32",
"description": "Budibase types", "description": "Budibase types",
"main": "dist/cjs/index.js", "main": "dist/cjs/index.js",
"types": "dist/mjs/index.d.ts", "types": "dist/mjs/index.d.ts",

View File

@ -1,4 +1,5 @@
import { QuotaUsage } from "../../documents" import { LicenseOverrides, QuotaUsage } from "../../documents"
import { PlanType } from "../../sdk"
export interface GetLicenseRequest { export interface GetLicenseRequest {
// All fields should be optional to cater for // All fields should be optional to cater for
@ -20,3 +21,8 @@ export interface QuotaTriggeredRequest {
export interface LicenseActivateRequest { export interface LicenseActivateRequest {
installVersion?: string installVersion?: string
} }
export interface UpdateLicenseRequest {
planType?: PlanType
overrides?: LicenseOverrides
}

View File

@ -1,4 +1,5 @@
import { Feature, Hosting, License, PlanType, Quotas } from "../../sdk" import { Feature, Hosting, License, PlanType, Quotas } from "../../sdk"
import { DeepPartial } from "../../shared"
import { QuotaUsage } from "../global" import { QuotaUsage } from "../global"
export interface CreateAccount { export interface CreateAccount {
@ -25,7 +26,7 @@ export const isCreatePasswordAccount = (
export interface LicenseOverrides { export interface LicenseOverrides {
features?: Feature[] features?: Feature[]
quotas?: Quotas quotas?: DeepPartial<Quotas>
} }
export interface Account extends CreateAccount { export interface Account extends CreateAccount {

View File

@ -0,0 +1 @@
export * from "./typeUtils"

View File

@ -0,0 +1,3 @@
export type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]
}

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "2.5.6-alpha.26", "version": "2.5.6-alpha.32",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -37,10 +37,10 @@
"author": "Budibase", "author": "Budibase",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@budibase/backend-core": "2.5.6-alpha.26", "@budibase/backend-core": "2.5.6-alpha.32",
"@budibase/pro": "2.5.6-alpha.26", "@budibase/pro": "2.5.6-alpha.32",
"@budibase/string-templates": "2.5.6-alpha.26", "@budibase/string-templates": "2.5.6-alpha.32",
"@budibase/types": "2.5.6-alpha.26", "@budibase/types": "2.5.6-alpha.32",
"@koa/router": "8.0.8", "@koa/router": "8.0.8",
"@sentry/node": "6.17.7", "@sentry/node": "6.17.7",
"@techpass/passport-openidconnect": "0.3.2", "@techpass/passport-openidconnect": "0.3.2",

View File

@ -1,7 +1,7 @@
import { Account, AccountMetadata } from "@budibase/types" import { Account, AccountMetadata, Ctx } from "@budibase/types"
import * as accounts from "../../../sdk/accounts" import * as accounts from "../../../sdk/accounts"
export const save = async (ctx: any) => { export const save = async (ctx: Ctx<Account, AccountMetadata>) => {
const account = ctx.request.body as Account const account = ctx.request.body as Account
let metadata: AccountMetadata = { let metadata: AccountMetadata = {
_id: accounts.metadata.formatAccountMetadataId(account.accountId), _id: accounts.metadata.formatAccountMetadataId(account.accountId),

View File

@ -16,7 +16,8 @@
cellspacing="0" cellspacing="0"
> >
<img <img
width="32px" width="32"
height="32"
style="margin-right:16px; vertical-align: middle;" style="margin-right:16px; vertical-align: middle;"
alt="Budibase Logo" alt="Budibase Logo"
src="https://i.imgur.com/Xhdt1YP.png" src="https://i.imgur.com/Xhdt1YP.png"

View File

@ -8,8 +8,10 @@ function init() {
const envFileJson = { const envFileJson = {
BUDIBASE_URL: "http://localhost:10000", BUDIBASE_URL: "http://localhost:10000",
ACCOUNT_PORTAL_URL: "http://localhost:10001", ACCOUNT_PORTAL_URL: "http://localhost:10001",
ACCOUNT_PORTAL_API_KEY: "budibase",
BB_ADMIN_USER_EMAIL: "admin", BB_ADMIN_USER_EMAIL: "admin",
BB_ADMIN_USER_PASSWORD: "admin", BB_ADMIN_USER_PASSWORD: "admin",
LOG_LEVEL: "info",
} }
let envFile = "" let envFile = ""
Object.keys(envFileJson).forEach(key => { Object.keys(envFileJson).forEach(key => {

View File

@ -8,6 +8,7 @@ interface ApiOptions {
method?: APIMethod method?: APIMethod
body?: object body?: object
headers?: HeadersInit | undefined headers?: HeadersInit | undefined
internal?: boolean
} }
export default class AccountInternalAPIClient { export default class AccountInternalAPIClient {
@ -18,6 +19,9 @@ export default class AccountInternalAPIClient {
if (!env.ACCOUNT_PORTAL_URL) { if (!env.ACCOUNT_PORTAL_URL) {
throw new Error("Must set ACCOUNT_PORTAL_URL env var") throw new Error("Must set ACCOUNT_PORTAL_URL env var")
} }
if (!env.ACCOUNT_PORTAL_API_KEY) {
throw new Error("Must set ACCOUNT_PORTAL_API_KEY env var")
}
this.host = `${env.ACCOUNT_PORTAL_URL}` this.host = `${env.ACCOUNT_PORTAL_URL}`
this.state = state this.state = state
} }
@ -39,6 +43,13 @@ export default class AccountInternalAPIClient {
credentials: "include", credentials: "include",
} }
if (options.internal) {
requestOptions.headers = {
...requestOptions.headers,
...{ "x-budibase-api-key": env.ACCOUNT_PORTAL_API_KEY },
}
}
// @ts-ignore // @ts-ignore
const response = await fetch(`${this.host}${url}`, requestOptions) const response = await fetch(`${this.host}${url}`, requestOptions)
@ -50,15 +61,20 @@ export default class AccountInternalAPIClient {
body = await response.text() body = await response.text()
} }
const message = `${method} ${url} - ${response.status} const data = {
Response body: ${JSON.stringify(body)} request: requestOptions.body,
Request body: ${requestOptions.body}` response: body,
}
const message = `${method} ${url} - ${response.status}`
if (response.status > 499) { if (response.status > 499) {
console.error(message) console.error(message, data)
} else if (response.status >= 400) { } else if (response.status >= 400) {
console.warn(message) console.warn(message, data)
} else {
console.debug(message, data)
} }
return [response, body] return [response, body]
} }

View File

@ -1,4 +1,6 @@
import AccountInternalAPIClient from "../AccountInternalAPIClient" import AccountInternalAPIClient from "../AccountInternalAPIClient"
import { Account, UpdateLicenseRequest } from "@budibase/types"
import { Response } from "node-fetch"
export default class LicenseAPI { export default class LicenseAPI {
client: AccountInternalAPIClient client: AccountInternalAPIClient
@ -6,4 +8,18 @@ export default class LicenseAPI {
constructor(client: AccountInternalAPIClient) { constructor(client: AccountInternalAPIClient) {
this.client = client this.client = client
} }
async updateLicense(
accountId: string,
body: UpdateLicenseRequest
): Promise<[Response, Account]> {
const [response, json] = await this.client.put(
`/api/accounts/${accountId}/license`,
{
body,
internal: true,
}
)
return [response, json]
}
} }

View File

@ -11,6 +11,7 @@ if (!LOADED) {
const env = { const env = {
BUDIBASE_URL: process.env.BUDIBASE_URL, BUDIBASE_URL: process.env.BUDIBASE_URL,
ACCOUNT_PORTAL_URL: process.env.ACCOUNT_PORTAL_URL, ACCOUNT_PORTAL_URL: process.env.ACCOUNT_PORTAL_URL,
ACCOUNT_PORTAL_API_KEY: process.env.ACCOUNT_PORTAL_API_KEY,
BB_ADMIN_USER_EMAIL: process.env.BB_ADMIN_USER_EMAIL, BB_ADMIN_USER_EMAIL: process.env.BB_ADMIN_USER_EMAIL,
BB_ADMIN_USER_PASSWORD: process.env.BB_ADMIN_USER_PASSWORD, BB_ADMIN_USER_PASSWORD: process.env.BB_ADMIN_USER_PASSWORD,
} }

View File

@ -18,7 +18,6 @@ class BudibaseInternalAPIClient {
if (!env.BUDIBASE_URL) { if (!env.BUDIBASE_URL) {
throw new Error("Must set BUDIBASE_URL env var") throw new Error("Must set BUDIBASE_URL env var")
} }
this.host = `${env.ACCOUNT_PORTAL_URL}/api`
this.host = `${env.BUDIBASE_URL}/api` this.host = `${env.BUDIBASE_URL}/api`
this.state = state this.state = state
} }
@ -53,14 +52,18 @@ class BudibaseInternalAPIClient {
body = await response.text() body = await response.text()
} }
const message = `${method} ${url} - ${response.status} const data = {
Response body: ${JSON.stringify(body)} request: requestOptions.body,
Request body: ${requestOptions.body}` response: body,
}
const message = `${method} ${url} - ${response.status}`
if (response.status > 499) { if (response.status > 499) {
console.error(message) console.error(message, data)
} else if (response.status >= 400) { } else if (response.status >= 400) {
console.warn(message) console.warn(message, data)
} else {
console.debug(message, data)
} }
return [response, body] return [response, body]

View File

@ -1,8 +1,8 @@
import { DEFAULT_TENANT_ID, logging } from "@budibase/backend-core"
import { AccountInternalAPI } from "../account-api" import { AccountInternalAPI } from "../account-api"
import * as fixtures from "../internal-api/fixtures" import * as fixtures from "../internal-api/fixtures"
import { BudibaseInternalAPI } from "../internal-api" import { BudibaseInternalAPI } from "../internal-api"
import { DEFAULT_TENANT_ID, logging } from "@budibase/backend-core" import { CreateAccountRequest, Feature } from "@budibase/types"
import { CreateAccountRequest } from "@budibase/types"
import env from "../environment" import env from "../environment"
import { APIRequestOpts } from "../types" import { APIRequestOpts } from "../types"
@ -22,10 +22,35 @@ async function createAccount() {
const account = fixtures.accounts.generateAccount() const account = fixtures.accounts.generateAccount()
await accountsApi.accounts.validateEmail(account.email, API_OPTS) await accountsApi.accounts.validateEmail(account.email, API_OPTS)
await accountsApi.accounts.validateTenantId(account.tenantId, API_OPTS) await accountsApi.accounts.validateTenantId(account.tenantId, API_OPTS)
await accountsApi.accounts.create(account, API_OPTS) const [res, newAccount] = await accountsApi.accounts.create(account, API_OPTS)
await updateLicense(newAccount.accountId)
return account return account
} }
const UNLIMITED = { value: -1 }
async function updateLicense(accountId: string) {
await accountsApi.licenses.updateLicense(accountId, {
overrides: {
// add all features
features: Object.values(Feature),
quotas: {
usage: {
monthly: {
automations: UNLIMITED,
},
static: {
rows: UNLIMITED,
users: UNLIMITED,
userGroups: UNLIMITED,
plugins: UNLIMITED,
},
},
},
},
})
}
async function loginAsAdmin() { async function loginAsAdmin() {
const username = env.BB_ADMIN_USER_EMAIL! const username = env.BB_ADMIN_USER_EMAIL!
const password = env.BB_ADMIN_USER_PASSWORD! const password = env.BB_ADMIN_USER_PASSWORD!

View File

@ -1 +1,4 @@
import { logging } from "@budibase/backend-core"
logging.LOG_CONTEXT = false
jest.setTimeout(60000) jest.setTimeout(60000)

View File

@ -51,14 +51,18 @@ class BudibasePublicAPIClient {
body = await response.text() body = await response.text()
} }
const message = `${method} ${url} - ${response.status} const data = {
Response body: ${JSON.stringify(body)} request: requestOptions.body,
Request body: ${requestOptions.body}` response: body,
}
const message = `${method} ${url} - ${response.status}`
if (response.status > 499) { if (response.status > 499) {
console.error(message) console.error(message, data)
} else if (response.status >= 400) { } else if (response.status >= 400) {
console.warn(message) console.warn(message, data)
} else {
console.debug(message, data)
} }
return [response, body] return [response, body]

View File

@ -3,9 +3,6 @@ import { AccountInternalAPI } from "../account-api"
import { CreateAppRequest, State } from "../types" import { CreateAppRequest, State } from "../types"
import * as fixtures from "../internal-api/fixtures" import * as fixtures from "../internal-api/fixtures"
// TEMP
import setup from "../jest/globalSetup"
export default class BudibaseTestConfiguration { export default class BudibaseTestConfiguration {
// apis // apis
internalApi: BudibaseInternalAPI internalApi: BudibaseInternalAPI
@ -23,11 +20,6 @@ export default class BudibaseTestConfiguration {
// LIFECYCLE // LIFECYCLE
async beforeAll() { async beforeAll() {
// TEMP - move back to single tenant when we integrate licensing with
// the test run - need to use multiple tenants in cloud to get around
// app limit restrictions
await setup()
// @ts-ignore // @ts-ignore
this.state.tenantId = global.qa.tenantId this.state.tenantId = global.qa.tenantId
// @ts-ignore // @ts-ignore

View File

@ -1486,15 +1486,15 @@
pouchdb-promise "^6.0.4" pouchdb-promise "^6.0.4"
through2 "^2.0.0" through2 "^2.0.0"
"@budibase/pro@2.5.6-alpha.25": "@budibase/pro@2.5.6-alpha.32":
version "2.5.6-alpha.25" version "2.5.6-alpha.32"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.5.6-alpha.25.tgz#bfe99536060422adb60da2f195acb05b24898c98" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-2.5.6-alpha.32.tgz#6d52da231efcacbb163a5af0cfe8284199ff659e"
integrity sha512-LEQec9N9gS+j5gt/eKwGzci5oKW1sRTIG8oUvH7faCoJjD7BWIhmNuQNFrNfg70C/zkxYy4CL/2s1P9oVR1B3g== integrity sha512-XzM55VcxpxTXCxYhLUOYvYqlCAPRPASJBbNonuRV6qUtXZxE5xSSEDnHQehhD8TrNP3QQ94DlCJ5DQCm/f3yJQ==
dependencies: dependencies:
"@budibase/backend-core" "2.5.6-alpha.25" "@budibase/backend-core" "2.5.6-alpha.32"
"@budibase/shared-core" "2.4.44-alpha.1" "@budibase/shared-core" "2.4.44-alpha.1"
"@budibase/string-templates" "2.4.44-alpha.1" "@budibase/string-templates" "2.4.44-alpha.1"
"@budibase/types" "2.5.6-alpha.25" "@budibase/types" "2.5.6-alpha.32"
"@koa/router" "8.0.8" "@koa/router" "8.0.8"
bull "4.10.1" bull "4.10.1"
joi "17.6.0" joi "17.6.0"
@ -11129,7 +11129,7 @@ fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
fast-redact@^3.0.0: fast-redact@^3.0.0, fast-redact@^3.1.1:
version "3.1.2" version "3.1.2"
resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.1.2.tgz#d58e69e9084ce9fa4c1a6fa98a3e1ecf5d7839aa" resolved "https://registry.yarnpkg.com/fast-redact/-/fast-redact-3.1.2.tgz#d58e69e9084ce9fa4c1a6fa98a3e1ecf5d7839aa"
integrity sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw== integrity sha512-+0em+Iya9fKGfEQGcd62Yv6onjBmmhV1uh86XVfOU8VwAe6kaFdQCWI9s0/Nnugx5Vd9tdbZ7e6gE2tR9dzXdw==
@ -19203,7 +19203,7 @@ pinkie@^2.0.0:
resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870"
integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==
pino-abstract-transport@^1.0.0: pino-abstract-transport@^1.0.0, pino-abstract-transport@v1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz#cc0d6955fffcadb91b7b49ef220a6cc111d48bb3" resolved "https://registry.yarnpkg.com/pino-abstract-transport/-/pino-abstract-transport-1.0.0.tgz#cc0d6955fffcadb91b7b49ef220a6cc111d48bb3"
integrity sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA== integrity sha512-c7vo5OpW4wIS42hUVcT5REsL8ZljsUfBjqV/e2sFxmFEFZiq1XLUp5EYLtuDH6PEHq9W1egWqRbnLUP5FuZmOA==
@ -19219,6 +19219,16 @@ pino-abstract-transport@v0.5.0:
duplexify "^4.1.2" duplexify "^4.1.2"
split2 "^4.0.0" split2 "^4.0.0"
pino-http@8.3.3:
version "8.3.3"
resolved "https://registry.yarnpkg.com/pino-http/-/pino-http-8.3.3.tgz#2b140e734bfc6babe0df272a43bb8f36f2b525c0"
integrity sha512-p4umsNIXXVu95HD2C8wie/vXH7db5iGRpc+yj1/ZQ3sRtTQLXNjoS6Be5+eI+rQbqCRxen/7k/KSN+qiZubGDw==
dependencies:
get-caller-file "^2.0.5"
pino "^8.0.0"
pino-std-serializers "^6.0.0"
process-warning "^2.0.0"
pino-http@^6.5.0: pino-http@^6.5.0:
version "6.6.0" version "6.6.0"
resolved "https://registry.yarnpkg.com/pino-http/-/pino-http-6.6.0.tgz#d0a1deacada8c93327fdaa48f5bdc94bc43d3407" resolved "https://registry.yarnpkg.com/pino-http/-/pino-http-6.6.0.tgz#d0a1deacada8c93327fdaa48f5bdc94bc43d3407"
@ -19264,7 +19274,42 @@ pino-std-serializers@^5.0.0:
resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-5.6.0.tgz#31b141155d6520967c5ec72944d08fb45c490fd3" resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-5.6.0.tgz#31b141155d6520967c5ec72944d08fb45c490fd3"
integrity sha512-VdUXCw8gO+xhir7sFuoYSjTnzB+TMDGxhAC/ph3YS3sdHnXNdsK0wMtADNUltfeGkn2KDxEM21fnjF3RwXyC8A== integrity sha512-VdUXCw8gO+xhir7sFuoYSjTnzB+TMDGxhAC/ph3YS3sdHnXNdsK0wMtADNUltfeGkn2KDxEM21fnjF3RwXyC8A==
pino@7.11.0, pino@^7.5.0: pino-std-serializers@^6.0.0:
version "6.2.0"
resolved "https://registry.yarnpkg.com/pino-std-serializers/-/pino-std-serializers-6.2.0.tgz#169048c0df3f61352fce56aeb7fb962f1b66ab43"
integrity sha512-IWgSzUL8X1w4BIWTwErRgtV8PyOGOOi60uqv0oKuS/fOA8Nco/OeI6lBuc4dyP8MMfdFwyHqTMcBIA7nDiqEqA==
pino@8.11.0, pino@^8.0.0:
version "8.11.0"
resolved "https://registry.yarnpkg.com/pino/-/pino-8.11.0.tgz#2a91f454106b13e708a66c74ebc1c2ab7ab38498"
integrity sha512-Z2eKSvlrl2rH8p5eveNUnTdd4AjJk8tAsLkHYZQKGHP4WTh2Gi1cOSOs3eWPqaj+niS3gj4UkoreoaWgF3ZWYg==
dependencies:
atomic-sleep "^1.0.0"
fast-redact "^3.1.1"
on-exit-leak-free "^2.1.0"
pino-abstract-transport v1.0.0
pino-std-serializers "^6.0.0"
process-warning "^2.0.0"
quick-format-unescaped "^4.0.3"
real-require "^0.2.0"
safe-stable-stringify "^2.3.1"
sonic-boom "^3.1.0"
thread-stream "^2.0.0"
pino@^6.11.2:
version "6.14.0"
resolved "https://registry.yarnpkg.com/pino/-/pino-6.14.0.tgz#b745ea87a99a6c4c9b374e4f29ca7910d4c69f78"
integrity sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg==
dependencies:
fast-redact "^3.0.0"
fast-safe-stringify "^2.0.8"
flatstr "^1.0.12"
pino-std-serializers "^3.1.0"
process-warning "^1.0.0"
quick-format-unescaped "^4.0.3"
sonic-boom "^1.0.2"
pino@^7.5.0:
version "7.11.0" version "7.11.0"
resolved "https://registry.yarnpkg.com/pino/-/pino-7.11.0.tgz#0f0ea5c4683dc91388081d44bff10c83125066f6" resolved "https://registry.yarnpkg.com/pino/-/pino-7.11.0.tgz#0f0ea5c4683dc91388081d44bff10c83125066f6"
integrity sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg== integrity sha512-dMACeu63HtRLmCG8VKdy4cShCPKaYDR4youZqoSWLxl5Gu99HUw8bw75thbPv9Nip+H+QYX8o3ZJbTdVZZ2TVg==
@ -19281,19 +19326,6 @@ pino@7.11.0, pino@^7.5.0:
sonic-boom "^2.2.1" sonic-boom "^2.2.1"
thread-stream "^0.15.1" thread-stream "^0.15.1"
pino@^6.11.2:
version "6.14.0"
resolved "https://registry.yarnpkg.com/pino/-/pino-6.14.0.tgz#b745ea87a99a6c4c9b374e4f29ca7910d4c69f78"
integrity sha512-iuhEDel3Z3hF9Jfe44DPXR8l07bhjuFY3GMHIXbjnY9XcafbyDDwl2sN2vw2GjMPf5Nkoe+OFao7ffn9SXaKDg==
dependencies:
fast-redact "^3.0.0"
fast-safe-stringify "^2.0.8"
flatstr "^1.0.12"
pino-std-serializers "^3.1.0"
process-warning "^1.0.0"
quick-format-unescaped "^4.0.3"
sonic-boom "^1.0.2"
pirates@^4.0.1, pirates@^4.0.4: pirates@^4.0.1, pirates@^4.0.4:
version "4.0.5" version "4.0.5"
resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b"
@ -20169,6 +20201,11 @@ process-warning@^1.0.0:
resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616" resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616"
integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q== integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==
process-warning@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-2.2.0.tgz#008ec76b579820a8e5c35d81960525ca64feb626"
integrity sha512-/1WZ8+VQjR6avWOgHeEPd7SDQmFQ1B5mC1eRXsCm5TarlNmx/wCsa5GEaxGm05BORRtyG/Ex/3xq3TuRvq57qg==
process@^0.11.10: process@^0.11.10:
version "0.11.10" version "0.11.10"
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
@ -20772,6 +20809,11 @@ real-require@^0.1.0:
resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.1.0.tgz#736ac214caa20632847b7ca8c1056a0767df9381" resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.1.0.tgz#736ac214caa20632847b7ca8c1056a0767df9381"
integrity sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg== integrity sha512-r/H9MzAWtrv8aSVjPCMFpDMl5q66GqtmmRkRjpHTsp4zBAa+snZyiQNlMONiUmEJcsnaw0wCauJ2GWODr/aFkg==
real-require@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/real-require/-/real-require-0.2.0.tgz#209632dea1810be2ae063a6ac084fee7e33fba78"
integrity sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==
recast@^0.10.1: recast@^0.10.1:
version "0.10.43" version "0.10.43"
resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.43.tgz#b95d50f6d60761a5f6252e15d80678168491ce7f" resolved "https://registry.yarnpkg.com/recast/-/recast-0.10.43.tgz#b95d50f6d60761a5f6252e15d80678168491ce7f"
@ -22151,7 +22193,7 @@ sonic-boom@^2.2.1:
dependencies: dependencies:
atomic-sleep "^1.0.0" atomic-sleep "^1.0.0"
sonic-boom@^3.0.0: sonic-boom@^3.0.0, sonic-boom@^3.1.0:
version "3.3.0" version "3.3.0"
resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.3.0.tgz#cffab6dafee3b2bcb88d08d589394198bee1838c" resolved "https://registry.yarnpkg.com/sonic-boom/-/sonic-boom-3.3.0.tgz#cffab6dafee3b2bcb88d08d589394198bee1838c"
integrity sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g== integrity sha512-LYxp34KlZ1a2Jb8ZQgFCK3niIHzibdwtwNUWKg0qQRzsDoJ3Gfgkf8KdBTFU3SkejDEIlWwnSnpVdOZIhFMl/g==
@ -23323,6 +23365,13 @@ thread-stream@^0.15.1:
dependencies: dependencies:
real-require "^0.1.0" real-require "^0.1.0"
thread-stream@^2.0.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/thread-stream/-/thread-stream-2.3.0.tgz#4fc07fb39eff32ae7bad803cb7dd9598349fed33"
integrity sha512-kaDqm1DET9pp3NXwR8382WHbnpXnRkN9xGN9dQt3B2+dmXiW8X1SOwmFOxAErEQ47ObhZ96J6yhZNXuyCOL7KA==
dependencies:
real-require "^0.2.0"
throat@^5.0.0: throat@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b" resolved "https://registry.yarnpkg.com/throat/-/throat-5.0.0.tgz#c5199235803aad18754a667d659b5e72ce16764b"