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/.github/workflows/release.yml b/.github/workflows/release.yml index 07df3bd427..b3b2b01316 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -71,3 +71,57 @@ jobs: DOCKER_USER: ${{ secrets.DOCKER_USERNAME }} DOCKER_PASSWORD: ${{ secrets.DOCKER_API_KEY }} BUDIBASE_RELEASE_VERSION: ${{ steps.previoustag.outputs.tag }} + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-region: eu-west-1 + + - name: Tag and release Proxy service docker image + run: | + docker login -u $DOCKER_USER -p $DOCKER_PASSWORD + yarn build:docker:proxy:preprod + docker tag proxy-service budibase/proxy:$PREPROD_TAG + docker push budibase/proxy:$PREPROD_TAG + env: + DOCKER_USER: ${{ secrets.DOCKER_USERNAME }} + DOCKER_PASSWORD: ${{ secrets.DOCKER_API_KEY }} + PREPROD_TAG: k8s-preprod + + - name: Pull values.yaml from budibase-infra + run: | + curl -H "Authorization: token ${{ secrets.GH_PERSONAL_TOKEN }}" \ + -H 'Accept: application/vnd.github.v3.raw' \ + -o values.preprod.yaml \ + -L https://api.github.com/repos/budibase/budibase-infra/contents/kubernetes/budibase-preprod/values.yaml + wc -l values.preprod.yaml + + - name: Deploy to Preprod Environment + uses: glopezep/helm@v1.7.1 + with: + release: budibase-preprod + namespace: budibase + chart: charts/budibase + token: ${{ github.token }} + helm: helm3 + values: | + globals: + appVersion: ${{ steps.previoustag.outputs.tag }} + ingress: + enabled: true + nginx: true + value-files: >- + [ + "values.preprod.yaml" + ] + env: + KUBECONFIG_FILE: '${{ secrets.PREPROD_KUBECONFIG }}' + + - name: Discord Webhook Action + uses: tsickert/discord-webhook@v4.0.0 + with: + webhook-url: ${{ secrets.PROD_DEPLOY_WEBHOOK_URL }} + content: "Preprod Deployment Complete: ${{ steps.previoustag.outputs.tag }} deployed to Budibase Pre-prod." + embed-title: ${{ steps.previoustag.outputs.tag }} diff --git a/hosting/docker-compose.dev.yaml b/hosting/docker-compose.dev.yaml index 43b8526e9e..be0bc74a26 100644 --- a/hosting/docker-compose.dev.yaml +++ b/hosting/docker-compose.dev.yaml @@ -27,6 +27,7 @@ services: image: nginx:latest volumes: - ./.generated-nginx.dev.conf:/etc/nginx/nginx.conf + - ./proxy/error.html:/usr/share/nginx/html/error.html ports: - "${MAIN_PORT}:10000" depends_on: diff --git a/hosting/nginx.dev.conf.hbs b/hosting/nginx.dev.conf.hbs index 9fc2345fb2..9398b7e719 100644 --- a/hosting/nginx.dev.conf.hbs +++ b/hosting/nginx.dev.conf.hbs @@ -28,6 +28,12 @@ http { ignore_invalid_headers off; proxy_buffering off; + error_page 502 503 504 /error.html; + location = /error.html { + root /usr/share/nginx/html; + internal; + } + location /db/ { proxy_pass http://couchdb-service:5984; rewrite ^/db/(.*)$ /$1 break; diff --git a/hosting/nginx.prod.conf.hbs b/hosting/nginx.prod.conf.hbs index 88570a4a2d..7ef597051b 100644 --- a/hosting/nginx.prod.conf.hbs +++ b/hosting/nginx.prod.conf.hbs @@ -56,6 +56,12 @@ http { set $csp_media "media-src 'self' https://js.intercomcdn.com"; set $csp_worker "worker-src 'none'"; + error_page 502 503 504 /error.html; + location = /error.html { + root /usr/share/nginx/html; + internal; + } + # Security Headers add_header X-Frame-Options SAMEORIGIN always; add_header X-Content-Type-Options nosniff always; diff --git a/hosting/proxy/Dockerfile b/hosting/proxy/Dockerfile index b577e3e40f..a2b17d3333 100644 --- a/hosting/proxy/Dockerfile +++ b/hosting/proxy/Dockerfile @@ -1,2 +1,3 @@ FROM nginx:latest -COPY .generated-nginx.prod.conf /etc/nginx/nginx.conf \ No newline at end of file +COPY .generated-nginx.prod.conf /etc/nginx/nginx.conf +COPY error.html /usr/share/nginx/html/error.html \ No newline at end of file diff --git a/hosting/proxy/error.html b/hosting/proxy/error.html new file mode 100644 index 0000000000..023c1ebaff --- /dev/null +++ b/hosting/proxy/error.html @@ -0,0 +1,175 @@ + + + + + Budibase + + + + + + + + + + +
+
+
+ Budibase Logo +
+
+
+

+

+ Houston we have a problem! +

+

+

+
+
+ + +
+
+
+
+ + + \ No newline at end of file diff --git a/lerna.json b/lerna.json index ec8a84538c..f6bce3d611 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "1.0.105-alpha.35", + "version": "1.0.105-alpha.42", "npmClient": "yarn", "packages": [ "packages/*" diff --git a/packages/backend-core/package.json b/packages/backend-core/package.json index f286389c22..b67ee44fbe 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.42", "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/backend-core/src/middleware/passport/google.js b/packages/backend-core/src/middleware/passport/google.js index 5e95a906d8..b12a668327 100644 --- a/packages/backend-core/src/middleware/passport/google.js +++ b/packages/backend-core/src/middleware/passport/google.js @@ -2,7 +2,7 @@ const GoogleStrategy = require("passport-google-oauth").OAuth2Strategy const { authenticateThirdParty } = require("./third-party-common") -const buildVerifyFn = async saveUserFn => { +const buildVerifyFn = saveUserFn => { return (accessToken, refreshToken, profile, done) => { const thirdPartyUser = { provider: profile.provider, // should always be 'google' diff --git a/packages/backend-core/src/utils.js b/packages/backend-core/src/utils.js index e4b358a676..56205f3487 100644 --- a/packages/backend-core/src/utils.js +++ b/packages/backend-core/src/utils.js @@ -176,11 +176,25 @@ exports.getGlobalUserByEmail = async email => { }) } -exports.getBuildersCount = async () => { +const getBuilders = async () => { const builders = await queryGlobalView(ViewNames.USER_BY_BUILDERS, { include_docs: false, }) - return builders ? builders.length : 0 + + if (!builders) { + return [] + } + + if (Array.isArray(builders)) { + return builders + } else { + return [builders] + } +} + +exports.getBuildersCount = async () => { + const builders = await getBuilders() + return builders.length } exports.saveUser = async ( diff --git a/packages/bbui/package.json b/packages/bbui/package.json index a9d8fc249a..9d8bc88c6c 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.42", "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.42", "@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 2997bd052a..2c2a43e711 100644 --- a/packages/builder/cypress/integration/autoScreensUI.spec.js +++ b/packages/builder/cypress/integration/autoScreensUI.spec.js @@ -4,35 +4,86 @@ filterTests(['smoke', 'all'], () => { context("Auto Screens UI", () => { before(() => { cy.login() - cy.createTestApp() }) - + + 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", () => { - // Create autogenerated screens from the internal table - cy.createAutogeneratedScreens(["Cypress Tests"]) + 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') + .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]) + // 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') + .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') + .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 @@ -40,11 +91,12 @@ filterTests(['smoke', 'all'], () => { // 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"]) + // 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') + .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..9273e7566d 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.42", "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.42", + "@budibase/client": "^1.0.105-alpha.42", + "@budibase/frontend-core": "^1.0.105-alpha.42", + "@budibase/string-templates": "^1.0.105-alpha.42", "@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/automation/AutomationBuilder/FlowChart/FlowItem.svelte b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte index ef0a61646e..0829b85a90 100644 --- a/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte +++ b/packages/builder/src/components/automation/AutomationBuilder/FlowChart/FlowItem.svelte @@ -48,10 +48,6 @@ $automationStore.selectedAutomation?.automation?.definition?.steps.length + 1 - $: hasCompletedInputs = Object.keys( - block.schema?.inputs?.properties || {} - ).every(x => block?.inputs[x]) - $: loopingSelected = $automationStore.selectedAutomation?.automation.definition.steps.find( x => x.blockToLoop === block.id @@ -290,13 +286,7 @@
- actionModal.show()} - disabled={!hasCompletedInputs} - hoverable - name="AddCircle" - size="S" -/> + actionModal.show()} hoverable name="AddCircle" size="S" /> {#if isTrigger ? totalBlocks > 1 : blockIdx !== totalBlocks - 2}
{/if} diff --git a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte index 856ab5f31a..4333e4a2e5 100644 --- a/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte +++ b/packages/builder/src/components/automation/SetupPanel/AutomationBlockSetup.svelte @@ -25,11 +25,11 @@ import QueryParamSelector from "./QueryParamSelector.svelte" import CronBuilder from "./CronBuilder.svelte" import Editor from "components/integration/QueryEditor.svelte" - import { debounce } from "lodash" import ModalBindableInput from "components/common/bindings/ModalBindableInput.svelte" import FilterDrawer from "components/design/PropertiesPanel/PropertyControls/FilterEditor/FilterDrawer.svelte" import { LuceneUtils } from "@budibase/frontend-core" import { getSchemaForTable } from "builderStore/dataBinding" + import { Utils } from "@budibase/frontend-core" export let block export let testData @@ -54,7 +54,7 @@ $: schema = getSchemaForTable(tableId, { searchableSchema: true }).schema $: schemaFields = Object.values(schema || {}) - const onChange = debounce(async function (e, key) { + const onChange = Utils.sequential(async (e, key) => { try { if (isTestModal) { // Special case for webhook, as it requires a body, but the schema already brings back the body's contents @@ -82,7 +82,7 @@ } catch (error) { notifications.error("Error saving automation") } - }, 800) + }) function getAvailableBindings(block, automation) { if (!block || !automation) { @@ -226,6 +226,7 @@ on:change={e => onChange(e, key)} {bindings} fillWidth + updateOnChange={false} /> {:else} onChange(e, key)} {bindings} allowJS={false} + updateOnChange={false} /> {/if} {:else if value.customType === "query"} @@ -310,6 +312,7 @@ type={value.customType} on:change={e => onChange(e, key)} {bindings} + updateOnChange={false} /> {:else}
@@ -321,6 +324,7 @@ value={inputData[key]} on:change={e => onChange(e, key)} {bindings} + updateOnChange={false} />
{/if} diff --git a/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte b/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte index 971a9cc51b..f91cd62bed 100644 --- a/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte +++ b/packages/builder/src/components/automation/SetupPanel/RowSelector.svelte @@ -43,6 +43,11 @@ } const coerce = (value, type) => { + const re = new RegExp(/{{([^{].*?)}}/g) + if (re.test(value)) { + return value + } + if (type === "boolean") { if (typeof value === "boolean") { return value @@ -120,6 +125,7 @@ {bindings} fillWidth={true} allowJS={true} + updateOnChange={false} /> {/if} {:else if !rowControl} @@ -137,6 +143,7 @@ {bindings} fillWidth={true} allowJS={true} + updateOnChange={false} /> {/if} {/if} diff --git a/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte b/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte index 2a51e8d200..f66df3a9b1 100644 --- a/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte +++ b/packages/builder/src/components/automation/SetupPanel/RowSelectorTypes.svelte @@ -60,5 +60,6 @@ {bindings} fillWidth={true} allowJS={true} + updateOnChange={false} /> {/if} diff --git a/packages/builder/src/components/automation/SetupPanel/SchemaSetup.svelte b/packages/builder/src/components/automation/SetupPanel/SchemaSetup.svelte index 71b10a3569..cb80072694 100644 --- a/packages/builder/src/components/automation/SetupPanel/SchemaSetup.svelte +++ b/packages/builder/src/components/automation/SetupPanel/SchemaSetup.svelte @@ -30,6 +30,10 @@ label: "DateTime", value: "datetime", }, + { + label: "Array", + value: "array", + }, ] function addField() { @@ -70,6 +74,7 @@ secondary placeholder="Enter field name" on:change={fieldNameChanged(field.name)} + updateOnChange={false} /> { + analytics.captureEvent(Events.SCREEN.CREATE_ROLE_UPDATED, { + screenAccessRole, + }) + }} + label="Access" + getOptionLabel={role => role.name} + getOptionValue={role => role._id} + getOptionColor={role => role.color} + options={$roles} + /> + newScreenModal.show()} + initialUrl={blankScreenUrl} /> 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/portal/manage/auth/index.svelte b/packages/builder/src/pages/builder/portal/manage/auth/index.svelte index b001f02fe9..242b21d945 100644 --- a/packages/builder/src/pages/builder/portal/manage/auth/index.svelte +++ b/packages/builder/src/pages/builder/portal/manage/auth/index.svelte @@ -160,6 +160,10 @@ } docs.forEach(element => { + // Delete unsupported fields + delete element.createdAt + delete element.updatedAt + if (element.type === ConfigTypes.OIDC) { // Add a UUID here so each config is distinguishable when it arrives at the login page for (let config of element.config.configs) { diff --git a/packages/cli/package.json b/packages/cli/package.json index 03b27b289b..7aa34fecf1 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/cli", - "version": "1.0.105-alpha.35", + "version": "1.0.105-alpha.42", "description": "Budibase CLI, for developers, self hosting and migrations.", "main": "src/index.js", "bin": { diff --git a/packages/client/package.json b/packages/client/package.json index c25cc0932f..149e80b367 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/client", - "version": "1.0.105-alpha.35", + "version": "1.0.105-alpha.42", "license": "MPL-2.0", "module": "dist/budibase-client.js", "main": "dist/budibase-client.js", @@ -19,9 +19,9 @@ "dev:builder": "rollup -cw" }, "dependencies": { - "@budibase/bbui": "^1.0.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.42", + "@budibase/frontend-core": "^1.0.105-alpha.42", + "@budibase/string-templates": "^1.0.105-alpha.42", "@spectrum-css/button": "^3.0.3", "@spectrum-css/card": "^3.0.3", "@spectrum-css/divider": "^1.0.3", diff --git a/packages/frontend-core/package.json b/packages/frontend-core/package.json index 7e482e0878..984ae7d922 100644 --- a/packages/frontend-core/package.json +++ b/packages/frontend-core/package.json @@ -1,12 +1,12 @@ { "name": "@budibase/frontend-core", - "version": "1.0.105-alpha.35", + "version": "1.0.105-alpha.42", "description": "Budibase frontend core libraries used in builder and client", "author": "Budibase", "license": "MPL-2.0", "svelte": "src/index.js", "dependencies": { - "@budibase/bbui": "^1.0.105-alpha.35", + "@budibase/bbui": "^1.0.105-alpha.42", "lodash": "^4.17.21", "svelte": "^3.46.2" } diff --git a/packages/server/package.json b/packages/server/package.json index 55730d61e1..71a97da731 100644 --- a/packages/server/package.json +++ b/packages/server/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/server", "email": "hi@budibase.com", - "version": "1.0.105-alpha.35", + "version": "1.0.105-alpha.42", "description": "Budibase Web Server", "main": "src/index.ts", "repository": { @@ -68,10 +68,10 @@ "license": "GPL-3.0", "dependencies": { "@apidevtools/swagger-parser": "^10.0.3", - "@budibase/backend-core": "^1.0.105-alpha.35", - "@budibase/client": "^1.0.105-alpha.35", - "@budibase/pro": "1.0.105-alpha.34", - "@budibase/string-templates": "^1.0.105-alpha.35", + "@budibase/backend-core": "^1.0.105-alpha.42", + "@budibase/client": "^1.0.105-alpha.42", + "@budibase/pro": "1.0.105-alpha.42", + "@budibase/string-templates": "^1.0.105-alpha.42", "@bull-board/api": "^3.7.0", "@bull-board/koa": "^3.7.0", "@elastic/elasticsearch": "7.10.0", @@ -160,6 +160,7 @@ "copyfiles": "^2.4.1", "docker-compose": "^0.23.6", "eslint": "^6.8.0", + "ioredis-mock": "^7.2.0", "is-wsl": "^2.2.0", "jest": "^27.0.5", "jest-openapi": "^0.14.2", diff --git a/packages/server/src/definitions/datasource.ts b/packages/server/src/definitions/datasource.ts index 4df08683f0..23266991ee 100644 --- a/packages/server/src/definitions/datasource.ts +++ b/packages/server/src/definitions/datasource.ts @@ -49,6 +49,7 @@ export enum SourceNames { ORACLE = "ORACLE", GOOGLE_SHEETS = "GOOGLE_SHEETS", FIREBASE = "FIREBASE", + REDIS = "REDIS", } export enum IncludeRelationships { diff --git a/packages/server/src/integrations/index.ts b/packages/server/src/integrations/index.ts index 7408c6e001..711e9d2262 100644 --- a/packages/server/src/integrations/index.ts +++ b/packages/server/src/integrations/index.ts @@ -11,6 +11,7 @@ const arangodb = require("./arangodb") const rest = require("./rest") const googlesheets = require("./googlesheets") const firebase = require("./firebase") +const redis = require("./redis") const { SourceNames } = require("../definitions/datasource") const environment = require("../environment") @@ -26,6 +27,8 @@ const DEFINITIONS = { [SourceNames.MYSQL]: mysql.schema, [SourceNames.ARANGODB]: arangodb.schema, [SourceNames.REST]: rest.schema, + [SourceNames.FIREBASE]: firebase.schema, + [SourceNames.REDIS]: redis.schema, } const INTEGRATIONS = { @@ -42,6 +45,7 @@ const INTEGRATIONS = { [SourceNames.REST]: rest.integration, [SourceNames.FIREBASE]: firebase.integration, [SourceNames.GOOGLE_SHEETS]: googlesheets.integration, + [SourceNames.REDIS]: redis.integration, } // optionally add oracle integration if the oracle binary can be installed diff --git a/packages/server/src/integrations/redis.ts b/packages/server/src/integrations/redis.ts new file mode 100644 index 0000000000..29971c4b62 --- /dev/null +++ b/packages/server/src/integrations/redis.ts @@ -0,0 +1,152 @@ +import { + DatasourceFieldTypes, + Integration, + QueryTypes, +} from "../definitions/datasource" +import Redis from "ioredis" + +module RedisModule { + interface RedisConfig { + host: string + port: number + username: string + password?: string + } + + const SCHEMA: Integration = { + docs: "https://redis.io/docs/", + description: "", + friendlyName: "Redis", + datasource: { + host: { + type: "string", + required: true, + default: "localhost", + }, + port: { + type: "number", + required: true, + default: 6379, + }, + username: { + type: "string", + required: false, + }, + password: { + type: "password", + required: false, + }, + }, + query: { + create: { + type: QueryTypes.FIELDS, + fields: { + key: { + type: DatasourceFieldTypes.STRING, + required: true, + }, + value: { + type: DatasourceFieldTypes.STRING, + required: true, + }, + ttl: { + type: DatasourceFieldTypes.NUMBER, + }, + }, + }, + read: { + readable: true, + type: QueryTypes.FIELDS, + fields: { + key: { + type: DatasourceFieldTypes.STRING, + required: true, + }, + }, + }, + delete: { + type: QueryTypes.FIELDS, + fields: { + key: { + type: DatasourceFieldTypes.STRING, + required: true, + }, + }, + }, + command: { + readable: true, + displayName: "Redis Command", + type: QueryTypes.JSON, + }, + }, + } + + class RedisIntegration { + private readonly config: RedisConfig + private client: any + + constructor(config: RedisConfig) { + this.config = config + this.client = new Redis({ + host: this.config.host, + port: this.config.port, + username: this.config.username, + password: this.config.password, + }) + } + + async disconnect() { + this.client.disconnect() + } + + async redisContext(query: Function) { + try { + return await query() + } catch (err) { + throw new Error(`Redis error: ${err}`) + } finally { + this.disconnect() + } + } + + async create(query: { key: string; value: string; ttl: number }) { + return this.redisContext(async () => { + const response = await this.client.set(query.key, query.value) + if (query.ttl) { + await this.client.expire(query.key, query.ttl) + } + return response + }) + } + + async read(query: { key: string }) { + return this.redisContext(async () => { + const response = await this.client.get(query.key) + return response + }) + } + + async delete(query: { key: string }) { + return this.redisContext(async () => { + const response = await this.client.del(query.key) + return response + }) + } + + async command(query: { json: string }) { + return this.redisContext(async () => { + const commands = query.json.trim().split(" ") + const pipeline = this.client.pipeline([commands]) + const result = await pipeline.exec() + return { + response: result[0][1], + } + }) + } + } + + module.exports = { + schema: SCHEMA, + integration: RedisIntegration, + } +} diff --git a/packages/server/src/integrations/tests/redis.spec.js b/packages/server/src/integrations/tests/redis.spec.js new file mode 100644 index 0000000000..219584bdb2 --- /dev/null +++ b/packages/server/src/integrations/tests/redis.spec.js @@ -0,0 +1,60 @@ +const Redis = require("ioredis-mock") +const RedisIntegration = require("../redis") + +class TestConfiguration { + constructor(config = {}) { + this.integration = new RedisIntegration.integration(config) + this.redis = new Redis({ + data: { + test: 'test', + result: "1" + }, + }) + this.integration.client = this.redis + } +} + +describe("Redis Integration", () => { + let config + + beforeEach(() => { + config = new TestConfiguration() + }) + + it("calls the create method with the correct params", async () => { + const body = { + key: "key", + value: "value" + } + const response = await config.integration.create(body) + expect(await config.redis.get("key")).toEqual("value") + }) + + it("calls the read method with the correct params", async () => { + const body = { + key: "test" + } + const response = await config.integration.read(body) + expect(response).toEqual("test") + }) + + it("calls the delete method with the correct params", async () => { + const body = { + key: "test" + } + await config.integration.delete(body) + expect(await config.redis.get(body.key)).toEqual(null) + }) + + it("calls the command method with the correct params", async () => { + const body = { + json: "KEYS *" + } + + // ioredis-mock doesn't support pipelines + config.integration.client.pipeline = jest.fn(() => ({ exec: jest.fn(() => [[]]) })) + + await config.integration.command(body) + expect(config.integration.client.pipeline).toHaveBeenCalledWith([["KEYS", "*"]]) + }) +}) \ No newline at end of file diff --git a/packages/server/src/threads/utils.js b/packages/server/src/threads/utils.js index bf89791874..5d8dc115e5 100644 --- a/packages/server/src/threads/utils.js +++ b/packages/server/src/threads/utils.js @@ -72,6 +72,7 @@ exports.hasExtraData = response => { return ( typeof response === "object" && !Array.isArray(response) && + response && response.data != null && response.info != null ) diff --git a/packages/server/yarn.lock b/packages/server/yarn.lock index 4e96e86028..4537e7efb8 100644 --- a/packages/server/yarn.lock +++ b/packages/server/yarn.lock @@ -1014,10 +1014,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@^1.0.0": - version "1.0.115" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.115.tgz#c188dc9d4abe8f7d8088c54aeaa9f9c620bbbdba" - integrity sha512-QGTaYyXWIInlKFzL514vDUh2gNob3Tckt1Lvtjk3Z5hhx2K7JZ5T2JwxSJ3qOBRKG6h2jI6HxlKEXPUefETAVA== +"@budibase/backend-core@1.0.105-alpha.40": + version "1.0.105-alpha.40" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.105-alpha.40.tgz#7e8e3c548d09001a002364d2a9fa4dabf2d1bcce" + integrity sha512-lOUJx5yFAcBld+SNwbO1hUV2ucM2J+Y4AIG0BQeNH2kPul74sHlZpEocbmNu+R88NgKinTLcnB0wCdoD0MP+4g== dependencies: "@techpass/passport-openidconnect" "^0.3.0" aws-sdk "^2.901.0" @@ -1087,12 +1087,12 @@ svelte-flatpickr "^3.2.3" svelte-portal "^1.0.0" -"@budibase/pro@1.0.105-alpha.34": - version "1.0.105-alpha.34" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.105-alpha.34.tgz#75cdb7e29a22b6dc7e2d2846d8fce95be22bedb4" - integrity sha512-OSPNjXYI2awQfKzGEDRjPVn/4R1Dd9xFhBwirrwq3BRrhBJbHg7uKRELCJfiLu7wsEqZSZqqbH5Ugyxcj8n+PQ== +"@budibase/pro@1.0.105-alpha.40": + version "1.0.105-alpha.40" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.105-alpha.40.tgz#155952a2645b547cb974a3c1bccb17f3075849e2" + integrity sha512-qrd7vxBkLcLoxWVtakg1P8M7NZUVM5d/ROveUsS1pDAVQndiI8fVrc7YeOfIiHmqdaFQXbwp9tn6ZviMuihVsA== dependencies: - "@budibase/backend-core" "^1.0.0" + "@budibase/backend-core" "1.0.105-alpha.40" node-fetch "^2.6.1" "@budibase/standard-components@^0.9.139": @@ -5844,6 +5844,20 @@ fecha@^4.2.0: resolved "https://registry.yarnpkg.com/fecha/-/fecha-4.2.1.tgz#0a83ad8f86ef62a091e22bb5a039cd03d23eecce" integrity sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q== +fengari-interop@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/fengari-interop/-/fengari-interop-0.1.3.tgz#3ad37a90e7430b69b365441e9fc0ba168942a146" + integrity sha512-EtZ+oTu3kEwVJnoymFPBVLIbQcCoy9uWCVnMA6h3M/RqHkUBsLYp29+RRHf9rKr6GwjubWREU1O7RretFIXjHw== + +fengari@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/fengari/-/fengari-0.1.4.tgz#72416693cd9e43bd7d809d7829ddc0578b78b0bb" + integrity sha512-6ujqUuiIYmcgkGz8MGAdERU57EIluGGPSUgGPTsco657EHa+srq0S3/YUl/r9kx1+D+d4rGfYObd+m8K22gB1g== + dependencies: + readline-sync "^1.4.9" + sprintf-js "^1.1.1" + tmp "^0.0.33" + fetch-cookie@0.10.1: version "0.10.1" resolved "https://registry.yarnpkg.com/fetch-cookie/-/fetch-cookie-0.10.1.tgz#5ea88f3d36950543c87997c27ae2aeafb4b5c4d4" @@ -6960,6 +6974,16 @@ invert-kv@^2.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== +ioredis-mock@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/ioredis-mock/-/ioredis-mock-7.2.0.tgz#48f006c07ef7f1f93f75e60d8f9035fa46c4ef0a" + integrity sha512-xzABBG3NhfDBGxH1KX9n6vs7WGNn9lhcxMT3b+vjynVImxlUV+vOXU+tjGzSUnGmx4IYllA8RqbXN8z6ROMPVA== + dependencies: + fengari "^0.1.4" + fengari-interop "^0.1.3" + redis-commands "^1.7.0" + standard-as-callback "^2.1.0" + ioredis@^4.27.0: version "4.28.0" resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.28.0.tgz#5a2be3f37ff2075e2332f280eaeb02ab4d9ff0d3" @@ -11122,6 +11146,11 @@ readdirp@~3.6.0: dependencies: picomatch "^2.2.1" +readline-sync@^1.4.9: + version "1.4.10" + resolved "https://registry.yarnpkg.com/readline-sync/-/readline-sync-1.4.10.tgz#41df7fbb4b6312d673011594145705bf56d8873b" + integrity sha512-gNva8/6UAe8QYepIQH/jQ2qn91Qj0B9sYjMBBs3QOB8F2CXcKgLxQaJRP76sWVRQt+QU+8fAkCbCvjjMFu7Ycw== + realpath-native@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.1.0.tgz#2003294fea23fb0672f2476ebe22fcf498a2d65c" @@ -11163,7 +11192,7 @@ rechoir@^0.7.0: dependencies: resolve "^1.9.0" -redis-commands@1.7.0: +redis-commands@1.7.0, redis-commands@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/redis-commands/-/redis-commands-1.7.0.tgz#15a6fea2d58281e27b1cd1acfb4b293e278c3a89" integrity sha512-nJWqw3bTFy21hX/CPKHth6sfhZbdiHP6bTawSgQBlKOVRG7EZkfHbbHwQJnrE4vsQf0CMNE+3gJ4Fmm16vdVlQ== @@ -11974,7 +12003,7 @@ split2@^4.1.0: resolved "https://registry.yarnpkg.com/split2/-/split2-4.1.0.tgz#101907a24370f85bb782f08adaabe4e281ecf809" integrity sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ== -sprintf-js@^1.1.2: +sprintf-js@^1.1.1, sprintf-js@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== diff --git a/packages/string-templates/package.json b/packages/string-templates/package.json index 1b352bb9fe..3d0e585f73 100644 --- a/packages/string-templates/package.json +++ b/packages/string-templates/package.json @@ -1,6 +1,6 @@ { "name": "@budibase/string-templates", - "version": "1.0.105-alpha.35", + "version": "1.0.105-alpha.42", "description": "Handlebars wrapper for Budibase templating.", "main": "src/index.cjs", "module": "dist/bundle.mjs", diff --git a/packages/worker/package.json b/packages/worker/package.json index 14f9fe48c4..52f47f2d09 100644 --- a/packages/worker/package.json +++ b/packages/worker/package.json @@ -1,7 +1,7 @@ { "name": "@budibase/worker", "email": "hi@budibase.com", - "version": "1.0.105-alpha.35", + "version": "1.0.105-alpha.42", "description": "Budibase background service", "main": "src/index.ts", "repository": { @@ -31,9 +31,9 @@ "author": "Budibase", "license": "GPL-3.0", "dependencies": { - "@budibase/backend-core": "^1.0.105-alpha.35", - "@budibase/pro": "1.0.105-alpha.34", - "@budibase/string-templates": "^1.0.105-alpha.35", + "@budibase/backend-core": "^1.0.105-alpha.42", + "@budibase/pro": "1.0.105-alpha.42", + "@budibase/string-templates": "^1.0.105-alpha.42", "@koa/router": "^8.0.0", "@sentry/node": "6.17.7", "@techpass/passport-openidconnect": "^0.3.0", diff --git a/packages/worker/yarn.lock b/packages/worker/yarn.lock index ffb20ec1d5..59cb73118a 100644 --- a/packages/worker/yarn.lock +++ b/packages/worker/yarn.lock @@ -286,10 +286,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@budibase/backend-core@^1.0.0": - version "1.0.115" - resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.115.tgz#c188dc9d4abe8f7d8088c54aeaa9f9c620bbbdba" - integrity sha512-QGTaYyXWIInlKFzL514vDUh2gNob3Tckt1Lvtjk3Z5hhx2K7JZ5T2JwxSJ3qOBRKG6h2jI6HxlKEXPUefETAVA== +"@budibase/backend-core@1.0.105-alpha.38": + version "1.0.105-alpha.38" + resolved "https://registry.yarnpkg.com/@budibase/backend-core/-/backend-core-1.0.105-alpha.38.tgz#399bc37877392f04c0072936d1c74d35d2997d6c" + integrity sha512-IKVw3+a42Yea49Qc8vbVd+KZzOIAfgAFEohApJZSxqKA5UaFeWPJ343LQ7oC6jbYOkRVlGRnkrvXXa1jRggqbA== dependencies: "@techpass/passport-openidconnect" "^0.3.0" aws-sdk "^2.901.0" @@ -310,12 +310,12 @@ uuid "^8.3.2" zlib "^1.0.5" -"@budibase/pro@1.0.105-alpha.34": - version "1.0.105-alpha.34" - resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.105-alpha.34.tgz#75cdb7e29a22b6dc7e2d2846d8fce95be22bedb4" - integrity sha512-OSPNjXYI2awQfKzGEDRjPVn/4R1Dd9xFhBwirrwq3BRrhBJbHg7uKRELCJfiLu7wsEqZSZqqbH5Ugyxcj8n+PQ== +"@budibase/pro@1.0.105-alpha.38": + version "1.0.105-alpha.38" + resolved "https://registry.yarnpkg.com/@budibase/pro/-/pro-1.0.105-alpha.38.tgz#f025daf5667798083a7b0af301d6231dc3e58f00" + integrity sha512-TXSov/c2KT7YuxZRspSd4iNPZPiJOdpl91ZTxGrqaPfK8PDmgk/XBWkHci+z1WLCvf6YY2kuQJGp1moldoLO1g== dependencies: - "@budibase/backend-core" "^1.0.0" + "@budibase/backend-core" "1.0.105-alpha.38" node-fetch "^2.6.1" "@cspotcode/source-map-consumer@0.8.0": diff --git a/scripts/link-dependencies.sh b/scripts/link-dependencies.sh index bb4b1f4f0d..8c4294fa08 100755 --- a/scripts/link-dependencies.sh +++ b/scripts/link-dependencies.sh @@ -9,13 +9,17 @@ yarn link cd - if [ -d "../budibase-pro" ]; then - cd ../budibase-pro/packages/pro + cd ../budibase-pro + yarn bootstrap + + cd packages/pro echo "Linking pro" yarn link echo "Linking backend-core to pro" yarn link '@budibase/backend-core' - cd - + + cd ../../../budibase echo "Linking pro to worker" cd packages/worker && yarn link '@budibase/pro' diff --git a/scripts/pro/release.sh b/scripts/pro/release.sh index 4b30fa4b1d..c505c45c28 100755 --- a/scripts/pro/release.sh +++ b/scripts/pro/release.sh @@ -5,18 +5,16 @@ if [[ -z "${CI}" ]]; then exit 0 fi -# Release pro as same version as budibase +############################################# +# SETUP # +############################################# + +# Release pro with same version as budibase VERSION=$(jq -r .version lerna.json) echo "Version: $VERSION" COMMAND=$1 echo "Command: $COMMAND" -# Go to pro package -cd ../budibase-pro - -# Install NPM credentials -echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} >> .npmrc - # Determine tag to use TAG="" if [[ $COMMAND == "develop" ]]; then @@ -27,24 +25,70 @@ fi echo "Releasing version $VERSION" echo "Releasing tag $TAG" + +############################################# +# PRE-PUBLISH # +############################################# + +# Go to pro repo root +cd ../budibase-pro + +# Install NPM credentials +echo //registry.npmjs.org/:_authToken=${NPM_TOKEN} >> .npmrc + +# Sync backend-core version in packages/pro/package.json +# Ensures pro does not use out of date dependency +cd packages/pro +jq '.dependencies."@budibase/backend-core"="'$VERSION'"' package.json > package.json.tmp && mv package.json.tmp package.json + +# Go back to pro repo root +cd - + +############################################# +# PUBLISH # +############################################# + lerna publish $VERSION --yes --force-publish --dist-tag $TAG -# reset main and types to point to src for dev +############################################# +# POST-PUBLISH - PRO # +############################################# + +# Revert build changes on packages/pro/package.json cd packages/pro jq '.main = "src/index.ts" | .types = "src/index.ts"' package.json > package.json.tmp && mv package.json.tmp package.json + +# Go back to pro repo root cd - + +# Commit and push changes git add packages/pro/package.json -git commit -m 'Prep dev' +git commit -m "Prep next development iteration" git push +############################################# +# POST-PUBLISH - BUDIBASE # +############################################# + +# Go to budibase repo root cd ../budibase -if [[ $COMMAND == "develop" ]]; then - # Pin pro version for develop container build - echo "Pinning pro version" - cd packages/server - jq '.dependencies."@budibase/pro"="'$VERSION'"' package.json > package.json.tmp && mv package.json.tmp package.json - cd - - cd packages/worker - jq '.dependencies."@budibase/pro"="'$VERSION'"' package.json > package.json.tmp && mv package.json.tmp package.json -fi +# Update pro version in packages/server/package.json +cd packages/server +jq '.dependencies."@budibase/pro"="'$VERSION'"' package.json > package.json.tmp && mv package.json.tmp package.json + +# Go back to budibase repo root +cd - + +# Update pro version in packages/worker/package.json +cd packages/worker +jq '.dependencies."@budibase/pro"="'$VERSION'"' package.json > package.json.tmp && mv package.json.tmp package.json + +# Go back to budibase repo root +cd - + +# Commit and push changes +git add packages/server/package.json +git add packages/worker/package.json +git commit -m "Update pro version to $VERSION" +git push \ No newline at end of file