Merge branch 'master' of github.com:Budibase/budibase into develop

This commit is contained in:
mike12345567 2022-11-02 20:12:01 +00:00
commit b9caf0aef5
40 changed files with 241 additions and 199 deletions

View File

@ -84,6 +84,8 @@ spec:
value: {{ .Values.services.objectStore.appsBucketName | quote }} value: {{ .Values.services.objectStore.appsBucketName | quote }}
- name: GLOBAL_CLOUD_BUCKET_NAME - name: GLOBAL_CLOUD_BUCKET_NAME
value: {{ .Values.services.objectStore.globalBucketName | quote }} value: {{ .Values.services.objectStore.globalBucketName | quote }}
- name: BACKUPS_BUCKET_NAME
value: {{ .Values.services.objectStore.backupsBucketName | quote }}
- name: PORT - name: PORT
value: {{ .Values.services.apps.port | quote }} value: {{ .Values.services.apps.port | quote }}
{{ if .Values.services.worker.publicApiRateLimitPerSecond }} {{ if .Values.services.worker.publicApiRateLimitPerSecond }}

View File

@ -83,6 +83,8 @@ spec:
value: {{ .Values.services.objectStore.appsBucketName | quote }} value: {{ .Values.services.objectStore.appsBucketName | quote }}
- name: GLOBAL_CLOUD_BUCKET_NAME - name: GLOBAL_CLOUD_BUCKET_NAME
value: {{ .Values.services.objectStore.globalBucketName | quote }} value: {{ .Values.services.objectStore.globalBucketName | quote }}
- name: BACKUPS_BUCKET_NAME
value: {{ .Values.services.objectStore.backupsBucketName | quote }}
- name: PORT - name: PORT
value: {{ .Values.services.worker.port | quote }} value: {{ .Values.services.worker.port | quote }}
- name: MULTI_TENANCY - name: MULTI_TENANCY

View File

@ -1,5 +1,5 @@
{ {
"version": "2.0.40-alpha.4", "version": "2.1.11",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/backend-core", "name": "@budibase/backend-core",
"version": "2.0.40-alpha.4", "version": "2.1.11",
"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",
@ -20,7 +20,7 @@
"test:watch": "jest --watchAll" "test:watch": "jest --watchAll"
}, },
"dependencies": { "dependencies": {
"@budibase/types": "2.0.40-alpha.4", "@budibase/types": "^2.1.11",
"@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-sdk": "2.1030.0", "aws-sdk": "2.1030.0",

View File

@ -24,10 +24,15 @@ import {
} from "./middleware" } from "./middleware"
import { invalidateUser } from "./cache/user" import { invalidateUser } from "./cache/user"
import { User } from "@budibase/types" import { User } from "@budibase/types"
import { logAlert } from "./logging"
// Strategies // Strategies
passport.use(new LocalStrategy(local.options, local.authenticate)) passport.use(new LocalStrategy(local.options, local.authenticate))
passport.use(new JwtStrategy(jwt.options, jwt.authenticate)) if (jwt.options.secretOrKey) {
passport.use(new JwtStrategy(jwt.options, jwt.authenticate))
} else {
logAlert("No JWT Secret supplied, cannot configure JWT strategy")
}
passport.serializeUser((user: User, done: any) => done(null, user)) passport.serializeUser((user: User, done: any) => done(null, user))

View File

@ -1,4 +1,5 @@
import events from "events" import events from "events"
import { timeout } from "../../utils"
/** /**
* Bull works with a Job wrapper around all messages that contains a lot more information about * Bull works with a Job wrapper around all messages that contains a lot more information about
@ -27,6 +28,7 @@ class InMemoryQueue {
_opts?: any _opts?: any
_messages: any[] _messages: any[]
_emitter: EventEmitter _emitter: EventEmitter
_runCount: number
/** /**
* The constructor the queue, exactly the same as that of Bulls. * The constructor the queue, exactly the same as that of Bulls.
* @param {string} name The name of the queue which is being configured. * @param {string} name The name of the queue which is being configured.
@ -38,6 +40,7 @@ class InMemoryQueue {
this._opts = opts this._opts = opts
this._messages = [] this._messages = []
this._emitter = new events.EventEmitter() this._emitter = new events.EventEmitter()
this._runCount = 0
} }
/** /**
@ -59,6 +62,7 @@ class InMemoryQueue {
if (resp.then != null) { if (resp.then != null) {
await resp await resp
} }
this._runCount++
}) })
} }
@ -122,6 +126,15 @@ class InMemoryQueue {
on() { on() {
// do nothing // do nothing
} }
async waitForCompletion() {
const currentCount = this._runCount
let increased = false
do {
await timeout(50)
increased = this._runCount > currentCount
} while (!increased)
}
} }
export = InMemoryQueue export = InMemoryQueue

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.0.40-alpha.4", "version": "2.1.11",
"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,7 +38,7 @@
], ],
"dependencies": { "dependencies": {
"@adobe/spectrum-css-workflow-icons": "^1.2.1", "@adobe/spectrum-css-workflow-icons": "^1.2.1",
"@budibase/string-templates": "2.0.40-alpha.4", "@budibase/string-templates": "^2.1.11",
"@spectrum-css/actionbutton": "^1.0.1", "@spectrum-css/actionbutton": "^1.0.1",
"@spectrum-css/actiongroup": "^1.0.1", "@spectrum-css/actiongroup": "^1.0.1",
"@spectrum-css/avatar": "^3.0.2", "@spectrum-css/avatar": "^3.0.2",

View File

@ -43,6 +43,7 @@
let selectedImageIdx = 0 let selectedImageIdx = 0
let fileDragged = false let fileDragged = false
let selectedUrl let selectedUrl
let fileInput
$: selectedImage = value?.[selectedImageIdx] ?? null $: selectedImage = value?.[selectedImageIdx] ?? null
$: fileCount = value?.length ?? 0 $: fileCount = value?.length ?? 0
$: isImage = $: isImage =
@ -102,6 +103,7 @@
await deleteAttachments( await deleteAttachments(
value.filter((x, idx) => idx === selectedImageIdx).map(item => item.key) value.filter((x, idx) => idx === selectedImageIdx).map(item => item.key)
) )
fileInput.value = ""
} }
selectedImageIdx = 0 selectedImageIdx = 0
} }
@ -234,6 +236,7 @@
type="file" type="file"
multiple multiple
accept={extensions} accept={extensions}
bind:this={fileInput}
on:change={handleFile} on:change={handleFile}
/> />
<svg <svg

View File

@ -1,6 +1,6 @@
<script> <script>
import Picker from "./Picker.svelte" import Picker from "./Picker.svelte"
import { createEventDispatcher, onMount } from "svelte" import { createEventDispatcher } from "svelte"
export let value = [] export let value = []
export let id = null export let id = null
@ -16,29 +16,6 @@
export let autoWidth = false export let autoWidth = false
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
const parseValues = value => {
return Array.isArray(value)
? value.reduce((acc, entry) => {
if (typeof ele === "string" && entry.trim() === "") {
return acc
}
let processedOption = String(entry)
if (options.indexOf(processedOption) > -1) {
acc.push(processedOption)
}
return acc
}, [])
: []
}
let loaded = false
$: combinedValues = value ? [...value].concat(options) : []
$: superSet = new Set(combinedValues)
$: if (loaded && options.length != superSet.size) {
// ensure that the values being pushed in are valid.
dispatch("change", parseValues(value))
}
$: selectedLookupMap = getSelectedLookupMap(value) $: selectedLookupMap = getSelectedLookupMap(value)
$: optionLookupMap = getOptionLookupMap(options) $: optionLookupMap = getOptionLookupMap(options)
@ -95,10 +72,6 @@
} }
} }
} }
onMount(() => {
loaded = true
})
</script> </script>
<Picker <Picker

View File

@ -61,6 +61,7 @@
const onPickPrimary = newValue => { const onPickPrimary = newValue => {
dispatch("pickprimary", newValue) dispatch("pickprimary", newValue)
primaryOpen = false primaryOpen = false
dispatch("closed")
} }
const onClearPrimary = () => { const onClearPrimary = () => {
@ -92,6 +93,7 @@
if (primaryOpen) { if (primaryOpen) {
event.stopPropagation() event.stopPropagation()
primaryOpen = false primaryOpen = false
dispatch("closed")
} }
} }

View File

@ -128,5 +128,6 @@
on:blur on:blur
on:focus on:focus
on:keyup on:keyup
on:closed
/> />
</Field> </Field>

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "2.0.40-alpha.4", "version": "2.1.11",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -71,10 +71,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "2.0.40-alpha.4", "@budibase/bbui": "^2.1.11",
"@budibase/client": "2.0.40-alpha.4", "@budibase/client": "^2.1.11",
"@budibase/frontend-core": "2.0.40-alpha.4", "@budibase/frontend-core": "^2.1.11",
"@budibase/string-templates": "2.0.40-alpha.4", "@budibase/string-templates": "^2.1.11",
"@sentry/browser": "5.19.1", "@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1", "@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1", "@spectrum-css/vars": "^3.0.1",

View File

@ -44,7 +44,7 @@
// run the validation whenever the config changes // run the validation whenever the config changes
$: validation.check(config) $: validation.check(config)
// dispatch the validation result // dispatch the validation result
$: dispatch("valid", $validation.valid) $: dispatch("valid", Object.keys($validation.errors).length === 0)
let addButton let addButton

View File

@ -132,7 +132,20 @@
config={integrationInfo.extra} config={integrationInfo.extra}
/> />
{/if} {/if}
<BindingBuilder bind:queryBindings={query.parameters} bindable={false} /> {#key query.parameters}
<BindingBuilder
queryBindings={query.parameters}
bindable={false}
on:change={e => {
query.parameters = e.detail.map(binding => {
return {
name: binding.name,
default: binding.value,
}
})
}}
/>
{/key}
{/if} {/if}
</div> </div>
{#if shouldShowQueryConfig} {#if shouldShowQueryConfig}

View File

@ -44,14 +44,7 @@
valuePlaceholder="Default" valuePlaceholder="Default"
bindings={[...userBindings]} bindings={[...userBindings]}
bindingDrawerLeft="260px" bindingDrawerLeft="260px"
on:change={e => { on:change
queryBindings = e.detail.map(binding => {
return {
name: binding.name,
default: binding.value,
}
})
}}
/> />
</div> </div>
</Layout> </Layout>

View File

@ -10,7 +10,7 @@
} from "@budibase/bbui" } from "@budibase/bbui"
import ConfirmDialog from "components/common/ConfirmDialog.svelte" import ConfirmDialog from "components/common/ConfirmDialog.svelte"
import CreateRestoreModal from "./CreateRestoreModal.svelte" import CreateRestoreModal from "./CreateRestoreModal.svelte"
import { createEventDispatcher } from "svelte" import { createEventDispatcher, onMount } from "svelte"
export let row export let row
@ -49,6 +49,10 @@
async function downloadExport() { async function downloadExport() {
window.open(`/api/apps/${row.appId}/backups/${row._id}/file`, "_blank") window.open(`/api/apps/${row.appId}/backups/${row._id}/file`, "_blank")
} }
onMount(() => {
name = row.name
})
</script> </script>
<div class="cell"> <div class="cell">
@ -62,7 +66,7 @@
<MenuItem on:click={deleteDialog.show} icon="Delete">Delete</MenuItem> <MenuItem on:click={deleteDialog.show} icon="Delete">Delete</MenuItem>
<MenuItem on:click={downloadExport} icon="Download">Download</MenuItem> <MenuItem on:click={downloadExport} icon="Download">Download</MenuItem>
{/if} {/if}
<MenuItem on:click={updateDialog.show} icon="Edit">Update</MenuItem> <MenuItem on:click={updateDialog.show} icon="Edit">Rename</MenuItem>
</ActionMenu> </ActionMenu>
</div> </div>
@ -100,7 +104,7 @@
title="Update Backup" title="Update Backup"
warning={false} warning={false}
> >
<Input onlabel="Backup name" placeholder={row.name} bind:value={name} /> <Input onlabel="Backup name" bind:value={name} />
</ConfirmDialog> </ConfirmDialog>
<style> <style>

View File

@ -21,13 +21,14 @@
import AppSizeRenderer from "./AppSizeRenderer.svelte" import AppSizeRenderer from "./AppSizeRenderer.svelte"
import CreateBackupModal from "./CreateBackupModal.svelte" import CreateBackupModal from "./CreateBackupModal.svelte"
import ActionsRenderer from "./ActionsRenderer.svelte" import ActionsRenderer from "./ActionsRenderer.svelte"
import DateRenderer from "./DateRenderer.svelte" import DateRenderer from "components/common/renderers/DateTimeRenderer.svelte"
import UserRenderer from "./UserRenderer.svelte" import UserRenderer from "./UserRenderer.svelte"
import StatusRenderer from "./StatusRenderer.svelte" import StatusRenderer from "./StatusRenderer.svelte"
import TypeRenderer from "./TypeRenderer.svelte" import TypeRenderer from "./TypeRenderer.svelte"
import NameRenderer from "./NameRenderer.svelte"
import BackupsDefault from "assets/backups-default.png" import BackupsDefault from "assets/backups-default.png"
import { BackupTrigger, BackupType } from "constants/backend/backups"
import { onMount } from "svelte" import { onMount } from "svelte"
export let app export let app
let backupData = null let backupData = null
@ -36,30 +37,34 @@
let filterOpt = null let filterOpt = null
let startDate = null let startDate = null
let endDate = null let endDate = null
let filters = getFilters()
let loaded = false let loaded = false
let filters = [
{
label: "Manual backup",
value: { type: BackupType.BACKUP, trigger: BackupTrigger.MANUAL },
},
{
label: "Published backup",
value: { type: BackupType.BACKUP, trigger: BackupTrigger.PUBLISH },
},
{
label: "Scheduled backup",
value: { type: BackupType.BACKUP, trigger: BackupTrigger.SCHEDULED },
},
{
label: "Pre-restore backup",
value: { type: BackupType.BACKUP, trigger: BackupTrigger.RESTORING },
},
{
label: "Manual restore",
value: { type: BackupType.RESTORE, trigger: BackupTrigger.MANUAL },
},
]
$: page = $pageInfo.page $: page = $pageInfo.page
$: fetchBackups(filterOpt, page, startDate, endDate) $: fetchBackups(filterOpt, page, startDate, endDate)
function getFilters() { let schema = {
const options = []
let types = ["backup"]
let triggers = ["manual", "publish", "scheduled", "restoring"]
for (let type of types) {
for (let trigger of triggers) {
let label = `${trigger} ${type}`
label = label.charAt(0).toUpperCase() + label?.slice(1)
options.push({ label, value: { type, trigger } })
}
}
options.push({
label: `Manual restore`,
value: { type: "restore", trigger: "manual" },
})
return options
}
const schema = {
type: { type: {
displayName: "Type", displayName: "Type",
width: "auto", width: "auto",
@ -97,6 +102,7 @@
{ column: "createdBy", component: UserRenderer }, { column: "createdBy", component: UserRenderer },
{ column: "status", component: StatusRenderer }, { column: "status", component: StatusRenderer },
{ column: "type", component: TypeRenderer }, { column: "type", component: TypeRenderer },
{ column: "name", component: NameRenderer },
] ]
function flattenBackups(backups) { function flattenBackups(backups) {
@ -260,7 +266,7 @@
> >
</div> </div>
</div> </div>
<div> <div class="table">
<Table <Table
{schema} {schema}
disableSorting disableSorting
@ -308,7 +314,7 @@
} }
.select { .select {
flex-basis: 150px; flex-basis: 100px;
} }
.pagination { .pagination {
@ -342,4 +348,8 @@
display: flex; display: flex;
gap: var(--spacing-m); gap: var(--spacing-m);
} }
.table {
overflow-x: scroll;
}
</style> </style>

View File

@ -1,22 +0,0 @@
<script>
import DateTimeRenderer from "components/common/renderers/DateTimeRenderer.svelte"
import dayjs from "dayjs"
import relativeTime from "dayjs/plugin/relativeTime"
dayjs.extend(relativeTime)
export let value
$: timeSince = dayjs(value).fromNow()
</script>
<div class="cell">
{timeSince} - <DateTimeRenderer {value} />
</div>
<style>
.cell {
display: flex;
flex-direction: row;
gap: var(--spacing-m);
align-items: center;
}
</style>

View File

@ -0,0 +1,8 @@
<script>
import { truncate } from "lodash"
export let value
$: truncatedValue = truncate(value, { length: 12 })
</script>
{truncatedValue}

View File

@ -1,13 +1,29 @@
<script> <script>
import { BackupTrigger } from "constants/backend/backups"
export let row export let row
$: baseTrig = row?.trigger || "manual" $: trigger = row?.trigger || "manual"
$: type = row?.type || "backup" $: type = row?.type || "backup"
$: trigger = baseTrig.charAt(0).toUpperCase() + baseTrig.slice(1)
function printTrigger(trig) {
let final = "undefined"
switch (trig) {
case BackupTrigger.PUBLISH:
final = "published"
break
case BackupTrigger.RESTORING:
final = "pre-restore"
break
default:
final = trig
break
}
return final.charAt(0).toUpperCase() + final.slice(1)
}
</script> </script>
<div class="cell"> <div class="cell">
{trigger} {printTrigger(trigger)}
{type} {type}
</div> </div>

View File

@ -0,0 +1,11 @@
export const BackupTrigger = {
MANUAL: "manual",
PUBLISH: "publish",
RESTORING: "restoring",
SCHEDULED: "scheduled",
}
export const BackupType = {
BACKUP: "backup",
RESTORE: "restore",
}

View File

@ -383,10 +383,5 @@
.user-dropdown { .user-dropdown {
flex: 0 1 0; flex: 0 1 0;
} }
/* Reduce BBUI page padding */
.content :global(> *) {
padding: calc(var(--spacing-xl) * 1.5) !important;
}
} }
</style> </style>

View File

@ -5,9 +5,16 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
let filter = null
$: filteredGroups = !filter
? $groups
: $groups.filter(group =>
group.name?.toLowerCase().includes(filter.toLowerCase())
)
$: optionSections = { $: optionSections = {
groups: { groups: {
data: $groups, data: filteredGroups,
getLabel: group => group.name, getLabel: group => group.name,
getValue: group => group._id, getValue: group => group._id,
getIcon: group => group.icon, getIcon: group => group.icon,
@ -15,21 +22,28 @@
}, },
} }
$: appData = [{ id: "", role: "" }]
$: onChange = selected => { $: onChange = selected => {
const { detail } = selected const { detail } = selected
if (!detail) return if (!detail || Object.keys(detail).length == 0) {
dispatch("change", null)
return
}
const groupSelected = $groups.find(x => x._id === detail) const groupSelected = $groups.find(x => x._id === detail)
const appIds = groupSelected?.apps || null const appRoleIds = groupSelected?.roles
dispatch("change", appIds) ? Object.keys(groupSelected?.roles)
: []
dispatch("change", appRoleIds)
} }
</script> </script>
<PickerDropdown <PickerDropdown
autocomplete autocomplete
bind:searchTerm={filter}
primaryOptions={optionSections} primaryOptions={optionSections}
placeholder={"Filter by access"} placeholder={"Filter by access"}
on:pickprimary={onChange} on:pickprimary={onChange}
on:closed={() => {
filter = null
}}
/> />

View File

@ -20,7 +20,14 @@
import { store, automationStore } from "builderStore" import { store, automationStore } from "builderStore"
import { API } from "api" import { API } from "api"
import { onMount } from "svelte" import { onMount } from "svelte"
import { apps, auth, admin, templates, licensing } from "stores/portal" import {
apps,
auth,
admin,
templates,
licensing,
groups,
} from "stores/portal"
import { goto } from "@roxi/routify" import { goto } from "@roxi/routify"
import AppRow from "components/start/AppRow.svelte" import AppRow from "components/start/AppRow.svelte"
import { AppStatus } from "constants" import { AppStatus } from "constants"
@ -59,10 +66,15 @@
$: enrichedApps = enrichApps($apps, $auth.user, sortBy) $: enrichedApps = enrichApps($apps, $auth.user, sortBy)
$: filteredApps = enrichedApps.filter( $: filteredApps = enrichedApps.filter(
app => app =>
app?.name?.toLowerCase().includes(searchTerm.toLowerCase()) && (searchTerm
(accessFilterList !== null ? accessFilterList.includes(app?.appId) : true) ? app?.name?.toLowerCase().includes(searchTerm.toLowerCase())
: true) &&
(accessFilterList !== null
? accessFilterList?.includes(
`${app?.type}_${app?.tenantId}_${app?.appId}`
)
: true)
) )
$: lockedApps = filteredApps.filter(app => app?.lockedYou || app?.lockedOther) $: lockedApps = filteredApps.filter(app => app?.lockedYou || app?.lockedOther)
$: unlocked = lockedApps?.length === 0 $: unlocked = lockedApps?.length === 0
$: automationErrors = getAutomationErrors(enrichedApps) $: automationErrors = getAutomationErrors(enrichedApps)
@ -231,6 +243,10 @@
// always load latest // always load latest
await licensing.init() await licensing.init()
if ($licensing.groupsEnabled) {
await groups.actions.init()
}
if ($templates?.length === 0) { if ($templates?.length === 0) {
notifications.error( notifications.error(
"There was a problem loading quick start templates." "There was a problem loading quick start templates."

View File

@ -391,11 +391,7 @@
gap: var(--spacing-l); gap: var(--spacing-l);
} }
} }
@media (max-width: 640px) {
.overview-wrap :global(.content > *) {
padding: calc(var(--spacing-xl) * 1.5) !important;
}
}
.app-title { .app-title {
display: flex; display: flex;
gap: var(--spacing-m); gap: var(--spacing-m);

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "2.0.40-alpha.4", "version": "2.1.11",
"description": "Budibase CLI, for developers, self hosting and migrations.", "description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js", "main": "src/index.js",
"bin": { "bin": {
@ -26,9 +26,9 @@
"outputPath": "build" "outputPath": "build"
}, },
"dependencies": { "dependencies": {
"@budibase/backend-core": "2.0.40-alpha.4", "@budibase/backend-core": "^2.1.11",
"@budibase/string-templates": "2.0.40-alpha.4", "@budibase/string-templates": "^2.1.11",
"@budibase/types": "2.0.40-alpha.4", "@budibase/types": "^2.1.11",
"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

@ -5037,45 +5037,6 @@
} }
] ]
}, },
"grid": {
"name": "Grid (Beta)",
"icon": "ViewGrid",
"hasChildren": true,
"styles": [
"size"
],
"illegalChildren": ["section", "grid"],
"legalDirectChildren": [
"container",
"tableblock",
"cardsblock",
"repeaterblock",
"formblock"
],
"size": {
"width": 800,
"height": 400
},
"showEmptyState": false,
"settings": [
{
"type": "number",
"label": "Rows",
"key": "rows",
"defaultValue": 12,
"min": 1,
"max": 32
},
{
"type": "number",
"label": "Columns",
"key": "cols",
"defaultValue": 12,
"min": 1,
"max": 32
}
]
},
"formblock": { "formblock": {
"name": "Form Block", "name": "Form Block",
"icon": "Form", "icon": "Form",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "2.0.40-alpha.4", "version": "2.1.11",
"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,9 +19,9 @@
"dev:builder": "rollup -cw" "dev:builder": "rollup -cw"
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "2.0.40-alpha.4", "@budibase/bbui": "^2.1.11",
"@budibase/frontend-core": "2.0.40-alpha.4", "@budibase/frontend-core": "^2.1.11",
"@budibase/string-templates": "2.0.40-alpha.4", "@budibase/string-templates": "^2.1.11",
"@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

@ -85,13 +85,8 @@
valueType: "Binding", valueType: "Binding",
}, },
] ]
// If we're using an "update" form, use the real data provider. If we're
// using a create form, we just want a fake array so that our repeater $: dataProvider = `{{ literal ${safe(providerId)} }}`
// will actually render the form, but data doesn't matter.
$: dataProvider =
actionType !== "Create"
? `{{ literal ${safe(providerId)} }}`
: { rows: [{}] }
$: renderDeleteButton = showDeleteButton && actionType === "Update" $: renderDeleteButton = showDeleteButton && actionType === "Update"
$: renderSaveButton = showSaveButton && actionType !== "View" $: renderSaveButton = showSaveButton && actionType !== "View"
$: renderButtons = renderDeleteButton || renderSaveButton $: renderButtons = renderDeleteButton || renderSaveButton

View File

@ -128,6 +128,23 @@
return fields.find(field => get(field).name === name) return fields.find(field => get(field).name === name)
} }
const getDefault = (defaultValue, schema, type) => {
// Remove any values not present in the field schema
// Convert any values supplied to string
if (Array.isArray(defaultValue) && type == "array") {
return defaultValue.reduce((acc, entry) => {
let processedOption = String(entry)
let schemaOptions = schema.constraints.inclusion
if (schemaOptions.indexOf(processedOption) > -1) {
acc.push(processedOption)
}
return acc
}, [])
} else {
return defaultValue
}
}
const formApi = { const formApi = {
registerField: ( registerField: (
field, field,
@ -153,8 +170,10 @@
table table
) )
const parsedDefault = getDefault(defaultValue, schema?.[field], type)
// If we've already registered this field then keep some existing state // If we've already registered this field then keep some existing state
let initialValue = Helpers.deepGet(initialValues, field) ?? defaultValue let initialValue = Helpers.deepGet(initialValues, field) ?? parsedDefault
let initialError = null let initialError = null
let fieldId = `id-${Helpers.uuid()}` let fieldId = `id-${Helpers.uuid()}`
const existingField = getField(field) const existingField = getField(field)
@ -187,11 +206,11 @@
error: initialError, error: initialError,
disabled: disabled:
disabled || fieldDisabled || (isAutoColumn && !editAutoColumns), disabled || fieldDisabled || (isAutoColumn && !editAutoColumns),
defaultValue, defaultValue: parsedDefault,
validator, validator,
lastUpdate: Date.now(), lastUpdate: Date.now(),
}, },
fieldApi: makeFieldApi(field, defaultValue), fieldApi: makeFieldApi(field, parsedDefault),
fieldSchema: schema?.[field] ?? {}, fieldSchema: schema?.[field] ?? {},
}) })

View File

@ -1,12 +1,12 @@
{ {
"name": "@budibase/frontend-core", "name": "@budibase/frontend-core",
"version": "2.0.40-alpha.4", "version": "2.1.11",
"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.0.40-alpha.4", "@budibase/bbui": "^2.1.11",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"svelte": "^3.46.2" "svelte": "^3.46.2"
} }

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/sdk", "name": "@budibase/sdk",
"version": "2.0.40-alpha.4", "version": "2.1.11",
"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.0.40-alpha.4", "version": "2.1.11",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -77,11 +77,11 @@
"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.0.40-alpha.4", "@budibase/backend-core": "^2.1.11",
"@budibase/client": "2.0.40-alpha.4", "@budibase/client": "^2.1.11",
"@budibase/pro": "2.0.40-alpha.4", "@budibase/pro": "2.1.11",
"@budibase/string-templates": "2.0.40-alpha.4", "@budibase/string-templates": "^2.1.11",
"@budibase/types": "2.0.40-alpha.4", "@budibase/types": "^2.1.11",
"@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

@ -82,10 +82,20 @@ exports.validate = async ({ tableId, row, table }) => {
// non required MultiSelect creates an empty array, which should not throw errors // non required MultiSelect creates an empty array, which should not throw errors
errors[fieldName] = [`${fieldName} is required`] errors[fieldName] = [`${fieldName} is required`]
} }
} else if (type === FieldTypes.JSON && typeof row[fieldName] === "string") { } else if (
(type === FieldTypes.ATTACHMENT || type === FieldTypes.JSON) &&
typeof row[fieldName] === "string"
) {
// this should only happen if there is an error // this should only happen if there is an error
try { try {
JSON.parse(row[fieldName]) const json = JSON.parse(row[fieldName])
if (type === FieldTypes.ATTACHMENT) {
if (Array.isArray(json)) {
row[fieldName] = json
} else {
errors[fieldName] = [`Must be an array`]
}
}
} catch (err) { } catch (err) {
errors[fieldName] = [`Contains invalid JSON`] errors[fieldName] = [`Contains invalid JSON`]
} }

View File

@ -5,7 +5,7 @@ require("svelte/register")
const send = require("koa-send") const send = require("koa-send")
const { resolve, join } = require("../../../utilities/centralPath") const { resolve, join } = require("../../../utilities/centralPath")
const uuid = require("uuid") const uuid = require("uuid")
const { ObjectStoreBuckets, ATTACHMENT_DIR } = require("../../../constants") const { ObjectStoreBuckets } = require("../../../constants")
const { processString } = require("@budibase/string-templates") const { processString } = require("@budibase/string-templates")
const { const {
loadHandlebarsFile, loadHandlebarsFile,
@ -90,7 +90,7 @@ export const uploadFile = async function (ctx: any) {
return prepareUpload({ return prepareUpload({
file, file,
s3Key: `${ctx.appId}/${ATTACHMENT_DIR}/${processedFileName}`, s3Key: `${ctx.appId}/attachments/${processedFileName}`,
bucket: ObjectStoreBuckets.APPS, bucket: ObjectStoreBuckets.APPS,
}) })
}) })

View File

@ -221,6 +221,7 @@ export interface components {
*/ */
type?: type?:
| "string" | "string"
| "barcodeqr"
| "longform" | "longform"
| "options" | "options"
| "number" | "number"
@ -326,6 +327,7 @@ export interface components {
*/ */
type?: type?:
| "string" | "string"
| "barcodeqr"
| "longform" | "longform"
| "options" | "options"
| "number" | "number"
@ -433,6 +435,7 @@ export interface components {
*/ */
type?: type?:
| "string" | "string"
| "barcodeqr"
| "longform" | "longform"
| "options" | "options"
| "number" | "number"

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "2.0.40-alpha.4", "version": "2.1.11",
"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.0.40-alpha.4", "version": "2.1.11",
"description": "Budibase types", "description": "Budibase types",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "2.0.40-alpha.4", "version": "2.1.11",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -36,10 +36,10 @@
"author": "Budibase", "author": "Budibase",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@budibase/backend-core": "2.0.40-alpha.4", "@budibase/backend-core": "^2.1.11",
"@budibase/pro": "2.0.40-alpha.4", "@budibase/pro": "2.1.11",
"@budibase/string-templates": "2.0.40-alpha.4", "@budibase/string-templates": "^2.1.11",
"@budibase/types": "2.0.40-alpha.4", "@budibase/types": "^2.1.11",
"@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

@ -86,7 +86,6 @@ describe("Internal API - /applications endpoints", () => {
// unpublish app // unpublish app
await config.applications.unpublish(<string>app.appId) await config.applications.unpublish(<string>app.appId)
}) })
it("POST - Sync application before deployment", async () => { it("POST - Sync application before deployment", async () => {