Merge remote-tracking branch 'origin/develop' into feature/foreach-block

This commit is contained in:
Peter Clement 2022-04-18 10:05:43 +01:00
commit 97c49b11d2
60 changed files with 1198 additions and 330 deletions

View File

@ -93,6 +93,8 @@ then `cd ` into your local copy.
#### 3. Install and Build #### 3. Install and Build
| **NOTE**: On Windows, all yarn commands must be executed on a bash shell (e.g. git bash)
To develop the Budibase platform you'll need [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/) installed. To develop the Budibase platform you'll need [Docker](https://www.docker.com/) and [Docker Compose](https://docs.docker.com/compose/) installed.
##### Quick method ##### Quick method

View File

@ -28,6 +28,7 @@ jobs:
- name: Cypress run - name: Cypress run
id: cypress id: cypress
continue-on-error: true
uses: cypress-io/github-action@v2 uses: cypress-io/github-action@v2
with: with:
install: false install: false

View File

@ -1,5 +1,5 @@
{ {
"version": "1.0.105-alpha.9", "version": "1.0.105-alpha.19",
"npmClient": "yarn", "npmClient": "yarn",
"packages": [ "packages": [
"packages/*" "packages/*"

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/backend-core", "name": "@budibase/backend-core",
"version": "1.0.105-alpha.9", "version": "1.0.105-alpha.19",
"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

@ -16,6 +16,7 @@ exports.Headers = {
API_VER: "x-budibase-api-version", API_VER: "x-budibase-api-version",
APP_ID: "x-budibase-app-id", APP_ID: "x-budibase-app-id",
TYPE: "x-budibase-type", TYPE: "x-budibase-type",
PREVIEW_ROLE: "x-budibase-role",
TENANT_ID: "x-budibase-tenant-id", TENANT_ID: "x-budibase-tenant-id",
TOKEN: "x-budibase-token", TOKEN: "x-budibase-token",
CSRF_TOKEN: "x-csrf-token", CSRF_TOKEN: "x-csrf-token",

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.105-alpha.9", "version": "1.0.105-alpha.19",
"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.105-alpha.9", "@budibase/string-templates": "^1.0.105-alpha.19",
"@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

@ -36,6 +36,10 @@
padding-left: var(--spacing-l); padding-left: var(--spacing-l);
padding-right: var(--spacing-l); padding-right: var(--spacing-l);
} }
.paddingX-XL {
padding-left: var(--spacing-xl);
padding-right: var(--spacing-xl);
}
.paddingY-S { .paddingY-S {
padding-top: var(--spacing-s); padding-top: var(--spacing-s);
padding-bottom: var(--spacing-s); padding-bottom: var(--spacing-s);
@ -48,6 +52,10 @@
padding-top: var(--spacing-l); padding-top: var(--spacing-l);
padding-bottom: var(--spacing-l); padding-bottom: var(--spacing-l);
} }
.paddingY-XL {
padding-top: var(--spacing-xl);
padding-bottom: var(--spacing-xl);
}
.gap-XXS { .gap-XXS {
grid-gap: var(--spacing-xs); grid-gap: var(--spacing-xs);
} }

View File

@ -1,42 +1,21 @@
<script> <script>
import Icon from "../Icon/Icon.svelte" import Icon from "../Icon/Icon.svelte"
import { copyToClipboard } from "../helpers"
import { notifications } from "../Stores/notifications" import { notifications } from "../Stores/notifications"
export let value export let value
const onClick = e => { const onClick = async e => {
e.stopPropagation() e.stopPropagation()
copyToClipboard(value) try {
} await copyToClipboard(value)
notifications.success("Copied to clipboard")
const copyToClipboard = value => { } catch (error) {
return new Promise(res => { notifications.error(
if (navigator.clipboard && window.isSecureContext) { "Failed to copy to clipboard. Check the dev console for the value."
// Try using the clipboard API first )
navigator.clipboard.writeText(value).then(res) console.warn("Failed to copy the value", value)
} else { }
// Fall back to the textarea hack
let textArea = document.createElement("textarea")
textArea.value = value
textArea.style.position = "fixed"
textArea.style.left = "-9999px"
textArea.style.top = "-9999px"
document.body.appendChild(textArea)
textArea.focus()
textArea.select()
document.execCommand("copy")
textArea.remove()
res()
}
})
.then(() => {
notifications.success("Copied to clipboard")
})
.catch(() => {
notifications.error(
"Failed to copy to clipboard. Check the dev console for the value."
)
console.warn("Failed to copy the value", value)
})
} }
</script> </script>

View File

@ -108,7 +108,7 @@
padding-left: var(--spacing-xl); padding-left: var(--spacing-xl);
padding-right: var(--spacing-xl); padding-right: var(--spacing-xl);
position: relative; position: relative;
border-bottom: var(--border-light); border-bottom: 1px solid var(--spectrum-global-color-gray-300);
} }
.spectrum-Tabs-content { .spectrum-Tabs-content {
margin-top: var(--spectrum-global-dimension-static-size-150); margin-top: var(--spectrum-global-dimension-static-size-150);

View File

@ -106,3 +106,29 @@ export const deepSet = (obj, key, value) => {
export const cloneDeep = obj => { export const cloneDeep = obj => {
return JSON.parse(JSON.stringify(obj)) return JSON.parse(JSON.stringify(obj))
} }
/**
* Copies a value to the clipboard
* @param value the value to copy
*/
export const copyToClipboard = value => {
return new Promise(res => {
if (navigator.clipboard && window.isSecureContext) {
// Try using the clipboard API first
navigator.clipboard.writeText(value).then(res)
} else {
// Fall back to the textarea hack
let textArea = document.createElement("textarea")
textArea.value = value
textArea.style.position = "fixed"
textArea.style.left = "-9999px"
textArea.style.top = "-9999px"
document.body.appendChild(textArea)
textArea.focus()
textArea.select()
document.execCommand("copy")
textArea.remove()
res()
}
})
}

View File

@ -53,10 +53,10 @@
to-gfm-code-block "^0.1.1" to-gfm-code-block "^0.1.1"
year "^0.2.1" year "^0.2.1"
"@budibase/string-templates@^1.0.104": "@budibase/string-templates@^1.0.105-alpha.4":
version "1.0.104" version "1.0.108"
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-1.0.104.tgz#f812700f2b21f638fd1e48dde065ae693fae2897" resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-1.0.108.tgz#14949560148ef11b6b385952ed5787c12b890f2c"
integrity sha512-3caq3qwpIieyb9m8eSl8OhcE0ppzuyJ/0ubDlWmtpbmwmG2v3ynI+DwxpbG4CcVQFuebD2yxU0CZfioU76vKCQ== integrity sha512-7Tts91Dzy+A7OdObTIaBNAdaixC7wmabnNTWYqk1d6TM6H5yv++bd/a9gVdUM7ptsuMy7uoP/ZoTZZRhp3ozfA==
dependencies: dependencies:
"@budibase/handlebars-helpers" "^0.11.8" "@budibase/handlebars-helpers" "^0.11.8"
dayjs "^1.10.4" dayjs "^1.10.4"

View File

@ -25,9 +25,13 @@ filterTests(['smoke', 'all'], () => {
cy.visit(`${Cypress.config().baseUrl}/builder`) cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.wait(500) cy.wait(500)
if (Cypress.env("TEST_ENV")) { cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
cy.get(".spectrum-Button").contains("Templates").click({force: true}) .its("body")
} .then(val => {
if (val.length > 0) {
cy.get(".spectrum-Button").contains("Templates").click({force: true})
}
})
cy.get(".template-category-filters").should("exist") cy.get(".template-category-filters").should("exist")
cy.get(".template-categories").should("exist") cy.get(".template-categories").should("exist")

View File

@ -55,13 +55,14 @@ filterTests(["smoke", "all"], () => {
if (Cypress.env("TEST_ENV")) { if (Cypress.env("TEST_ENV")) {
// No Pagination in CI - Test env only for the next two tests // No Pagination in CI - Test env only for the next two tests
it("Adds 15 rows and checks pagination", () => { xit("Adds 15 rows and checks pagination", () => {
// 10 rows per page, 15 rows should create 2 pages within table // 10 rows per page, 15 rows should create 2 pages within table
const totalRows = 16 const totalRows = 16
for (let i = 1; i < totalRows; i++) { for (let i = 1; i < totalRows; i++) {
cy.addRow([i]) cy.addRow([i])
} }
cy.wait(1000) cy.reload()
cy.wait(2000)
cy.get(".spectrum-Pagination").within(() => { cy.get(".spectrum-Pagination").within(() => {
cy.get(".spectrum-ActionButton").eq(1).click() cy.get(".spectrum-ActionButton").eq(1).click()
}) })
@ -70,13 +71,13 @@ filterTests(["smoke", "all"], () => {
}) })
}) })
it("Deletes rows and checks pagination", () => { xit("Deletes rows and checks pagination", () => {
// Delete rows, removing second page of rows from table // Delete rows, removing second page from table
const deleteRows = 5
cy.get(".spectrum-Checkbox-input").check({ force: true }) cy.get(".spectrum-Checkbox-input").check({ force: true })
cy.get(".spectrum-Table") cy.get(".popovers").within(() => {
cy.contains("Delete 5 row(s)").click() cy.get(".spectrum-Button").click({ force: true })
cy.get(".spectrum-Modal").contains("Delete").click() })
cy.get(".spectrum-Dialog-grid").contains("Delete").click({ force: true })
cy.wait(1000) cy.wait(1000)
// Confirm table only has one page // Confirm table only has one page

View File

@ -19,6 +19,7 @@ filterTests(["all"], () => {
cy.get(".spectrum-Button") cy.get(".spectrum-Button")
.contains("Save and fetch tables") .contains("Save and fetch tables")
.click({ force: true }) .click({ force: true })
cy.wait(500)
// Intercept Request after button click & apply assertions // Intercept Request after button click & apply assertions
cy.wait("@datasource") cy.wait("@datasource")
cy.get("@datasource") cy.get("@datasource")
@ -31,6 +32,7 @@ filterTests(["all"], () => {
cy.get("@datasource") cy.get("@datasource")
.its("response.body") .its("response.body")
.should("have.property", "status", 500) .should("have.property", "status", 500)
cy.get(".spectrum-Button").contains("Skip table fetch").click({ force: true })
}) })
it("should add MySQL data source and fetch tables", () => { it("should add MySQL data source and fetch tables", () => {
@ -72,10 +74,13 @@ filterTests(["all"], () => {
cy.get(".spectrum-Popover").contains("COUNTRIES").click() cy.get(".spectrum-Popover").contains("COUNTRIES").click()
cy.get(".spectrum-Picker").eq(4).click() cy.get(".spectrum-Picker").eq(4).click()
cy.get(".spectrum-Popover").contains("REGION_ID").click() cy.get(".spectrum-Popover").contains("REGION_ID").click()
// Save relationship & reload page
cy.get(".spectrum-Button").contains("Save").click({ force: true })
cy.reload()
}) })
// Save relationship & reload page
cy.get(".spectrum-ButtonGroup").within(() => {
cy.get(".spectrum-Button").contains("Save").click({ force: true })
})
cy.reload()
// Confirm table length & column name // Confirm table length & column name
cy.get(".spectrum-Table") cy.get(".spectrum-Table")
.eq(1) .eq(1)
@ -131,7 +136,7 @@ filterTests(["all"], () => {
cy.get(".spectrum-Table") cy.get(".spectrum-Table")
.eq(1) .eq(1)
.within(() => { .within(() => {
cy.get(".spectrum-Table-row").eq(0).click() cy.get(".spectrum-Table-row").eq(0).click({ force: true })
cy.wait(500) cy.wait(500)
}) })
cy.get(".spectrum-Dialog-grid").within(() => { cy.get(".spectrum-Dialog-grid").within(() => {
@ -175,11 +180,12 @@ filterTests(["all"], () => {
}) })
it("should duplicate a query", () => { it("should duplicate a query", () => {
// Get last nav item - The query /// Get query nav item - QueryName
cy.get(".nav-item") cy.get(".nav-item")
.last() .contains(queryName)
.parent()
.within(() => { .within(() => {
cy.get(".icon").eq(1).click({ force: true }) cy.get(".spectrum-Icon").eq(1).click({ force: true })
}) })
// Select and confirm duplication // Select and confirm duplication
cy.get(".spectrum-Menu").contains("Duplicate").click() cy.get(".spectrum-Menu").contains("Duplicate").click()
@ -199,23 +205,21 @@ filterTests(["all"], () => {
}) })
it("should delete a query", () => { it("should delete a query", () => {
// Get last nav item - The query // Get query nav item - QueryName
for (let i = 0; i < 2; i++) { cy.get(".nav-item")
cy.get(".nav-item") .contains(queryName)
.last() .parent()
.within(() => { .within(() => {
cy.get(".icon").eq(1).click({ force: true }) cy.get(".spectrum-Icon").eq(1).click({ force: true })
}) })
// Select Delete // Select Delete
cy.get(".spectrum-Menu").contains("Delete").click() cy.get(".spectrum-Menu").contains("Delete").click()
cy.get(".spectrum-Button") cy.get(".spectrum-Button")
.contains("Delete Query") .contains("Delete Query")
.click({ force: true }) .click({ force: true })
cy.wait(1000) cy.wait(1000)
}
// Confirm deletion // Confirm deletion
cy.get(".nav-item").should("not.contain", queryName) cy.get(".nav-item").should("not.contain", queryName)
cy.get(".nav-item").should("not.contain", queryRename)
}) })
} }
}) })

View File

@ -46,9 +46,10 @@ filterTests(["all"], () => {
cy.get("@datasource") cy.get("@datasource")
.its("response.body") .its("response.body")
.should("have.property", "status", 500) .should("have.property", "status", 500)
cy.get(".spectrum-Button").contains("Skip table fetch").click({ force: true })
}) })
it("should add Oracle data source and fetch tables", () => { xit("should add Oracle data source and fetch tables", () => {
// Add & configure Oracle data source // Add & configure Oracle data source
cy.selectExternalDatasource(datasource) cy.selectExternalDatasource(datasource)
cy.intercept("**/datasources").as("datasource") cy.intercept("**/datasources").as("datasource")
@ -64,7 +65,7 @@ filterTests(["all"], () => {
.should("be.gt", 0) .should("be.gt", 0)
}) })
it("should define a One relationship type", () => { xit("should define a One relationship type", () => {
// Select relationship type & configure // Select relationship type & configure
cy.get(".spectrum-Button") cy.get(".spectrum-Button")
.contains("Define relationship") .contains("Define relationship")
@ -93,7 +94,7 @@ filterTests(["all"], () => {
cy.get(".spectrum-Table-cell").should("contain", "COUNTRIES to REGIONS") cy.get(".spectrum-Table-cell").should("contain", "COUNTRIES to REGIONS")
}) })
it("should define a Many relationship type", () => { xit("should define a Many relationship type", () => {
// Select relationship type & configure // Select relationship type & configure
cy.get(".spectrum-Button") cy.get(".spectrum-Button")
.contains("Define relationship") .contains("Define relationship")
@ -127,7 +128,7 @@ filterTests(["all"], () => {
) )
}) })
it("should delete relationships", () => { xit("should delete relationships", () => {
// Delete both relationships // Delete both relationships
cy.get(".spectrum-Table") cy.get(".spectrum-Table")
.eq(1) .eq(1)
@ -156,7 +157,7 @@ filterTests(["all"], () => {
}) })
}) })
it("should add a query", () => { xit("should add a query", () => {
// Add query // Add query
cy.get(".spectrum-Button").contains("Add query").click({ force: true }) cy.get(".spectrum-Button").contains("Add query").click({ force: true })
cy.get(".spectrum-Form-item") cy.get(".spectrum-Form-item")
@ -181,7 +182,7 @@ filterTests(["all"], () => {
cy.get(".nav-item").should("contain", queryName) cy.get(".nav-item").should("contain", queryName)
}) })
it("should duplicate a query", () => { xit("should duplicate a query", () => {
// Get query nav item // Get query nav item
cy.get(".nav-item") cy.get(".nav-item")
.contains(queryName) .contains(queryName)
@ -194,7 +195,7 @@ filterTests(["all"], () => {
cy.get(".nav-item").should("contain", queryName + " (1)") cy.get(".nav-item").should("contain", queryName + " (1)")
}) })
it("should edit a query name", () => { xit("should edit a query name", () => {
// Rename query // Rename query
cy.get(".spectrum-Form-item") cy.get(".spectrum-Form-item")
.eq(0) .eq(0)
@ -206,7 +207,7 @@ filterTests(["all"], () => {
cy.get(".nav-item").should("contain", queryRename) cy.get(".nav-item").should("contain", queryRename)
}) })
it("should delete a query", () => { xit("should delete a query", () => {
// Get query nav item - QueryName // Get query nav item - QueryName
cy.get(".nav-item") cy.get(".nav-item")
.contains(queryName) .contains(queryName)

View File

@ -21,16 +21,10 @@ filterTests(["all"], () => {
.click({ force: true }) .click({ force: true })
// Intercept Request after button click & apply assertions // Intercept Request after button click & apply assertions
cy.wait("@datasource") cy.wait("@datasource")
cy.get("@datasource")
.its("response.body")
.should(
"have.property",
"message",
"connect ECONNREFUSED 127.0.0.1:5432"
)
cy.get("@datasource") cy.get("@datasource")
.its("response.body") .its("response.body")
.should("have.property", "status", 500) .should("have.property", "status", 500)
cy.get(".spectrum-Button").contains("Skip table fetch").click({ force: true })
}) })
it("should add PostgreSQL data source and fetch tables", () => { it("should add PostgreSQL data source and fetch tables", () => {
@ -113,13 +107,13 @@ filterTests(["all"], () => {
}) })
it("should delete a relationship", () => { it("should delete a relationship", () => {
cy.get(".hierarchy-items-container").contains(datasource).click() cy.get(".hierarchy-items-container").contains("PostgreSQL-2").click()
cy.reload() cy.reload()
// Delete one relationship // Delete one relationship
cy.get(".spectrum-Table") cy.get(".spectrum-Table")
.eq(1) .eq(1)
.within(() => { .within(() => {
cy.get(".spectrum-Table-row").eq(0).click() cy.get(".spectrum-Table-row").eq(0).click({ force: true })
cy.wait(500) cy.wait(500)
}) })
cy.get(".spectrum-Dialog-grid").within(() => { cy.get(".spectrum-Dialog-grid").within(() => {
@ -161,7 +155,7 @@ filterTests(["all"], () => {
it("should switch to schema with no tables", () => { it("should switch to schema with no tables", () => {
// Switch Schema - To one without any tables // Switch Schema - To one without any tables
cy.get(".hierarchy-items-container").contains(datasource).click() cy.get(".hierarchy-items-container").contains("PostgreSQL-2").click()
switchSchema("randomText") switchSchema("randomText")
// No tables displayed // No tables displayed
@ -208,11 +202,12 @@ filterTests(["all"], () => {
}) })
it("should duplicate a query", () => { it("should duplicate a query", () => {
// Get last nav item - The query // Locate previously created query
cy.get(".nav-item") cy.get(".nav-item")
.last() .contains(queryName)
.siblings(".actions")
.within(() => { .within(() => {
cy.get(".icon").eq(1).click({ force: true }) cy.get(".icon").click({ force: true })
}) })
// Select and confirm duplication // Select and confirm duplication
cy.get(".spectrum-Menu").contains("Duplicate").click() cy.get(".spectrum-Menu").contains("Duplicate").click()
@ -240,23 +235,21 @@ filterTests(["all"], () => {
}) })
it("should delete a query", () => { it("should delete a query", () => {
// Get last nav item - The query // Get query nav item - QueryName
for (let i = 0; i < 2; i++) { cy.get(".nav-item")
cy.get(".nav-item") .contains(queryName)
.last() .parent()
.within(() => { .within(() => {
cy.get(".icon").eq(1).click({ force: true }) cy.get(".spectrum-Icon").eq(1).click({ force: true })
}) })
// Select Delete // Select Delete
cy.get(".spectrum-Menu").contains("Delete").click() cy.get(".spectrum-Menu").contains("Delete").click()
cy.get(".spectrum-Button") cy.get(".spectrum-Button")
.contains("Delete Query") .contains("Delete Query")
.click({ force: true }) .click({ force: true })
cy.wait(1000) cy.wait(1000)
}
// Confirm deletion // Confirm deletion
cy.get(".nav-item").should("not.contain", queryName) cy.get(".nav-item").should("not.contain", queryName)
cy.get(".nav-item").should("not.contain", queryRename)
}) })
const switchSchema = schema => { const switchSchema = schema => {

View File

@ -60,43 +60,48 @@ Cypress.Commands.add("deleteApp", name => {
cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`) cy.request(`${Cypress.config().baseUrl}/api/applications?status=all`)
.its("body") .its("body")
.then(val => { .then(val => {
if (val.length > 0) { const findAppName = val.some(val => val.name == name)
if (Cypress.env("TEST_ENV")) { if (findAppName) {
cy.searchForApplication(name) if (val.length > 0) {
cy.get(".appTable").within(() => { if (Cypress.env("TEST_ENV")) {
cy.get(".spectrum-Icon").eq(1).click() cy.searchForApplication(name)
cy.get(".appTable").within(() => {
cy.get(".spectrum-Icon").eq(1).click()
})
} else {
const appId = val.reduce((acc, app) => {
if (name === app.name) {
acc = app.appId
}
return acc
}, "")
if (appId == "") {
return
}
const appIdParsed = appId.split("_").pop()
const actionEleId = `[data-cy=row_actions_${appIdParsed}]`
cy.get(actionEleId).within(() => {
cy.get(".spectrum-Icon").eq(0).click()
})
}
cy.get(".spectrum-Menu").then($menu => {
if ($menu.text().includes("Unpublish")) {
cy.get(".spectrum-Menu").contains("Unpublish").click()
cy.get(".spectrum-Dialog-grid").contains("Unpublish app").click()
} else {
cy.get(".spectrum-Menu").contains("Delete").click()
cy.get(".spectrum-Dialog-grid").within(() => {
cy.get("input").type(name)
})
cy.get(".spectrum-Button--warning").click()
}
}) })
} else { } else {
const appId = val.reduce((acc, app) => { return
if (name === app.name) {
acc = app.appId
}
return acc
}, "")
if (appId == "") {
return
}
const appIdParsed = appId.split("_").pop()
const actionEleId = `[data-cy=row_actions_${appIdParsed}]`
cy.get(actionEleId).within(() => {
cy.get(".spectrum-Icon").eq(0).click()
})
} }
cy.get(".spectrum-Menu").then($menu => {
if ($menu.text().includes("Unpublish")) {
cy.get(".spectrum-Menu").contains("Unpublish").click()
cy.get(".spectrum-Dialog-grid").contains("Unpublish app").click()
} else {
cy.get(".spectrum-Menu").contains("Delete").click()
cy.get(".spectrum-Dialog-grid").within(() => {
cy.get("input").type(name)
})
cy.get(".spectrum-Button--warning").click()
}
})
} else { } else {
return return
} }
@ -410,7 +415,9 @@ Cypress.Commands.add("addDatasourceConfig", (datasource, skipFetch) => {
if (datasource == "Oracle") { if (datasource == "Oracle") {
cy.get("input").clear().type(Cypress.env("oracle").HOST) cy.get("input").clear().type(Cypress.env("oracle").HOST)
} else { } else {
cy.get("input").clear().type(Cypress.env("HOST_IP")) cy.get("input")
.clear({ force: true })
.type(Cypress.env("mysql").HOST, { force: true })
} }
}) })
}) })

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/builder", "name": "@budibase/builder",
"version": "1.0.105-alpha.9", "version": "1.0.105-alpha.19",
"license": "GPL-3.0", "license": "GPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {
@ -65,10 +65,10 @@
} }
}, },
"dependencies": { "dependencies": {
"@budibase/bbui": "^1.0.105-alpha.9", "@budibase/bbui": "^1.0.105-alpha.19",
"@budibase/client": "^1.0.105-alpha.9", "@budibase/client": "^1.0.105-alpha.19",
"@budibase/frontend-core": "^1.0.105-alpha.9", "@budibase/frontend-core": "^1.0.105-alpha.19",
"@budibase/string-templates": "^1.0.105-alpha.9", "@budibase/string-templates": "^1.0.105-alpha.19",
"@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

@ -127,6 +127,8 @@
outputs.map(([name, value]) => { outputs.map(([name, value]) => {
let runtimeName = isLoopBlock let runtimeName = isLoopBlock
? `loop.${name}` ? `loop.${name}`
: block.name.startsWith("JS")
? `steps[${idx}].${name}`
: `steps.${idx}.${name}` : `steps.${idx}.${name}`
const runtime = idx === 0 ? `trigger.${name}` : runtimeName const runtime = idx === 0 ? `trigger.${name}` : runtimeName
return { return {

View File

@ -52,7 +52,6 @@
.map(query => ({ .map(query => ({
label: query.name, label: query.name,
name: query.name, name: query.name,
tableId: query._id,
...query, ...query,
type: "query", type: "query",
})) }))

View File

@ -10,9 +10,15 @@
let drawer let drawer
let tempValue = value || [] let tempValue = value || []
const saveFilter = async () => { const saveOptions = async () => {
// Filter out incomplete options // Filter out incomplete options, default if needed
tempValue = tempValue.filter(option => option.value && option.label) tempValue = tempValue.filter(option => option.value || option.label)
for (let i = 0; i < tempValue.length; i++) {
let option = tempValue[i]
option.label = option.label ? option.label : option.value
option.value = option.value ? option.value : option.label
tempValue[i] = option
}
dispatch("change", tempValue) dispatch("change", tempValue)
drawer.hide() drawer.hide()
} }
@ -23,6 +29,6 @@
<svelte:fragment slot="description"> <svelte:fragment slot="description">
Define the options for this picker. Define the options for this picker.
</svelte:fragment> </svelte:fragment>
<Button cta slot="buttons" on:click={saveFilter}>Save</Button> <Button cta slot="buttons" on:click={saveOptions}>Save</Button>
<OptionsDrawer bind:options={tempValue} slot="body" /> <OptionsDrawer bind:options={tempValue} slot="body" />
</Drawer> </Drawer>

View File

@ -15,16 +15,14 @@
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
$: datasource = getDatasourceForProvider($currentAsset, componentInstance) $: datasource = getDatasourceForProvider($currentAsset, componentInstance)
$: schema = getSchemaForDatasource($currentAsset, datasource, { $: schema = getSchemaForDatasource($currentAsset, datasource).schema
searchableSchema: true,
}).schema
$: options = getOptions(datasource, schema || {}) $: options = getOptions(datasource, schema || {})
$: boundValue = getSelectedOption(value, options) $: boundValue = getSelectedOption(value, options)
function getOptions(ds, dsSchema) { function getOptions(ds, dsSchema) {
let base = Object.values(dsSchema) let base = Object.values(dsSchema)
if (!ds?.tableId) { if (!ds?.tableId) {
return base return base.map(field => field.name)
} }
const currentTable = $tables.list.find(table => table._id === ds.tableId) const currentTable = $tables.list.find(table => table._id === ds.tableId)
return getFields(base, { allowLinks: currentTable?.sql }).map( return getFields(base, { allowLinks: currentTable?.sql }).map(

View File

@ -1,6 +1,6 @@
{ {
"name": "@budibase/cli", "name": "@budibase/cli",
"version": "1.0.105-alpha.9", "version": "1.0.105-alpha.19",
"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.105-alpha.9", "version": "1.0.105-alpha.19",
"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.105-alpha.9", "@budibase/bbui": "^1.0.105-alpha.19",
"@budibase/frontend-core": "^1.0.105-alpha.9", "@budibase/frontend-core": "^1.0.105-alpha.19",
"@budibase/string-templates": "^1.0.105-alpha.9", "@budibase/string-templates": "^1.0.105-alpha.19",
"@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

@ -1,5 +1,5 @@
import { createAPIClient } from "@budibase/frontend-core" import { createAPIClient } from "@budibase/frontend-core"
import { notificationStore, authStore } from "../stores" import { notificationStore, authStore, devToolsStore } from "../stores"
import { get } from "svelte/store" import { get } from "svelte/store"
export const API = createAPIClient({ export const API = createAPIClient({
@ -21,6 +21,12 @@ export const API = createAPIClient({
if (auth?.csrfToken) { if (auth?.csrfToken) {
headers["x-csrf-token"] = auth.csrfToken headers["x-csrf-token"] = auth.csrfToken
} }
// Add role header
const role = get(devToolsStore).role
if (role) {
headers["x-budibase-role"] = role
}
}, },
// Show an error notification for all API failures. // Show an error notification for all API failures.

View File

@ -14,6 +14,8 @@
routeStore, routeStore,
builderStore, builderStore,
themeStore, themeStore,
appStore,
devToolsStore,
} from "stores" } from "stores"
import NotificationDisplay from "components/overlay/NotificationDisplay.svelte" import NotificationDisplay from "components/overlay/NotificationDisplay.svelte"
import ConfirmationDisplay from "components/overlay/ConfirmationDisplay.svelte" import ConfirmationDisplay from "components/overlay/ConfirmationDisplay.svelte"
@ -28,6 +30,8 @@
import CustomThemeWrapper from "./CustomThemeWrapper.svelte" import CustomThemeWrapper from "./CustomThemeWrapper.svelte"
import DNDHandler from "components/preview/DNDHandler.svelte" import DNDHandler from "components/preview/DNDHandler.svelte"
import KeyboardManager from "components/preview/KeyboardManager.svelte" import KeyboardManager from "components/preview/KeyboardManager.svelte"
import DevToolsHeader from "components/devtools/DevToolsHeader.svelte"
import DevTools from "components/devtools/DevTools.svelte"
// Provide contexts // Provide contexts
setContext("sdk", SDK) setContext("sdk", SDK)
@ -55,8 +59,22 @@
if ($authStore) { if ($authStore) {
// There is a logged in user, so handle them // There is a logged in user, so handle them
if ($screenStore.screens.length) { if ($screenStore.screens.length) {
let firstRoute
// If using devtools, find the first screen matching our role
if ($devToolsStore.role) {
const roleRoutes = $screenStore.screens.filter(
screen => screen.routing?.roleId === $devToolsStore.role
)
firstRoute = roleRoutes[0]?.routing?.route || "/"
}
// Otherwise just use the first route
else {
firstRoute = $screenStore.screens[0]?.routing?.route ?? "/"
}
// Screens exist so navigate back to the home screen // Screens exist so navigate back to the home screen
const firstRoute = $screenStore.screens[0].routing?.route ?? "/"
routeStore.actions.navigate(firstRoute) routeStore.actions.navigate(firstRoute)
} else { } else {
// No screens likely means the user has no permissions to view this app // No screens likely means the user has no permissions to view this app
@ -70,6 +88,8 @@
} }
} }
} }
$: isDevPreview = $appStore.isDevApp && !$builderStore.inBuilder
</script> </script>
{#if dataLoaded} {#if dataLoaded}
@ -109,39 +129,49 @@
> >
<!-- Actual app --> <!-- Actual app -->
<div id="app-root"> <div id="app-root">
<CustomThemeWrapper> {#if isDevPreview}
{#key `${$screenStore.activeLayout._id}-${$builderStore.previewType}`} <DevToolsHeader />
<Component {/if}
isLayout
instance={$screenStore.activeLayout.props}
/>
{/key}
<!-- <div id="app-body">
Flatpickr needs to be inside the theme wrapper. <CustomThemeWrapper>
It also needs its own container because otherwise it hijacks {#key `${$screenStore.activeLayout._id}-${$builderStore.previewType}`}
key events on the whole page. It is painful to work with. <Component
--> isLayout
<div id="flatpickr-root" /> instance={$screenStore.activeLayout.props}
/>
{/key}
<!-- Modal container to ensure they sit on top --> <!--
<div class="modal-container" /> 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" />
<!-- Layers on top of app --> <!-- Modal container to ensure they sit on top -->
<NotificationDisplay /> <div class="modal-container" />
<ConfirmationDisplay />
<PeekScreenDisplay /> <!-- Layers on top of app -->
</CustomThemeWrapper> <NotificationDisplay />
<ConfirmationDisplay />
<PeekScreenDisplay />
</CustomThemeWrapper>
{#if $appStore.isDevApp && !$builderStore.inBuilder}
<DevTools />
{/if}
</div>
</div> </div>
<!-- Selection indicators should be bounded by device --> <!-- Preview and dev tools utilities -->
<!-- {#if $appStore.isDevApp}
We don't want to key these by componentID as they control their own
re-mounting to avoid flashes.
-->
{#if $builderStore.inBuilder}
<SelectionIndicator /> <SelectionIndicator />
{/if}
{#if $builderStore.inBuilder || $devToolsStore.allowSelection}
<HoverIndicator /> <HoverIndicator />
{/if}
{#if $builderStore.inBuilder}
<DNDHandler /> <DNDHandler />
{/if} {/if}
</div> </div>
@ -167,6 +197,7 @@
justify-content: center; justify-content: center;
align-items: center; align-items: center;
} }
#clip-root { #clip-root {
max-width: 100%; max-width: 100%;
max-height: 100%; max-height: 100%;
@ -176,10 +207,24 @@
overflow: hidden; overflow: hidden;
background-color: transparent; background-color: transparent;
} }
#app-root { #app-root {
overflow: hidden; overflow: hidden;
height: 100%; height: 100%;
width: 100%; width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
align-items: stretch;
}
#app-body {
flex: 1 1 auto;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: stretch;
overflow: hidden;
} }
.error { .error {
@ -192,19 +237,23 @@
text-align: center; text-align: center;
padding: 20px; padding: 20px;
} }
.error :global(svg) { .error :global(svg) {
fill: var(--spectrum-global-color-gray-500); fill: var(--spectrum-global-color-gray-500);
width: 80px; width: 80px;
height: 80px; height: 80px;
} }
.error :global(h1), .error :global(h1),
.error :global(p) { .error :global(p) {
color: var(--spectrum-global-color-gray-800); color: var(--spectrum-global-color-gray-800);
} }
.error :global(p) { .error :global(p) {
font-style: italic; font-style: italic;
margin-top: -0.5em; margin-top: -0.5em;
} }
.error :global(h1) { .error :global(h1) {
font-weight: 400; font-weight: 400;
} }
@ -214,14 +263,17 @@
#clip-root.preview { #clip-root.preview {
padding: 2px; padding: 2px;
} }
#clip-root.tablet-preview { #clip-root.tablet-preview {
width: calc(1024px + 6px); width: calc(1024px + 6px);
height: calc(768px + 6px); height: calc(768px + 6px);
} }
#clip-root.mobile-preview { #clip-root.mobile-preview {
width: calc(390px + 6px); width: calc(390px + 6px);
height: calc(844px + 6px); height: calc(844px + 6px);
} }
.preview #app-root { .preview #app-root {
border: 1px solid var(--spectrum-global-color-gray-300); border: 1px solid var(--spectrum-global-color-gray-300);
border-radius: 4px; border-radius: 4px;

View File

@ -9,12 +9,16 @@
</script> </script>
<script> <script>
import { getContext, setContext } from "svelte" import { getContext, setContext, onMount, onDestroy } from "svelte"
import { writable, get } from "svelte/store" import { writable, get } from "svelte/store"
import * as AppComponents from "components/app" import * as AppComponents from "components/app"
import Router from "./Router.svelte" import Router from "./Router.svelte"
import { enrichProps, propsAreSame } from "utils/componentProps" import {
import { builderStore } from "stores" enrichProps,
propsAreSame,
getSettingsDefinition,
} from "utils/componentProps"
import { builderStore, devToolsStore, componentStore, appStore } from "stores"
import { Helpers } from "@budibase/bbui" import { Helpers } from "@budibase/bbui"
import Manifest from "manifest.json" import Manifest from "manifest.json"
import { getActiveConditions, reduceConditionActions } from "utils/conditions" import { getActiveConditions, reduceConditionActions } from "utils/conditions"
@ -30,8 +34,8 @@
const insideScreenslot = !!getContext("screenslot") const insideScreenslot = !!getContext("screenslot")
// Create component context // Create component context
const componentStore = writable({}) const store = writable({})
setContext("component", componentStore) setContext("component", store)
// Ref to the svelte component // Ref to the svelte component
let ref let ref
@ -90,7 +94,7 @@
// leading to the selected component // leading to the selected component
$: selected = $: selected =
$builderStore.inBuilder && $builderStore.selectedComponentId === id $builderStore.inBuilder && $builderStore.selectedComponentId === id
$: inSelectedPath = $builderStore.selectedComponentPath?.includes(id) $: inSelectedPath = $componentStore.selectedComponentPath?.includes(id)
$: inDragPath = inSelectedPath && $builderStore.editMode $: inDragPath = inSelectedPath && $builderStore.editMode
// Derive definition properties which can all be optional, so need to be // Derive definition properties which can all be optional, so need to be
@ -101,10 +105,12 @@
// Interactive components can be selected, dragged and highlighted inside // Interactive components can be selected, dragged and highlighted inside
// the builder preview // the builder preview
$: interactive = $: builderInteractive =
$builderStore.inBuilder && $builderStore.inBuilder &&
($builderStore.previewType === "layout" || insideScreenslot) && ($builderStore.previewType === "layout" || insideScreenslot) &&
!isBlock !isBlock
$: devToolsInteractive = $devToolsStore.allowSelection && !isBlock
$: interactive = builderInteractive || devToolsInteractive
$: editing = editable && selected && $builderStore.editMode $: editing = editable && selected && $builderStore.editMode
$: draggable = $: draggable =
!inDragPath && !inDragPath &&
@ -133,7 +139,7 @@
$: applySettings(staticSettings, enrichedSettings, conditionalSettings) $: applySettings(staticSettings, enrichedSettings, conditionalSettings)
// Update component context // Update component context
$: componentStore.set({ $: store.set({
id, id,
children: children.length, children: children.length,
styles: { styles: {
@ -217,22 +223,6 @@
return type ? Manifest[type] : null return type ? Manifest[type] : null
} }
// Gets the definition of this component's settings from the manifest
const getSettingsDefinition = definition => {
if (!definition) {
return []
}
let settings = []
definition.settings?.forEach(setting => {
if (setting.section) {
settings = settings.concat(setting.settings || [])
} else {
settings.push(setting)
}
})
return settings
}
const getSettingsDefinitionMap = settingsDefinition => { const getSettingsDefinitionMap = settingsDefinition => {
let map = {} let map = {}
settingsDefinition?.forEach(setting => { settingsDefinition?.forEach(setting => {
@ -385,6 +375,28 @@
}) })
} }
} }
onMount(() => {
if (
$appStore.isDevApp &&
!componentStore.actions.isComponentRegistered(id)
) {
componentStore.actions.registerInstance(id, {
getSettings: () => cachedSettings,
getRawSettings: () => ({ ...staticSettings, ...dynamicSettings }),
getDataContext: () => get(context),
})
}
})
onDestroy(() => {
if (
$appStore.isDevApp &&
componentStore.actions.isComponentRegistered(id)
) {
componentStore.actions.unregisterInstance(id)
}
})
</script> </script>
{#if constructor && initialSettings && (visible || inSelectedPath)} {#if constructor && initialSettings && (visible || inSelectedPath)}
@ -419,12 +431,15 @@
.component { .component {
display: contents; display: contents;
} }
.interactive :global(*:hover) { .interactive :global(*:hover) {
cursor: pointer; cursor: pointer;
} }
.draggable :global(*:hover) { .draggable :global(*:hover) {
cursor: grab; cursor: grab;
} }
.editing :global(*:hover) { .editing :global(*:hover) {
cursor: auto; cursor: auto;
} }

View File

@ -179,6 +179,7 @@
justify-content: flex-start; justify-content: flex-start;
align-items: stretch; align-items: stretch;
height: 100%; height: 100%;
flex: 1 1 auto;
overflow: auto; overflow: auto;
overflow-x: hidden; overflow-x: hidden;
position: relative; position: relative;

View File

@ -80,7 +80,7 @@
} }
const fetchTable = async dataSource => { const fetchTable = async dataSource => {
if (dataSource?.tableId) { if (dataSource?.tableId && dataSource?.type !== "query") {
try { try {
table = await API.fetchTableDefinition(dataSource.tableId) table = await API.fetchTableDefinition(dataSource.tableId)
} catch (error) { } catch (error) {

View File

@ -5,7 +5,7 @@
export let step = 1 export let step = 1
const { styleable, builderStore } = getContext("sdk") const { styleable, builderStore, componentStore } = getContext("sdk")
const component = getContext("component") const component = getContext("component")
const formContext = getContext("form") const formContext = getContext("form")
@ -22,7 +22,7 @@
if ( if (
formContext && formContext &&
$builderStore.inBuilder && $builderStore.inBuilder &&
$builderStore.selectedComponentPath?.includes($component.id) $componentStore.selectedComponentPath?.includes($component.id)
) { ) {
formContext.formApi.setStep(step) formContext.formApi.setStep(step)
} }

View File

@ -1,6 +1,6 @@
<script> <script>
import Provider from "./Provider.svelte" import Provider from "./Provider.svelte"
import { authStore } from "stores" import { authStore, devToolsStore } from "stores"
import { ActionTypes } from "constants" import { ActionTypes } from "constants"
import { Constants } from "@budibase/frontend-core" import { Constants } from "@budibase/frontend-core"
@ -17,6 +17,10 @@
] ]
</script> </script>
<Provider key="user" data={$authStore} {actions}> <Provider
key="user"
data={{ ...$authStore, roleId: $devToolsStore.role || $authStore?.roleId }}
{actions}
>
<slot /> <slot />
</Provider> </Provider>

View File

@ -0,0 +1,69 @@
<script>
import { getContext } from "svelte"
import { Layout, Heading, Tabs, Tab, Icon } from "@budibase/bbui"
import DevToolsStatsTab from "./DevToolsStatsTab.svelte"
import DevToolsComponentTab from "./DevToolsComponentTab.svelte"
import { devToolsStore } from "stores"
const context = getContext("context")
</script>
<div
class="devtools"
class:hidden={!$devToolsStore.visible}
class:mobile={$context.device.mobile}
>
{#if $devToolsStore.visible}
<Layout noPadding gap="XS">
<div class="header">
<Heading size="XS">Budibase DevTools</Heading>
<Icon
hoverable
name="Close"
on:click={() => devToolsStore.actions.setVisible(false)}
/>
</div>
<Tabs selected="Application">
<Tab title="Application">
<div class="tab-content">
<DevToolsStatsTab />
</div>
</Tab>
<Tab title="Components">
<div class="tab-content">
<DevToolsComponentTab />
</div>
</Tab>
</Tabs>
</Layout>
{/if}
</div>
<style>
.devtools {
background: var(--spectrum-alias-background-color-primary);
flex: 0 0 320px;
border-left: 1px solid var(--spectrum-global-color-gray-300);
overflow: auto;
transition: margin-right 300ms ease;
margin-right: 0;
}
.devtools.hidden {
margin-right: -320px;
}
.devtools.mobile {
display: none;
}
.header {
padding: var(--spacing-xl) var(--spacing-xl) 0 var(--spacing-xl);
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.tab-content {
padding: 0 var(--spacing-xl);
}
</style>

View File

@ -0,0 +1,113 @@
<script>
import { Layout, Select, Body } from "@budibase/bbui"
import { componentStore } from "stores/index.js"
import DevToolsStat from "./DevToolsStat.svelte"
const ReadableBindingMap = {
user: "Current user",
state: "State",
url: "URL",
device: "Device",
rowSelection: "Selected rows",
}
let category
$: selectedInstance = $componentStore.selectedComponentInstance
$: context = selectedInstance?.getDataContext()
$: bindingCategories = getContextProviders(context)
$: bindings = Object.entries(context?.[category] || {})
const getContextProviders = context => {
const filteredContext = { ...context }
// Remove some keys from context
delete filteredContext.key
delete filteredContext.closestComponentId
delete filteredContext.user_RefreshDataSource
// Keep track of encountered IDs so we can find actions
let actions = []
let encounteredCategories = []
// Create readable bindings
let categories = []
Object.keys(filteredContext)
.sort()
.forEach(category => {
let isAction = false
for (let cat of encounteredCategories) {
if (category.startsWith(`${cat}_`)) {
isAction = true
break
}
}
if (isAction) {
actions.push(category)
return
}
// Mark category as encountered so we can find any matching actions
encounteredCategories.push(category)
// Map any static categories to pretty names
if (ReadableBindingMap[category]) {
categories.push({
label: ReadableBindingMap[category],
value: category,
})
} else {
const component = componentStore.actions.getComponentById(category)
if (component) {
categories.push({
label: component._instanceName,
value: category,
})
} else {
// Check if its a block
if (category.includes("-")) {
const split = category.split("-")
const potentialId = split[0]
const component =
componentStore.actions.getComponentById(potentialId)
if (component) {
categories.push({
label: `${component._instanceName} (${split[1]})`,
value: category,
})
return
}
}
// Otherwise we don't know
categories.push({
label: "Unknown - " + category,
value: category,
})
}
}
})
return categories
}
</script>
<Layout noPadding gap="S">
<Body size="S">
Choose a category to see the value of all its available bindings.
</Body>
<Select bind:value={category} label="Category" options={bindingCategories} />
{#if bindings?.length}
<Layout noPadding gap="XS">
{#each bindings as binding}
<DevToolsStat
copyable
label={binding[0]}
value={JSON.stringify(binding[1])}
/>
{/each}
</Layout>
{:else if category}
<Body size="XS">There aren't any bindings available in this category.</Body>
{/if}
</Layout>

View File

@ -0,0 +1,13 @@
<script>
import DevToolsStat from "./DevToolsStat.svelte"
export let name
export let value
export let settingsMap
$: prettyName = settingsMap?.[name]?.label
</script>
{#if prettyName}
<DevToolsStat label={prettyName} value={JSON.stringify(value)} />
{/if}

View File

@ -0,0 +1,30 @@
<script>
import { Layout, Toggle } from "@budibase/bbui"
import DevToolsStat from "./DevToolsStat.svelte"
import { componentStore } from "stores/index.js"
import { getSettingsDefinition } from "utils/componentProps.js"
let showEnrichedSettings = true
$: selectedInstance = $componentStore.selectedComponentInstance
$: settingsDefinition = getSettingsDefinition(
$componentStore.selectedComponentDefinition
)
$: rawSettings = selectedInstance?.getRawSettings()
$: settings = selectedInstance?.getSettings()
</script>
<Layout noPadding gap="S">
<Toggle text="Show enriched settings" bind:value={showEnrichedSettings} />
<Layout noPadding gap="XS">
{#each settingsDefinition as setting}
<DevToolsStat
copyable
label={setting.label}
value={JSON.stringify(
(showEnrichedSettings ? settings : rawSettings)?.[setting.key]
)}
/>
{/each}
</Layout>
</Layout>

View File

@ -0,0 +1,102 @@
<script>
import { Body, Layout, Heading, Button, Tabs, Tab } from "@budibase/bbui"
import { builderStore, devToolsStore, componentStore } from "stores"
import DevToolsStat from "./DevToolsStat.svelte"
import DevToolsComponentSettingsTab from "./DevToolsComponentSettingsTab.svelte"
import DevToolsComponentContextTab from "./DevToolsComponentContextTab.svelte"
$: {
// Reset selection store if we can't find a matching instance
if (!$componentStore.selectedComponentInstance) {
builderStore.actions.selectComponent(null)
}
}
</script>
{#if !$builderStore.selectedComponentId}
<Layout noPadding gap="S">
<Heading size="XS">Please choose a component</Heading>
<Body size="S">
Press the button below to enable component selection, then click a
component in your app to view its settings and available data bindings.
</Body>
<div>
<Button
cta
on:click={() => devToolsStore.actions.setAllowSelection(true)}
>
Choose component
</Button>
</div>
</Layout>
{:else}
<Layout noPadding gap="S">
<Heading size="XS">
{$componentStore.selectedComponent?._instanceName}
</Heading>
<Layout noPadding gap="XS">
<DevToolsStat
label="Type"
value={$componentStore.selectedComponentDefinition?.name}
/>
<DevToolsStat
copyable
label="Component ID"
value={$componentStore.selectedComponent?._id}
/>
</Layout>
<div class="buttons">
<Button
cta
on:click={() => devToolsStore.actions.setAllowSelection(true)}
>
Change component
</Button>
<Button
quiet
secondary
on:click={() => builderStore.actions.selectComponent(null)}
>
Reset
</Button>
</div>
<div class="data">
<Layout noPadding gap="XS">
<Tabs selected="Settings">
<Tab title="Settings">
<div class="tab-content">
<DevToolsComponentSettingsTab />
</div>
</Tab>
<Tab title="Bindings">
<div class="tab-content">
<DevToolsComponentContextTab />
</div>
</Tab>
</Tabs>
</Layout>
</div>
</Layout>
{/if}
<style>
.buttons {
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
gap: var(--spacing-xs);
}
.data {
margin: 0 calc(-1 * var(--spacing-xl));
}
.data :global(.spectrum-Textfield-input) {
min-height: 200px !important;
white-space: pre;
font-size: var(--font-size-s);
}
.tab-content {
padding: 0 var(--spacing-xl);
}
</style>

View File

@ -0,0 +1,74 @@
<script>
import { Heading, Button, Select } from "@budibase/bbui"
import { devToolsStore } from "../../stores"
import { getContext } from "svelte"
const context = getContext("context")
$: previewOptions = [
{
label: "View as yourself",
value: "self",
},
{
label: "View as public user",
value: "PUBLIC",
},
{
label: "View as basic user",
value: "BASIC",
},
{
label: "View as power user",
value: "POWER",
},
{
label: "View as admin user",
value: "ADMIN",
},
]
</script>
<div class="dev-preview-header" class:mobile={$context.device.mobile}>
<Heading size="XS">Budibase App Preview</Heading>
<Select
quiet
options={previewOptions}
value={$devToolsStore.role || "self"}
placeholder={null}
autoWidth
on:change={e => devToolsStore.actions.changeRole(e.detail)}
/>
{#if !$context.device.mobile}
<Button
quiet
overBackground
icon="Code"
on:click={() => devToolsStore.actions.setVisible(!$devToolsStore.visible)}
>
{$devToolsStore.visible ? "Close" : "Open"} DevTools
</Button>
{/if}
</div>
<style>
.dev-preview-header {
flex: 0 0 50px;
height: 50px;
display: grid;
align-items: center;
background-color: var(--spectrum-global-color-blue-400);
padding: 0 var(--spacing-xl);
grid-template-columns: 1fr auto auto;
grid-gap: var(--spacing-xl);
}
.dev-preview-header.mobile {
flex: 0 0 50px;
grid-template-columns: 1fr auto;
}
.dev-preview-header :global(.spectrum-Heading),
.dev-preview-header :global(.spectrum-Picker-menuIcon),
.dev-preview-header :global(.spectrum-Picker-label) {
color: white !important;
}
</style>

View File

@ -0,0 +1,75 @@
<script>
import { Helpers } from "@budibase/bbui"
import { notificationStore } from "stores"
export let label
export let value
export let copyable = false
$: prettyLabel = label == null ? "-" : label
$: prettyValue = value == null ? "-" : value
$: empty = value == null
$: canCopy = copyable && !empty
const copyValue = async () => {
try {
await Helpers.copyToClipboard(value)
notificationStore.actions.success("Copied to clipboard")
} catch (error) {
notificationStore.actions.error(
"Failed to copy to clipboard. Check the dev console for the value."
)
console.warn("Failed to copy the value", value)
}
}
</script>
<div class="stat">
<div class="stat-label" title={prettyLabel}>{prettyLabel}</div>
<div
class="stat-value"
class:copyable={canCopy}
class:empty
title={prettyValue}
on:click={canCopy ? copyValue : null}
>
{prettyValue}
</div>
</div>
<style>
.stat {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
gap: var(--spacing-xl);
}
.stat-label {
font-size: var(--font-size-xs);
color: var(--spectrum-global-color-gray-600);
text-transform: uppercase;
flex: 0 0 auto;
width: 120px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.stat-value {
flex: 1 1 auto;
width: 0;
text-align: right;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
transition: color var(--spectrum-global-animation-duration-100, 130ms)
ease-in-out;
}
.stat-value.empty {
color: var(--spectrum-global-color-gray-500);
}
.stat-value.copyable:hover {
color: var(--spectrum-global-color-blue-600);
cursor: pointer;
}
</style>

View File

@ -0,0 +1,27 @@
<script>
import { Layout } from "@budibase/bbui"
import { authStore, appStore, screenStore, componentStore } from "stores"
import DevToolsStat from "./DevToolsStat.svelte"
</script>
<Layout noPadding gap="XS">
<DevToolsStat label="App" value={$appStore.application?.name} />
<DevToolsStat label="Tenant" value={$appStore.application?.tenantId} />
<DevToolsStat label="Version" value={$appStore.application?.version} />
{#if $appStore.clientLoadTime}
<DevToolsStat
label="Client load time"
value={`${$appStore.clientLoadTime} ms`}
/>
{/if}
<DevToolsStat label="App layouts" value={$screenStore.layouts?.length || 0} />
<DevToolsStat label="Active layout" value={$screenStore.activeLayout?.name} />
<DevToolsStat label="App screens" value={$screenStore.screens?.length || 0} />
<DevToolsStat
label="Active screen"
value={$screenStore.activeScreen?.routing.route}
/>
<DevToolsStat label="Components" value={$componentStore.mountedComponents} />
<DevToolsStat label="User" value={$authStore.email} />
<DevToolsStat label="Role" value={$authStore.roleId} />
</Layout>

View File

@ -2,6 +2,7 @@
import { onMount, onDestroy } from "svelte" import { onMount, onDestroy } from "svelte"
import Indicator from "./Indicator.svelte" import Indicator from "./Indicator.svelte"
import { domDebounce } from "utils/domDebounce" import { domDebounce } from "utils/domDebounce"
import { builderStore } from "stores"
export let componentId export let componentId
export let color export let color
@ -13,6 +14,7 @@
let interval let interval
let text let text
$: visibleIndicators = indicators.filter(x => x.visible) $: visibleIndicators = indicators.filter(x => x.visible)
$: offset = $builderStore.inBuilder ? 0 : 2
let updating = false let updating = false
let observers = [] let observers = []
@ -88,8 +90,8 @@
const elBounds = child.getBoundingClientRect() const elBounds = child.getBoundingClientRect()
nextIndicators.push({ nextIndicators.push({
top: elBounds.top + scrollY - deviceBounds.top, top: elBounds.top + scrollY - deviceBounds.top - offset,
left: elBounds.left + scrollX - deviceBounds.left, left: elBounds.left + scrollX - deviceBounds.left - offset,
width: elBounds.width + 4, width: elBounds.width + 4,
height: elBounds.height + 4, height: elBounds.height + 4,
visible: false, visible: false,

View File

@ -3,7 +3,7 @@
import SettingsButton from "./SettingsButton.svelte" import SettingsButton from "./SettingsButton.svelte"
import SettingsColorPicker from "./SettingsColorPicker.svelte" import SettingsColorPicker from "./SettingsColorPicker.svelte"
import SettingsPicker from "./SettingsPicker.svelte" import SettingsPicker from "./SettingsPicker.svelte"
import { builderStore } from "stores" import { builderStore, componentStore } from "stores"
import { domDebounce } from "utils/domDebounce" import { domDebounce } from "utils/domDebounce"
const verticalOffset = 28 const verticalOffset = 28
@ -15,7 +15,7 @@
let self let self
let measured = false let measured = false
$: definition = $builderStore.selectedComponentDefinition $: definition = $componentStore.selectedComponentDefinition
$: showBar = definition?.showSettingsBar && !$builderStore.isDragging $: showBar = definition?.showSettingsBar && !$builderStore.isDragging
$: settings = getBarSettings(definition) $: settings = getBarSettings(definition)
@ -67,7 +67,7 @@
} }
//If element is at the very top of the screen, put the bar below the element //If element is at the very top of the screen, put the bar below the element
if (elBounds.top < elBounds.height) { if (elBounds.top < elBounds.height && elBounds.height < 80) {
newTop = elBounds.bottom + verticalOffset newTop = elBounds.bottom + verticalOffset
} }
@ -163,9 +163,7 @@
<SettingsButton <SettingsButton
icon="Delete" icon="Delete"
on:click={() => { on:click={() => {
builderStore.actions.deleteComponent( builderStore.actions.deleteComponent($builderStore.selectedComponentId)
$builderStore.selectedComponent._id
)
}} }}
title="Delete component" title="Delete component"
/> />

View File

@ -1,6 +1,6 @@
<script> <script>
import { Icon } from "@budibase/bbui" import { Icon } from "@budibase/bbui"
import { builderStore } from "stores" import { builderStore, componentStore } from "stores"
import { createEventDispatcher } from "svelte" import { createEventDispatcher } from "svelte"
export let prop export let prop
@ -11,7 +11,7 @@
export let bool = false export let bool = false
const dispatch = createEventDispatcher() const dispatch = createEventDispatcher()
$: currentValue = $builderStore.selectedComponent?.[prop] $: currentValue = $componentStore.selectedComponent?.[prop]
$: active = prop && (bool ? !!currentValue : currentValue === value) $: active = prop && (bool ? !!currentValue : currentValue === value)
</script> </script>

View File

@ -1,10 +1,10 @@
<script> <script>
import { ColorPicker } from "@budibase/bbui" import { ColorPicker } from "@budibase/bbui"
import { builderStore } from "stores" import { builderStore, componentStore } from "stores"
export let prop export let prop
$: currentValue = $builderStore.selectedComponent?.[prop] $: currentValue = $componentStore.selectedComponent?.[prop]
</script> </script>
<div> <div>

View File

@ -1,12 +1,12 @@
<script> <script>
import { Select } from "@budibase/bbui" import { Select } from "@budibase/bbui"
import { builderStore } from "stores" import { builderStore, componentStore } from "stores"
export let prop export let prop
export let options export let options
export let label export let label
$: currentValue = $builderStore.selectedComponent?.[prop] $: currentValue = $componentStore.selectedComponent?.[prop]
</script> </script>
<div> <div>

View File

@ -1,8 +1,14 @@
import { API } from "api" import { API } from "api"
import { get, writable } from "svelte/store" import { get, writable } from "svelte/store"
const initialState = {
appId: null,
isDevApp: false,
clientLoadTime: window.INIT_TIME ? Date.now() - window.INIT_TIME : null,
}
const createAppStore = () => { const createAppStore = () => {
const store = writable(null) const store = writable(initialState)
// Fetches the app definition including screens, layouts and theme // Fetches the app definition including screens, layouts and theme
const fetchAppDefinition = async () => { const fetchAppDefinition = async () => {
@ -13,11 +19,13 @@ const createAppStore = () => {
try { try {
const appDefinition = await API.fetchAppPackage(appId) const appDefinition = await API.fetchAppPackage(appId)
store.set({ store.set({
...initialState,
...appDefinition, ...appDefinition,
appId: appDefinition?.application?.appId, appId: appDefinition?.application?.appId,
isDevApp: appId.startsWith("app_dev"),
}) })
} catch (error) { } catch (error) {
store.set(null) store.set(initialState)
} }
} }

View File

@ -1,7 +1,6 @@
import { writable, derived, get } from "svelte/store" import { writable, get } from "svelte/store"
import Manifest from "manifest.json"
import { findComponentById, findComponentPathById } from "../utils/components"
import { API } from "api" import { API } from "api"
import { devToolsStore } from "./devTools.js"
const dispatchEvent = (type, data = {}) => { const dispatchEvent = (type, data = {}) => {
window.parent.postMessage({ type, data }) window.parent.postMessage({ type, data })
@ -22,38 +21,18 @@ const createBuilderStore = () => {
previewDevice: "desktop", previewDevice: "desktop",
isDragging: false, isDragging: false,
} }
const writableStore = writable(initialState) const store = writable(initialState)
const derivedStore = derived(writableStore, $state => {
// Avoid any of this logic if we aren't in the builder preview
if (!$state.inBuilder) {
return $state
}
// Derive the selected component instance and definition
const { layout, screen, previewType, selectedComponentId } = $state
const asset = previewType === "layout" ? layout : screen
const component = findComponentById(asset?.props, selectedComponentId)
const prefix = "@budibase/standard-components/"
const type = component?._component?.replace(prefix, "")
const definition = type ? Manifest[type] : null
// Derive the selected component path
const path = findComponentPathById(asset.props, selectedComponentId) || []
return {
...$state,
selectedComponent: component,
selectedComponentDefinition: definition,
selectedComponentPath: path?.map(component => component._id),
}
})
const actions = { const actions = {
selectComponent: id => { selectComponent: id => {
if (id === get(writableStore).selectedComponentId) { if (id === get(store).selectedComponentId) {
return return
} }
writableStore.update(state => ({ ...state, editMode: false })) store.update(state => ({
...state,
editMode: false,
selectedComponentId: id,
}))
devToolsStore.actions.setAllowSelection(false)
dispatchEvent("select-component", { id }) dispatchEvent("select-component", { id })
}, },
updateProp: (prop, value) => { updateProp: (prop, value) => {
@ -76,7 +55,7 @@ const createBuilderStore = () => {
} }
}, },
setSelectedPath: path => { setSelectedPath: path => {
writableStore.update(state => ({ ...state, selectedPath: path })) store.update(state => ({ ...state, selectedPath: path }))
}, },
moveComponent: (componentId, destinationComponentId, mode) => { moveComponent: (componentId, destinationComponentId, mode) => {
dispatchEvent("move-component", { dispatchEvent("move-component", {
@ -86,22 +65,21 @@ const createBuilderStore = () => {
}) })
}, },
setDragging: dragging => { setDragging: dragging => {
if (dragging === get(writableStore).isDragging) { if (dragging === get(store).isDragging) {
return return
} }
writableStore.update(state => ({ ...state, isDragging: dragging })) store.update(state => ({ ...state, isDragging: dragging }))
}, },
setEditMode: enabled => { setEditMode: enabled => {
if (enabled === get(writableStore).editMode) { if (enabled === get(store).editMode) {
return return
} }
writableStore.update(state => ({ ...state, editMode: enabled })) store.update(state => ({ ...state, editMode: enabled }))
}, },
} }
return { return {
...writableStore, ...store,
set: state => writableStore.set({ ...initialState, ...state }), set: state => store.set({ ...initialState, ...state }),
subscribe: derivedStore.subscribe,
actions, actions,
} }
} }

View File

@ -0,0 +1,81 @@
import { get, writable, derived } from "svelte/store"
import Manifest from "manifest.json"
import { findComponentById, findComponentPathById } from "../utils/components"
import { devToolsStore } from "./devTools"
import { screenStore } from "./screens"
import { builderStore } from "./builder"
const createComponentStore = () => {
const store = writable({})
const derivedStore = derived(
[store, builderStore, devToolsStore, screenStore],
([$store, $builderState, $devToolsState, $screenState]) => {
// Avoid any of this logic if we aren't in the builder preview
if (!$builderState.inBuilder && !$devToolsState.visible) {
return {}
}
// Derive the selected component instance and definition
let asset
const { layout, screen, previewType, selectedComponentId } = $builderState
if ($builderState.inBuilder) {
asset = previewType === "layout" ? layout : screen
} else {
asset = $screenState.activeScreen
}
const component = findComponentById(asset?.props, selectedComponentId)
const prefix = "@budibase/standard-components/"
const type = component?._component?.replace(prefix, "")
const definition = type ? Manifest[type] : null
// Derive the selected component path
const path =
findComponentPathById(asset?.props, selectedComponentId) || []
return {
selectedComponentInstance: $store[selectedComponentId],
selectedComponent: component,
selectedComponentDefinition: definition,
selectedComponentPath: path?.map(component => component._id),
mountedComponents: Object.keys($store).length,
currentAsset: asset,
}
}
)
const registerInstance = (id, instance) => {
store.update(state => ({
...state,
[id]: instance,
}))
}
const unregisterInstance = id => {
store.update(state => {
delete state[id]
return state
})
}
const isComponentRegistered = id => {
return get(store)[id] != null
}
const getComponentById = id => {
const asset = get(derivedStore).currentAsset
return findComponentById(asset?.props, id)
}
return {
...derivedStore,
actions: {
registerInstance,
unregisterInstance,
isComponentRegistered,
getComponentById,
},
}
}
export const componentStore = createComponentStore()

View File

@ -0,0 +1,47 @@
import { get } from "svelte/store"
import { createLocalStorageStore } from "@budibase/frontend-core"
import { appStore } from "./app"
import { initialise } from "./initialise"
import { authStore } from "./auth"
const initialState = {
visible: false,
allowSelection: false,
role: null,
}
const createDevToolStore = () => {
const localStorageKey = `${get(appStore).appId}.devTools`
const store = createLocalStorageStore(localStorageKey, initialState)
const setVisible = visible => {
store.update(state => ({
...state,
visible: visible,
}))
}
const setAllowSelection = allowSelection => {
store.update(state => ({
...state,
allowSelection,
}))
}
const changeRole = async role => {
store.update(state => ({
...state,
role: role === "self" ? null : role,
}))
// location.reload()
await authStore.actions.fetchUser()
await initialise()
}
return {
subscribe: store.subscribe,
actions: { setVisible, setAllowSelection, changeRole },
}
}
export const devToolsStore = createDevToolStore()

View File

@ -9,6 +9,8 @@ export { confirmationStore } from "./confirmation"
export { peekStore } from "./peek" export { peekStore } from "./peek"
export { stateStore } from "./state" export { stateStore } from "./state"
export { themeStore } from "./theme" export { themeStore } from "./theme"
export { devToolsStore } from "./devTools"
export { componentStore } from "./components"
export { uploadStore } from "./uploads.js" export { uploadStore } from "./uploads.js"
export { rowSelectionStore } from "./rowSelection.js" export { rowSelectionStore } from "./rowSelection.js"
// Context stores are layered and duplicated, so it is not a singleton // Context stores are layered and duplicated, so it is not a singleton

View File

@ -66,7 +66,6 @@ const createScreenStore = () => {
} }
let children = [] let children = []
findChildrenByType(component, type, children) findChildrenByType(component, type, children)
console.log(children)
return children return children
}, },
} }

View File

@ -107,3 +107,21 @@ export const propsUseBinding = (props, bindingKey) => {
} }
return false return false
} }
/**
* Gets the definition of this component's settings from the manifest
*/
export const getSettingsDefinition = definition => {
if (!definition) {
return []
}
let settings = []
definition.settings?.forEach(setting => {
if (setting.section) {
settings = settings.concat(setting.settings || [])
} else {
settings.push(setting)
}
})
return settings
}

View File

@ -1,12 +1,12 @@
{ {
"name": "@budibase/frontend-core", "name": "@budibase/frontend-core",
"version": "1.0.105-alpha.9", "version": "1.0.105-alpha.19",
"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.105-alpha.9", "@budibase/bbui": "^1.0.105-alpha.19",
"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.105-alpha.9", "version": "1.0.105-alpha.19",
"description": "Budibase Web Server", "description": "Budibase Web Server",
"main": "src/index.ts", "main": "src/index.ts",
"repository": { "repository": {
@ -9,7 +9,7 @@
"url": "https://github.com/Budibase/budibase.git" "url": "https://github.com/Budibase/budibase.git"
}, },
"scripts": { "scripts": {
"build": "rimraf dist/ && tsc && mv dist/src/* dist/ && rmdir dist/src/ && yarn postbuild", "build": "rimraf dist/ && tsc && mv dist/src/* dist/ && rimraf dist/src/ && yarn postbuild",
"postbuild": "copyfiles -u 1 src/**/*.svelte dist/ && copyfiles -u 1 src/**/*.hbs dist/ && copyfiles -u 1 src/**/*.json dist/", "postbuild": "copyfiles -u 1 src/**/*.svelte dist/ && copyfiles -u 1 src/**/*.hbs dist/ && copyfiles -u 1 src/**/*.json dist/",
"test": "jest --coverage --maxWorkers=2", "test": "jest --coverage --maxWorkers=2",
"test:watch": "jest --watch", "test:watch": "jest --watch",
@ -68,9 +68,9 @@
"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.105-alpha.9", "@budibase/backend-core": "^1.0.105-alpha.19",
"@budibase/client": "^1.0.105-alpha.9", "@budibase/client": "^1.0.105-alpha.19",
"@budibase/string-templates": "^1.0.105-alpha.9", "@budibase/string-templates": "^1.0.105-alpha.19",
"@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

@ -78,6 +78,9 @@
app. app.
</h2> </h2>
</div> </div>
<script type="application/javascript">
window.INIT_TIME = Date.now()
</script>
<script type="application/javascript" src={clientLibPath}> <script type="application/javascript" src={clientLibPath}>
</script> </script>
<script type="application/javascript"> <script type="application/javascript">

View File

@ -25,7 +25,7 @@ function getFormulaThatUseColumn(table, columnNames) {
if (!isStaticFormula(column)) { if (!isStaticFormula(column)) {
continue continue
} }
if (!doesContainStrings(column.formula, columnNames)) { if (!doesContainStrings(column?.formula ?? "", columnNames)) {
continue continue
} }
formula.push(column.name) formula.push(column.name)

View File

@ -4,7 +4,7 @@ const {
getCookie, getCookie,
clearCookie, clearCookie,
} = require("@budibase/backend-core/utils") } = require("@budibase/backend-core/utils")
const { Cookies } = require("@budibase/backend-core/constants") const { Cookies, Headers } = require("@budibase/backend-core/constants")
const { getRole } = require("@budibase/backend-core/roles") const { getRole } = require("@budibase/backend-core/roles")
const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles") const { BUILTIN_ROLE_IDS } = require("@budibase/backend-core/roles")
const { generateUserMetadataID, isDevAppID } = require("../db/utils") const { generateUserMetadataID, isDevAppID } = require("../db/utils")
@ -63,6 +63,21 @@ module.exports = async (ctx, next) => {
appId = requestAppId appId = requestAppId
// retrieving global user gets the right role // retrieving global user gets the right role
roleId = globalUser.roleId || roleId roleId = globalUser.roleId || roleId
// Allow builders to specify their role via a header
const isBuilder =
globalUser && globalUser.builder && globalUser.builder.global
const isDevApp = appId && isDevAppID(appId)
const roleHeader = ctx.request && ctx.request.headers[Headers.PREVIEW_ROLE]
if (isBuilder && isDevApp && roleHeader) {
// Ensure the role is valid by ensuring a definition exists
try {
await getRole(roleHeader)
roleId = roleHeader
} catch (error) {
// Swallow error and do nothing
}
}
} }
// nothing more to do // nothing more to do

View File

@ -995,10 +995,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.97": "@budibase/backend-core@^1.0.105-alpha.10":
version "1.0.97" version "1.0.112"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.97.tgz#32bd09324437256bcfedda11d34fc4c4cb1a5b9b" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.112.tgz#38d5f8faadc08d863b13eeadefce067fd572ae27"
integrity sha512-NLH6fbWpU0Ej0dTL37PoEAMRMI8zYVwnJI1b74VqWFmyR/moOmyPcptfLnysY1aD5hizHgz+P8sFYsSGKiMozg== integrity sha512-/MS1+QZjEpwcy1Lj4tkvXE3V8Q1Kd41/30y8Y2yaDosnDoKZmUs5muZjXZJnmYADajPwA3u7uop2psEdg5diLg==
dependencies: dependencies:
"@techpass/passport-openidconnect" "^0.3.0" "@techpass/passport-openidconnect" "^0.3.0"
aws-sdk "^2.901.0" aws-sdk "^2.901.0"
@ -1068,13 +1068,13 @@
svelte-flatpickr "^3.2.3" svelte-flatpickr "^3.2.3"
svelte-portal "^1.0.0" svelte-portal "^1.0.0"
"@budibase/bbui@^1.0.97": "@budibase/bbui@^1.0.112":
version "1.0.97" version "1.0.112"
resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.0.97.tgz#6d36cdbeeaaf3a7b299a8a677f0739b682b49bf3" resolved "https://registry.yarnpkg.com/@budibase/bbui/-/bbui-1.0.112.tgz#93fa44b5051caee6bb6883be678700fca1f91682"
integrity sha512-AMwDCz0qm4rZp50TY83JJTjqOAlZPdirzsLM4Iydn2dWlaqpcwnB3VZOzgb+mC72uH5eKeqI74Nu7hh3CvtXBw== integrity sha512-ooc3yZnKCpXTZulq55dqvJrdUPgAaC+kHL5eo5S62qBSoHx3RHUxPEAG+aOpVuj2I8UJ05/2iVCxlgTQCcYp3A==
dependencies: dependencies:
"@adobe/spectrum-css-workflow-icons" "^1.2.1" "@adobe/spectrum-css-workflow-icons" "^1.2.1"
"@budibase/string-templates" "^1.0.97" "@budibase/string-templates" "^1.0.112"
"@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"
@ -1120,14 +1120,14 @@
svelte-flatpickr "^3.2.3" svelte-flatpickr "^3.2.3"
svelte-portal "^1.0.0" svelte-portal "^1.0.0"
"@budibase/client@^1.0.97": "@budibase/client@^1.0.105-alpha.10":
version "1.0.97" version "1.0.112"
resolved "https://registry.yarnpkg.com/@budibase/client/-/client-1.0.97.tgz#e110737c98fb0f7145cf1f5ec41912ff46fc0c3f" resolved "https://registry.yarnpkg.com/@budibase/client/-/client-1.0.112.tgz#babd4e75de9f4161aa40c15138afb810d0a91993"
integrity sha512-bdXVVZe6m+P4azdGvU2AoVMDC+bQ2MUkot5hTcRTfyCAwqCQABlkL/t+iA4Zv8feFc5ZUtfrsCIm+jOKGEUUow== integrity sha512-pl8kZHvJN0EMcDUscH4RHXKXQhcHa5wDDIe6C4FMZ18TCp6hDM47Cv/xeejOqajllixASV8oD0ZymNZi3sLGvQ==
dependencies: dependencies:
"@budibase/bbui" "^1.0.97" "@budibase/bbui" "^1.0.112"
"@budibase/frontend-core" "^1.0.97" "@budibase/frontend-core" "^1.0.112"
"@budibase/string-templates" "^1.0.97" "@budibase/string-templates" "^1.0.112"
"@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"
@ -1139,20 +1139,23 @@
apexcharts "^3.22.1" apexcharts "^3.22.1"
dayjs "^1.10.5" dayjs "^1.10.5"
downloadjs "1.4.7" downloadjs "1.4.7"
leaflet "^1.7.1"
regexparam "^1.3.0" regexparam "^1.3.0"
rollup-plugin-polyfill-node "^0.8.0" rollup-plugin-polyfill-node "^0.8.0"
sanitize-html "^2.7.0"
screenfull "^6.0.1"
shortid "^2.2.15" shortid "^2.2.15"
svelte "^3.38.2" svelte "^3.38.2"
svelte-apexcharts "^1.0.2" svelte-apexcharts "^1.0.2"
svelte-flatpickr "^3.1.0" svelte-flatpickr "^3.1.0"
svelte-spa-router "^3.0.5" svelte-spa-router "^3.0.5"
"@budibase/frontend-core@^1.0.97": "@budibase/frontend-core@^1.0.112":
version "1.0.97" version "1.0.112"
resolved "https://registry.yarnpkg.com/@budibase/frontend-core/-/frontend-core-1.0.97.tgz#530a0c716fbc93dd38d7abcc22b3131030f9cd16" resolved "https://registry.yarnpkg.com/@budibase/frontend-core/-/frontend-core-1.0.112.tgz#b1615e6cf6690fa6672fa579f6ccd74e94f702d9"
integrity sha512-B2LG9UIEKSLE0NBhPlVLYXAypjBZgiPdqGwoNm7xcWZHG12YzYWqflV3NLwaDx9Qt3UTIaRzXqBGoNNQCGdUdQ== integrity sha512-NcElXZk9wSld2kl3YbCwrGlQaqM+fqiGU0czurFieGLKanKuIRquLtyfxGGRqxC8zxKacxhWKpaVRoUt87dTnw==
dependencies: dependencies:
"@budibase/bbui" "^1.0.97" "@budibase/bbui" "^1.0.112"
lodash "^4.17.21" lodash "^4.17.21"
svelte "^3.46.2" svelte "^3.46.2"
@ -1199,10 +1202,10 @@
svelte-apexcharts "^1.0.2" svelte-apexcharts "^1.0.2"
svelte-flatpickr "^3.1.0" svelte-flatpickr "^3.1.0"
"@budibase/string-templates@^1.0.97": "@budibase/string-templates@^1.0.105-alpha.10", "@budibase/string-templates@^1.0.112":
version "1.0.97" version "1.0.112"
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-1.0.97.tgz#b7485cb8774d6dff8280fbf411c6383b82add72d" resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-1.0.112.tgz#754f8f725262e58dfeac946de5f87d8d333077b7"
integrity sha512-8NfpYzDJgSkic3E0ARfNsPsFEMmOcH+kEeTioLkNnkKrviIRhSQ9Oa3S87QIYo5WGYxpEFEfhYutXsbp3GTLbg== integrity sha512-UOVpMT87eR8ZfoZo5D3UsxR0Qsfj0FmE6bxcJLYd5ey/RIeMErnl8fKu/0Chj6JOxGc3q/U7dIYsqzdVsJFhyQ==
dependencies: dependencies:
"@budibase/handlebars-helpers" "^0.11.8" "@budibase/handlebars-helpers" "^0.11.8"
dayjs "^1.10.4" dayjs "^1.10.4"
@ -4985,11 +4988,25 @@ doctrine@3.0.0, doctrine@^3.0.0:
dependencies: dependencies:
esutils "^2.0.2" esutils "^2.0.2"
dom-serializer@^1.0.1:
version "1.4.1"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30"
integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==
dependencies:
domelementtype "^2.0.1"
domhandler "^4.2.0"
entities "^2.0.0"
dom-walk@^0.1.0: dom-walk@^0.1.0:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84" resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.2.tgz#0c548bef048f4d1f2a97249002236060daa3fd84"
integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w== integrity sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==
domelementtype@^2.0.1, domelementtype@^2.2.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
domexception@^1.0.1: domexception@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90" resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
@ -5004,6 +5021,22 @@ domexception@^2.0.1:
dependencies: dependencies:
webidl-conversions "^5.0.0" webidl-conversions "^5.0.0"
domhandler@^4.0.0, domhandler@^4.2.0:
version "4.3.1"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c"
integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==
dependencies:
domelementtype "^2.2.0"
domutils@^2.5.2:
version "2.8.0"
resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135"
integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
dependencies:
dom-serializer "^1.0.1"
domelementtype "^2.2.0"
domhandler "^4.2.0"
dot-prop@^5.2.0: dot-prop@^5.2.0:
version "5.3.0" version "5.3.0"
resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88"
@ -5190,6 +5223,11 @@ ent@^2.2.0:
resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d"
integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0=
entities@^2.0.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55"
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
entities@~2.1.0: entities@~2.1.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5"
@ -5433,6 +5471,11 @@ escape-string-regexp@^2.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344"
integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==
escape-string-regexp@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
escodegen@^1.9.1: escodegen@^1.9.1:
version "1.14.3" version "1.14.3"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503" resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.14.3.tgz#4e7b81fba61581dc97582ed78cab7f0e8d63f503"
@ -6764,6 +6807,16 @@ html-tag@^2.0.0:
is-self-closing "^1.0.1" is-self-closing "^1.0.1"
kind-of "^6.0.0" kind-of "^6.0.0"
htmlparser2@^6.0.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
dependencies:
domelementtype "^2.0.1"
domhandler "^4.0.0"
domutils "^2.5.2"
entities "^2.0.0"
http-assert@^1.3.0: http-assert@^1.3.0:
version "1.5.0" version "1.5.0"
resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f" resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f"
@ -7312,6 +7365,11 @@ is-plain-object@^2.0.3, is-plain-object@^2.0.4:
dependencies: dependencies:
isobject "^3.0.1" isobject "^3.0.1"
is-plain-object@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344"
integrity sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==
is-potential-custom-element-name@^1.0.1: is-potential-custom-element-name@^1.0.1:
version "1.0.1" version "1.0.1"
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
@ -8904,6 +8962,11 @@ lcid@^2.0.0:
dependencies: dependencies:
invert-kv "^2.0.0" invert-kv "^2.0.0"
leaflet@^1.7.1:
version "1.7.1"
resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.7.1.tgz#10d684916edfe1bf41d688a3b97127c0322a2a19"
integrity sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw==
left-pad@^1.3.0: left-pad@^1.3.0:
version "1.3.0" version "1.3.0"
resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e"
@ -9688,6 +9751,11 @@ nanoid@^2.1.0:
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-2.1.11.tgz#ec24b8a758d591561531b4176a01e3ab4f0f0280"
integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA== integrity sha512-s/snB+WGm6uwi0WjsZdaVcuf3KJXlfGl2LcxgwkEwJF0D/BWzVWAZW/XY4bFaiR7s0Jk3FPvlnepg1H1b1UwlA==
nanoid@^3.3.1:
version "3.3.2"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.2.tgz#c89622fafb4381cd221421c69ec58547a1eec557"
integrity sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==
nanomatch@^1.2.9: nanomatch@^1.2.9:
version "1.2.13" version "1.2.13"
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
@ -10245,6 +10313,11 @@ parse-json@^4.0.0:
error-ex "^1.3.1" error-ex "^1.3.1"
json-parse-better-errors "^1.0.1" json-parse-better-errors "^1.0.1"
parse-srcset@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1"
integrity sha1-8r0iH2zJcKk42IVWq8WJyqqiveE=
parse5@4.0.0: parse5@4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608"
@ -10598,6 +10671,15 @@ posix-character-classes@^0.1.0:
resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab"
integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=
postcss@^8.3.11:
version "8.4.12"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905"
integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==
dependencies:
nanoid "^3.3.1"
picocolors "^1.0.0"
source-map-js "^1.0.2"
postgres-array@~2.0.0: postgres-array@~2.0.0:
version "2.0.0" version "2.0.0"
resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e" resolved "https://registry.yarnpkg.com/postgres-array/-/postgres-array-2.0.0.tgz#48f8fce054fbc69671999329b8834b772652d82e"
@ -11636,6 +11718,18 @@ sane@^4.0.3:
minimist "^1.1.1" minimist "^1.1.1"
walker "~1.0.5" walker "~1.0.5"
sanitize-html@^2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/sanitize-html/-/sanitize-html-2.7.0.tgz#e106205b468aca932e2f9baf241f24660d34e279"
integrity sha512-jfQelabOn5voO7FAfnQF7v+jsA6z9zC/O4ec0z3E35XPEtHYJT/OdUziVWlKW4irCr2kXaQAyXTXDHWAibg1tA==
dependencies:
deepmerge "^4.2.2"
escape-string-regexp "^4.0.0"
htmlparser2 "^6.0.0"
is-plain-object "^5.0.0"
parse-srcset "^1.0.2"
postcss "^8.3.11"
sanitize-s3-objectkey@^0.0.1: sanitize-s3-objectkey@^0.0.1:
version "0.0.1" version "0.0.1"
resolved "https://registry.yarnpkg.com/sanitize-s3-objectkey/-/sanitize-s3-objectkey-0.0.1.tgz#efa9887cd45275b40234fb4bb12fc5754fe64e7e" resolved "https://registry.yarnpkg.com/sanitize-s3-objectkey/-/sanitize-s3-objectkey-0.0.1.tgz#efa9887cd45275b40234fb4bb12fc5754fe64e7e"
@ -11674,6 +11768,11 @@ schema-utils@^3.1.0, schema-utils@^3.1.1:
ajv "^6.12.5" ajv "^6.12.5"
ajv-keywords "^3.5.2" ajv-keywords "^3.5.2"
screenfull@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-6.0.1.tgz#3b71e6f06b72d817a8d3be73c45ebe71fa8da1ce"
integrity sha512-yzQW+j4zMUBQC51xxWaoDYjxOtl8Kn+xvue3p6v/fv2pIi1jH4AldgVLU8TBfFVgH2x3VXlf3+YiA/AYIPlaew==
search-params@3.0.0: search-params@3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.yarnpkg.com/search-params/-/search-params-3.0.0.tgz#dbc7c243058e5a33ae1e9870be91f5aced4100d8" resolved "https://registry.yarnpkg.com/search-params/-/search-params-3.0.0.tgz#dbc7c243058e5a33ae1e9870be91f5aced4100d8"
@ -11940,6 +12039,11 @@ source-list-map@^2.0.1:
resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34"
integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==
source-map-js@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
source-map-resolve@^0.5.0: source-map-resolve@^0.5.0:
version "0.5.3" version "0.5.3"
resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a"
@ -13187,9 +13291,9 @@ uri-js@^4.2.2:
punycode "^2.1.0" punycode "^2.1.0"
urijs@^1.19.0: urijs@^1.19.0:
version "1.19.8" version "1.19.11"
resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.8.tgz#ee0407a18528934d3c383e691912f47043a58feb" resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.11.tgz#204b0d6b605ae80bea54bea39280cdb7c9f923cc"
integrity sha512-iIXHrjomQ0ZCuDRy44wRbyTZVnfVNLVo3Ksz1yxNyE5wV1IDZW2S5Jszy45DTlw/UdsnRT7DyDhIz7Gy+vJumw== integrity sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==
urix@^0.1.0: urix@^0.1.0:
version "0.1.0" version "0.1.0"

View File

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

View File

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

View File

@ -286,10 +286,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.104": "@budibase/backend-core@^1.0.105-alpha.10":
version "1.0.104" version "1.0.112"
resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.104.tgz#eb435c27327a1f2ea5e83b3cf242b9f85b7a5f8a" resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.112.tgz#38d5f8faadc08d863b13eeadefce067fd572ae27"
integrity sha512-YXI8R/bdWDO5RhcMdqu88DQ7Dn26v3SUTs5FuHvXG4b2mKDTHduecqTuhIhrbqQ5KjnTYuhAEz2iG0sP2rj5rg== integrity sha512-/MS1+QZjEpwcy1Lj4tkvXE3V8Q1Kd41/30y8Y2yaDosnDoKZmUs5muZjXZJnmYADajPwA3u7uop2psEdg5diLg==
dependencies: dependencies:
"@techpass/passport-openidconnect" "^0.3.0" "@techpass/passport-openidconnect" "^0.3.0"
aws-sdk "^2.901.0" aws-sdk "^2.901.0"
@ -335,10 +335,10 @@
to-gfm-code-block "^0.1.1" to-gfm-code-block "^0.1.1"
year "^0.2.1" year "^0.2.1"
"@budibase/string-templates@^1.0.104": "@budibase/string-templates@^1.0.105-alpha.10":
version "1.0.104" version "1.0.112"
resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-1.0.104.tgz#f812700f2b21f638fd1e48dde065ae693fae2897" resolved "https://registry.yarnpkg.com/@budibase/string-templates/-/string-templates-1.0.112.tgz#754f8f725262e58dfeac946de5f87d8d333077b7"
integrity sha512-3caq3qwpIieyb9m8eSl8OhcE0ppzuyJ/0ubDlWmtpbmwmG2v3ynI+DwxpbG4CcVQFuebD2yxU0CZfioU76vKCQ== integrity sha512-UOVpMT87eR8ZfoZo5D3UsxR0Qsfj0FmE6bxcJLYd5ey/RIeMErnl8fKu/0Chj6JOxGc3q/U7dIYsqzdVsJFhyQ==
dependencies: dependencies:
"@budibase/handlebars-helpers" "^0.11.8" "@budibase/handlebars-helpers" "^0.11.8"
dayjs "^1.10.4" dayjs "^1.10.4"
@ -6805,9 +6805,9 @@ uri-js@^4.2.2:
punycode "^2.1.0" punycode "^2.1.0"
urijs@^1.19.2: urijs@^1.19.2:
version "1.19.8" version "1.19.11"
resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.8.tgz#ee0407a18528934d3c383e691912f47043a58feb" resolved "https://registry.yarnpkg.com/urijs/-/urijs-1.19.11.tgz#204b0d6b605ae80bea54bea39280cdb7c9f923cc"
integrity sha512-iIXHrjomQ0ZCuDRy44wRbyTZVnfVNLVo3Ksz1yxNyE5wV1IDZW2S5Jszy45DTlw/UdsnRT7DyDhIz7Gy+vJumw== integrity sha512-HXgFDgDommxn5/bIv0cnQZsPhHDA90NPHD6+c/v21U5+Sx5hoP8+dP9IZXBU1gIfvdRfhG8cel9QNPeionfcCQ==
urix@^0.1.0: urix@^0.1.0:
version "0.1.0" version "0.1.0"