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 @@ + + + + + + {#each filteredSources as datasource} +
+
+
+ +
+
{datasource.name}
+
+ {#if Array.isArray(datasource.entities)} + {#each datasource.entities.filter(table => table._id !== "ta_users") as table} +
x.table === table.name + )} + on:click={() => toggleScreenSelection(table, datasource)} + > + + + + {table.name} + + {#if selectedScreens.find(x => x.table === table.name)} + + + + {/if} +
+ {/each} + {/if} + {#if datasource["entities"] && !Array.isArray(datasource.entities)} + {#each Object.keys(datasource.entities).filter(table => table._id !== "ta_users") as table_key} +
x.table === datasource.entities[table_key].name + )} + on:click={() => + toggleScreenSelection( + datasource.entities[table_key], + datasource + )} + > + + + + {datasource.entities[table_key].name} + + {#if selectedScreens.find(x => x.table === datasource.entities[table_key].name)} + + + + {/if} +
+ {/each} + {/if} +
+ {/each} +
+
+
+ + diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/NewScreenModal.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/NewScreenModal.svelte index cd83d81235..8fd22926e4 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/NewScreenModal.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/NewScreenModal.svelte @@ -1,117 +1,98 @@
- - Please select the screens you would like to add to your application. - Autogenerated screens come with CRUD functionality. - - Blank screen
x.id.includes(blankScreen))} - on:click={() => - toggleScreenSelection(templates.find(t => t.id === blankScreen))} - class:disabled={autoSelected} + class="screen-type item" + class:selected={selectedScreenMode == blankScreenModeKey} + on:click={() => { + selectedScreenMode = blankScreenModeKey + }} > -
-
Blank
+
+ +
+ Blank screen + Add a blank screen +
- {#if selectedScreens.find(x => x.id === blankScreen)} + {#if selectedScreenMode == blankScreenModeKey}
- +
{/if}
- {#if $tables.list.filter(table => table._id !== "ta_users").length > 0} - Autogenerated Screens - {#each $tables.list.filter(table => table._id !== "ta_users") as table} -
x.table === table.name)} - on:click={() => toggleScreenSelection(table)} - class="item" - > -
-
{table.name}
-
-
- {#if selectedScreens.find(x => x.table === table.name)} -
- -
- {/if} -
+
{ + selectedScreenMode = autoCreateModeKey + }} + class:disabled={!$tables.list.filter(table => table._id !== "ta_users") + .length} + > +
+ +
+ Autogenerated screens + + Add autogenerated screens with CRUD functionality to get a working + app quickly! (Requires a data source) +
- {/each} - {/if} +
+
+ {#if selectedScreenMode == autoCreateModeKey} +
+ +
+ {/if} +
+
-
- {#if showProgressCircle} - - {/if} -
diff --git a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenDetailsModal.svelte b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenDetailsModal.svelte index 75977e51a2..4afd182053 100644 --- a/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenDetailsModal.svelte +++ b/packages/builder/src/pages/builder/app/[application]/design/[screenId]/screens/_components/ScreenDetailsModal.svelte @@ -1,18 +1,23 @@ - + + + + + newScreenModal.show()} + initalScreens={!selectedTemplates ? [] : [...selectedTemplates]} + /> + + + + + Select which level of access you want your screens to have +