diff --git a/.github/stale.yml b/.github/stale.yml
index 5875ed1282..2a2c10cb7d 100644
--- a/.github/stale.yml
+++ b/.github/stale.yml
@@ -14,7 +14,6 @@ staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
- recent activity. It will be closed if no further activity occurs. Thank you
- for your contributions.
+ recent activity.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false
diff --git a/lerna.json b/lerna.json
index ec8a84538c..25e75c38e5 100644
--- a/lerna.json
+++ b/lerna.json
@@ -1,5 +1,5 @@
{
- "version": "1.0.105-alpha.35",
+ "version": "1.0.105-alpha.38",
"npmClient": "yarn",
"packages": [
"packages/*"
diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json
index f286389c22..fce19fb395 100644
--- a/packages/backend-core/package.json
+++ b/packages/backend-core/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/backend-core",
- "version": "1.0.105-alpha.35",
+ "version": "1.0.105-alpha.38",
"description": "Budibase backend core libraries used in server and worker",
"main": "src/index.js",
"author": "Budibase",
diff --git a/packages/backend-core/src/environment.js b/packages/backend-core/src/environment.js
index 856ab1b97c..527760c1ca 100644
--- a/packages/backend-core/src/environment.js
+++ b/packages/backend-core/src/environment.js
@@ -22,7 +22,8 @@ module.exports = {
MINIO_URL: process.env.MINIO_URL,
INTERNAL_API_KEY: process.env.INTERNAL_API_KEY,
MULTI_TENANCY: process.env.MULTI_TENANCY,
- ACCOUNT_PORTAL_URL: process.env.ACCOUNT_PORTAL_URL,
+ ACCOUNT_PORTAL_URL:
+ process.env.ACCOUNT_PORTAL_URL || "https://account.budibase.app",
ACCOUNT_PORTAL_API_KEY: process.env.ACCOUNT_PORTAL_API_KEY,
DISABLE_ACCOUNT_PORTAL: process.env.DISABLE_ACCOUNT_PORTAL,
SELF_HOSTED: !!parseInt(process.env.SELF_HOSTED),
diff --git a/packages/bbui/package.json b/packages/bbui/package.json
index fe52f069bb..26db4b2409 100644
--- a/packages/bbui/package.json
+++ b/packages/bbui/package.json
@@ -1,7 +1,7 @@
{
"name": "@budibase/bbui",
"description": "A UI solution used in the different Budibase projects.",
- "version": "1.0.105-alpha.35",
+ "version": "1.0.105-alpha.38",
"license": "MPL-2.0",
"svelte": "src/index.js",
"module": "dist/bbui.es.js",
@@ -38,7 +38,7 @@
],
"dependencies": {
"@adobe/spectrum-css-workflow-icons": "^1.2.1",
- "@budibase/string-templates": "^1.0.105-alpha.35",
+ "@budibase/string-templates": "^1.0.105-alpha.38",
"@spectrum-css/actionbutton": "^1.0.1",
"@spectrum-css/actiongroup": "^1.0.1",
"@spectrum-css/avatar": "^3.0.2",
diff --git a/packages/builder/cypress/integration/autoScreensUI.spec.js b/packages/builder/cypress/integration/autoScreensUI.spec.js
index ab65e6561f..2c2a43e711 100644
--- a/packages/builder/cypress/integration/autoScreensUI.spec.js
+++ b/packages/builder/cypress/integration/autoScreensUI.spec.js
@@ -1,51 +1,103 @@
import filterTests from "../support/filterTests"
filterTests(['smoke', 'all'], () => {
- context("Auto Screens UI", () => {
- before(() => {
- cy.login()
- cy.createTestApp()
- })
-
- it("should generate internal table screens", () => {
- // Create autogenerated screens from the internal table
- cy.createAutogeneratedScreens(["Cypress Tests"])
- // Confirm screens have been auto generated
- cy.get(".nav-items-container").contains("cypress-tests").click({ force: true })
- cy.get(".nav-items-container").should('contain', 'cypress-tests/:id')
- .and('contain', 'cypress-tests/new/row')
- })
-
- it("should generate multiple internal table screens at once", () => {
- // Create a second internal table
- const initialTable = "Cypress Tests"
- const secondTable = "Table Two"
- cy.createTable(secondTable)
- // Create autogenerated screens from the internal tables
- cy.createAutogeneratedScreens([initialTable, secondTable])
- // Confirm screens have been auto generated
- cy.get(".nav-items-container").contains("cypress-tests").click({ force: true })
- // Previously generated tables are suffixed with numbers - as expected
- cy.get(".nav-items-container").should('contain', 'cypress-tests-2/:id')
- .and('contain', 'cypress-tests-2/new/row')
- cy.get(".nav-items-container").contains("table-two").click()
- cy.get(".nav-items-container").should('contain', 'table-two/:id')
- .and('contain', 'table-two/new/row')
- })
-
- if (Cypress.env("TEST_ENV")) {
- it("should generate data source screens", () => {
- // Using MySQL data source for testing this
- const datasource = "MySQL"
- // Select & configure MySQL data source
- cy.selectExternalDatasource(datasource)
- cy.addDatasourceConfig(datasource)
- // Create autogenerated screens from a MySQL table - MySQL contains books table
- cy.createAutogeneratedScreens(["books"])
- cy.get(".nav-items-container").contains("books").click()
- cy.get(".nav-items-container").should('contain', 'books/:id')
- .and('contain', 'books/new/row')
- })
- }
+ context("Auto Screens UI", () => {
+ before(() => {
+ cy.login()
})
+
+ it("should disable the autogenerated screen options if no sources are available", () => {
+ cy.createApp("First Test App", false)
+
+ cy.closeModal();
+
+ cy.contains("Design").click()
+ cy.get("[aria-label=AddCircle]").click()
+ cy.get(".spectrum-Modal").within(() => {
+ cy.get(".item.disabled").contains("Autogenerated screens")
+ cy.get(".confirm-wrap .spectrum-Button").should('be.disabled')
+ })
+
+ cy.deleteAllApps()
+ });
+
+ it("should not display incompatible sources", () => {
+ cy.createApp("Test App")
+
+ cy.selectExternalDatasource("REST")
+ cy.selectExternalDatasource("S3")
+ cy.get(".spectrum-Modal").within(() => {
+ cy.get(".spectrum-Button").contains("Save and continue to query").click({ force : true })
+ })
+
+ cy.navigateToAutogeneratedModal()
+
+ cy.get('.data-source-entry').should('have.length', 1)
+ cy.get('.data-source-entry')
+
+ cy.deleteAllApps()
+ });
+
+ it("should generate internal table screens", () => {
+ cy.createTestApp()
+ // Create Autogenerated screens from the internal table
+ cy.createDatasourceScreen(["Cypress Tests"])
+ // Confirm screens have been auto generated
+ cy.get(".nav-items-container").contains("cypress-tests").click({ force: true })
+ cy.get(".nav-items-container").should('contain', 'cypress-tests/:id')
+ .and('contain', 'cypress-tests/new/row')
+ })
+
+ it("should generate multiple internal table screens at once", () => {
+ // Create a second internal table
+ const initialTable = "Cypress Tests"
+ const secondTable = "Table Two"
+ cy.createTable(secondTable)
+ // Create Autogenerated screens from the internal tables
+ cy.createDatasourceScreen([initialTable, secondTable])
+ // Confirm screens have been auto generated
+ cy.get(".nav-items-container").contains("cypress-tests").click({ force: true })
+ // Previously generated tables are suffixed with numbers - as expected
+ cy.get(".nav-items-container").should('contain', 'cypress-tests-2/:id')
+ .and('contain', 'cypress-tests-2/new/row')
+ cy.get(".nav-items-container").contains("table-two").click()
+ cy.get(".nav-items-container").should('contain', 'table-two/:id')
+ .and('contain', 'table-two/new/row')
+ })
+
+ it("should generate multiple internal table screens with the same screen access level", () => {
+ //The tables created in the previous step still exist
+ cy.createTable("Table Three")
+ cy.createTable("Table Four")
+ cy.createDatasourceScreen(["Table Three", "Table Four"], "Admin")
+
+ cy.get(".nav-items-container").contains("table-three").click()
+ cy.get(".nav-items-container").should('contain', 'table-three/:id')
+ .and('contain', 'table-three/new/row')
+
+ cy.get(".nav-items-container").contains("table-four").click()
+ cy.get(".nav-items-container").should('contain', 'table-four/:id')
+ .and('contain', 'table-four/new/row')
+
+ //The access level should now be set to admin. Previous screens should be filtered.
+ cy.get(".nav-items-container").contains("table-two").should('not.exist')
+ cy.get(".nav-items-container").contains("cypress-tests").should('not.exist')
+ })
+
+ if (Cypress.env("TEST_ENV")) {
+ it("should generate data source screens", () => {
+ // Using MySQL data source for testing this
+ const datasource = "MySQL"
+ // Select & configure MySQL data source
+ cy.selectExternalDatasource(datasource)
+ cy.addDatasourceConfig(datasource)
+ // Create Autogenerated screens from a MySQL table - MySQL contains books table
+ cy.createDatasourceScreen(["books"])
+
+ cy.get(".nav-items-container").contains("books").click()
+ cy.get(".nav-items-container").should('contain', 'books/:id')
+ .and('contain', 'books/new/row')
+ })
+ }
+ })
})
diff --git a/packages/builder/cypress/integration/createBinding.spec.js b/packages/builder/cypress/integration/createBinding.spec.js
index 8bf1ec8ea4..57cd0cc5fc 100644
--- a/packages/builder/cypress/integration/createBinding.spec.js
+++ b/packages/builder/cypress/integration/createBinding.spec.js
@@ -26,7 +26,7 @@ filterTests(['smoke', 'all'], () => {
it("should add a URL param binding", () => {
const paramName = "foo"
- cy.createScreen("Test Param", `/test/:${paramName}`)
+ cy.createScreen(`/test/:${paramName}`)
cy.addComponent("Elements", "Paragraph").then(componentId => {
addSettingBinding("text", `URL.${paramName}`)
// The builder preview pages don't have a real URL, so all we can do
diff --git a/packages/builder/cypress/integration/createScreen.js b/packages/builder/cypress/integration/createScreen.js
index ada68d82dc..ae10577ff0 100644
--- a/packages/builder/cypress/integration/createScreen.js
+++ b/packages/builder/cypress/integration/createScreen.js
@@ -9,17 +9,33 @@ filterTests(["smoke", "all"], () => {
})
it("Should successfully create a screen", () => {
- cy.createScreen("Test Screen", "/test")
+ cy.createScreen("/test")
cy.get(".nav-items-container").within(() => {
cy.contains("/test").should("exist")
})
})
it("Should update the url", () => {
- cy.createScreen("Test Screen", "test with spaces")
+ cy.createScreen("test with spaces")
cy.get(".nav-items-container").within(() => {
cy.contains("/test-with-spaces").should("exist")
})
})
+
+ it("Should create a blank screen with the selected access level", () => {
+ cy.createScreen("admin only", "Admin")
+
+ cy.get(".nav-items-container").within(() => {
+ cy.contains("/admin-only").should("exist")
+ })
+
+ cy.createScreen("open to all", "Public")
+
+ cy.get(".nav-items-container").within(() => {
+ cy.contains("/open-to-all").should("exist")
+ //The access level should now be set to admin. Previous screens should be filtered.
+ cy.get(".nav-item").contains("/test-screen").should("not.exist")
+ })
+ })
})
})
diff --git a/packages/builder/cypress/support/commands.js b/packages/builder/cypress/support/commands.js
index 672e9dfc00..e4a7f44bac 100644
--- a/packages/builder/cypress/support/commands.js
+++ b/packages/builder/cypress/support/commands.js
@@ -32,7 +32,17 @@ Cypress.Commands.add("login", () => {
})
})
-Cypress.Commands.add("createApp", name => {
+Cypress.Commands.add("closeModal", () => {
+ cy.get(".spectrum-Modal").within(() => {
+ cy.get(".close-icon").click()
+ cy.wait(500)
+ })
+})
+
+Cypress.Commands.add("createApp", (name, addDefaultTable) => {
+ const shouldCreateDefaultTable =
+ typeof addDefaultTable != "boolean" ? true : addDefaultTable
+
cy.visit(`${Cypress.config().baseUrl}/builder`)
cy.wait(500)
cy.get(`[data-cy="create-app-btn"]`).click({ force: true })
@@ -51,7 +61,9 @@ Cypress.Commands.add("createApp", name => {
cy.get(".spectrum-ButtonGroup").contains("Create app").click()
cy.wait(10000)
})
- cy.createTable("Cypress Tests", true)
+ if (shouldCreateDefaultTable) {
+ cy.createTable("Cypress Tests", true)
+ }
})
Cypress.Commands.add("deleteApp", name => {
@@ -135,7 +147,7 @@ Cypress.Commands.add("createTestApp", () => {
const appName = "Cypress Tests"
cy.deleteApp(appName)
cy.createApp(appName, "This app is used for Cypress testing.")
- cy.createScreen("home", "home")
+ cy.createScreen("home")
})
Cypress.Commands.add("createTestTableWithData", () => {
@@ -275,33 +287,99 @@ Cypress.Commands.add("navigateToDataSection", () => {
cy.contains("Data").click()
})
-Cypress.Commands.add("createScreen", (screenName, route) => {
+//Blank
+Cypress.Commands.add("createScreen", (route, accessLevelLabel) => {
cy.contains("Design").click()
cy.get("[aria-label=AddCircle]").click()
cy.get(".spectrum-Modal").within(() => {
- cy.get(".item").contains("Blank").click()
- cy.get(".spectrum-Button").contains("Add screens").click({ force: true })
+ cy.get(".item").contains("Blank screen").click()
+ cy.get(".spectrum-Button").contains("Continue").click({ force: true })
cy.wait(500)
})
cy.get(".spectrum-Dialog-grid").within(() => {
- cy.get(".spectrum-Form-itemField").eq(0).type(screenName)
- cy.get(".spectrum-Form-itemField").eq(1).type(route)
+ cy.get(".spectrum-Form-itemField").eq(0).type(route)
cy.get(".spectrum-Button").contains("Continue").click({ force: true })
cy.wait(1000)
})
+
+ cy.get(".spectrum-Modal").within(() => {
+ if (accessLevelLabel) {
+ cy.get(".spectrum-Picker-label").click()
+ cy.wait(500)
+ cy.contains(accessLevelLabel).click()
+ }
+ cy.get(".spectrum-Button").contains("Done").click({ force: true })
+ })
})
-Cypress.Commands.add("createAutogeneratedScreens", screenNames => {
+Cypress.Commands.add(
+ "createDatasourceScreen",
+ (datasourceNames, accessLevelLabel) => {
+ cy.contains("Design").click()
+ cy.get("[aria-label=AddCircle]").click()
+ cy.get(".spectrum-Modal").within(() => {
+ cy.get(".item").contains("Autogenerated screens").click()
+ cy.get(".spectrum-Button").contains("Continue").click({ force: true })
+ cy.wait(500)
+ })
+ cy.get(".spectrum-Modal [data-cy='data-source-modal']").within(() => {
+ for (let i = 0; i < datasourceNames.length; i++) {
+ cy.get(".data-source-entry").contains(datasourceNames[i]).click()
+ //Ensure the check mark is visible
+ cy.get(".data-source-entry")
+ .contains(datasourceNames[i])
+ .get(".data-source-check")
+ .should("exist")
+ }
+
+ cy.get(".spectrum-Button").contains("Confirm").click({ force: true })
+ })
+
+ cy.get(".spectrum-Modal").within(() => {
+ if (accessLevelLabel) {
+ cy.get(".spectrum-Picker-label").click()
+ cy.wait(500)
+ cy.contains(accessLevelLabel).click()
+ }
+ cy.get(".spectrum-Button").contains("Done").click({ force: true })
+ })
+
+ cy.contains("Design").click()
+ }
+)
+
+Cypress.Commands.add("navigateToAutogeneratedModal", () => {
// Screen name must already exist within data source
cy.contains("Design").click()
cy.get("[aria-label=AddCircle]").click()
- for (let i = 0; i < screenNames.length; i++) {
- cy.get(".item").contains(screenNames[i]).click()
- }
- cy.get(".spectrum-Button").contains("Add screens").click({ force: true })
- cy.wait(4000)
+ cy.get(".spectrum-Modal").within(() => {
+ cy.get(".item").contains("Autogenerated screens").click()
+ cy.get(".spectrum-Button").contains("Continue").click({ force: true })
+ cy.wait(500)
+ })
})
+Cypress.Commands.add(
+ "createAutogeneratedScreens",
+ (screenNames, accessLevelLabel) => {
+ cy.navigateToAutogeneratedModal()
+
+ for (let i = 0; i < screenNames.length; i++) {
+ cy.get(".data-source-entry").contains(screenNames[i]).click()
+ }
+
+ cy.get(".spectrum-Modal").within(() => {
+ if (accessLevelLabel) {
+ cy.get(".spectrum-Picker-label").click()
+ cy.wait(500)
+ cy.contains(accessLevelLabel).click()
+ }
+ cy.get(".spectrum-Button").contains("Confirm").click({ force: true })
+ cy.wait(4000)
+ })
+ }
+)
+
Cypress.Commands.add("addRow", values => {
cy.contains("Create row").click()
cy.get(".spectrum-Modal").within(() => {
diff --git a/packages/builder/package.json b/packages/builder/package.json
index 44bfb215c5..0b487f5b44 100644
--- a/packages/builder/package.json
+++ b/packages/builder/package.json
@@ -1,6 +1,6 @@
{
"name": "@budibase/builder",
- "version": "1.0.105-alpha.35",
+ "version": "1.0.105-alpha.38",
"license": "GPL-3.0",
"private": true,
"scripts": {
@@ -65,10 +65,10 @@
}
},
"dependencies": {
- "@budibase/bbui": "^1.0.105-alpha.35",
- "@budibase/client": "^1.0.105-alpha.35",
- "@budibase/frontend-core": "^1.0.105-alpha.35",
- "@budibase/string-templates": "^1.0.105-alpha.35",
+ "@budibase/bbui": "^1.0.105-alpha.38",
+ "@budibase/client": "^1.0.105-alpha.38",
+ "@budibase/frontend-core": "^1.0.105-alpha.38",
+ "@budibase/string-templates": "^1.0.105-alpha.38",
"@sentry/browser": "5.19.1",
"@spectrum-css/page": "^3.0.1",
"@spectrum-css/vars": "^3.0.1",
diff --git a/packages/builder/src/analytics/constants.js b/packages/builder/src/analytics/constants.js
index 780bbcd46d..300b1e058d 100644
--- a/packages/builder/src/analytics/constants.js
+++ b/packages/builder/src/analytics/constants.js
@@ -22,6 +22,7 @@ export const Events = {
},
SCREEN: {
CREATED: "Screen Created",
+ CREATE_ROLE_UPDATED: "Changed Role On Screen Creation",
},
AUTOMATION: {
CREATED: "Automation Created",
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/Redis.svelte b/packages/builder/src/components/backend/DatasourceNavigator/icons/Redis.svelte
new file mode 100644
index 0000000000..d325782fd5
--- /dev/null
+++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/Redis.svelte
@@ -0,0 +1,49 @@
+
+
+
diff --git a/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js
index 515f20a93b..1a8df2507e 100644
--- a/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js
+++ b/packages/builder/src/components/backend/DatasourceNavigator/icons/index.js
@@ -13,6 +13,7 @@ import Budibase from "./Budibase.svelte"
import Oracle from "./Oracle.svelte"
import GoogleSheets from "./GoogleSheets.svelte"
import Firebase from "./Firebase.svelte"
+import Redis from "./Redis.svelte"
export default {
BUDIBASE: Budibase,
@@ -30,4 +31,5 @@ export default {
ORACLE: Oracle,
GOOGLE_SHEETS: GoogleSheets,
FIREBASE: Firebase,
+ REDIS: Redis,
}
diff --git a/packages/builder/src/components/integration/QueryViewer.svelte b/packages/builder/src/components/integration/QueryViewer.svelte
index 19c1165d9e..8f6f9eeb53 100644
--- a/packages/builder/src/components/integration/QueryViewer.svelte
+++ b/packages/builder/src/components/integration/QueryViewer.svelte
@@ -72,7 +72,7 @@
fields = response.schema
notifications.success("Query executed successfully")
} catch (error) {
- notifications.error("Error previewing query")
+ notifications.error(`Query Error: ${error.message}`)
}
}
diff --git a/packages/builder/src/constants/backend/index.js b/packages/builder/src/constants/backend/index.js
index 9a623241ca..2f5f26e476 100644
--- a/packages/builder/src/constants/backend/index.js
+++ b/packages/builder/src/constants/backend/index.js
@@ -179,6 +179,7 @@ export const IntegrationTypes = {
INTERNAL: "INTERNAL",
GOOGLE_SHEETS: "GOOGLE_SHEETS",
FIREBASE: "FIREBASE",
+ REDIS: "REDIS",
}
export const IntegrationNames = {
@@ -197,6 +198,7 @@ export const IntegrationNames = {
[IntegrationTypes.INTERNAL]: "Internal",
[IntegrationTypes.GOOGLE_SHEETS]: "Google Sheets",
[IntegrationTypes.FIREBASE]: "Firebase",
+ [IntegrationTypes.REDIS]: "Redis",
}
export const SchemaTypeOptions = [
diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/DatasourceModal.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/DatasourceModal.svelte
new file mode 100644
index 0000000000..e9233113bb
--- /dev/null
+++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/DatasourceModal.svelte
@@ -0,0 +1,199 @@
+
+
+
+