1
0
Fork 0
mirror of synced 2024-06-27 02:20:35 +12:00

Merge branch 'develop' of github.com:Budibase/budibase into feature/budibase-api

This commit is contained in:
mike12345567 2022-03-01 18:43:52 +00:00
commit 2436bc2e32
41 changed files with 705 additions and 319 deletions

View file

@ -1,5 +1,5 @@
{
"version": "1.0.76-alpha.5",
"version": "1.0.79-alpha.5",
"npmClient": "yarn",
"packages": [
"packages/*"

View file

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

View file

@ -1,7 +1,7 @@
{
"name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.",
"version": "1.0.76-alpha.5",
"version": "1.0.79-alpha.5",
"license": "MPL-2.0",
"svelte": "src/index.js",
"module": "dist/bbui.es.js",
@ -38,7 +38,7 @@
],
"dependencies": {
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
"@budibase/string-templates": "^1.0.72-alpha.0",
"@budibase/string-templates": "^1.0.79-alpha.5",
"@spectrum-css/actionbutton": "^1.0.1",
"@spectrum-css/actiongroup": "^1.0.1",
"@spectrum-css/avatar": "^3.0.2",

View file

@ -47,7 +47,9 @@
<use xlink:href="#spectrum-css-icon-Dash100" />
</svg>
</span>
<span class="spectrum-Checkbox-label">{text || ""}</span>
{#if text}
<span class="spectrum-Checkbox-label">{text}</span>
{/if}
</label>
<style>

View file

@ -54,34 +54,43 @@
<svelte:window on:keydown={handleKey} />
<!-- These svelte if statements need to be defined like this. -->
<!-- The modal transitions do not work if nested inside more than one "if" -->
{#if visible && inline}
<div use:focusFirstInput class="spectrum-Modal inline is-open">
<slot />
</div>
{:else if visible}
{#if inline}
{#if visible}
<div use:focusFirstInput class="spectrum-Modal inline is-open">
<slot />
</div>
{/if}
{:else}
<!--
We cannot conditionally render the portal as this leads to a missing
insertion point when using nested modals. Therefore we just conditionally
render the content of the portal.
It still breaks the modal animation, but its better than soft bricking the
screen.
-->
<Portal target=".modal-container">
<div
class="spectrum-Underlay is-open"
in:fade={{ duration: 200 }}
out:fade|local={{ duration: 200 }}
on:mousedown|self={cancel}
>
<div class="modal-wrapper" on:mousedown|self={cancel}>
<div class="modal-inner-wrapper" on:mousedown|self={cancel}>
<slot name="outside" />
<div
use:focusFirstInput
class="spectrum-Modal is-open"
in:fly={{ y: 30, duration: 200 }}
out:fly|local={{ y: 30, duration: 200 }}
>
<slot />
{#if visible}
<div
class="spectrum-Underlay is-open"
in:fade={{ duration: 200 }}
out:fade|local={{ duration: 200 }}
on:mousedown|self={cancel}
>
<div class="modal-wrapper" on:mousedown|self={cancel}>
<div class="modal-inner-wrapper" on:mousedown|self={cancel}>
<slot name="outside" />
<div
use:focusFirstInput
class="spectrum-Modal is-open"
in:fly={{ y: 30, duration: 200 }}
out:fly|local={{ y: 30, duration: 200 }}
>
<slot />
</div>
</div>
</div>
</div>
</div>
{/if}
</Portal>
{/if}

View file

@ -8,9 +8,21 @@
export let allowEditRows = false
</script>
{#if allowSelectRows}
<Checkbox value={selected} />
{/if}
{#if allowEditRows}
<ActionButton size="S" on:click={onEdit}>Edit</ActionButton>
{/if}
<div>
{#if allowSelectRows}
<Checkbox value={selected} />
{/if}
{#if allowEditRows}
<ActionButton size="S" on:click={onEdit}>Edit</ActionButton>
{/if}
</div>
<style>
div {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: var(--spacing-m);
}
</style>

View file

@ -5,6 +5,7 @@
import SelectEditRenderer from "./SelectEditRenderer.svelte"
import { cloneDeep, deepGet } from "../helpers"
import ProgressCircle from "../ProgressCircle/ProgressCircle.svelte"
import Checkbox from "../Form/Checkbox.svelte"
/**
* The expected schema is our normal couch schemas for our tables.
@ -31,7 +32,6 @@
export let allowEditRows = true
export let allowEditColumns = true
export let selectedRows = []
export let editColumnTitle = "Edit"
export let customRenderers = []
export let disableSorting = false
export let autoSortColumns = true
@ -50,6 +50,8 @@
// Table state
let height = 0
let loaded = false
let checkboxStatus = false
$: schema = fixSchema(schema)
$: if (!loading) loaded = true
$: fields = getFields(schema, showAutoColumns, autoSortColumns)
@ -67,6 +69,16 @@
$: showEditColumn = allowEditRows || allowSelectRows
$: cellStyles = computeCellStyles(schema)
// Deselect the "select all" checkbox when the user navigates to a new page
$: {
let checkRowCount = rows.filter(o1 =>
selectedRows.some(o2 => o1._id === o2._id)
)
if (checkRowCount.length === 0) {
checkboxStatus = false
}
}
const fixSchema = schema => {
let fixedSchema = {}
Object.entries(schema || {}).forEach(([fieldName, fieldSchema]) => {
@ -197,13 +209,32 @@
if (!allowSelectRows) {
return
}
if (selectedRows.includes(row)) {
selectedRows = selectedRows.filter(selectedRow => selectedRow !== row)
if (selectedRows.some(selectedRow => selectedRow._id === row._id)) {
selectedRows = selectedRows.filter(
selectedRow => selectedRow._id !== row._id
)
} else {
selectedRows = [...selectedRows, row]
}
}
const toggleSelectAll = e => {
const select = !!e.detail
if (select) {
// Add any rows which are not already in selected rows
rows.forEach(row => {
if (selectedRows.findIndex(x => x._id === row._id) === -1) {
selectedRows.push(row)
}
})
} else {
// Remove any rows from selected rows that are in the current data set
selectedRows = selectedRows.filter(el =>
rows.every(f => f._id !== el._id)
)
}
}
const computeCellStyles = schema => {
let styles = {}
Object.keys(schema || {}).forEach(field => {
@ -244,7 +275,14 @@
<div
class="spectrum-Table-headCell spectrum-Table-headCell--divider spectrum-Table-headCell--edit"
>
{editColumnTitle || ""}
{#if allowSelectRows}
<Checkbox
bind:value={checkboxStatus}
on:change={toggleSelectAll}
/>
{:else}
Edit
{/if}
</div>
{/if}
{#each fields as field}
@ -302,11 +340,16 @@
{#if showEditColumn}
<div
class="spectrum-Table-cell spectrum-Table-cell--divider spectrum-Table-cell--edit"
on:click={e => {
toggleSelectRow(row)
e.stopPropagation()
}}
>
<SelectEditRenderer
data={row}
selected={selectedRows.includes(row)}
onToggleSelection={() => toggleSelectRow(row)}
selected={selectedRows.findIndex(
selectedRow => selectedRow._id === row._id
) !== -1}
onEdit={e => editRow(e, row)}
{allowSelectRows}
{allowEditRows}

View file

@ -33,7 +33,7 @@ filterTests(['smoke', 'all'], () => {
cy.get(".spectrum-Button--cta").click()
})
cy.contains("Setup").click()
cy.get(".spectrum-Picker-label").click()
cy.get(".spectrum-Picker-label").eq(1).click()
cy.contains("dog").click()
cy.get(".spectrum-Textfield-input")
.first()

View file

@ -27,10 +27,13 @@ filterTests(["smoke", "all"], () => {
it("updates a column on the table", () => {
cy.get(".title").click()
cy.get(".spectrum-Table-editIcon > use").click()
cy.get("input").eq(1).type("updated", { force: true })
cy.get(".modal-inner-wrapper").within(() => {
cy.get("input").eq(0).type("updated", { force: true })
// Unset table display column
cy.get(".spectrum-Switch-input").eq(1).click()
cy.contains("Save Column").click()
})
cy.contains("nameupdated ").should("contain", "nameupdated")
})

View file

@ -1,43 +1,45 @@
import filterTests from "../../support/filterTests"
filterTests(['smoke', 'all'], () => {
context("REST Datasource Testing", () => {
before(() => {
cy.login()
cy.createTestApp()
})
const datasource = "REST"
const restUrl = "https://api.openbrewerydb.org/breweries"
it("Should add REST data source with incorrect API", () => {
// Select REST data source
cy.selectExternalDatasource(datasource)
// Enter incorrect api & attempt to send query
cy.wait(500)
cy.get(".spectrum-Button").contains("Add query").click({ force: true })
cy.intercept('**/preview').as('queryError')
cy.get("input").clear().type("random text")
cy.get(".spectrum-Button").contains("Send").click({ force: true })
// Intercept Request after button click & apply assertions
cy.wait("@queryError")
cy.get("@queryError").its('response.body')
.should('have.property', 'message', 'Invalid URL: http://random text?')
cy.get("@queryError").its('response.body')
.should('have.property', 'status', 400)
})
it("should add and configure a REST datasource", () => {
// Select REST datasource and create query
cy.selectExternalDatasource(datasource)
cy.wait(500)
// createRestQuery confirms query creation
cy.createRestQuery("GET", restUrl)
// Confirm status code response within REST datasource
cy.get(".spectrum-FieldLabel")
.contains("Status")
.children()
.should('contain', 200)
})
filterTests(["smoke", "all"], () => {
context("REST Datasource Testing", () => {
before(() => {
cy.login()
cy.createTestApp()
})
const datasource = "REST"
const restUrl = "https://api.openbrewerydb.org/breweries"
it("Should add REST data source with incorrect API", () => {
// Select REST data source
cy.selectExternalDatasource(datasource)
// Enter incorrect api & attempt to send query
cy.wait(500)
cy.get(".spectrum-Button").contains("Add query").click({ force: true })
cy.intercept("**/preview").as("queryError")
cy.get("input").clear().type("random text")
cy.get(".spectrum-Button").contains("Send").click({ force: true })
// Intercept Request after button click & apply assertions
cy.wait("@queryError")
cy.get("@queryError")
.its("response.body")
.should("have.property", "message", "Invalid URL: http://random text?")
cy.get("@queryError")
.its("response.body")
.should("have.property", "status", 400)
})
it("should add and configure a REST datasource", () => {
// Select REST datasource and create query
cy.selectExternalDatasource(datasource)
cy.wait(500)
// createRestQuery confirms query creation
cy.createRestQuery("GET", restUrl, "/breweries")
// Confirm status code response within REST datasource
cy.get(".spectrum-FieldLabel")
.contains("Status")
.children()
.should("contain", 200)
})
})
})

View file

@ -1,115 +1,139 @@
import filterTests from "../support/filterTests"
filterTests(['smoke', 'all'], () => {
filterTests(["smoke", "all"], () => {
context("Query Level Transformers", () => {
before(() => {
cy.login()
cy.deleteApp("Cypress Tests")
cy.createApp("Cypress Tests")
})
it("should write a transformer function", () => {
// Add REST datasource - contains API for breweries
const datasource = "REST"
const restUrl = "https://api.openbrewerydb.org/breweries"
cy.selectExternalDatasource(datasource)
cy.createRestQuery("GET", restUrl)
cy.get(".spectrum-Tabs-itemLabel").contains("Transformer").click()
// Get Transformer Function from file
cy.readFile("cypress/support/queryLevelTransformerFunction.js").then((transformerFunction) => {
// Add REST datasource - contains API for breweries
const datasource = "REST"
const restUrl = "https://api.openbrewerydb.org/breweries"
cy.selectExternalDatasource(datasource)
cy.createRestQuery("GET", restUrl, "/breweries")
cy.get(".spectrum-Tabs-itemLabel").contains("Transformer").click()
// Get Transformer Function from file
cy.readFile("cypress/support/queryLevelTransformerFunction.js").then(
transformerFunction => {
cy.get(".CodeMirror textarea")
// Highlight current text and overwrite with file contents
.type(Cypress.platform === 'darwin' ? '{cmd}a' : '{ctrl}a', { force: true })
.type(transformerFunction, { parseSpecialCharSequences: false })
})
// Send Query
cy.intercept('**/queries/preview').as('query')
cy.get(".spectrum-Button").contains("Send").click({ force: true })
cy.wait("@query")
// Assert against Status Code, body, & body rows
cy.get("@query").its('response.statusCode')
.should('eq', 200)
cy.get("@query").its('response.body').should('not.be.empty')
cy.get("@query").its('response.body.rows').should('not.be.empty')
})
// Highlight current text and overwrite with file contents
.type(Cypress.platform === "darwin" ? "{cmd}a" : "{ctrl}a", {
force: true,
})
.type(transformerFunction, { parseSpecialCharSequences: false })
}
)
// Send Query
cy.intercept("**/queries/preview").as("query")
cy.get(".spectrum-Button").contains("Send").click({ force: true })
cy.wait("@query")
// Assert against Status Code, body, & body rows
cy.get("@query").its("response.statusCode").should("eq", 200)
cy.get("@query").its("response.body").should("not.be.empty")
cy.get("@query").its("response.body.rows").should("not.be.empty")
})
it("should add data to the previous query", () => {
// Add REST datasource - contains API for breweries
const datasource = "REST"
const restUrl = "https://api.openbrewerydb.org/breweries"
cy.selectExternalDatasource(datasource)
cy.createRestQuery("GET", restUrl)
cy.createRestQuery("GET", restUrl, "/breweries")
cy.get(".spectrum-Tabs-itemLabel").contains("Transformer").click()
// Get Transformer Function with Data from file
cy.readFile("cypress/support/queryLevelTransformerFunctionWithData.js").then((transformerFunction) => {
cy.readFile(
"cypress/support/queryLevelTransformerFunctionWithData.js"
).then(transformerFunction => {
//console.log(transformerFunction[1])
cy.get(".CodeMirror textarea")
// Highlight current text and overwrite with file contents
.type(Cypress.platform === 'darwin' ? '{cmd}a' : '{ctrl}a', { force: true })
.type(transformerFunction, { parseSpecialCharSequences: false })
// Highlight current text and overwrite with file contents
.type(Cypress.platform === "darwin" ? "{cmd}a" : "{ctrl}a", {
force: true,
})
.type(transformerFunction, { parseSpecialCharSequences: false })
})
// Send Query
cy.intercept('**/queries/preview').as('query')
cy.intercept("**/queries/preview").as("query")
cy.get(".spectrum-Button").contains("Send").click({ force: true })
cy.wait("@query")
// Assert against Status Code, body, & body rows
cy.get("@query").its('response.statusCode')
.should('eq', 200)
cy.get("@query").its('response.body').should('not.be.empty')
cy.get("@query").its('response.body.rows').should('not.be.empty')
cy.get("@query").its("response.statusCode").should("eq", 200)
cy.get("@query").its("response.body").should("not.be.empty")
cy.get("@query").its("response.body.rows").should("not.be.empty")
})
it("should run an invalid query within the transformer section", () => {
// Add REST datasource - contains API for breweries
const datasource = "REST"
const restUrl = "https://api.openbrewerydb.org/breweries"
cy.selectExternalDatasource(datasource)
cy.createRestQuery("GET", restUrl)
cy.createRestQuery("GET", restUrl, "/breweries")
cy.get(".spectrum-Tabs-itemLabel").contains("Transformer").click()
// Clear the code box and add "test"
cy.get(".CodeMirror textarea")
.type(Cypress.platform === 'darwin' ? '{cmd}a' : '{ctrl}a', { force: true })
.type("test")
.type(Cypress.platform === "darwin" ? "{cmd}a" : "{ctrl}a", {
force: true,
})
.type("test")
// Run Query and intercept
cy.intercept('**/preview').as('queryError')
cy.intercept("**/preview").as("queryError")
cy.get(".spectrum-Button").contains("Send").click({ force: true })
cy.wait("@queryError")
cy.wait(500)
// Assert against message and status for the query error
cy.get("@queryError").its('response.body').should('have.property', 'message', "test is not defined")
cy.get("@queryError").its('response.body').should('have.property', 'status', 400)
cy.get("@queryError")
.its("response.body")
.should("have.property", "message", "test is not defined")
cy.get("@queryError")
.its("response.body")
.should("have.property", "status", 400)
})
xit("should run an invalid query via POST request", () => {
// POST request with transformer as null
cy.request({method: 'POST',
url: `${Cypress.config().baseUrl}/api/queries/`,
body: {fields : {"headers":{},"queryString":null,"path":null},
parameters : [],
schema : {},
name : "test",
queryVerb : "read",
transformer : null,
datasourceId: "test"},
// Expected 400 error - Transformer must be a string
failOnStatusCode: false}).then((response) => {
cy.request({
method: "POST",
url: `${Cypress.config().baseUrl}/api/queries/`,
body: {
fields: { headers: {}, queryString: null, path: null },
parameters: [],
schema: {},
name: "test",
queryVerb: "read",
transformer: null,
datasourceId: "test",
},
// Expected 400 error - Transformer must be a string
failOnStatusCode: false,
}).then(response => {
expect(response.status).to.equal(400)
expect(response.body.message).to.include('Invalid body - "transformer" must be a string')
expect(response.body.message).to.include(
'Invalid body - "transformer" must be a string'
)
})
})
xit("should run an empty query", () => {
// POST request with Transformer as an empty string
cy.request({method: 'POST',
url: `${Cypress.config().baseUrl}/api/queries/preview`,
body: {fields : {"headers":{},"queryString":null,"path":null},
queryVerb : "read",
transformer : "",
datasourceId: "test"},
// Expected 400 error - Transformer is not allowed to be empty
failOnStatusCode: false}).then((response) => {
cy.request({
method: "POST",
url: `${Cypress.config().baseUrl}/api/queries/preview`,
body: {
fields: { headers: {}, queryString: null, path: null },
queryVerb: "read",
transformer: "",
datasourceId: "test",
},
// Expected 400 error - Transformer is not allowed to be empty
failOnStatusCode: false,
}).then(response => {
expect(response.status).to.equal(400)
expect(response.body.message).to.include('Invalid body - "transformer" is not allowed to be empty')
expect(response.body.message).to.include(
'Invalid body - "transformer" is not allowed to be empty'
)
})
})
})

View file

@ -39,7 +39,7 @@ Cypress.Commands.add("createApp", name => {
cy.get(".spectrum-Modal").within(() => {
cy.get("input").eq(0).type(name).should("have.value", name).blur()
cy.get(".spectrum-ButtonGroup").contains("Create app").click()
cy.wait(5000)
cy.wait(10000)
})
cy.createTable("Cypress Tests", true)
})
@ -116,10 +116,10 @@ Cypress.Commands.add("createTestTableWithData", () => {
Cypress.Commands.add("createTable", (tableName, initialTable) => {
if (!initialTable) {
cy.navigateToDataSection()
cy.get(".add-button").click()
cy.get(`[data-cy="new-table"]`).click()
}
cy.wait(7000)
cy.get(".spectrum-Modal")
cy.wait(5000)
cy.get(".spectrum-Dialog-grid")
.contains("Budibase DB")
.click({ force: true })
.then(() => {
@ -172,17 +172,19 @@ Cypress.Commands.add("addRow", values => {
Cypress.Commands.add("addRowMultiValue", values => {
cy.contains("Create row").click()
cy.get(".spectrum-Form-itemField")
.click()
.then(() => {
cy.get(".spectrum-Popover").within(() => {
for (let i = 0; i < values.length; i++) {
cy.get(".spectrum-Menu-item").eq(i).click()
}
cy.get(".spectrum-Modal").within(() => {
cy.get(".spectrum-Form-itemField")
.click()
.then(() => {
cy.get(".spectrum-Popover").within(() => {
for (let i = 0; i < values.length; i++) {
cy.get(".spectrum-Menu-item").eq(i).click()
}
})
cy.get(".spectrum-Dialog-grid").click("top")
cy.get(".spectrum-ButtonGroup").contains("Create").click()
})
cy.get(".spectrum-Dialog-grid").click("top")
cy.get(".spectrum-ButtonGroup").contains("Create").click()
})
})
})
Cypress.Commands.add("createUser", email => {
@ -435,7 +437,7 @@ Cypress.Commands.add("addDatasourceConfig", (datasource, skipFetch) => {
}
})
Cypress.Commands.add("createRestQuery", (method, restUrl) => {
Cypress.Commands.add("createRestQuery", (method, restUrl, queryPrettyName) => {
// addExternalDatasource should be called prior to this
// Configures REST datasource & sends query
cy.wait(1000)
@ -450,5 +452,5 @@ Cypress.Commands.add("createRestQuery", (method, restUrl) => {
cy.get(".spectrum-Button").contains("Save").click({ force: true })
cy.get(".hierarchy-items-container")
.should("contain", method)
.and("contain", restUrl)
.and("contain", queryPrettyName)
})

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
"version": "1.0.76-alpha.5",
"version": "1.0.79-alpha.5",
"license": "GPL-3.0",
"private": true,
"scripts": {
@ -13,7 +13,7 @@
"cy:setup:ci": "node ./cypress/setup.js",
"cy:open": "cypress open",
"cy:run": "cypress run",
"cy:run:ci": "xvfb-run cypress run --headed --browser chrome --record",
"cy:run:ci": "xvfb-run cypress run --headed --browser chrome",
"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:debug": "start-server-and-test cy:setup http://localhost:4100/builder cy:open",
@ -65,10 +65,10 @@
}
},
"dependencies": {
"@budibase/bbui": "^1.0.76-alpha.5",
"@budibase/client": "^1.0.76-alpha.5",
"@budibase/frontend-core": "^1.0.76-alpha.5",
"@budibase/string-templates": "^1.0.76-alpha.5",
"@budibase/bbui": "^1.0.79-alpha.5",
"@budibase/client": "^1.0.79-alpha.5",
"@budibase/frontend-core": "^1.0.79-alpha.5",
"@budibase/string-templates": "^1.0.79-alpha.5",
"@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",

View file

@ -32,12 +32,14 @@ export const getBindableProperties = (asset, componentId) => {
const urlBindings = getUrlBindings(asset)
const deviceBindings = getDeviceBindings()
const stateBindings = getStateBindings()
const selectedRowsBindings = getSelectedRowsBindings(asset)
return [
...contextBindings,
...urlBindings,
...stateBindings,
...userBindings,
...deviceBindings,
...selectedRowsBindings,
]
}
@ -315,6 +317,40 @@ const getDeviceBindings = () => {
return bindings
}
/**
* Gets all selected rows bindings for tables in the current asset.
*/
const getSelectedRowsBindings = asset => {
let bindings = []
if (get(store).clientFeatures?.rowSelection) {
// Add bindings for table components
let tables = findAllMatchingComponents(asset?.props, component =>
component._component.endsWith("table")
)
const safeState = makePropSafe("rowSelection")
bindings = bindings.concat(
tables.map(table => ({
type: "context",
runtimeBinding: `${safeState}.${makePropSafe(table._id)}`,
readableBinding: `${table._instanceName}.Selected rows`,
}))
)
// Add bindings for table blocks
let tableBlocks = findAllMatchingComponents(asset?.props, component =>
component._component.endsWith("tableblock")
)
bindings = bindings.concat(
tableBlocks.map(block => ({
type: "context",
runtimeBinding: `${safeState}.${makePropSafe(block._id + "-table")}`,
readableBinding: `${block._instanceName}.Selected rows`,
}))
)
}
return bindings
}
/**
* Gets all state bindings that are globally available.
*/
@ -597,14 +633,9 @@ const buildFormSchema = component => {
* in the app.
*/
export const getAllStateVariables = () => {
// Get all component containing assets
let allAssets = []
allAssets = allAssets.concat(get(store).layouts || [])
allAssets = allAssets.concat(get(store).screens || [])
// Find all button action settings in all components
let eventSettings = []
allAssets.forEach(asset => {
getAllAssets().forEach(asset => {
findAllMatchingComponents(asset.props, component => {
const settings = getComponentSettings(component._component)
settings
@ -635,6 +666,15 @@ export const getAllStateVariables = () => {
return Array.from(bindingSet)
}
export const getAllAssets = () => {
// Get all component containing assets
let allAssets = []
allAssets = allAssets.concat(get(store).layouts || [])
allAssets = allAssets.concat(get(store).screens || [])
return allAssets
}
/**
* Recurses the input object to remove any instances of bindings.
*/

View file

@ -57,6 +57,7 @@ const automationActions = store => ({
return state
})
},
save: async automation => {
const response = await API.updateAutomation(automation)
store.update(state => {
@ -130,6 +131,12 @@ const automationActions = store => ({
name: block.name,
})
},
toggleFieldControl: value => {
store.update(state => {
state.selectedBlock.rowControl = value
return state
})
},
deleteAutomationBlock: block => {
store.update(state => {
const idx =

View file

@ -41,6 +41,7 @@ const INITIAL_FRONTEND_STATE = {
intelligentLoading: false,
deviceAwareness: false,
state: false,
rowSelection: false,
customThemes: false,
devicePreview: false,
messagePassing: false,

View file

@ -3,14 +3,8 @@
import Flowchart from "./FlowChart/FlowChart.svelte"
$: automation = $automationStore.selectedAutomation?.automation
function onSelect(block) {
automationStore.update(state => {
state.selectedBlock = block
return state
})
}
</script>
{#if automation}
<Flowchart {automation} {onSelect} />
<Flowchart {automation} />
{/if}

View file

@ -14,7 +14,7 @@
} from "@budibase/bbui"
export let automation
export let onSelect
let testDataModal
let blocks
let confirmDeleteDialog
@ -45,7 +45,7 @@
<div class="title">
<div class="subtitle">
<Heading size="S">{automation.name}</Heading>
<div style="display:flex;">
<div style="display:flex; align-items: center;">
<div class="iconPadding">
<div class="icon">
<Icon
@ -72,7 +72,7 @@
animate:flip={{ duration: 500 }}
in:fly|local={{ x: 500, duration: 1500 }}
>
<FlowItem {testDataModal} {onSelect} {block} />
<FlowItem {testDataModal} {block} />
</div>
{/each}
</div>

View file

@ -10,6 +10,7 @@
Button,
StatusLight,
ActionButton,
Select,
notifications,
} from "@budibase/bbui"
import AutomationBlockSetup from "../../SetupPanel/AutomationBlockSetup.svelte"
@ -18,7 +19,6 @@
import ActionModal from "./ActionModal.svelte"
import { externalActions } from "./ExternalActions"
export let onSelect
export let block
export let testDataModal
let selected
@ -28,6 +28,10 @@
let setupToggled
let blockComplete
$: rowControl = $automationStore.selectedAutomation.automation.rowControl
$: showBindingPicker =
block.stepId === "CREATE_ROW" || block.stepId === "UPDATE_ROW"
$: testResult = $automationStore.selectedAutomation.testResults?.steps.filter(
step => (block.id ? step.id === block.id : step.stepId === block.stepId)
)
@ -44,12 +48,6 @@
$automationStore.selectedAutomation?.automation?.definition?.steps.length +
1
// Logic for hiding / showing the add button.first we check if it has a child
// then we check to see whether its inputs have been commpleted
$: disableAddButton = isTrigger
? $automationStore.selectedAutomation?.automation?.definition?.steps
.length > 0
: !isTrigger && steps.length - blockIdx > 1
$: hasCompletedInputs = Object.keys(
block.schema?.inputs?.properties || {}
).every(x => block?.inputs[x])
@ -64,6 +62,26 @@
notifications.error("Error saving notification")
}
}
function toggleFieldControl(evt) {
onSelect(block)
let rowControl
if (evt.detail === "Use values") {
rowControl = false
} else {
rowControl = true
}
automationStore.actions.toggleFieldControl(rowControl)
automationStore.actions.save(
$automationStore.selectedAutomation?.automation
)
}
async function onSelect(block) {
await automationStore.update(state => {
state.selectedBlock = block
return state
})
}
</script>
<div
@ -126,15 +144,33 @@
<Layout noPadding gap="S">
<div class="splitHeader">
<ActionButton
on:click={() => (setupToggled = !setupToggled)}
on:click={() => {
onSelect(block)
setupToggled = !setupToggled
}}
quiet
icon={setupToggled ? "ChevronDown" : "ChevronRight"}
>
<Detail size="S">Setup</Detail>
</ActionButton>
{#if !isTrigger}
<div on:click={() => deleteStep()}>
<Icon name="DeleteOutline" />
<div class="block-options">
{#if showBindingPicker}
<div>
<Select
on:change={toggleFieldControl}
quiet
defaultValue="Use values"
autoWidth
value={rowControl ? "Use bindings" : "Use values"}
options={["Use values", "Use bindings"]}
placeholder={null}
/>
</div>
{/if}
<div class="delete-padding" on:click={() => deleteStep()}>
<Icon name="DeleteOutline" />
</div>
</div>
{/if}
</div>
@ -180,6 +216,13 @@
{/if}
<style>
.delete-padding {
padding-left: 30px;
}
.block-options {
display: flex;
align-items: center;
}
.center-items {
display: flex;
align-items: center;

View file

@ -227,6 +227,7 @@
/>
{:else if value.customType === "row"}
<RowSelector
{block}
value={inputData[key]}
on:change={e => onChange(e, key)}
{bindings}

View file

@ -1,26 +1,31 @@
<script>
import { tables } from "stores/backend"
import {
Select,
Toggle,
DatePicker,
Multiselect,
TextArea,
} from "@budibase/bbui"
import { Select } from "@budibase/bbui"
import DrawerBindableInput from "../../common/bindings/DrawerBindableInput.svelte"
import AutomationBindingPanel from "../../common/bindings/ServerBindingPanel.svelte"
import { createEventDispatcher } from "svelte"
import ModalBindableInput from "components/common/bindings/ModalBindableInput.svelte"
import LinkedRowSelector from "components/common/LinkedRowSelector.svelte"
import { automationStore } from "builderStore"
import RowSelectorTypes from "./RowSelectorTypes.svelte"
const dispatch = createEventDispatcher()
export let value
export let bindings
export let block
let table
let schemaFields
let placeholders = {
number: 10,
boolean: "true",
datetime: "2022-02-16T12:00:00.000Z ",
options: "1",
array: "1 2 3 4",
link: "ro_ta_123_456",
longform: "long form text",
}
$: rowControl = block.rowControl
$: {
table = $tables.list.find(table => table._id === value?.tableId)
schemaFields = Object.entries(table?.schema ?? {})
@ -37,18 +42,48 @@
dispatch("change", value)
}
const onChange = (e, field) => {
value[field] = e.detail
const coerce = (value, type) => {
if (type === "boolean") {
if (typeof value === "boolean") {
return value
}
return value === "true"
}
if (type === "number") {
if (typeof value === "number") {
return value
}
return Number(value)
}
if (type === "options") {
return [value]
}
if (type === "array") {
if (Array.isArray(value)) {
return value
}
return value.split(",").map(x => x.trim())
}
if (type === "link") {
if (Array.isArray(value)) {
return value
}
return [value]
}
return value
}
const onChange = (e, field, type) => {
value[field] = coerce(e.detail, type)
dispatch("change", value)
}
// Ensure any nullish tableId values get set to empty string so
// that the select works
$: if (value?.tableId == null) value = { tableId: "" }
function schemaHasOptions(schema) {
return !!schema.constraints?.inclusion?.length
}
</script>
<Select
@ -62,55 +97,46 @@
<div class="schema-fields">
{#each schemaFields as [field, schema]}
{#if !schema.autocolumn}
{#if schemaHasOptions(schema) && schema.type !== "array"}
<Select
on:change={e => onChange(e, field)}
label={field}
value={value[field]}
options={schema.constraints.inclusion}
/>
{:else if schema.type === "datetime"}
<DatePicker
label={field}
value={value[field]}
on:change={e => onChange(e, field)}
/>
{:else if schema.type === "boolean"}
<Toggle
text={field}
value={value[field]}
on:change={e => onChange(e, field)}
/>
{:else if schema.type === "array"}
<Multiselect
bind:value={value[field]}
label={field}
options={schema.constraints.inclusion}
/>
{:else if schema.type === "longform"}
<TextArea label={field} bind:value={value[field]} />
{:else if schema.type === "link"}
<LinkedRowSelector bind:linkedRows={value[field]} {schema} />
{:else if schema.type === "string" || schema.type === "number"}
{#if schema.type !== "attachment"}
{#if $automationStore.selectedAutomation.automation.testData}
<ModalBindableInput
value={value[field]}
panel={AutomationBindingPanel}
label={field}
type={value.customType}
on:change={e => onChange(e, field)}
{bindings}
/>
{#if !rowControl}
<RowSelectorTypes
{field}
{schema}
{bindings}
{value}
{onChange}
/>
{:else}
<DrawerBindableInput
placeholder={placeholders[schema.type]}
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}
/>
{/if}
{:else if !rowControl}
<RowSelectorTypes {field} {schema} {bindings} {value} {onChange} />
{:else}
<DrawerBindableInput
placeholder={placeholders[schema.type]}
panel={AutomationBindingPanel}
value={value[field]}
on:change={e => onChange(e, field)}
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={false}
allowJS={true}
/>
{/if}
{/if}

View file

@ -0,0 +1,64 @@
<script>
import {
Select,
Toggle,
DatePicker,
Multiselect,
TextArea,
} from "@budibase/bbui"
import LinkedRowSelector from "components/common/LinkedRowSelector.svelte"
import DrawerBindableInput from "../../common/bindings/DrawerBindableInput.svelte"
import AutomationBindingPanel from "../../common/bindings/ServerBindingPanel.svelte"
export let onChange
export let field
export let schema
export let value
export let bindings
function schemaHasOptions(schema) {
return !!schema.constraints?.inclusion?.length
}
</script>
{#if schemaHasOptions(schema) && schema.type !== "array"}
<Select
on:change={e => onChange(e, field)}
label={field}
value={value[field]}
options={schema.constraints.inclusion}
/>
{:else if schema.type === "datetime"}
<DatePicker
label={field}
value={value[field]}
on:change={e => onChange(e, field)}
/>
{:else if schema.type === "boolean"}
<Toggle
text={field}
value={value[field]}
on:change={e => onChange(e, field)}
/>
{:else if schema.type === "array"}
<Multiselect
bind:value={value[field]}
label={field}
options={schema.constraints.inclusion}
/>
{:else if schema.type === "longform"}
<TextArea label={field} bind:value={value[field]} />
{:else if schema.type === "link"}
<LinkedRowSelector bind:linkedRows={value[field]} {schema} />
{:else if schema.type === "string" || schema.type === "number"}
<DrawerBindableInput
panel={AutomationBindingPanel}
value={value[field]}
on:change={e => onChange(e, field)}
label={field}
type="string"
{bindings}
fillWidth={true}
allowJS={true}
/>
{/if}

View file

@ -57,7 +57,8 @@
{data}
{loading}
{type}
allowEditing={!view?.calculation}
allowEditing={false}
rowCount={10}
bind:hideAutocolumns
>
<ViewFilterButton {view} />

View file

@ -8,7 +8,11 @@
import EditQueryPopover from "./popovers/EditQueryPopover.svelte"
import NavItem from "components/common/NavItem.svelte"
import TableNavigator from "components/backend/TableNavigator/TableNavigator.svelte"
import { customQueryIconText, customQueryIconColor } from "helpers/data/utils"
import {
customQueryIconText,
customQueryIconColor,
customQueryText,
} from "helpers/data/utils"
import ICONS from "./icons"
import { notifications } from "@budibase/bbui"
@ -137,7 +141,7 @@
icon="SQLQuery"
iconText={customQueryIconText(datasource, query)}
iconColor={customQueryIconColor(datasource, query)}
text={query.name}
text={customQueryText(datasource, query)}
opened={$queries.selected === query._id}
selected={$queries.selected === query._id}
on:click={() => onClickQuery(query)}

View file

@ -30,8 +30,8 @@ export function breakQueryString(qs) {
const params = qs.split("&")
let paramObj = {}
for (let param of params) {
const [key, value] = param.split("=")
paramObj[key] = value
const split = param.split("=")
paramObj[split[0]] = split.slice(1).join("=")
}
return paramObj
}
@ -109,6 +109,36 @@ export function customQueryIconColor(datasource, query) {
}
}
export function customQueryText(datasource, query) {
if (!query.name || datasource.source !== IntegrationTypes.REST) {
return query.name
}
// Remove protocol
let name = query.name
if (name.includes("//")) {
name = name.split("//")[1]
}
// If no path, return the full name
if (!name.includes("/")) {
return name
}
// Remove trailing slash
if (name.endsWith("/")) {
name = name.slice(0, -1)
}
// Only use path
const split = name.split("/")
if (split[1]) {
return `/${split.slice(1).join("/")}`
} else {
return split[0]
}
}
export function flipHeaderState(headersActivity) {
if (!headersActivity) {
return {}

View file

@ -60,7 +60,7 @@ export function createQueriesStore() {
})
return savedQuery
},
import: async (data, datasourceId) => {
import: async ({ data, datasourceId }) => {
return await API.importQueries({
datasourceId,
data,

View file

@ -1,6 +1,6 @@
{
"name": "@budibase/cli",
"version": "1.0.76-alpha.5",
"version": "1.0.79-alpha.5",
"description": "Budibase CLI, for developers, self hosting and migrations.",
"main": "src/index.js",
"bin": {

View file

@ -6,7 +6,8 @@
"state": true,
"customThemes": true,
"devicePreview": true,
"messagePassing": true
"messagePassing": true,
"rowSelection": true
},
"layout": {
"name": "Layout",
@ -2714,6 +2715,13 @@
"key": "showAutoColumns",
"defaultValue": false
},
{
"type": "boolean",
"label": "Allow row selection",
"key": "allowSelectRows",
"defaultValue": false
},
{
"type": "boolean",
"label": "Link table rows",
@ -2973,6 +2981,11 @@
"label": "Show auto columns",
"key": "showAutoColumns"
},
{
"type": "boolean",
"label": "Allow row selection",
"key": "allowSelectRows"
},
{
"type": "boolean",
"label": "Link table rows",

View file

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

View file

@ -21,6 +21,7 @@
import UserBindingsProvider from "components/context/UserBindingsProvider.svelte"
import DeviceBindingsProvider from "components/context/DeviceBindingsProvider.svelte"
import StateBindingsProvider from "components/context/StateBindingsProvider.svelte"
import RowSelectionProvider from "components/context/RowSelectionProvider.svelte"
import SettingsBar from "components/preview/SettingsBar.svelte"
import SelectionIndicator from "components/preview/SelectionIndicator.svelte"
import HoverIndicator from "components/preview/HoverIndicator.svelte"
@ -90,59 +91,61 @@
<UserBindingsProvider>
<DeviceBindingsProvider>
<StateBindingsProvider>
<!-- Settings bar can be rendered outside of device preview -->
<!-- Key block needs to be outside the if statement or it breaks -->
{#key $builderStore.selectedComponentId}
{#if $builderStore.inBuilder}
<SettingsBar />
{/if}
{/key}
<RowSelectionProvider>
<!-- Settings bar can be rendered outside of device preview -->
<!-- Key block needs to be outside the if statement or it breaks -->
{#key $builderStore.selectedComponentId}
{#if $builderStore.inBuilder}
<SettingsBar />
{/if}
{/key}
<!-- Clip boundary for selection indicators -->
<div
id="clip-root"
class:preview={$builderStore.inBuilder}
class:tablet-preview={$builderStore.previewDevice === "tablet"}
class:mobile-preview={$builderStore.previewDevice === "mobile"}
>
<!-- Actual app -->
<div id="app-root">
<CustomThemeWrapper>
{#key `${$screenStore.activeLayout._id}-${$builderStore.previewType}`}
<Component
isLayout
instance={$screenStore.activeLayout.props}
/>
{/key}
<!-- Clip boundary for selection indicators -->
<div
id="clip-root"
class:preview={$builderStore.inBuilder}
class:tablet-preview={$builderStore.previewDevice === "tablet"}
class:mobile-preview={$builderStore.previewDevice === "mobile"}
>
<!-- Actual app -->
<div id="app-root">
<CustomThemeWrapper>
{#key `${$screenStore.activeLayout._id}-${$builderStore.previewType}`}
<Component
isLayout
instance={$screenStore.activeLayout.props}
/>
{/key}
<!--
<!--
Flatpickr needs to be inside the theme wrapper.
It also needs its own container because otherwise it hijacks
key events on the whole page. It is painful to work with.
-->
<div id="flatpickr-root" />
<div id="flatpickr-root" />
<!-- Modal container to ensure they sit on top -->
<div class="modal-container" />
<!-- Modal container to ensure they sit on top -->
<div class="modal-container" />
<!-- Layers on top of app -->
<NotificationDisplay />
<ConfirmationDisplay />
<PeekScreenDisplay />
</CustomThemeWrapper>
</div>
<!-- Layers on top of app -->
<NotificationDisplay />
<ConfirmationDisplay />
<PeekScreenDisplay />
</CustomThemeWrapper>
</div>
<!-- Selection indicators should be bounded by device -->
<!--
<!-- Selection indicators should be bounded by device -->
<!--
We don't want to key these by componentID as they control their own
re-mounting to avoid flashes.
-->
{#if $builderStore.inBuilder}
<SelectionIndicator />
<HoverIndicator />
<DNDHandler />
{/if}
</div>
{#if $builderStore.inBuilder}
<SelectionIndicator />
<HoverIndicator />
<DNDHandler />
{/if}
</div>
</RowSelectionProvider>
</StateBindingsProvider>
</DeviceBindingsProvider>
</UserBindingsProvider>

View file

@ -18,6 +18,7 @@
export let quiet
export let compact
export let size
export let allowSelectRows
export let linkRows
export let linkURL
export let linkColumn
@ -157,6 +158,7 @@
>
<BlockComponent
type="table"
context="table"
props={{
dataProvider: `{{ literal ${safe(dataProviderId)} }}`,
columns: tableColumns,
@ -164,6 +166,7 @@
rowCount,
quiet,
compact,
allowSelectRows,
size,
linkRows,
linkURL,

View file

@ -3,6 +3,7 @@
import { Table } from "@budibase/bbui"
import SlotRenderer from "./SlotRenderer.svelte"
import { UnsortableTypes } from "../../../constants"
import { onDestroy } from "svelte"
export let dataProvider
export let columns
@ -14,10 +15,12 @@
export let linkURL
export let linkColumn
export let linkPeek
export let allowSelectRows
export let compact
const component = getContext("component")
const { styleable, getAction, ActionTypes, routeStore } = getContext("sdk")
const { styleable, getAction, ActionTypes, routeStore, rowSelectionStore } =
getContext("sdk")
const customColumnKey = `custom-${Math.random()}`
const customRenderers = [
{
@ -25,7 +28,7 @@
component: SlotRenderer,
},
]
let selectedRows = []
$: hasChildren = $component.children
$: loading = dataProvider?.loading ?? false
$: data = dataProvider?.rows || []
@ -36,6 +39,12 @@
dataProvider?.id,
ActionTypes.SetDataProviderSorting
)
$: {
rowSelectionStore.actions.updateSelection(
$component.id,
selectedRows.map(row => row._id)
)
}
const getFields = (schema, customColumns, showAutoColumns) => {
// Check for an invalid column selection
@ -117,6 +126,10 @@
const split = linkURL.split("/:")
routeStore.actions.navigate(`${split[0]}/${id}`, linkPeek)
}
onDestroy(() => {
rowSelectionStore.actions.updateSelection($component.id, [])
})
</script>
<div use:styleable={$component.styles} class={size}>
@ -128,7 +141,8 @@
{quiet}
{compact}
{customRenderers}
allowSelectRows={false}
allowSelectRows={!!allowSelectRows}
bind:selectedRows
allowEditRows={false}
allowEditColumns={false}
showAutoColumns={true}
@ -139,10 +153,19 @@
>
<slot />
</Table>
{#if allowSelectRows && selectedRows.length}
<div class="row-count">
{selectedRows.length} row{selectedRows.length === 1 ? "" : "s"} selected
</div>
{/if}
</div>
<style>
div {
background-color: var(--spectrum-alias-background-color-secondary);
}
.row-count {
margin-top: var(--spacing-l);
}
</style>

View file

@ -0,0 +1,8 @@
<script>
import Provider from "./Provider.svelte"
import { rowSelectionStore } from "stores"
</script>
<Provider key="rowSelection" data={$rowSelectionStore}>
<slot />
</Provider>

View file

@ -6,6 +6,7 @@ import {
screenStore,
builderStore,
uploadStore,
rowSelectionStore,
} from "stores"
import { styleable } from "utils/styleable"
import { linkable } from "utils/linkable"
@ -19,6 +20,7 @@ export default {
authStore,
notificationStore,
routeStore,
rowSelectionStore,
screenStore,
builderStore,
uploadStore,

View file

@ -10,7 +10,7 @@ export { peekStore } from "./peek"
export { stateStore } from "./state"
export { themeStore } from "./theme"
export { uploadStore } from "./uploads.js"
export { rowSelectionStore } from "./rowSelection.js"
// Context stores are layered and duplicated, so it is not a singleton
export { createContextStore } from "./context"

View file

@ -0,0 +1,22 @@
import { writable } from "svelte/store"
const createRowSelectionStore = () => {
const store = writable({})
function updateSelection(componentId, selectedRows) {
store.update(state => {
state[componentId] = [...selectedRows]
return state
})
}
return {
subscribe: store.subscribe,
set: store.set,
actions: {
updateSelection,
},
}
}
export const rowSelectionStore = createRowSelectionStore()

View file

@ -127,12 +127,16 @@ const queryExecutionHandler = async action => {
// Trigger a notification and invalidate the datasource as long as this
// was not a readable query
if (!query.readable) {
API.notifications.error.success("Query executed successfully")
notificationStore.actions.success("Query executed successfully")
await dataSourceStore.actions.invalidateDataSource(query.datasourceId)
}
return { result }
} catch (error) {
notificationStore.actions.error(
"An error occurred while executing the query"
)
// Abort next actions
return false
}

View file

@ -1,12 +1,12 @@
{
"name": "@budibase/frontend-core",
"version": "1.0.76-alpha.5",
"version": "1.0.79-alpha.5",
"description": "Budibase frontend core libraries used in builder and client",
"author": "Budibase",
"license": "MPL-2.0",
"svelte": "src/index.js",
"dependencies": {
"@budibase/bbui": "^1.0.76-alpha.5",
"@budibase/bbui": "^1.0.79-alpha.5",
"lodash": "^4.17.21",
"svelte": "^3.46.2"
}

View file

@ -1,7 +1,7 @@
{
"name": "@budibase/server",
"email": "hi@budibase.com",
"version": "1.0.76-alpha.3",
"version": "1.0.79-alpha.5",
"description": "Budibase Web Server",
"main": "src/index.ts",
"repository": {
@ -74,9 +74,9 @@
"license": "GPL-3.0",
"dependencies": {
"@apidevtools/swagger-parser": "^10.0.3",
"@budibase/backend-core": "^1.0.76-alpha.5",
"@budibase/client": "^1.0.76-alpha.5",
"@budibase/string-templates": "^1.0.76-alpha.5",
"@budibase/backend-core": "^1.0.79-alpha.5",
"@budibase/client": "^1.0.79-alpha.5",
"@budibase/string-templates": "^1.0.79-alpha.5",
"@bull-board/api": "^3.7.0",
"@bull-board/koa": "^3.7.0",
"@elastic/elasticsearch": "7.10.0",

View file

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

View file

@ -1,7 +1,7 @@
{
"name": "@budibase/worker",
"email": "hi@budibase.com",
"version": "1.0.76-alpha.5",
"version": "1.0.79-alpha.5",
"description": "Budibase background service",
"main": "src/index.ts",
"repository": {
@ -34,8 +34,8 @@
"author": "Budibase",
"license": "GPL-3.0",
"dependencies": {
"@budibase/backend-core": "^1.0.76-alpha.5",
"@budibase/string-templates": "^1.0.76-alpha.5",
"@budibase/backend-core": "^1.0.79-alpha.5",
"@budibase/string-templates": "^1.0.79-alpha.5",
"@koa/router": "^8.0.0",
"@sentry/node": "^6.0.0",
"@techpass/passport-openidconnect": "^0.3.0",