Merge pull request #5701 from Budibase/merge-master-develop

Merge master develop
This commit is contained in:
Michael Drury 2022-05-04 15:16:05 +01:00 committed by GitHub
commit 7cf71a1937
40 changed files with 448 additions and 315 deletions

View File

@ -32,7 +32,9 @@ jobs:
uses: cypress-io/github-action@v2 uses: cypress-io/github-action@v2
with: with:
install: false install: false
command: yarn test:e2e:ci command: yarn test:e2e:ci:record
env:
CYPRESS_RECORD_KEY: ${{ secrets.CYPRESS_RECORD_KEY }}
# TODO: upload recordings to s3 # TODO: upload recordings to s3
# - name: Configure AWS Credentials # - name: Configure AWS Credentials

View File

@ -112,6 +112,8 @@ spec:
value: {{ .Values.globals.google.secret | quote }} value: {{ .Values.globals.google.secret | quote }}
- name: AUTOMATION_MAX_ITERATIONS - name: AUTOMATION_MAX_ITERATIONS
value: {{ .Values.globals.automationMaxIterations | quote }} value: {{ .Values.globals.automationMaxIterations | quote }}
- name: EXCLUDE_QUOTAS_TENANTS
value: {{ .Values.globals.excludeQuotasTenants | quote }}
image: budibase/apps:{{ .Values.globals.appVersion }} image: budibase/apps:{{ .Values.globals.appVersion }}
imagePullPolicy: Always imagePullPolicy: Always

View File

@ -1,5 +1,5 @@
{ {
"version": "1.0.130-alpha.7", "version": "1.0.141",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -47,6 +47,7 @@
"lint:fix": "yarn run lint:fix:prettier && yarn run lint:fix:eslint", "lint:fix": "yarn run lint:fix:prettier && yarn run lint:fix:eslint",
"test:e2e": "lerna run cy:test --stream", "test:e2e": "lerna run cy:test --stream",
"test:e2e:ci": "lerna run cy:ci --stream", "test:e2e:ci": "lerna run cy:ci --stream",
"test:e2e:ci:record": "lerna run cy:ci:record --stream",
"build:specs": "lerna run specs", "build:specs": "lerna run specs",
"build:docker": "lerna run build:docker && npm run build:docker:proxy:compose && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh $BUDIBASE_RELEASE_VERSION && cd -", "build:docker": "lerna run build:docker && npm run build:docker:proxy:compose && cd hosting/scripts/linux/ && ./release-to-docker-hub.sh $BUDIBASE_RELEASE_VERSION && cd -",
"build:docker:proxy": "docker build hosting/proxy -t proxy-service", "build:docker:proxy": "docker build hosting/proxy -t proxy-service",

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/backend-core", "name": "@budibase/backend-core",
"version": "1.0.130-alpha.7", "version": "1.0.141",
"description": "Budibase backend core libraries used in server and worker", "description": "Budibase backend core libraries used in server and worker",
"main": "src/index.js", "main": "src/index.js",
"author": "Budibase", "author": "Budibase",

View File

@ -6,6 +6,10 @@ function isTest() {
) )
} }
function isDev() {
return process.env.NODE_ENV !== "production"
}
module.exports = { module.exports = {
JWT_SECRET: process.env.JWT_SECRET, JWT_SECRET: process.env.JWT_SECRET,
COUCH_DB_URL: process.env.COUCH_DB_URL || "http://localhost:4005", COUCH_DB_URL: process.env.COUCH_DB_URL || "http://localhost:4005",
@ -32,6 +36,7 @@ module.exports = {
TENANT_FEATURE_FLAGS: process.env.TENANT_FEATURE_FLAGS, TENANT_FEATURE_FLAGS: process.env.TENANT_FEATURE_FLAGS,
USE_COUCH: process.env.USE_COUCH || true, USE_COUCH: process.env.USE_COUCH || true,
isTest, isTest,
isDev,
_set(key, value) { _set(key, value) {
process.env[key] = value process.env[key] = value
module.exports[key] = value module.exports[key] = value

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": "1.0.130-alpha.7", "version": "1.0.141",
"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": "^1.0.130-alpha.7", "@budibase/string-templates": "^1.0.141",
"@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

@ -2,17 +2,22 @@
import dayjs from "dayjs" import dayjs from "dayjs"
export let value export let value
export let schema
// adding the 0- will turn a string like 00:00:00 into a valid ISO // adding the 0- will turn a string like 00:00:00 into a valid ISO
// date, but will make actual ISO dates invalid // date, but will make actual ISO dates invalid
$: time = new Date(`0-${value}`) $: time = new Date(`0-${value}`)
$: isTime = !isNaN(time) $: isTimeOnly = !isNaN(time) || schema?.timeOnly
$: isDateOnly = schema?.dateOnly
$: format = isTimeOnly
? "HH:mm:ss"
: isDateOnly
? "MMMM D YYYY"
: "MMMM D YYYY, HH:mm"
</script> </script>
<div> <div>
{dayjs(isTime ? time : value).format( {dayjs(isTimeOnly ? time : value).format(format)}
isTime ? "HH:mm:ss" : "MMMM D YYYY, HH:mm"
)}
</div> </div>
<style> <style>

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "1.0.130-alpha.7", "version": "1.0.141",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -14,8 +14,10 @@
"cy:open": "cypress open", "cy:open": "cypress open",
"cy:run": "cypress run", "cy:run": "cypress run",
"cy:run:ci": "xvfb-run cypress run --headed --browser chrome", "cy:run:ci": "xvfb-run cypress run --headed --browser chrome",
"cy:run:ci:record": "xvfb-run cypress run --headed --browser chrome --record",
"cy:test": "start-server-and-test cy:setup http://localhost:4100/builder cy:run", "cy:test": "start-server-and-test cy:setup http://localhost:4100/builder cy:run",
"cy:ci": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:run:ci", "cy:ci": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:run:ci",
"cy:ci:record": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:run:ci:record",
"cy:debug": "start-server-and-test cy:setup http://localhost:4100/builder cy:open", "cy:debug": "start-server-and-test cy:setup http://localhost:4100/builder cy:open",
"cy:debug:ci": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:open" "cy:debug:ci": "start-server-and-test cy:setup:ci http://localhost:4100/builder cy:open"
}, },
@ -65,10 +67,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.0.130-alpha.7", "@budibase/bbui": "^1.0.141",
"@budibase/client": "^1.0.130-alpha.7", "@budibase/client": "^1.0.141",
"@budibase/frontend-core": "^1.0.130-alpha.7", "@budibase/frontend-core": "^1.0.141",
"@budibase/string-templates": "^1.0.130-alpha.7", "@budibase/string-templates": "^1.0.141",
"@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

@ -53,6 +53,18 @@
x => x.blockToLoop === block.id x => x.blockToLoop === block.id
) )
async function removeLooping() {
loopingSelected = false
let loopBlock =
$automationStore.selectedAutomation?.automation.definition.steps.find(
x => x.blockToLoop === block.id
)
automationStore.actions.deleteAutomationBlock(loopBlock)
await automationStore.actions.save(
$automationStore.selectedAutomation?.automation
)
}
async function deleteStep() { async function deleteStep() {
let loopBlock = let loopBlock =
$automationStore.selectedAutomation?.automation.definition.steps.find( $automationStore.selectedAutomation?.automation.definition.steps.find(
@ -151,9 +163,7 @@
{#if !showLooping} {#if !showLooping}
<div class="blockSection"> <div class="blockSection">
<div class="block-options"> <div class="block-options">
<div class="delete-padding" on:click={() => deleteStep()}> <ActionButton on:click={() => removeLooping()} icon="DeleteOutline" />
<Icon name="DeleteOutline" />
</div>
</div> </div>
<Layout noPadding gap="S"> <Layout noPadding gap="S">
<AutomationBlockSetup <AutomationBlockSetup

View File

@ -182,7 +182,11 @@
<div class="fields"> <div class="fields">
{#each schemaProperties as [key, value]} {#each schemaProperties as [key, value]}
<div class="block-field"> <div class="block-field">
<Label>{value.title || (key === "row" ? "Table" : key)}</Label> <Label
tooltip={value.title === "Binding / Value"
? "If using the String input type, please use a comma or newline separated string"
: null}>{value.title || (key === "row" ? "Table" : key)}</Label
>
{#if value.type === "string" && value.enum} {#if value.type === "string" && value.enum}
<Select <Select
on:change={e => onChange(e, key)} on:change={e => onChange(e, key)}
@ -265,6 +269,7 @@
value={inputData[key]} value={inputData[key]}
on:change={e => onChange(e, key)} on:change={e => onChange(e, key)}
{bindings} {bindings}
{isTestModal}
/> />
{:else if value.customType === "webhookUrl"} {:else if value.customType === "webhookUrl"}
<WebhookDisplay <WebhookDisplay

View File

@ -4,14 +4,15 @@
import DrawerBindableInput from "../../common/bindings/DrawerBindableInput.svelte" import DrawerBindableInput from "../../common/bindings/DrawerBindableInput.svelte"
import AutomationBindingPanel from "../../common/bindings/ServerBindingPanel.svelte" import AutomationBindingPanel from "../../common/bindings/ServerBindingPanel.svelte"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
import { automationStore } from "builderStore"
import RowSelectorTypes from "./RowSelectorTypes.svelte" import RowSelectorTypes from "./RowSelectorTypes.svelte"
import ModalBindableInput from "../../common/bindings/ModalBindableInput.svelte"
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
export let value export let value
export let bindings export let bindings
export let block export let block
export let isTestModal
let table let table
let schemaFields let schemaFields
@ -103,9 +104,9 @@
{#each schemaFields as [field, schema]} {#each schemaFields as [field, schema]}
{#if !schema.autocolumn} {#if !schema.autocolumn}
{#if schema.type !== "attachment"} {#if schema.type !== "attachment"}
{#if $automationStore.selectedAutomation.automation.testData}
{#if !rowControl} {#if !rowControl}
<RowSelectorTypes <RowSelectorTypes
{isTestModal}
{field} {field}
{schema} {schema}
{bindings} {bindings}
@ -113,25 +114,8 @@
{onChange} {onChange}
/> />
{:else} {:else}
<DrawerBindableInput <svelte:component
placeholder={placeholders[schema.type]} this={isTestModal ? ModalBindableInput : DrawerBindableInput}
panel={AutomationBindingPanel}
value={Array.isArray(value[field])
? value[field].join(" ")
: value[field]}
on:change={e => onChange(e, field, schema.type)}
label={field}
type="string"
{bindings}
fillWidth={true}
allowJS={true}
updateOnChange={false}
/>
{/if}
{:else if !rowControl}
<RowSelectorTypes {field} {schema} {bindings} {value} {onChange} />
{:else}
<DrawerBindableInput
placeholder={placeholders[schema.type]} placeholder={placeholders[schema.type]}
panel={AutomationBindingPanel} panel={AutomationBindingPanel}
value={Array.isArray(value[field]) value={Array.isArray(value[field])

View File

@ -8,6 +8,7 @@
} from "@budibase/bbui" } from "@budibase/bbui"
import LinkedRowSelector from "components/common/LinkedRowSelector.svelte" import LinkedRowSelector from "components/common/LinkedRowSelector.svelte"
import DrawerBindableInput from "../../common/bindings/DrawerBindableInput.svelte" import DrawerBindableInput from "../../common/bindings/DrawerBindableInput.svelte"
import ModalBindableInput from "../../common/bindings/ModalBindableInput.svelte"
import AutomationBindingPanel from "../../common/bindings/ServerBindingPanel.svelte" import AutomationBindingPanel from "../../common/bindings/ServerBindingPanel.svelte"
export let onChange export let onChange
@ -15,6 +16,7 @@
export let schema export let schema
export let value export let value
export let bindings export let bindings
export let isTestModal
function schemaHasOptions(schema) { function schemaHasOptions(schema) {
return !!schema.constraints?.inclusion?.length return !!schema.constraints?.inclusion?.length
@ -51,7 +53,8 @@
{:else if schema.type === "link"} {:else if schema.type === "link"}
<LinkedRowSelector bind:linkedRows={value[field]} {schema} /> <LinkedRowSelector bind:linkedRows={value[field]} {schema} />
{:else if schema.type === "string" || schema.type === "number"} {:else if schema.type === "string" || schema.type === "number"}
<DrawerBindableInput <svelte:component
this={isTestModal ? ModalBindableInput : DrawerBindableInput}
panel={AutomationBindingPanel} panel={AutomationBindingPanel}
value={value[field]} value={value[field]}
on:change={e => onChange(e, field)} on:change={e => onChange(e, field)}

View File

@ -20,6 +20,9 @@
export let readonly export let readonly
const resolveTimeStamp = timestamp => { const resolveTimeStamp = timestamp => {
if (!timestamp) {
return null
}
let maskedDate = new Date(`0-${timestamp}`) let maskedDate = new Date(`0-${timestamp}`)
if (maskedDate instanceof Date && !isNaN(maskedDate.getTime())) { if (maskedDate instanceof Date && !isNaN(maskedDate.getTime())) {
return maskedDate return maskedDate
@ -34,7 +37,7 @@
$: label = meta.name ? capitalise(meta.name) : "" $: label = meta.name ? capitalise(meta.name) : ""
const timeStamp = resolveTimeStamp(value) const timeStamp = resolveTimeStamp(value)
const isTimeStamp = !!timeStamp const isTimeStamp = !!timeStamp || meta?.timeOnly
</script> </script>
{#if type === "options" && meta.constraints.inclusion.length !== 0} {#if type === "options" && meta.constraints.inclusion.length !== 0}
@ -46,7 +49,12 @@
sort sort
/> />
{:else if type === "datetime"} {:else if type === "datetime"}
<DatePicker {label} timeOnly={isTimeStamp} bind:value /> <DatePicker
{label}
timeOnly={isTimeStamp}
enableTime={!meta?.dateOnly}
bind:value
/>
{:else if type === "attachment"} {:else if type === "attachment"}
<Dropzone {label} bind:value /> <Dropzone {label} bind:value />
{:else if type === "boolean"} {:else if type === "boolean"}

View File

@ -1,5 +1,4 @@
<script> <script>
import { ActionButton } from "@budibase/bbui"
import GoogleLogo from "assets/google-logo.png" import GoogleLogo from "assets/google-logo.png"
import { store } from "builderStore" import { store } from "builderStore"
import { auth } from "stores/portal" import { auth } from "stores/portal"
@ -10,7 +9,7 @@
$: tenantId = $auth.tenantId $: tenantId = $auth.tenantId
</script> </script>
<ActionButton <button
on:click={async () => { on:click={async () => {
let ds = datasource let ds = datasource
if (!ds) { if (!ds) {
@ -22,26 +21,32 @@
) )
}} }}
> >
<div class="inner">
<img src={GoogleLogo} alt="google icon" /> <img src={GoogleLogo} alt="google icon" />
<p>Sign in with Google</p> <p>Sign in with Google</p>
</div> </button>
</ActionButton>
<style> <style>
.inner { button {
width: 195px;
height: 40px;
font-size: 14px;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
justify-content: center; font-weight: 500;
padding-top: var(--spacing-xs); background: #4285f4;
padding-bottom: var(--spacing-xs); color: #ffffff;
border: none;
cursor: pointer;
padding: 2px;
border-radius: 2px;
} }
.inner img {
img {
border-radius: 2px;
width: 18px; width: 18px;
margin: 3px 10px 3px 3px; margin-right: 11px;
} background: #ffffff;
.inner p { padding: 10px;
margin: 0;
} }
</style> </style>

View File

@ -0,0 +1,145 @@
<script>
export let width = 100
export let height = 100
</script>
<svg
{width}
{height}
viewBox="0 0 46 46"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sketch="http://www.bohemiancoding.com/sketch/ns"
>
<!-- Generator: Sketch 3.3.3 (12081) - http://www.bohemiancoding.com/sketch -->
<title>btn_google_dark_normal_ios</title>
<desc>Created with Sketch.</desc>
<defs>
<filter
x="-50%"
y="-50%"
width="200%"
height="200%"
filterUnits="objectBoundingBox"
id="filter-1"
>
<feOffset dx="0" dy="1" in="SourceAlpha" result="shadowOffsetOuter1" />
<feGaussianBlur
stdDeviation="0.5"
in="shadowOffsetOuter1"
result="shadowBlurOuter1"
/>
<feColorMatrix
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.168 0"
in="shadowBlurOuter1"
type="matrix"
result="shadowMatrixOuter1"
/>
<feOffset dx="0" dy="0" in="SourceAlpha" result="shadowOffsetOuter2" />
<feGaussianBlur
stdDeviation="0.5"
in="shadowOffsetOuter2"
result="shadowBlurOuter2"
/>
<feColorMatrix
values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.084 0"
in="shadowBlurOuter2"
type="matrix"
result="shadowMatrixOuter2"
/>
<feMerge>
<feMergeNode in="shadowMatrixOuter1" />
<feMergeNode in="shadowMatrixOuter2" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
<rect id="path-2" x="0" y="0" width="40" height="40" rx="2" />
<rect id="path-3" x="5" y="5" width="38" height="38" rx="1" />
</defs>
<g
id="Google-Button"
stroke="none"
stroke-width="1"
fill="none"
fill-rule="evenodd"
sketch:type="MSPage"
>
<g
id="9-PATCH"
sketch:type="MSArtboardGroup"
transform="translate(-608.000000, -219.000000)"
/>
<g
id="btn_google_dark_normal"
sketch:type="MSArtboardGroup"
transform="translate(-1.000000, -1.000000)"
>
<g
id="button"
sketch:type="MSLayerGroup"
transform="translate(4.000000, 4.000000)"
filter="url(#filter-1)"
>
<g id="button-bg">
<use
fill="#4285F4"
fill-rule="evenodd"
sketch:type="MSShapeGroup"
xlink:href="#path-2"
/>
<use fill="none" xlink:href="#path-2" />
<use fill="none" xlink:href="#path-2" />
<use fill="none" xlink:href="#path-2" />
</g>
</g>
<g id="button-bg-copy">
<use
fill="#FFFFFF"
fill-rule="evenodd"
sketch:type="MSShapeGroup"
xlink:href="#path-3"
/>
<use fill="none" xlink:href="#path-3" />
<use fill="none" xlink:href="#path-3" />
<use fill="none" xlink:href="#path-3" />
</g>
<g
id="logo_googleg_48dp"
sketch:type="MSLayerGroup"
transform="translate(15.000000, 15.000000)"
>
<path
d="M17.64,9.20454545 C17.64,8.56636364 17.5827273,7.95272727 17.4763636,7.36363636 L9,7.36363636 L9,10.845 L13.8436364,10.845 C13.635,11.97 13.0009091,12.9231818 12.0477273,13.5613636 L12.0477273,15.8195455 L14.9563636,15.8195455 C16.6581818,14.2527273 17.64,11.9454545 17.64,9.20454545 L17.64,9.20454545 Z"
id="Shape"
fill="#4285F4"
sketch:type="MSShapeGroup"
/>
<path
d="M9,18 C11.43,18 13.4672727,17.1940909 14.9563636,15.8195455 L12.0477273,13.5613636 C11.2418182,14.1013636 10.2109091,14.4204545 9,14.4204545 C6.65590909,14.4204545 4.67181818,12.8372727 3.96409091,10.71 L0.957272727,10.71 L0.957272727,13.0418182 C2.43818182,15.9831818 5.48181818,18 9,18 L9,18 Z"
id="Shape"
fill="#34A853"
sketch:type="MSShapeGroup"
/>
<path
d="M3.96409091,10.71 C3.78409091,10.17 3.68181818,9.59318182 3.68181818,9 C3.68181818,8.40681818 3.78409091,7.83 3.96409091,7.29 L3.96409091,4.95818182 L0.957272727,4.95818182 C0.347727273,6.17318182 0,7.54772727 0,9 C0,10.4522727 0.347727273,11.8268182 0.957272727,13.0418182 L3.96409091,10.71 L3.96409091,10.71 Z"
id="Shape"
fill="#FBBC05"
sketch:type="MSShapeGroup"
/>
<path
d="M9,3.57954545 C10.3213636,3.57954545 11.5077273,4.03363636 12.4404545,4.92545455 L15.0218182,2.34409091 C13.4631818,0.891818182 11.4259091,0 9,0 C5.48181818,0 2.43818182,2.01681818 0.957272727,4.95818182 L3.96409091,7.29 C4.67181818,5.16272727 6.65590909,3.57954545 9,3.57954545 L9,3.57954545 Z"
id="Shape"
fill="#EA4335"
sketch:type="MSShapeGroup"
/>
<path
d="M0,0 L18,0 L18,18 L0,18 L0,0 Z"
id="Shape"
sketch:type="MSShapeGroup"
/>
</g>
<g id="handles_square" sketch:type="MSLayerGroup" />
</g>
</g>
</svg>

View File

@ -49,6 +49,10 @@
filters = [...filters, duplicate] filters = [...filters, duplicate]
} }
const getSchema = filter => {
return schemaFields.find(field => field.name === filter.field)
}
const onFieldChange = (expression, field) => { const onFieldChange = (expression, field) => {
// Update the field type // Update the field type
expression.type = enrichedSchemaFields.find(x => x.name === field)?.type expression.type = enrichedSchemaFields.find(x => x.name === field)?.type
@ -150,7 +154,12 @@
bind:value={filter.value} bind:value={filter.value}
/> />
{:else if filter.type === "datetime"} {:else if filter.type === "datetime"}
<DatePicker disabled={filter.noValue} bind:value={filter.value} /> <DatePicker
disabled={filter.noValue}
enableTime={!getSchema(filter).dateOnly}
timeOnly={getSchema(filter).timeOnly}
bind:value={filter.value}
/>
{:else} {:else}
<DrawerBindableInput disabled /> <DrawerBindableInput disabled />
{/if} {/if}

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "1.0.130-alpha.7", "version": "1.0.141",
"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": {

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/client", "name": "@budibase/client",
"version": "1.0.130-alpha.7", "version": "1.0.141",
"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": "^1.0.130-alpha.7", "@budibase/bbui": "^1.0.141",
"@budibase/frontend-core": "^1.0.130-alpha.7", "@budibase/frontend-core": "^1.0.141",
"@budibase/string-templates": "^1.0.130-alpha.7", "@budibase/string-templates": "^1.0.141",
"@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

@ -88,6 +88,10 @@
const schema = schemaFields.find(x => x.name === field) const schema = schemaFields.find(x => x.name === field)
return schema?.constraints?.inclusion || [] return schema?.constraints?.inclusion || []
} }
const getSchema = filter => {
return schemaFields.find(field => field.name === filter.field)
}
</script> </script>
<div class="container" class:mobile={$context.device.mobile}> <div class="container" class:mobile={$context.device.mobile}>
@ -134,7 +138,12 @@
bind:value={filter.value} bind:value={filter.value}
/> />
{:else if filter.type === "datetime"} {:else if filter.type === "datetime"}
<DatePicker disabled={filter.noValue} bind:value={filter.value} /> <DatePicker
disabled={filter.noValue}
enableTime={!getSchema(filter).dateOnly}
timeOnly={getSchema(filter).timeOnly}
bind:value={filter.value}
/>
{:else} {:else}
<Input disabled /> <Input disabled />
{/if} {/if}

View File

@ -44,7 +44,6 @@
fieldApi = value?.fieldApi fieldApi = value?.fieldApi
fieldSchema = value?.fieldSchema fieldSchema = value?.fieldSchema
}) })
onDestroy(() => unsubscribe?.())
// Determine label class from position // Determine label class from position
$: labelClass = labelPos === "above" ? "" : `spectrum-FieldLabel--${labelPos}` $: labelClass = labelPos === "above" ? "" : `spectrum-FieldLabel--${labelPos}`
@ -52,6 +51,11 @@
const updateLabel = e => { const updateLabel = e => {
builderStore.actions.updateProp("label", e.target.textContent) builderStore.actions.updateProp("label", e.target.textContent)
} }
onDestroy(() => {
fieldApi?.deregister()
unsubscribe?.()
})
</script> </script>
<FieldGroupFallback> <FieldGroupFallback>

View File

@ -22,7 +22,7 @@
if ( if (
formContext && formContext &&
$builderStore.inBuilder && $builderStore.inBuilder &&
$componentStore.selectedComponentPath?.includes($component.id) $componentStore?.selectedComponentPath?.includes($component.id)
) { ) {
formContext.formApi.setStep(step) formContext.formApi.setStep(step)
} }

View File

@ -329,6 +329,17 @@
} }
} }
// We don't want to actually remove the field state when deregistering, just
// remove any errors and validation
const deregister = () => {
const fieldInfo = getField(field)
fieldInfo.update(state => {
state.fieldState.validator = null
state.fieldState.error = null
return state
})
}
// Updates the disabled state of a certain field // Updates the disabled state of a certain field
const setDisabled = fieldDisabled => { const setDisabled = fieldDisabled => {
const fieldInfo = getField(field) const fieldInfo = getField(field)
@ -348,6 +359,7 @@
reset, reset,
updateValidation, updateValidation,
setDisabled, setDisabled,
deregister,
validate: () => { validate: () => {
// Validate the field by force setting the same value again // Validate the field by force setting the same value again
const { fieldState } = get(getField(field)) const { fieldState } = get(getField(field))

View File

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

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/server", "name": "@budibase/server",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "1.0.130-alpha.7", "version": "1.0.141",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -68,10 +68,10 @@
"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": "^1.0.130-alpha.7", "@budibase/backend-core": "^1.0.141",
"@budibase/client": "^1.0.130-alpha.7", "@budibase/client": "^1.0.141",
"@budibase/pro": "1.0.130-alpha.7", "@budibase/pro": "1.0.141",
"@budibase/string-templates": "^1.0.130-alpha.7", "@budibase/string-templates": "^1.0.141",
"@bull-board/api": "^3.7.0", "@bull-board/api": "^3.7.0",
"@bull-board/koa": "^3.7.0", "@bull-board/koa": "^3.7.0",
"@elastic/elasticsearch": "7.10.0", "@elastic/elasticsearch": "7.10.0",

View File

@ -86,3 +86,15 @@ exports.substituteLoopStep = (hbsString, substitute) => {
return hbsString return hbsString
} }
exports.stringSplit = value => {
if (value == null) {
return []
}
if (value.split("\n").length > 1) {
value = value.split("\n")
} else {
value = value.split(",")
}
return value
}

View File

@ -242,12 +242,10 @@ module MSSQLModule {
if (typeof name !== "string") { if (typeof name !== "string") {
continue continue
} }
const type: string = convertSqlType(def.DATA_TYPE)
schema[name] = { schema[name] = {
autocolumn: !!autoColumns.find((col: string) => col === name), autocolumn: !!autoColumns.find((col: string) => col === name),
name: name, name: name,
type, ...convertSqlType(def.DATA_TYPE),
} }
} }
tables[tableName] = { tables[tableName] = {

View File

@ -15,6 +15,7 @@ import {
} from "./utils" } from "./utils"
import { DatasourcePlus } from "./base/datasourcePlus" import { DatasourcePlus } from "./base/datasourcePlus"
import dayjs from "dayjs" import dayjs from "dayjs"
import { FieldTypes } from "../constants"
const { NUMBER_REGEX } = require("../utilities") const { NUMBER_REGEX } = require("../utilities")
module MySQLModule { module MySQLModule {
@ -101,7 +102,7 @@ module MySQLModule {
} }
// if not a number, see if it is a date - important to do in this order as any // if not a number, see if it is a date - important to do in this order as any
// integer will be considered a valid date // integer will be considered a valid date
else if (dayjs(binding).isValid()) { else if (/^\d/.test(binding) && dayjs(binding).isValid()) {
bindings[i] = dayjs(binding).toDate() bindings[i] = dayjs(binding).toDate()
} }
} }
@ -151,20 +152,24 @@ module MySQLModule {
async internalQuery( async internalQuery(
query: SqlQuery, query: SqlQuery,
connect: boolean = true opts: { connect?: boolean; disableCoercion?: boolean } = {
connect: true,
disableCoercion: false,
}
): Promise<any[] | any> { ): Promise<any[] | any> {
try { try {
if (connect) { if (opts?.connect) {
await this.connect() await this.connect()
} }
const baseBindings = query.bindings || []
const bindings = opts?.disableCoercion
? baseBindings
: bindingTypeCoerce(baseBindings)
// Node MySQL is callback based, so we must wrap our call in a promise // Node MySQL is callback based, so we must wrap our call in a promise
const response = await this.client.query( const response = await this.client.query(query.sql, bindings)
query.sql,
bindingTypeCoerce(query.bindings || [])
)
return response[0] return response[0]
} finally { } finally {
if (connect) { if (opts?.connect) {
await this.disconnect() await this.disconnect()
} }
} }
@ -179,7 +184,7 @@ module MySQLModule {
// get the tables first // get the tables first
const tablesResp = await this.internalQuery( const tablesResp = await this.internalQuery(
{ sql: "SHOW TABLES;" }, { sql: "SHOW TABLES;" },
false { connect: false }
) )
const tableNames = tablesResp.map( const tableNames = tablesResp.map(
(obj: any) => (obj: any) =>
@ -191,7 +196,7 @@ module MySQLModule {
const schema: TableSchema = {} const schema: TableSchema = {}
const descResp = await this.internalQuery( const descResp = await this.internalQuery(
{ sql: `DESCRIBE \`${tableName}\`;` }, { sql: `DESCRIBE \`${tableName}\`;` },
false { connect: false }
) )
for (let column of descResp) { for (let column of descResp) {
const columnName = column.Field const columnName = column.Field
@ -211,8 +216,8 @@ module MySQLModule {
schema[columnName] = { schema[columnName] = {
name: columnName, name: columnName,
autocolumn: isAuto, autocolumn: isAuto,
type: convertSqlType(column.Type),
constraints, constraints,
...convertSqlType(column.Type),
} }
} }
if (!tables[tableName]) { if (!tables[tableName]) {
@ -254,7 +259,8 @@ module MySQLModule {
async query(json: QueryJson) { async query(json: QueryJson) {
await this.connect() await this.connect()
try { try {
const queryFn = (query: any) => this.internalQuery(query, false) const queryFn = (query: any) =>
this.internalQuery(query, { connect: false, disableCoercion: true })
return await this.queryWithReturning(json, queryFn) return await this.queryWithReturning(json, queryFn)
} finally { } finally {
await this.disconnect() await this.disconnect()

View File

@ -279,9 +279,9 @@ module OracleModule {
) )
} }
private internalConvertType(column: OracleColumn): string { private internalConvertType(column: OracleColumn): { type: string } {
if (this.isBooleanType(column)) { if (this.isBooleanType(column)) {
return FieldTypes.BOOLEAN return { type: FieldTypes.BOOLEAN }
} }
return convertSqlType(column.type) return convertSqlType(column.type)
@ -328,7 +328,7 @@ module OracleModule {
fieldSchema = { fieldSchema = {
autocolumn: OracleIntegration.isAutoColumn(oracleColumn), autocolumn: OracleIntegration.isAutoColumn(oracleColumn),
name: columnName, name: columnName,
type: this.internalConvertType(oracleColumn), ...this.internalConvertType(oracleColumn),
} }
table.schema[columnName] = fieldSchema table.schema[columnName] = fieldSchema
} }

View File

@ -227,7 +227,6 @@ module PostgresModule {
} }
} }
const type: string = convertSqlType(column.data_type)
const identity = !!( const identity = !!(
column.identity_generation || column.identity_generation ||
column.identity_start || column.identity_start ||
@ -242,7 +241,7 @@ module PostgresModule {
tables[tableName].schema[columnName] = { tables[tableName].schema[columnName] = {
autocolumn: isAuto, autocolumn: isAuto,
name: columnName, name: columnName,
type, ...convertSqlType(column.data_type),
} }
} }

View File

@ -35,6 +35,9 @@ const SQL_DATE_TYPE_MAP = {
date: FieldTypes.DATETIME, date: FieldTypes.DATETIME,
} }
const SQL_DATE_ONLY_TYPES = ["date"]
const SQL_TIME_ONLY_TYPES = ["time"]
const SQL_STRING_TYPE_MAP = { const SQL_STRING_TYPE_MAP = {
varchar: FieldTypes.STRING, varchar: FieldTypes.STRING,
char: FieldTypes.STRING, char: FieldTypes.STRING,
@ -85,9 +88,9 @@ export function breakExternalTableId(tableId: string | undefined) {
return {} return {}
} }
const parts = tableId.split(DOUBLE_SEPARATOR) const parts = tableId.split(DOUBLE_SEPARATOR)
let tableName = parts.pop() let datasourceId = parts.shift()
// if they need joined // if they need joined
let datasourceId = parts.join(DOUBLE_SEPARATOR) let tableName = parts.join(DOUBLE_SEPARATOR)
return { datasourceId, tableName } return { datasourceId, tableName }
} }
@ -137,12 +140,20 @@ export function breakRowIdField(_id: string | { _id: string }): any[] {
} }
export function convertSqlType(type: string) { export function convertSqlType(type: string) {
let foundType = FieldTypes.STRING
const lcType = type.toLowerCase()
for (let [external, internal] of Object.entries(SQL_TYPE_MAP)) { for (let [external, internal] of Object.entries(SQL_TYPE_MAP)) {
if (type.toLowerCase().includes(external)) { if (lcType.includes(external)) {
return internal foundType = internal
break
} }
} }
return FieldTypes.STRING const schema: any = { type: foundType }
if (foundType === FieldTypes.DATETIME) {
schema.dateOnly = SQL_DATE_ONLY_TYPES.includes(lcType)
schema.timeOnly = SQL_TIME_ONLY_TYPES.includes(lcType)
}
return schema
} }
export function getSqlQuery(query: SqlQuery | string): SqlQuery { export function getSqlQuery(query: SqlQuery | string): SqlQuery {

View File

@ -100,10 +100,10 @@ class Orchestrator {
let automation = this._automation let automation = this._automation
const app = await this.getApp() const app = await this.getApp()
let stopped = false let stopped = false
let loopStep let loopStep = null
let stepCount = 0 let stepCount = 0
let loopStepNumber let loopStepNumber = null
let loopSteps = [] let loopSteps = []
for (let step of automation.definition.steps) { for (let step of automation.definition.steps) {
stepCount++ stepCount++
@ -117,15 +117,17 @@ class Orchestrator {
if (loopStep) { if (loopStep) {
input = await processObject(loopStep.inputs, this._context) input = await processObject(loopStep.inputs, this._context)
} }
let iterations = loopStep ? input.binding.length : 1 let iterations = loopStep
? Array.isArray(input.binding)
? input.binding.length
: automationUtils.stringSplit(input.binding).length
: 1
let iterationCount = 0 let iterationCount = 0
for (let index = 0; index < iterations; index++) { for (let index = 0; index < iterations; index++) {
let originalStepInput = cloneDeep(step.inputs) let originalStepInput = cloneDeep(step.inputs)
// Handle if the user has set a max iteration count or if it reaches the max limit set by us // Handle if the user has set a max iteration count or if it reaches the max limit set by us
if (loopStep) { if (loopStep) {
// lets first of all handle the input
// if the input is array then use it, if it is a string then split it on every new line
let newInput = await processObject( let newInput = await processObject(
loopStep.inputs, loopStep.inputs,
cloneDeep(this._context) cloneDeep(this._context)
@ -134,9 +136,6 @@ class Orchestrator {
newInput, newInput,
loopStep.schema.inputs loopStep.schema.inputs
) )
this._context.steps[loopStepNumber] = {
currentItem: newInput.binding[index],
}
let tempOutput = { items: loopSteps, iterations: iterationCount } let tempOutput = { items: loopSteps, iterations: iterationCount }
if ( if (
@ -154,6 +153,20 @@ class Orchestrator {
break break
} }
let item
if (
typeof loopStep.inputs.binding === "string" &&
loopStep.inputs.option === "String"
) {
item = automationUtils.stringSplit(newInput.binding)
} else {
item = loopStep.inputs.binding
}
this._context.steps[loopStepNumber] = {
currentItem: item[index],
}
// The "Loop" binding in the front end is "fake", so replace it here so the context can understand it // The "Loop" binding in the front end is "fake", so replace it here so the context can understand it
// Pretty hacky because we need to account for the row object // Pretty hacky because we need to account for the row object
for (let [key, value] of Object.entries(originalStepInput)) { for (let [key, value] of Object.entries(originalStepInput)) {
@ -178,7 +191,6 @@ class Orchestrator {
} }
} }
} }
if ( if (
index === parseInt(env.AUTOMATION_MAX_ITERATIONS) || index === parseInt(env.AUTOMATION_MAX_ITERATIONS) ||
index === loopStep.inputs.iterations index === loopStep.inputs.iterations
@ -192,10 +204,25 @@ class Orchestrator {
break break
} }
let isFailure = false
if ( if (
typeof this._context.steps[loopStepNumber]?.currentItem === "object"
) {
isFailure = Object.keys(
this._context.steps[loopStepNumber].currentItem
).some(value => {
return (
this._context.steps[loopStepNumber].currentItem[value] ===
loopStep.inputs.failure
)
})
} else {
isFailure =
this._context.steps[loopStepNumber]?.currentItem === this._context.steps[loopStepNumber]?.currentItem ===
loopStep.inputs.failure loopStep.inputs.failure
) { }
if (isFailure) {
this.updateContextAndOutput(loopStepNumber, step, tempOutput, { this.updateContextAndOutput(loopStepNumber, step, tempOutput, {
status: AutomationErrors.FAILURE_CONDITION, status: AutomationErrors.FAILURE_CONDITION,
success: false, success: false,
@ -286,18 +313,16 @@ class Orchestrator {
module.exports = (input, callback) => { module.exports = (input, callback) => {
const appId = input.data.event.appId const appId = input.data.event.appId
doInAppContext(appId, () => { doInAppContext(appId, async () => {
const automationOrchestrator = new Orchestrator( const automationOrchestrator = new Orchestrator(
input.data.automation, input.data.automation,
input.data.event input.data.event
) )
automationOrchestrator try {
.execute() const response = await automationOrchestrator.execute()
.then(response => {
callback(null, response) callback(null, response)
}) } catch (err) {
.catch(err => {
callback(err) callback(err)
}) }
}) })
} }

View File

@ -191,14 +191,13 @@ class QueryRunner {
} }
module.exports = (input, callback) => { module.exports = (input, callback) => {
doInAppContext(input.appId, () => { doInAppContext(input.appId, async () => {
const Runner = new QueryRunner(input) const Runner = new QueryRunner(input)
Runner.execute() try {
.then(response => { const response = await Runner.execute()
callback(null, response) callback(null, response)
}) } catch (err) {
.catch(err => {
callback(err) callback(err)
}) }
}) })
} }

View File

@ -2,7 +2,11 @@ const { budibaseTempDir } = require("../budibaseDir")
const fs = require("fs") const fs = require("fs")
const { join } = require("path") const { join } = require("path")
const uuid = require("uuid/v4") const uuid = require("uuid/v4")
const { doWithDB } = require("@budibase/backend-core/db") const {
doWithDB,
dangerousGetDB,
closeDB,
} = require("@budibase/backend-core/db")
const { ObjectStoreBuckets } = require("../../constants") const { ObjectStoreBuckets } = require("../../constants")
const { const {
upload, upload,
@ -151,14 +155,18 @@ exports.streamBackup = async appId => {
* @return {*} either a readable stream or a string * @return {*} either a readable stream or a string
*/ */
exports.exportDB = async (dbName, { stream, filter, exportName } = {}) => { exports.exportDB = async (dbName, { stream, filter, exportName } = {}) => {
return doWithDB(dbName, async db => { // streaming a DB dump is a bit more complicated, can't close DB
// Stream the dump if required
if (stream) { if (stream) {
const db = dangerousGetDB(dbName)
const memStream = new MemoryStream() const memStream = new MemoryStream()
memStream.on("end", async () => {
await closeDB(db)
})
db.dump(memStream, { filter }) db.dump(memStream, { filter })
return memStream return memStream
} }
return doWithDB(dbName, async db => {
// Write the dump to file if required // Write the dump to file if required
if (exportName) { if (exportName) {
const path = join(budibaseTempDir(), exportName) const path = join(budibaseTempDir(), exportName)

View File

@ -1014,38 +1014,10 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@1.0.130-alpha.1": "@budibase/backend-core@1.0.138":
version "1.0.130-alpha.1" version "1.0.138"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.130-alpha.1.tgz#670c090b91edef708fa77f63d0288f9a2f8733c1" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.138.tgz#5297d6cf5b9ec8c15f0a6df4c7d8273b8ac900f0"
integrity sha512-Twhy7YtK7URjrw948UVO0MkxRtz8GWAROGTf/uZkla2visuinKVlmXvsnjK/eDKWVfzN+WBqoMjgslEpDqLLtg== integrity sha512-1qN/5urKX8bBXwEz266Z94rco8dTI7VqIh75m8ZcqrAfoUpjvZJS76gZxfc5U/QWPwrgVFnLtYvnEjaLbGEflg==
dependencies:
"@techpass/passport-openidconnect" "^0.3.0"
aws-sdk "^2.901.0"
bcryptjs "^2.4.3"
cls-hooked "^4.2.2"
ioredis "^4.27.1"
jsonwebtoken "^8.5.1"
koa-passport "^4.1.4"
lodash "^4.17.21"
lodash.isarguments "^3.1.0"
node-fetch "^2.6.1"
passport-google-auth "^1.0.2"
passport-google-oauth "^2.0.0"
passport-jwt "^4.0.0"
passport-local "^1.0.0"
posthog-node "^1.3.0"
pouchdb "7.3.0"
pouchdb-find "^7.2.2"
pouchdb-replication-stream "^1.2.9"
sanitize-s3-objectkey "^0.0.1"
tar-fs "^2.1.1"
uuid "^8.3.2"
zlib "^1.0.5"
"@budibase/backend-core@^1.0.130-alpha.1":
version "1.0.134"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.134.tgz#c5fa1b179d7545339d6c5f2d69b0cd3f99d43c5c"
integrity sha512-otv40D1q6UOozv38XcXpyXzyIdqdNJA3PxJ7hQDUEoEQXy8xSdzvWy/wiC/paDy5zh4pCVYyIHPGZQtodHmbXQ==
dependencies: dependencies:
"@techpass/passport-openidconnect" "^0.3.0" "@techpass/passport-openidconnect" "^0.3.0"
aws-sdk "^2.901.0" aws-sdk "^2.901.0"
@ -1119,128 +1091,12 @@
svelte-flatpickr "^3.2.3" svelte-flatpickr "^3.2.3"
svelte-portal "^1.0.0" svelte-portal "^1.0.0"
"@budibase/bbui@^1.0.134": "@budibase/pro@1.0.138":
version "1.0.134" version "1.0.138"
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.0.134.tgz#bd7cec68ccca9392829dccf7c6154b6979e278d1" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.138.tgz#cacbebe5ce93eb533af62a794a638944c2c61544"
integrity sha512-KcZwUy22jCrePb39yUsz8kiSXK458rpRL2iDdsdZD+GIHITP3qrUu5uu6HfcHrlDQHBgPcI/vJfppjBspCC79A== integrity sha512-4ABlUZvl2h8sd8awJATf3KJeoFWV/8SoqdbKiH1ICdUcM/6dad7nhbJ15QqJL+Uuh/+mN2yEbr8V6Un2+yF+CA==
dependencies: dependencies:
"@adobe/spectrum-css-workflow-icons" "^1.2.1" "@budibase/backend-core" "1.0.138"
"@budibase/string-templates" "^1.0.134"
"@spectrum-css/actionbutton" "^1.0.1"
"@spectrum-css/actiongroup" "^1.0.1"
"@spectrum-css/avatar" "^3.0.2"
"@spectrum-css/button" "^3.0.1"
"@spectrum-css/buttongroup" "^3.0.2"
"@spectrum-css/checkbox" "^3.0.2"
"@spectrum-css/dialog" "^3.0.1"
"@spectrum-css/divider" "^1.0.3"
"@spectrum-css/dropzone" "^3.0.2"
"@spectrum-css/fieldgroup" "^3.0.2"
"@spectrum-css/fieldlabel" "^3.0.1"
"@spectrum-css/icon" "^3.0.1"
"@spectrum-css/illustratedmessage" "^3.0.2"
"@spectrum-css/inlinealert" "^2.0.1"
"@spectrum-css/inputgroup" "^3.0.2"
"@spectrum-css/label" "^2.0.10"
"@spectrum-css/link" "^3.1.1"
"@spectrum-css/menu" "^3.0.1"
"@spectrum-css/modal" "^3.0.1"
"@spectrum-css/pagination" "^3.0.3"
"@spectrum-css/picker" "^1.0.1"
"@spectrum-css/popover" "^3.0.1"
"@spectrum-css/progressbar" "^1.0.2"
"@spectrum-css/progresscircle" "^1.0.2"
"@spectrum-css/radio" "^3.0.2"
"@spectrum-css/search" "^3.0.2"
"@spectrum-css/sidenav" "^3.0.2"
"@spectrum-css/statuslight" "^3.0.2"
"@spectrum-css/stepper" "^3.0.3"
"@spectrum-css/switch" "^1.0.2"
"@spectrum-css/table" "^3.0.1"
"@spectrum-css/tabs" "^3.0.1"
"@spectrum-css/tags" "^3.0.2"
"@spectrum-css/textfield" "^3.0.1"
"@spectrum-css/toast" "^3.0.1"
"@spectrum-css/tooltip" "^3.0.3"
"@spectrum-css/treeview" "^3.0.2"
"@spectrum-css/typography" "^3.0.1"
"@spectrum-css/underlay" "^2.0.9"
"@spectrum-css/vars" "^3.0.1"
dayjs "^1.10.4"
easymde "^2.16.1"
svelte-flatpickr "^3.2.3"
svelte-portal "^1.0.0"
"@budibase/client@^1.0.130-alpha.1":
version "1.0.134"
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-1.0.134.tgz#b1dfd9137045e80e4f6c6982d7d28ae3053f0910"
integrity sha512-SYBDaybFyoJalabjzgvqG80l/bZjI8lG1rzR4bGGqyHf0wDYbHbHWeDvj/4pLhCSpcjGabi7eC+wvjxejIqQng==
dependencies:
"@budibase/bbui" "^1.0.134"
"@budibase/frontend-core" "^1.0.134"
"@budibase/string-templates" "^1.0.134"
"@spectrum-css/button" "^3.0.3"
"@spectrum-css/card" "^3.0.3"
"@spectrum-css/divider" "^1.0.3"
"@spectrum-css/link" "^3.1.3"
"@spectrum-css/page" "^3.0.1"
"@spectrum-css/tag" "^3.1.4"
"@spectrum-css/typography" "^3.0.2"
"@spectrum-css/vars" "^3.0.1"
apexcharts "^3.22.1"
dayjs "^1.10.5"
downloadjs "1.4.7"
leaflet "^1.7.1"
regexparam "^1.3.0"
rollup-plugin-polyfill-node "^0.8.0"
sanitize-html "^2.7.0"
screenfull "^6.0.1"
shortid "^2.2.15"
svelte "^3.38.2"
svelte-apexcharts "^1.0.2"
svelte-flatpickr "^3.1.0"
svelte-spa-router "^3.0.5"
"@budibase/frontend-core@^1.0.134":
version "1.0.134"
resolved "https://registry.yarnpkg.com/@budibase/frontend-core/-/frontend-core-1.0.134.tgz#3d5efbddea60cf8fd479ad396dd41ab82d794cad"
integrity sha512-PZxUVNXSHngBASfsNXx5TWy6JiGlqJrnAqux9Unc7oeQcHFkoDH21BcJjNZlX5TgCmAMQCeNksB42q9nfkMlgg==
dependencies:
"@budibase/bbui" "^1.0.134"
lodash "^4.17.21"
svelte "^3.46.2"
"@budibase/handlebars-helpers@^0.11.8":
version "0.11.8"
resolved "https://registry.yarnpkg.com/@budibase/handlebars-helpers/-/handlebars-helpers-0.11.8.tgz#6953d29673a8c5c407e096c0a84890465c7ce841"
integrity sha512-ggWJUt0GqsHFAEup5tlWlcrmYML57nKhpNGGLzVsqXVYN8eVmf3xluYmmMe7fDYhQH0leSprrdEXmsdFQF3HAQ==
dependencies:
array-sort "^1.0.0"
define-property "^2.0.2"
extend-shallow "^3.0.2"
for-in "^1.0.2"
get-object "^0.2.0"
get-value "^3.0.1"
handlebars "^4.7.7"
handlebars-utils "^1.0.6"
has-value "^2.0.2"
helper-md "^0.2.2"
html-tag "^2.0.0"
is-even "^1.0.0"
is-glob "^4.0.1"
kind-of "^6.0.3"
micromatch "^3.1.5"
relative "^3.0.2"
striptags "^3.1.1"
to-gfm-code-block "^0.1.1"
year "^0.2.1"
"@budibase/pro@1.0.130-alpha.1":
version "1.0.130-alpha.1"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.130-alpha.1.tgz#12112ef0c178b25cbc24d2e42a644fdcf948cd80"
integrity sha512-TMcfMPM8DSSgoh7WfyPyXvWqOLYBnK8/8LsUcnDsvlEx22YsY/UNQX8eDZ+U9unE+pyCNR1dhP78uvQiFwiq3w==
dependencies:
"@budibase/backend-core" "1.0.130-alpha.1"
node-fetch "^2.6.1" node-fetch "^2.6.1"
"@budibase/standard-components@^0.9.139": "@budibase/standard-components@^0.9.139":

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/string-templates", "name": "@budibase/string-templates",
"version": "1.0.130-alpha.7", "version": "1.0.141",
"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

@ -70,7 +70,7 @@ function createTemplate(string, opts) {
* @param {object|array} object The input structure which is to be recursed, it is important to note that * @param {object|array} object The input structure which is to be recursed, it is important to note that
* if the structure contains any cycles then this will fail. * if the structure contains any cycles then this will fail.
* @param {object} context The context that handlebars should fill data from. * @param {object} context The context that handlebars should fill data from.
* @param {object|undefined} opts optional - specify some options for processing. * @param {object|undefined} [opts] optional - specify some options for processing.
* @returns {Promise<object|array>} The structure input, as fully updated as possible. * @returns {Promise<object|array>} The structure input, as fully updated as possible.
*/ */
module.exports.processObject = async (object, context, opts) => { module.exports.processObject = async (object, context, opts) => {
@ -101,7 +101,7 @@ module.exports.processObject = async (object, context, opts) => {
* then nothing will occur. * then nothing will occur.
* @param {string} string The template string which is the filled from the context object. * @param {string} string The template string which is the filled from the context object.
* @param {object} context An object of information which will be used to enrich the string. * @param {object} context An object of information which will be used to enrich the string.
* @param {object|undefined} opts optional - specify some options for processing. * @param {object|undefined} [opts] optional - specify some options for processing.
* @returns {Promise<string>} The enriched string, all templates should have been replaced if they can be. * @returns {Promise<string>} The enriched string, all templates should have been replaced if they can be.
*/ */
module.exports.processString = async (string, context, opts) => { module.exports.processString = async (string, context, opts) => {
@ -115,7 +115,7 @@ module.exports.processString = async (string, context, opts) => {
* @param {object|array} object The input structure which is to be recursed, it is important to note that * @param {object|array} object The input structure which is to be recursed, it is important to note that
* if the structure contains any cycles then this will fail. * if the structure contains any cycles then this will fail.
* @param {object} context The context that handlebars should fill data from. * @param {object} context The context that handlebars should fill data from.
* @param {object|undefined} opts optional - specify some options for processing. * @param {object|undefined} [opts] optional - specify some options for processing.
* @returns {object|array} The structure input, as fully updated as possible. * @returns {object|array} The structure input, as fully updated as possible.
*/ */
module.exports.processObjectSync = (object, context, opts) => { module.exports.processObjectSync = (object, context, opts) => {
@ -136,7 +136,7 @@ module.exports.processObjectSync = (object, context, opts) => {
* then nothing will occur. This is a pure sync call and therefore does not have the full functionality of the async call. * then nothing will occur. This is a pure sync call and therefore does not have the full functionality of the async call.
* @param {string} string The template string which is the filled from the context object. * @param {string} string The template string which is the filled from the context object.
* @param {object} context An object of information which will be used to enrich the string. * @param {object} context An object of information which will be used to enrich the string.
* @param {object|undefined} opts optional - specify some options for processing. * @param {object|undefined} [opts] optional - specify some options for processing.
* @returns {string} The enriched string, all templates should have been replaced if they can be. * @returns {string} The enriched string, all templates should have been replaced if they can be.
*/ */
module.exports.processStringSync = (string, context, opts) => { module.exports.processStringSync = (string, context, opts) => {
@ -194,7 +194,7 @@ module.exports.makePropSafe = property => {
/** /**
* Checks whether or not a template string contains totally valid syntax (simply tries running it) * Checks whether or not a template string contains totally valid syntax (simply tries running it)
* @param string The string to test for valid syntax - this may contain no templates and will be considered valid. * @param string The string to test for valid syntax - this may contain no templates and will be considered valid.
* @param opts optional - specify some options for processing. * @param [opts] optional - specify some options for processing.
* @returns {boolean} Whether or not the input string is valid. * @returns {boolean} Whether or not the input string is valid.
*/ */
module.exports.isValid = (string, opts) => { module.exports.isValid = (string, opts) => {
@ -205,6 +205,7 @@ module.exports.isValid = (string, opts) => {
"array", "array",
"cannot read property", "cannot read property",
"undefined", "undefined",
"json at position 0",
] ]
// this is a portion of a specific string always output by handlebars in the case of a syntax error // this is a portion of a specific string always output by handlebars in the case of a syntax error
const invalidCases = [`expecting '`] const invalidCases = [`expecting '`]

View File

@ -360,6 +360,13 @@ describe("Test the literal helper", () => {
}) })
}) })
describe("Test that JSONpase helper", () => {
it("should state that the JSONparse helper is valid", async () => {
const output = isValid(`{{ JSONparse input }}`)
expect(output).toBe(true)
})
})
describe("Cover a few complex use cases", () => { describe("Cover a few complex use cases", () => {
it("should allow use of three different collection helpers", async () => { it("should allow use of three different collection helpers", async () => {
const output = await processString( const output = await processString(

View File

@ -1,7 +1,7 @@
{ {
"name": "@budibase/worker", "name": "@budibase/worker",
"email": "hi@budibase.com", "email": "hi@budibase.com",
"version": "1.0.130-alpha.7", "version": "1.0.141",
"description": "Budibase background service", "description": "Budibase background service",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -31,9 +31,9 @@
"author": "Budibase", "author": "Budibase",
"license": "GPL-3.0", "license": "GPL-3.0",
"dependencies": { "dependencies": {
"@budibase/backend-core": "^1.0.130-alpha.7", "@budibase/backend-core": "^1.0.141",
"@budibase/pro": "1.0.130-alpha.7", "@budibase/pro": "1.0.141",
"@budibase/string-templates": "^1.0.130-alpha.7", "@budibase/string-templates": "^1.0.141",
"@koa/router": "^8.0.0", "@koa/router": "^8.0.0",
"@sentry/node": "6.17.7", "@sentry/node": "6.17.7",
"@techpass/passport-openidconnect": "^0.3.0", "@techpass/passport-openidconnect": "^0.3.0",

View File

@ -293,10 +293,10 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@budibase/backend-core@1.0.130-alpha.1": "@budibase/backend-core@1.0.138":
version "1.0.130-alpha.1" version "1.0.138"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.130-alpha.1.tgz#670c090b91edef708fa77f63d0288f9a2f8733c1" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.138.tgz#5297d6cf5b9ec8c15f0a6df4c7d8273b8ac900f0"
integrity sha512-Twhy7YtK7URjrw948UVO0MkxRtz8GWAROGTf/uZkla2visuinKVlmXvsnjK/eDKWVfzN+WBqoMjgslEpDqLLtg== integrity sha512-1qN/5urKX8bBXwEz266Z94rco8dTI7VqIh75m8ZcqrAfoUpjvZJS76gZxfc5U/QWPwrgVFnLtYvnEjaLbGEflg==
dependencies: dependencies:
"@techpass/passport-openidconnect" "^0.3.0" "@techpass/passport-openidconnect" "^0.3.0"
aws-sdk "^2.901.0" aws-sdk "^2.901.0"
@ -321,12 +321,12 @@
uuid "^8.3.2" uuid "^8.3.2"
zlib "^1.0.5" zlib "^1.0.5"
"@budibase/pro@1.0.130-alpha.1": "@budibase/pro@1.0.138":
version "1.0.130-alpha.1" version "1.0.138"
resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.130-alpha.1.tgz#12112ef0c178b25cbc24d2e42a644fdcf948cd80" resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.138.tgz#cacbebe5ce93eb533af62a794a638944c2c61544"
integrity sha512-TMcfMPM8DSSgoh7WfyPyXvWqOLYBnK8/8LsUcnDsvlEx22YsY/UNQX8eDZ+U9unE+pyCNR1dhP78uvQiFwiq3w== integrity sha512-4ABlUZvl2h8sd8awJATf3KJeoFWV/8SoqdbKiH1ICdUcM/6dad7nhbJ15QqJL+Uuh/+mN2yEbr8V6Un2+yF+CA==
dependencies: dependencies:
"@budibase/backend-core" "1.0.130-alpha.1" "@budibase/backend-core" "1.0.138"
node-fetch "^2.6.1" node-fetch "^2.6.1"
"@cspotcode/source-map-consumer@0.8.0": "@cspotcode/source-map-consumer@0.8.0":