diff --git a/.github/workflows/cypress-appbuilder.yml b/.github/workflows/cypress-appbuilder.yml index 67eb0ae432..8f0a20615e 100644 --- a/.github/workflows/cypress-appbuilder.yml +++ b/.github/workflows/cypress-appbuilder.yml @@ -13,16 +13,18 @@ jobs: Cypress-App-Builder: runs-on: ubuntu-22.04 if: | - contains(github.event.pull_request.labels.*.name, 'run-ce-app-builder') || - contains(github.event.pull_request.labels.*.name, 'run-ee-app-builder') || - contains(github.event.pull_request.labels.*.name, 'run-cypress') + contains(github.event.pull_request.labels.*.name, 'run-cypress-app-builder-ce') || + contains(github.event.pull_request.labels.*.name, 'run-cypress-app-builder-ee') || + contains(github.event.pull_request.labels.*.name, 'run-cypress') || + contains(github.event.pull_request.labels.*.name, 'run-cypress-ce') strategy: matrix: edition: >- ${{ - contains(github.event.pull_request.labels.*.name, 'run-ce-app-builder') && fromJson('["ce"]') || - contains(github.event.pull_request.labels.*.name, 'run-ee-app-builder') && fromJson('["ee"]') || + contains(github.event.pull_request.labels.*.name, 'run-cypress-app-builder-ce') && fromJson('["ce"]') || + contains(github.event.pull_request.labels.*.name, 'run-cypress-ce') && fromJson('["ce"]') || + contains(github.event.pull_request.labels.*.name, 'run-cypress-app-builder-ee') && fromJson('["ee"]') || contains(github.event.pull_request.labels.*.name, 'run-cypress') && fromJson('["ce", "ee"]') || fromJson('[]') }} @@ -158,35 +160,35 @@ jobs: name: screenshots-appbuilder-${{ matrix.edition }} path: cypress-tests/cypress/screenshots - Cypress-App-builder-Subpath: - runs-on: ubuntu-22.04 - if: contains(github.event.pull_request.labels.*.name, 'run-cypress') || - contains(github.event.pull_request.labels.*.name, 'run-cypress-app-builder-subpath') + # Cypress-App-builder-Subpath: + # runs-on: ubuntu-22.04 + # if: contains(github.event.pull_request.labels.*.name, 'run-cypress') || + # contains(github.event.pull_request.labels.*.name, 'run-cypress-app-builder-subpath') - steps: - - name: Checkout - uses: actions/checkout@v3 - with: - ref: ${{ github.event.pull_request.head.ref }} + # steps: + # - name: Checkout + # uses: actions/checkout@v3 + # with: + # ref: ${{ github.event.pull_request.head.ref }} - - name: Create Cypress environment file - id: create-json - uses: jsdaniell/create-json@1.1.2 - with: - name: "cypress.env.json" - json: ${{ secrets.CYPRESS_SECRETS }} - dir: "./cypress-tests" + # - name: Create Cypress environment file + # id: create-json + # uses: jsdaniell/create-json@1.1.2 + # with: + # name: "cypress.env.json" + # json: ${{ secrets.CYPRESS_SECRETS }} + # dir: "./cypress-tests" - - name: App Builder subpath - uses: cypress-io/github-action@v5 - with: - working-directory: ./cypress-tests - config: "baseUrl=http://localhost:80/apps/tooljet/" - config-file: cypress-app-builder.config.js + # - name: App Builder subpath + # uses: cypress-io/github-action@v5 + # with: + # working-directory: ./cypress-tests + # config: "baseUrl=http://localhost:80/apps/tooljet/" + # config-file: cypress-app-builder.config.js - - name: Capture Screenshots - uses: actions/upload-artifact@v4 - if: always() - with: - name: screenshots - path: cypress-tests/cypress/screenshots + # - name: Capture Screenshots + # uses: actions/upload-artifact@v4 + # if: always() + # with: + # name: screenshots + # path: cypress-tests/cypress/screenshots diff --git a/.github/workflows/cypress-marketplace.yml b/.github/workflows/cypress-marketplace.yml index 7193798ec4..9ddf476e12 100644 --- a/.github/workflows/cypress-marketplace.yml +++ b/.github/workflows/cypress-marketplace.yml @@ -15,16 +15,18 @@ jobs: runs-on: ubuntu-22.04 if: contains(github.event.pull_request.labels.*.name, 'run-cypress') || - contains(github.event.pull_request.labels.*.name, 'run-ce-cypress-marketplace') || - contains(github.event.pull_request.labels.*.name, 'run-ee-cypress-marketplace') + contains(github.event.pull_request.labels.*.name, 'run-cypress-marketplace-ce') || + contains(github.event.pull_request.labels.*.name, 'run-cypress-marketplace-ee') || + contains(github.event.pull_request.labels.*.name, 'run-cypress-ce') strategy: matrix: edition: >- ${{ contains(github.event.pull_request.labels.*.name, 'run-cypress') && fromJson('["ce", "ee"]') || - contains(github.event.pull_request.labels.*.name, 'run-ce-cypress-marketplace') && fromJson('["ce"]') || - contains(github.event.pull_request.labels.*.name, 'run-ee-cypress-marketplace') && fromJson('["ee"]') || + contains(github.event.pull_request.labels.*.name, 'run-cypress-ce') && fromJson('["ce"]') || + contains(github.event.pull_request.labels.*.name, 'run-cypress-marketplace-ce') && fromJson('["ce"]') || + contains(github.event.pull_request.labels.*.name, 'run-cypress-marketplace-ee') && fromJson('["ee"]') || fromJson('[]') }} diff --git a/.github/workflows/cypress-platform.yml b/.github/workflows/cypress-platform.yml index 0480db39f9..c6a0db4be6 100644 --- a/.github/workflows/cypress-platform.yml +++ b/.github/workflows/cypress-platform.yml @@ -13,15 +13,18 @@ jobs: Cypress-Platform: runs-on: ubuntu-22.04 if: contains(github.event.pull_request.labels.*.name, 'run-cypress') || - contains(github.event.pull_request.labels.*.name, 'run-ce-cypress-platform') || - contains(github.event.pull_request.labels.*.name, 'run-ee-cypress-platform') + contains(github.event.pull_request.labels.*.name, 'run-cypress-platform-ce') || + contains(github.event.pull_request.labels.*.name, 'run-cypress-platform-ee') || + contains(github.event.pull_request.labels.*.name, 'run-cypress-ce') + strategy: matrix: edition: >- ${{ contains(github.event.pull_request.labels.*.name, 'run-cypress') && fromJson('["ce", "ee"]') || - contains(github.event.pull_request.labels.*.name, 'run-ce-cypress-platform') && fromJson('["ce"]') || - contains(github.event.pull_request.labels.*.name, 'run-ee-cypress-platform') && fromJson('["ee"]') || + contains(github.event.pull_request.labels.*.name, 'run-cypress-ce') && fromJson('["ce"]') || + contains(github.event.pull_request.labels.*.name, 'run-cypress-platform-ce') && fromJson('["ce"]') || + contains(github.event.pull_request.labels.*.name, 'run-cypress-platform-ee') && fromJson('["ee"]') || fromJson('[]') }} diff --git a/.github/workflows/render-preview-deploy.yml b/.github/workflows/render-preview-deploy.yml index d1ca481d02..fda3981a3e 100644 --- a/.github/workflows/render-preview-deploy.yml +++ b/.github/workflows/render-preview-deploy.yml @@ -80,7 +80,7 @@ jobs: }, { "key": "PG_USER", - "value": "tooljet" + "value": "postgres" }, { "key": "PG_PASS", @@ -100,7 +100,7 @@ jobs: }, { "key": "TOOLJET_DB_USER", - "value": "tooljet" + "value": "postgres" }, { "key": "TOOLJET_DB_PASS", @@ -116,7 +116,7 @@ jobs: }, { "key": "PGRST_DB_URI", - "value": "postgres://tooljet:postgres@localhost/${{ env.PR_NUMBER }}-ce-tjdb" + "value": "postgres://postgres:postgres@localhost/${{ env.PR_NUMBER }}-ce-tjdb" }, { "key": "PGRST_HOST", @@ -168,7 +168,11 @@ jobs: } ], "serviceDetails": { - "disk": null, + "disk": { + "name": "tooljet-ce-pr-${{ env.PR_NUMBER }}-postgresql", + "mountPath": "/data", + "sizeGB": 10 + }, "env": "docker", "envSpecificDetails": { "dockerCommand": "", @@ -279,35 +283,35 @@ jobs: console.log(e) } - - name: Install PostgreSQL client - run: | - sudo apt update - sudo apt install postgresql-client -y + # - name: Install PostgreSQL client + # run: | + # sudo apt update + # sudo apt install postgresql-client -y - - name: Wait after installing PostgreSQL - run: sleep 25 + # - name: Wait after installing PostgreSQL + # run: sleep 25 - - name: Drop PostgreSQL PR databases - env: - PGHOST: ${{ secrets.RENDER_DS_PG_HOST }} - PGPORT: 5432 - PGUSER: ${{ secrets.RENDER_DS_PG_USER }} - PGDATABASE: ${{ env.PR_NUMBER }}-ce - PGTJBDATABASE: ${{ env.PR_NUMBER }}-ce-tjdb - run: | - if PGPASSWORD=${{ secrets.RENDER_DS_PG_PASS }} psql -h $PGHOST -p $PGPORT -U $PGUSER -lqt | cut -d \| -f 1 | grep -qw $PGDATABASE; then - echo "Database $PGDATABASE exists, deleting..." - PGPASSWORD=${{ secrets.RENDER_DS_PG_PASS }} psql -h $PGHOST -p $PGPORT -U $PGUSER -d postgres -c "drop database \"$PGDATABASE\" ;" - else - echo "Database $PGDATABASE does not exist." - fi + # - name: Drop PostgreSQL PR databases + # env: + # PGHOST: ${{ secrets.RENDER_DS_PG_HOST }} + # PGPORT: 5432 + # PGUSER: ${{ secrets.RENDER_DS_PG_USER }} + # PGDATABASE: ${{ env.PR_NUMBER }}-ce + # PGTJBDATABASE: ${{ env.PR_NUMBER }}-ce-tjdb + # run: | + # if PGPASSWORD=${{ secrets.RENDER_DS_PG_PASS }} psql -h $PGHOST -p $PGPORT -U $PGUSER -lqt | cut -d \| -f 1 | grep -qw $PGDATABASE; then + # echo "Database $PGDATABASE exists, deleting..." + # PGPASSWORD=${{ secrets.RENDER_DS_PG_PASS }} psql -h $PGHOST -p $PGPORT -U $PGUSER -d postgres -c "drop database \"$PGDATABASE\" ;" + # else + # echo "Database $PGDATABASE does not exist." + # fi - if PGPASSWORD=${{ secrets.RENDER_DS_PG_PASS }} psql -h $PGHOST -p $PGPORT -U $PGUSER -lqt | cut -d \| -f 1 | grep -qw $PGTJBDATABASE; then - echo "Database $PGTJBDATABASE exists, deleting..." - PGPASSWORD=${{ secrets.RENDER_DS_PG_PASS }} psql -h $PGHOST -p $PGPORT -U $PGUSER -d postgres -c "drop database \"$PGTJBDATABASE\" ;" - else - echo "Database $PGTJBDATABASE does not exist." - fi + # if PGPASSWORD=${{ secrets.RENDER_DS_PG_PASS }} psql -h $PGHOST -p $PGPORT -U $PGUSER -lqt | cut -d \| -f 1 | grep -qw $PGTJBDATABASE; then + # echo "Database $PGTJBDATABASE exists, deleting..." + # PGPASSWORD=${{ secrets.RENDER_DS_PG_PASS }} psql -h $PGHOST -p $PGPORT -U $PGUSER -d postgres -c "drop database \"$PGTJBDATABASE\" ;" + # else + # echo "Database $PGTJBDATABASE does not exist." + # fi suspend-ce-review-app: if: ${{ github.event.action == 'labeled' && github.event.label.name == 'suspend-ce-review-app' }} @@ -317,7 +321,7 @@ jobs: - name: Suspend service run: | export SERVICE_ID=$(curl --request GET \ - --url 'https://api.render.com/v1/services?name=ToolJet%20PR%20%23${{ env.PR_NUMBER }}&limit=1' \ + --url 'https://api.render.com/v1/services?name=ToolJet%20CE%20PR%20%23${{ env.PR_NUMBER }}&limit=1' \ --header 'accept: application/json' \ --header 'authorization: Bearer ${{ secrets.RENDER_API_KEY }}' | \ jq -r '.[0].service.id') @@ -349,7 +353,7 @@ jobs: - name: Resume service run: | export SERVICE_ID=$(curl --request GET \ - --url 'https://api.render.com/v1/services?name=ToolJet%20PR%20%23${{ env.PR_NUMBER }}&limit=1' \ + --url 'https://api.render.com/v1/services?name=ToolJet%20CE%20PR%20%23${{ env.PR_NUMBER }}&limit=1' \ --header 'accept: application/json' \ --header 'authorization: Bearer ${{ secrets.RENDER_API_KEY }}' | \ jq -r '.[0].service.id') @@ -420,7 +424,7 @@ jobs: }, { "key": "PG_USER", - "value": "tooljet" + "value": "postgres" }, { "key": "PG_PASS", @@ -440,7 +444,7 @@ jobs: }, { "key": "TOOLJET_DB_USER", - "value": "tooljet" + "value": "postgres" }, { "key": "TOOLJET_DB_PASS", @@ -456,7 +460,7 @@ jobs: }, { "key": "PGRST_DB_URI", - "value": "postgres://tooljet:postgres@localhost/${{ env.PR_NUMBER }}-ee-tjdb" + "value": "postgres://postgres:postgres@localhost/${{ env.PR_NUMBER }}-ee-tjdb" }, { "key": "PGRST_HOST", @@ -536,7 +540,11 @@ jobs: } ], "serviceDetails": { - "disk": null, + "disk": { + "name": "tooljet-ee-pr-${{ env.PR_NUMBER }}-postgresql", + "mountPath": "/var/lib/postgresql/13/main", + "sizeGB": 10 + }, "env": "docker", "envSpecificDetails": { "dockerCommand": "", @@ -549,7 +557,7 @@ jobs: "port": 80, "protocol": "TCP" }], - "plan": "starter", + "plan": "standard", "pullRequestPreviewsEnabled": "no", "region": "oregon", "url": "https://tooljet-ee-pr-${{ env.PR_NUMBER }}.onrender.com" @@ -647,35 +655,35 @@ jobs: console.log(e) } - - name: Install PostgreSQL client - run: | - sudo apt update - sudo apt install postgresql-client -y + # - name: Install PostgreSQL client + # run: | + # sudo apt update + # sudo apt install postgresql-client -y - - name: Wait after installing PostgreSQL - run: sleep 25 + # - name: Wait after installing PostgreSQL + # run: sleep 25 - - name: Drop PostgreSQL PR databases - env: - PGHOST: ${{ secrets.RENDER_DS_PG_HOST }} - PGPORT: 5432 - PGUSER: ${{ secrets.RENDER_DS_PG_USER }} - PGDATABASE: ${{ env.PR_NUMBER }}-ee - PGTJBDATABASE: ${{ env.PR_NUMBER }}-ee-tjdb - run: | - if PGPASSWORD=${{ secrets.RENDER_DS_PG_PASS }} psql -h $PGHOST -p $PGPORT -U $PGUSER -lqt | cut -d \| -f 1 | grep -qw $PGDATABASE; then - echo "Database $PGDATABASE exists, deleting..." - PGPASSWORD=${{ secrets.RENDER_DS_PG_PASS }} psql -h $PGHOST -p $PGPORT -U $PGUSER -d postgres -c "drop database \"$PGDATABASE\" ;" - else - echo "Database $PGDATABASE does not exist." - fi + # - name: Drop PostgreSQL PR databases + # env: + # PGHOST: ${{ secrets.RENDER_DS_PG_HOST }} + # PGPORT: 5432 + # PGUSER: ${{ secrets.RENDER_DS_PG_USER }} + # PGDATABASE: ${{ env.PR_NUMBER }}-ee + # PGTJBDATABASE: ${{ env.PR_NUMBER }}-ee-tjdb + # run: | + # if PGPASSWORD=${{ secrets.RENDER_DS_PG_PASS }} psql -h $PGHOST -p $PGPORT -U $PGUSER -lqt | cut -d \| -f 1 | grep -qw $PGDATABASE; then + # echo "Database $PGDATABASE exists, deleting..." + # PGPASSWORD=${{ secrets.RENDER_DS_PG_PASS }} psql -h $PGHOST -p $PGPORT -U $PGUSER -d postgres -c "drop database \"$PGDATABASE\" ;" + # else + # echo "Database $PGDATABASE does not exist." + # fi - if PGPASSWORD=${{ secrets.RENDER_DS_PG_PASS }} psql -h $PGHOST -p $PGPORT -U $PGUSER -lqt | cut -d \| -f 1 | grep -qw $PGTJBDATABASE; then - echo "Database $PGTJBDATABASE exists, deleting..." - PGPASSWORD=${{ secrets.RENDER_DS_PG_PASS }} psql -h $PGHOST -p $PGPORT -U $PGUSER -d postgres -c "drop database \"$PGTJBDATABASE\" ;" - else - echo "Database $PGTJBDATABASE does not exist." - fi + # if PGPASSWORD=${{ secrets.RENDER_DS_PG_PASS }} psql -h $PGHOST -p $PGPORT -U $PGUSER -lqt | cut -d \| -f 1 | grep -qw $PGTJBDATABASE; then + # echo "Database $PGTJBDATABASE exists, deleting..." + # PGPASSWORD=${{ secrets.RENDER_DS_PG_PASS }} psql -h $PGHOST -p $PGPORT -U $PGUSER -d postgres -c "drop database \"$PGTJBDATABASE\" ;" + # else + # echo "Database $PGTJBDATABASE does not exist." + # fi suspend-ee-review-app: if: ${{ github.event.action == 'labeled' && github.event.label.name == 'suspend-ee-review-app' }} @@ -685,7 +693,7 @@ jobs: - name: Suspend service run: | export SERVICE_ID=$(curl --request GET \ - --url 'https://api.render.com/v1/services?name=ToolJet%20PR%20%23${{ env.PR_NUMBER }}&limit=1' \ + --url 'https://api.render.com/v1/services?name=ToolJet%20EE%20PR%20%23${{ env.PR_NUMBER }}&limit=1' \ --header 'accept: application/json' \ --header 'authorization: Bearer ${{ secrets.RENDER_API_KEY }}' | \ jq -r '.[0].service.id') @@ -717,7 +725,7 @@ jobs: - name: Resume service run: | export SERVICE_ID=$(curl --request GET \ - --url 'https://api.render.com/v1/services?name=ToolJet%20PR%20%23${{ env.PR_NUMBER }}&limit=1' \ + --url 'https://api.render.com/v1/services?name=ToolJet%20EE%20PR%20%23${{ env.PR_NUMBER }}&limit=1' \ --header 'accept: application/json' \ --header 'authorization: Bearer ${{ secrets.RENDER_API_KEY }}' | \ jq -r '.[0].service.id') diff --git a/cypress-tests/cypress/commands/apiCommands.js b/cypress-tests/cypress/commands/apiCommands.js index e9891c962e..c625e6cc33 100644 --- a/cypress-tests/cypress/commands/apiCommands.js +++ b/cypress-tests/cypress/commands/apiCommands.js @@ -689,7 +689,7 @@ Cypress.Commands.add( name: dataSourceName, options: [ { key: "connection_type", value: "manual", encrypted: false }, - { key: "host", value: "35.238.9.114" }, + { key: "host", value: "9.234.17.31" }, { key: "port", value: 5432 }, { key: "database", value: "student" }, { key: "username", value: "postgres" }, diff --git a/cypress-tests/cypress/constants/selectors/common.js b/cypress-tests/cypress/constants/selectors/common.js index 42e3378825..ec104d67bc 100644 --- a/cypress-tests/cypress/constants/selectors/common.js +++ b/cypress-tests/cypress/constants/selectors/common.js @@ -6,7 +6,8 @@ export const commonSelectors = { toastMessage: ".go3958317564", oldToastMessage: ".go318386747", appSlugAccept: '[data-cy="app-slug-accepted-label"]', - newToastMessage: '.drawer-container > [style="position: fixed; z-index: 9999; inset: 16px; pointer-events: none;"] > .go4109123758 > .go2072408551 > .go3958317564', + newToastMessage: + '.drawer-container > [style="position: fixed; z-index: 9999; inset: 16px; pointer-events: none;"] > .go4109123758 > .go2072408551 > .go3958317564', toastCloseButton: '[data-cy="toast-close-button"]', editButton: "[data-cy=edit-button]", workspaceConstantNameInput: '[data-cy="name-input-field"]', @@ -18,7 +19,7 @@ export const commonSelectors = { appCardOptionsButton: "[data-cy=app-card-menu-icon]", autoSave: "[data-cy=autosave-indicator]", nameInputFieldd: "[data-cy=name-input-field]", - valueInputFieldd: '[data-cy=value-input-field]', + valueInputFieldd: "[data-cy=value-input-field]", skipButton: ".driver-close-btn", skipInstallationModal: "[data-cy=skip-button]", homePageLogo: "[data-cy=home-page-logo]", @@ -395,7 +396,7 @@ export const commonWidgetSelector = { modalCloseButton: '[data-cy="modal-close-button"]', iframeLinkLabel: '[data-cy="iframe-link-label"]', ifameLinkCopyButton: '[data-cy="iframe-link-copy-button"]', - appSlugLabel: '[data-cy="input-field-label"]', + appSlugLabel: '[data-cy="unique-app-slug-field-label"]', appSlugInput: '[data-cy="app-slug-input-field"]', appSlugInfoLabel: '[data-cy="helper-text"]', appLinkLabel: '[data-cy="app-link-label"]', diff --git a/cypress-tests/cypress/constants/selectors/restAPI.js b/cypress-tests/cypress/constants/selectors/restAPI.js index a24c115712..5ec2d73954 100644 --- a/cypress-tests/cypress/constants/selectors/restAPI.js +++ b/cypress-tests/cypress/constants/selectors/restAPI.js @@ -24,7 +24,7 @@ export const restAPISelector = { return `[data-cy="${cyParamName(header)}-delete-button-${cyParamName(index)}"]`; }, addMoreButton: (header) => { - return `[data-cy="${cyParamName(header)}-add-more-button"]`; + return `[data-cy="${cyParamName(header)}-add-button"]`; }, dropdownLabel: (label) => { return `[data-cy="${cyParamName(label)}-dropdown-label"]`; diff --git a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/workspaceConstants.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/workspaceConstants.cy.js index 4ccd01274a..17f0872846 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/workspaceConstants.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/workspaceConstants.cy.js @@ -80,8 +80,8 @@ describe("Workspace constants", () => { addNewconstants("restapiHeaderKey", "customHeader"); addNewconstants("restapiHeaderValue", "key=value"); addNewconstants("deleteConst", "deleteconst"); - addNewconstants("gconst", "236"); - addNewconstants("gconstUrl", "http://34.66.166.236:4000/"); + addNewconstants("gconst", "108"); + addNewconstants("gconstUrl", "http://20.29.40.108:4000/"); addNewconstants("gconstEndpoint", "production"); // create secret constants @@ -118,6 +118,7 @@ describe("Workspace constants", () => { //Verify all static and datasource queries output in components for (let i = 3; i <= 16; i++) { + cy.log("Verifying textinput" + i); cy.get(commonWidgetSelector.draggableWidget(`textinput${i}`)) .verifyVisibleElement("have.value", "Production environment testing"); } diff --git a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/basicPermissions.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/basicPermissions.skip.js similarity index 95% rename from cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/basicPermissions.cy.js rename to cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/basicPermissions.skip.js index d69cf77689..96b87dd9b7 100644 --- a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/basicPermissions.cy.js +++ b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/groups/basicPermissions.skip.js @@ -78,14 +78,16 @@ describe("Manage Groups", () => { cy.createApp(data.appName); cy.verifyToastMessage( commonSelectors.toastMessage, - commonText.appCreatedToast + commonText.appCreatedToast, + false ); cy.backToApps(); cy.deleteApp(data.appName); cy.verifyToastMessage( commonSelectors.toastMessage, - commonText.appDeletedToast + commonText.appDeletedToast, + false ); // Folder operations @@ -115,7 +117,8 @@ describe("Manage Groups", () => { cy.get(commonSelectors.cloneAppButton).click(); cy.verifyToastMessage( commonSelectors.toastMessage, - dashboardText.appClonedToast + dashboardText.appClonedToast, + false ); // cy.get(commonSelectors.cancelButton).click(); cy.apiLogout(); @@ -177,14 +180,16 @@ describe("Manage Groups", () => { cy.createApp(data.appName); cy.verifyToastMessage( commonSelectors.toastMessage, - commonText.appCreatedToast + commonText.appCreatedToast, + false ); cy.backToApps(); cy.deleteApp(data.appName); cy.verifyToastMessage( commonSelectors.toastMessage, - commonText.appDeletedToast + commonText.appDeletedToast, + false ); // Folder operations diff --git a/cypress-tests/cypress/fixtures/templates/invalid_app.json b/cypress-tests/cypress/fixtures/templates/invalid_app.json index e2ebed4bce..edb4c882a3 100644 --- a/cypress-tests/cypress/fixtures/templates/invalid_app.json +++ b/cypress-tests/cypress/fixtures/templates/invalid_app.json @@ -2127,7 +2127,7 @@ "encrypted": false }, "host": { - "value": "35.238.9.114", + "value": "9.234.17.31", "encrypted": false }, "port": { diff --git a/cypress-tests/cypress/fixtures/templates/one_version.json b/cypress-tests/cypress/fixtures/templates/one_version.json index 08dad1d1c0..a93a35900e 100644 --- a/cypress-tests/cypress/fixtures/templates/one_version.json +++ b/cypress-tests/cypress/fixtures/templates/one_version.json @@ -585,7 +585,7 @@ "encrypted": false }, "host": { - "value": "35.238.9.114", + "value": "9.234.17.31", "encrypted": false }, "port": { diff --git a/cypress-tests/cypress/fixtures/templates/three-versions.json b/cypress-tests/cypress/fixtures/templates/three-versions.json index bff312ac54..715f5e68cc 100644 --- a/cypress-tests/cypress/fixtures/templates/three-versions.json +++ b/cypress-tests/cypress/fixtures/templates/three-versions.json @@ -1862,7 +1862,7 @@ "encrypted": false }, "host": { - "value": "35.238.9.114", + "value": "9.234.17.31", "encrypted": false }, "port": { diff --git a/cypress-tests/cypress/fixtures/templates/workspace_constants.json b/cypress-tests/cypress/fixtures/templates/workspace_constants.json index c5c015e832..074e6e408b 100644 --- a/cypress-tests/cypress/fixtures/templates/workspace_constants.json +++ b/cypress-tests/cypress/fixtures/templates/workspace_constants.json @@ -2766,7 +2766,7 @@ "name": "restapiStaticUrlG", "options": { "method": "get", - "url": "http://34.66.166.236:4000/{{constants.gconstEndpoint}}", + "url": "http://20.29.40.108:4000/{{constants.gconstEndpoint}}", "url_params": [ [ "", @@ -2814,7 +2814,7 @@ "name": "restapiUrlS", "options": { "method": "get", - "url": "http://34.66.166.236:4000/{{secrets.sconstEndpoint}}", + "url": "http://20.29.40.108:4000/{{secrets.sconstEndpoint}}", "url_params": [ [ "", @@ -2908,7 +2908,7 @@ "name": "restapiUrlGS", "options": { "method": "get", - "url": "http://34.66.166.{{constants.gconst}}{{secrets.sconst}}/production", + "url": "http://20.29.40.{{constants.gconst}}{{secrets.sconst}}/production", "url_params": [ [ "", @@ -3419,7 +3419,7 @@ "environmentId": "dab04b8d-7d1a-468a-b219-b2e1d0169d8c", "options": { "url": { - "value": "http://34.66.166.236:4000/{{constants.gconstEndpoint}}", + "value": "http://20.29.40.108:4000/{{constants.gconstEndpoint}}", "encrypted": false }, "auth_type": { @@ -3540,7 +3540,7 @@ "environmentId": "dab04b8d-7d1a-468a-b219-b2e1d0169d8c", "options": { "url": { - "value": "http://34.66.166.236:4000/{{secrets.sconstEndpoint}}", + "value": "http://20.29.40.108:4000/{{secrets.sconstEndpoint}}", "encrypted": false }, "auth_type": { @@ -3782,7 +3782,7 @@ "environmentId": "dab04b8d-7d1a-468a-b219-b2e1d0169d8c", "options": { "url": { - "value": "http://34.66.166.{{constants.gconst}}{{secrets.sconst}}/production", + "value": "http://20.29.40.{{constants.gconst}}{{secrets.sconst}}/production", "encrypted": false }, "auth_type": { diff --git a/docker/ce-preview.Dockerfile b/docker/ce-preview.Dockerfile index d71e6c1dbc..e1a4b75c01 100644 --- a/docker/ce-preview.Dockerfile +++ b/docker/ce-preview.Dockerfile @@ -38,7 +38,7 @@ COPY --from=postgrest/postgrest:v12.2.0 /bin/postgrest /bin ENV NODE_ENV=production ENV NODE_OPTIONS="--max-old-space-size=4096" -RUN apt-get update && apt-get install -y postgresql-client freetds-dev libaio1 wget supervisor +RUN apt-get update && apt-get install -y freetds-dev libaio1 wget supervisor # Install Instantclient Basic Light Oracle and Dependencies WORKDIR /opt/oracle @@ -54,9 +54,6 @@ ENV LD_LIBRARY_PATH="/opt/oracle/instantclient_11_2:/opt/oracle/instantclient_21 WORKDIR / -RUN mkdir -p /app /var/log/supervisor -COPY /deploy/docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf - # copy npm scripts COPY --from=builder /app/package.json ./app/package.json # copy plugins dependencies @@ -77,32 +74,56 @@ COPY --from=builder /app/server/dist ./app/server/dist WORKDIR /app +# Install PostgreSQL USER root RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list -RUN echo "deb http://deb.debian.org/debian" RUN apt update && apt -y install postgresql-13 postgresql-client-13 supervisor -USER postgres -RUN service postgresql start && \ - psql -c "create role tooljet with login superuser password 'postgres';" -USER root + +RUN mkdir -p /var/log/supervisor /var/run/postgresql && \ + chown -R postgres:postgres /var/run/postgresql /var/log/supervisor + +# Explicitly create PG main directory with correct ownerships +RUN mkdir -p /var/lib/postgresql/13/main && \ + chown -R postgres:postgres /var/lib/postgresql + +# Configure Supervisor to manage PostgREST, ToolJet, and Redis +RUN echo "[supervisord] \n" \ + "nodaemon=true \n" \ + "user=root \n" \ + "\n" \ + "[program:postgrest] \n" \ + "command=/bin/postgrest \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "\n" \ + "[program:tooljet] \n" \ + "user=root \n" \ + "command=/bin/bash -c '/app/server/scripts/boot.sh' \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "stderr_logfile=/dev/stdout \n" \ + "stderr_logfile_maxbytes=0 \n" \ + "stdout_logfile=/dev/stdout \n" \ + "stdout_logfile_maxbytes=0 \n" | sed 's/ //' > /etc/supervisor/conf.d/supervisord.conf # ENV defaults ENV TOOLJET_HOST=http://localhost \ + PORT=80 \ NODE_ENV=production \ LOCKBOX_MASTER_KEY=replace_with_lockbox_master_key \ SECRET_KEY_BASE=replace_with_secret_key_base \ PG_DB=tooljet_production \ - PG_USER=tooljet \ + PG_USER=postgres \ PG_PASS=postgres \ PG_HOST=localhost \ ENABLE_TOOLJET_DB=true \ TOOLJET_DB_HOST=localhost \ - TOOLJET_DB_USER=tooljet \ + TOOLJET_DB_USER=postgres \ TOOLJET_DB_PASS=postgres \ TOOLJET_DB=tooljet_db \ PGRST_HOST=http://localhost:3000 \ - PGRST_DB_URI=postgres://tooljet:postgres@localhost/tooljet_db \ + PGRST_DB_URI=postgres://postgres:postgres@localhost/tooljet_db \ PGRST_JWT_SECRET=r9iMKoe5CRMgvJBBtp4HrqN7QiPpUToj \ PGRST_DB_PRE_CONFIG=postgrest.pre_config \ ORM_LOGGING=true \ @@ -110,4 +131,7 @@ ENV TOOLJET_HOST=http://localhost \ HOME=/home/appuser \ TERM=xterm -CMD ["/usr/bin/supervisord"] + +RUN chmod +x ./server/scripts/preview.sh +# Set the entrypoint +ENTRYPOINT ["./server/scripts/preview.sh"] diff --git a/docker/ee/ee-preview.Dockerfile b/docker/ee/ee-preview.Dockerfile index 91e0864bd0..77ecbff29d 100644 --- a/docker/ee/ee-preview.Dockerfile +++ b/docker/ee/ee-preview.Dockerfile @@ -9,7 +9,7 @@ WORKDIR /app ARG CUSTOM_GITHUB_TOKEN ARG BRANCH_NAME -# Clone and checkout the frontend repository +# Clone and checkout the frontend repositorys RUN git config --global url."https://x-access-token:${CUSTOM_GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/" RUN git config --global http.version HTTP/1.1 @@ -66,7 +66,7 @@ COPY --from=postgrest/postgrest:v12.2.0 /bin/postgrest /bin ENV NODE_ENV=production ENV TOOLJET_EDITION=ee ENV NODE_OPTIONS="--max-old-space-size=4096" -RUN apt-get update && apt-get install -y postgresql-client freetds-dev libaio1 wget supervisor +RUN apt-get update && apt-get install -y freetds-dev libaio1 wget supervisor # Install Instantclient Basic Light Oracle and Dependencies WORKDIR /opt/oracle @@ -82,9 +82,6 @@ ENV LD_LIBRARY_PATH="/opt/oracle/instantclient_11_2:/opt/oracle/instantclient_21 WORKDIR / -RUN mkdir -p /app /var/log/supervisor -COPY /deploy/docker/supervisord.conf /etc/supervisor/conf.d/supervisord.conf - # copy npm scripts COPY --from=builder /app/package.json ./app/package.json # copy plugins dependencies @@ -106,38 +103,73 @@ COPY --from=builder /app/server/dist ./app/server/dist WORKDIR /app -# ENV defaults +# Install PostgreSQL USER root RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add - RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list -RUN echo "deb http://deb.debian.org/debian" -RUN apt update && apt -y install postgresql-13 postgresql-client-13 supervisor -USER postgres -RUN service postgresql start && \ - psql -c "create role tooljet with login superuser password 'postgres';" -USER root +RUN apt update && apt -y install postgresql-13 postgresql-client-13 supervisor --fix-missing + + +# Explicitly create PG main directory with correct ownership +RUN mkdir -p /var/lib/postgresql/13/main && \ + chown -R postgres:postgres /var/lib/postgresql + +RUN mkdir -p /var/log/supervisor /var/run/postgresql && \ + chown -R postgres:postgres /var/run/postgresql /var/log/supervisor + +# Remove existing data and create directory with proper ownership +RUN rm -rf /var/lib/postgresql/13/main && \ + mkdir -p /var/lib/postgresql/13/main && \ + chown -R postgres:postgres /var/lib/postgresql + +# Initialize PostgreSQL +RUN su - postgres -c "/usr/lib/postgresql/13/bin/initdb -D /var/lib/postgresql/13/main" + +# Configure Supervisor to manage PostgREST, ToolJet, and Redis +RUN echo "[supervisord] \n" \ + "nodaemon=true \n" \ + "user=root \n" \ + "\n" \ + "[program:postgrest] \n" \ + "command=/bin/postgrest \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "\n" \ + "[program:tooljet] \n" \ + "user=root \n" \ + "command=/bin/bash -c '/app/server/scripts/boot.sh' \n" \ + "autostart=true \n" \ + "autorestart=true \n" \ + "stderr_logfile=/dev/stdout \n" \ + "stderr_logfile_maxbytes=0 \n" \ + "stdout_logfile=/dev/stdout \n" \ + "stdout_logfile_maxbytes=0 \n" | sed 's/ //' > /etc/supervisor/conf.d/supervisord.conf # ENV defaults ENV TOOLJET_HOST=http://localhost \ + PORT=80 \ NODE_ENV=production \ LOCKBOX_MASTER_KEY=replace_with_lockbox_master_key \ SECRET_KEY_BASE=replace_with_secret_key_base \ PG_DB=tooljet_production \ - PG_USER=tooljet \ + PG_USER=postgres \ PG_PASS=postgres \ PG_HOST=localhost \ ENABLE_TOOLJET_DB=true \ TOOLJET_DB_HOST=localhost \ - TOOLJET_DB_USER=tooljet \ + TOOLJET_DB_USER=postgres \ TOOLJET_DB_PASS=postgres \ TOOLJET_DB=tooljet_db \ PGRST_HOST=http://localhost:3000 \ - PGRST_DB_URI=postgres://tooljet:postgres@localhost/tooljet_db \ + PGRST_DB_URI=postgres://postgres:postgres@localhost/tooljet_db \ PGRST_JWT_SECRET=r9iMKoe5CRMgvJBBtp4HrqN7QiPpUToj \ PGRST_DB_PRE_CONFIG=postgrest.pre_config \ ORM_LOGGING=true \ DEPLOYMENT_PLATFORM=docker:local \ - REDIS_PASS= \ + HOME=/home/appuser \ TERM=xterm -CMD ["/usr/bin/supervisord"] + +RUN chmod +x ./server/scripts/preview.sh +# Set the entrypoint +ENTRYPOINT ["./server/scripts/preview.sh"] diff --git a/frontend/src/AppBuilder/QueryManager/Components/QueryManagerHeader.jsx b/frontend/src/AppBuilder/QueryManager/Components/QueryManagerHeader.jsx index 37d206b340..88a0e2664a 100644 --- a/frontend/src/AppBuilder/QueryManager/Components/QueryManagerHeader.jsx +++ b/frontend/src/AppBuilder/QueryManager/Components/QueryManagerHeader.jsx @@ -254,7 +254,7 @@ const RunButton = ({ buttonLoadingState }) => { runQuery(selectedQuery?.id, selectedQuery?.name, undefined, 'edit', {}, true)} + onClick={() => runQuery(selectedQuery?.id, selectedQuery?.name, undefined, 'edit', {}, true, undefined, true)} leadingIcon="play01" disabled={isInDraft} isLoading={isLoading} diff --git a/frontend/src/AppBuilder/_hooks/useAppData.js b/frontend/src/AppBuilder/_hooks/useAppData.js index f131b4eb59..433a677dc7 100644 --- a/frontend/src/AppBuilder/_hooks/useAppData.js +++ b/frontend/src/AppBuilder/_hooks/useAppData.js @@ -216,248 +216,237 @@ const useAppData = (appId, moduleId, darkMode, mode = 'edit', { environmentId, v } // const appDataPromise = appService.fetchApp(appId); - appDataPromise - .then(async (result) => { - let appData = { ...result }; - let editorEnvironment = result.editorEnvironment; - if (isPreviewForVersion) { - const rawDataQueries = appData?.data_queries; - const rawEditingVersionDataQueries = appData?.editing_version?.data_queries; - appData = convertAllKeysToSnakeCase(appData); + appDataPromise.then(async (result) => { + let appData = { ...result }; + let editorEnvironment = result.editorEnvironment; + if (isPreviewForVersion) { + const rawDataQueries = appData?.data_queries; + const rawEditingVersionDataQueries = appData?.editing_version?.data_queries; + appData = convertAllKeysToSnakeCase(appData); - appData.data_queries = rawDataQueries; - if (appData.editing_version && rawEditingVersionDataQueries) { - appData.editing_version.data_queries = rawEditingVersionDataQueries; - } + appData.data_queries = rawDataQueries; + if (appData.editing_version && rawEditingVersionDataQueries) { + appData.editing_version.data_queries = rawEditingVersionDataQueries; + } + editorEnvironment = { + id: environmentId, + name: queryParams.env, + }; + } + + let constantsResp; + if (mode !== 'edit') { + try { + const queryParams = { slug: slug }; + const viewerEnvironment = await appEnvironmentService.getEnvironment(environmentId, queryParams); editorEnvironment = { - id: environmentId, - name: queryParams.env, + id: viewerEnvironment?.environment?.id, + name: viewerEnvironment?.environment?.name, }; + constantsResp = + isPublicAccess && appData.is_public + ? await orgEnvironmentConstantService.getConstantsFromPublicApp(slug, viewerEnvironment?.environment?.id) + : await orgEnvironmentConstantService.getConstantsFromApp(slug, viewerEnvironment?.environment?.id); + } catch (error) { + console.error('Error fetching viewer environment:', error); } + } - let constantsResp; - if (mode !== 'edit') { - try { - const queryParams = { slug: slug }; - const viewerEnvironment = await appEnvironmentService.getEnvironment(environmentId, queryParams); - editorEnvironment = { - id: viewerEnvironment?.environment?.id, - name: viewerEnvironment?.environment?.name, - }; - constantsResp = - isPublicAccess && appData.is_public - ? await orgEnvironmentConstantService.getConstantsFromPublicApp( - slug, - viewerEnvironment?.environment?.id - ) - : await orgEnvironmentConstantService.getConstantsFromApp(slug, viewerEnvironment?.environment?.id); - } catch (error) { - console.error('Error fetching viewer environment:', error); - } - } + if (mode === 'edit') { + constantsResp = await orgEnvironmentConstantService.getConstantsFromEnvironment(editorEnvironment?.id); + } + // get the constants for specific environment + constantsResp.constants = extractEnvironmentConstantsFromConstantsList( + constantsResp?.constants, + editorEnvironment?.name + ); - if (mode === 'edit') { - constantsResp = await orgEnvironmentConstantService.getConstantsFromEnvironment(editorEnvironment?.id); - } - // get the constants for specific environment - constantsResp.constants = extractEnvironmentConstantsFromConstantsList( - constantsResp?.constants, - editorEnvironment?.name - ); + setIsPublicAccess(isPublicAccess && mode !== 'edit' && appData.is_public); - setIsPublicAccess(isPublicAccess && mode !== 'edit' && appData.is_public); + fetchAndInjectCustomStyles(isPublicAccess && mode !== 'edit' && appData.is_public); - fetchAndInjectCustomStyles(isPublicAccess && mode !== 'edit' && appData.is_public); - - const pages = appData.pages.map((page) => { - return page; - }); - const conversation = appData.ai_conversation; - const docsConversation = appData.ai_conversation_learn; - if (setConversation && setDocsConversation) { - setConversation(conversation); - setDocsConversation(docsConversation); - // important to control ai inputs - getCreditBalance(); - } - - let showWalkthrough = true; - // if app was created from propmt, and no earlier messages are present in the conversation, send the prompt message - - // handles the getappdataby slug api call. Gets the homePageId from the appData. - const homePageId = - appData.editing_version?.homePageId || appData.editing_version?.home_page_id || appData.home_page_id; - - setApp({ - appName: appData.name, - appId: appData.id, - slug: appData.slug, - currentAppEnvironmentId: editorEnvironment.id, - isMaintenanceOn: - 'is_maintenance_on' in result - ? result.is_maintenance_on - : 'isMaintenanceOn' in result - ? result.isMaintenanceOn - : false, - organizationId: appData.organizationId || appData.organization_id, - homePageId: homePageId, - isPublic: appData.is_public, - creationMode: appData.creation_mode, - }); - setIsEditorFreezed(appData.should_freeze_editor); - const global_settings = mapKeys( - appData.editing_version?.global_settings || appData.global_settings, - (value, key) => camelCase(key) - ); - if (!global_settings?.theme) { - global_settings.theme = baseTheme; - } - setGlobalSettings(global_settings); - setPages(pages, moduleId); - setPageSettings( - computePageSettings(deepCamelCase(appData?.editing_version?.page_settings ?? appData?.page_settings)) - ); - - // set starting page as homepage initially - let startingPage = appData.pages.find((page) => page.id === homePageId); - - //no access to homepage, set to the next available page - if (startingPage?.restricted) { - startingPage = appData.pages.find((page) => !page?.restricted); - } - - if (initialLoadRef.current) { - // if initial load, check if the path has a page handle and set that as the starting page - const initialLoadPath = location.pathname.split('/').pop(); - - const page = appData.pages.find((page) => page.handle === initialLoadPath && !page.isPageGroup); - if (page) { - // if page is disabled, and not editing redirect to home page - const shouldRedirect = page?.restricted || (mode !== 'edit' && page?.disabled); - - if (shouldRedirect) { - const newUrl = window.location.href.replace(initialLoadPath, startingPage.handle); - window.history.replaceState(null, null, newUrl); - - if (page?.restricted) { - toast.error('Access to this page is restricted. Contact admin to know more.', { - className: 'text-nowrap w-auto mw-100', - }); - } - } else { - startingPage = page; - } - } - - // navigate(`/${getWorkspaceId()}/apps/${slug ?? appId}/${startingPage.handle}`); - } - - // Add page id and handle to the state on initial load - const currentState = window.history.state || {}; - const pageInfo = { - id: startingPage.id, - handle: startingPage.handle, - }; - const newState = { ...currentState, ...pageInfo }; - window.history.replaceState(newState, '', window.location.href); - - setCurrentPageHandle(startingPage.handle); - updateFeatureAccess(); - setCurrentPageId(startingPage.id, moduleId); - setResolvedPageConstants({ - id: startingPage?.id, - handle: startingPage?.handle, - name: startingPage?.name, - }); - setComponentNameIdMapping(moduleId); - updateEventsField('events', appData.events); - setCurrentVersionId(appData.editing_version?.id || appData.current_version_id); - setAppHomePageId(homePageId); - - const queryData = - isPublicAccess || (mode !== 'edit' && appData.is_public) - ? appData - : await dataqueryService.getAll(appData.editing_version?.id || appData.current_version_id); - const dataQueries = queryData.data_queries || queryData?.editing_version?.data_queries; - dataQueries.forEach((query) => normalizeQueryTransformationOptions(query)); - setQueries(dataQueries); - if (dataQueries?.length > 0) { - setSelectedQuery(dataQueries[0]?.id); - initialiseResolvedQuery(dataQueries.map((query) => query.id)); - } - const constants = constantsResp?.constants; - - if (constants) { - const orgConstants = {}; - const orgSecrets = {}; - constants.map((constant) => { - if (constant.type !== 'Secret') { - orgConstants[constant.name] = constant.value; - } else { - orgSecrets[constant.name] = constant.value; - } - }); - setResolvedConstants(orgConstants); - setSecrets(orgSecrets); - } - setQueryMapping(moduleId); - - setResolvedGlobals('environment', editorEnvironment); - setResolvedGlobals('mode', { value: mode }); - setResolvedGlobals('currentUser', { - ...user, - groups: currentSession?.groups, - role: currentSession?.role?.name, - ssoUserInfo: currentSession?.ssoUserInfo, - ...(currentSession?.currentUser?.metadata && !isEmpty(currentSession?.currentUser?.metadata) - ? { metadata: currentSession?.currentUser?.metadata } - : {}), - }); - setResolvedGlobals('urlparams', JSON.parse(JSON.stringify(queryString.parse(location?.search)))); - initDependencyGraph(moduleId); - setCurrentMode(mode); // TODO: set mode based on the slug/appDef - if ( - state.ai && - state?.prompt && - initialLoadRef.current && - (conversation?.aiConversationMessages || []).length === 0 - ) { - setSelectedSidebarItem('tooljetai'); - toggleLeftSidebar('true'); - sendMessage(state.prompt); - setConversationZeroState(true); - showWalkthrough = false; - } - // fetchDataSources(appData.editing_version.id, editorEnvironment.id); - if (!isPublicAccess) { - const envFromQueryParams = mode === 'view' && new URLSearchParams(location?.search)?.get('env'); - useStore.getState().init(appData.editing_version?.id || appData.current_version_id, envFromQueryParams); - fetchGlobalDataSources( - appData.organization_id, - appData.editing_version?.id || appData.current_version_id, - editorEnvironment.id - ); - } - useStore.getState().updateEditingVersion(appData.editing_version?.id || appData.current_version_id); //check if this is needed - updateReleasedVersionId(appData.current_version_id); - - setEditorLoading(false); - initialLoadRef.current = false; - // only show if app is not created from prompt - if (showWalkthrough) initEditorWalkThrough(); - checkAndSetTrueBuildSuggestionsFlag(); - return () => { - document.title = retrieveWhiteLabelText(); - }; - }) - .catch((error) => { - if (isPublicAccess) { - if (mode !== 'edit') { - handleError('view', error); - } - } + const pages = appData.pages.map((page) => { + return page; }); + const conversation = appData.ai_conversation; + const docsConversation = appData.ai_conversation_learn; + if (setConversation && setDocsConversation) { + setConversation(conversation); + setDocsConversation(docsConversation); + // important to control ai inputs + getCreditBalance(); + } + + let showWalkthrough = true; + // if app was created from propmt, and no earlier messages are present in the conversation, send the prompt message + + // handles the getappdataby slug api call. Gets the homePageId from the appData. + const homePageId = + appData.editing_version?.homePageId || appData.editing_version?.home_page_id || appData.home_page_id; + + setApp({ + appName: appData.name, + appId: appData.id, + slug: appData.slug, + currentAppEnvironmentId: editorEnvironment.id, + isMaintenanceOn: + 'is_maintenance_on' in result + ? result.is_maintenance_on + : 'isMaintenanceOn' in result + ? result.isMaintenanceOn + : false, + organizationId: appData.organizationId || appData.organization_id, + homePageId: homePageId, + isPublic: appData.is_public, + creationMode: appData.creation_mode, + }); + setIsEditorFreezed(appData.should_freeze_editor); + const global_settings = mapKeys( + appData.editing_version?.global_settings || appData.global_settings, + (value, key) => camelCase(key) + ); + if (!global_settings?.theme) { + global_settings.theme = baseTheme; + } + setGlobalSettings(global_settings); + setPages(pages, moduleId); + setPageSettings( + computePageSettings(deepCamelCase(appData?.editing_version?.page_settings ?? appData?.page_settings)) + ); + + // set starting page as homepage initially + let startingPage = appData.pages.find((page) => page.id === homePageId); + + //no access to homepage, set to the next available page + if (startingPage?.restricted) { + startingPage = appData.pages.find((page) => !page?.restricted); + } + + if (initialLoadRef.current) { + // if initial load, check if the path has a page handle and set that as the starting page + const initialLoadPath = location.pathname.split('/').pop(); + + const page = appData.pages.find((page) => page.handle === initialLoadPath && !page.isPageGroup); + if (page) { + // if page is disabled, and not editing redirect to home page + const shouldRedirect = page?.restricted || (mode !== 'edit' && page?.disabled); + + if (shouldRedirect) { + const newUrl = window.location.href.replace(initialLoadPath, startingPage.handle); + window.history.replaceState(null, null, newUrl); + + if (page?.restricted) { + toast.error('Access to this page is restricted. Contact admin to know more.', { + className: 'text-nowrap w-auto mw-100', + }); + } + } else { + startingPage = page; + } + } + + // navigate(`/${getWorkspaceId()}/apps/${slug ?? appId}/${startingPage.handle}`); + } + + // Add page id and handle to the state on initial load + const currentState = window.history.state || {}; + const pageInfo = { + id: startingPage.id, + handle: startingPage.handle, + }; + const newState = { ...currentState, ...pageInfo }; + window.history.replaceState(newState, '', window.location.href); + + setCurrentPageHandle(startingPage.handle); + updateFeatureAccess(); + setCurrentPageId(startingPage.id, moduleId); + setResolvedPageConstants({ + id: startingPage?.id, + handle: startingPage?.handle, + name: startingPage?.name, + }); + setComponentNameIdMapping(moduleId); + updateEventsField('events', appData.events); + setCurrentVersionId(appData.editing_version?.id || appData.current_version_id); + setAppHomePageId(homePageId); + + const queryData = + isPublicAccess || (mode !== 'edit' && appData.is_public) + ? appData + : await dataqueryService.getAll(appData.editing_version?.id || appData.current_version_id); + const dataQueries = queryData.data_queries || queryData?.editing_version?.data_queries; + dataQueries.forEach((query) => normalizeQueryTransformationOptions(query)); + setQueries(dataQueries); + if (dataQueries?.length > 0) { + setSelectedQuery(dataQueries[0]?.id); + initialiseResolvedQuery(dataQueries.map((query) => query.id)); + } + const constants = constantsResp?.constants; + + if (constants) { + const orgConstants = {}; + const orgSecrets = {}; + constants.map((constant) => { + if (constant.type !== 'Secret') { + orgConstants[constant.name] = constant.value; + } else { + orgSecrets[constant.name] = constant.value; + } + }); + setResolvedConstants(orgConstants); + setSecrets(orgSecrets); + } + setQueryMapping(moduleId); + + setResolvedGlobals('environment', editorEnvironment); + setResolvedGlobals('mode', { value: mode }); + setResolvedGlobals('currentUser', { + ...user, + groups: currentSession?.groups, + role: currentSession?.role?.name, + ssoUserInfo: currentSession?.ssoUserInfo, + ...(currentSession?.currentUser?.metadata && !isEmpty(currentSession?.currentUser?.metadata) + ? { metadata: currentSession?.currentUser?.metadata } + : {}), + }); + setResolvedGlobals('urlparams', JSON.parse(JSON.stringify(queryString.parse(location?.search)))); + initDependencyGraph(moduleId); + setCurrentMode(mode); // TODO: set mode based on the slug/appDef + if ( + state.ai && + state?.prompt && + initialLoadRef.current && + (conversation?.aiConversationMessages || []).length === 0 + ) { + setSelectedSidebarItem('tooljetai'); + toggleLeftSidebar('true'); + sendMessage(state.prompt); + setConversationZeroState(true); + showWalkthrough = false; + } + // fetchDataSources(appData.editing_version.id, editorEnvironment.id); + if (!isPublicAccess) { + const envFromQueryParams = mode === 'view' && new URLSearchParams(location?.search)?.get('env'); + useStore.getState().init(appData.editing_version?.id || appData.current_version_id, envFromQueryParams); + fetchGlobalDataSources( + appData.organization_id, + appData.editing_version?.id || appData.current_version_id, + editorEnvironment.id + ); + } + useStore.getState().updateEditingVersion(appData.editing_version?.id || appData.current_version_id); //check if this is needed + updateReleasedVersionId(appData.current_version_id); + + setEditorLoading(false); + initialLoadRef.current = false; + // only show if app is not created from prompt + if (showWalkthrough) initEditorWalkThrough(); + checkAndSetTrueBuildSuggestionsFlag(); + return () => { + document.title = retrieveWhiteLabelText(); + }; + }); }, [setApp, setEditorLoading, currentSession]); useEffect(() => { diff --git a/frontend/src/_components/DynamicForm.jsx b/frontend/src/_components/DynamicForm.jsx index 0f5db30e9b..a71974afc4 100644 --- a/frontend/src/_components/DynamicForm.jsx +++ b/frontend/src/_components/DynamicForm.jsx @@ -26,6 +26,7 @@ import { Constants } from '@/_helpers/utils'; import { OverlayTrigger, Tooltip } from 'react-bootstrap'; import Sharepoint from '@/_components/Sharepoint'; import AccordionForm from './AccordionForm'; +import { generateCypressDataCy } from '../modules/common/helpers/cypressHelpers'; const DynamicForm = ({ schema, @@ -534,7 +535,7 @@ const DynamicForm = ({ const labelElement = ( )} -
+
{flipComponentDropdown.helpText && ( diff --git a/frontend/src/_ui/HttpHeaders/SourceEditor.jsx b/frontend/src/_ui/HttpHeaders/SourceEditor.jsx index 04df343740..dda0775468 100644 --- a/frontend/src/_ui/HttpHeaders/SourceEditor.jsx +++ b/frontend/src/_ui/HttpHeaders/SourceEditor.jsx @@ -21,8 +21,7 @@ export default ({ return (
{options.length === 0 && ( -
+
There are no key value pairs added
@@ -86,7 +85,7 @@ export default ({
addNewKeyValuePair(options)} diff --git a/frontend/src/components/ui/Input/CommonInput/Index.jsx b/frontend/src/components/ui/Input/CommonInput/Index.jsx index baee3ad5d8..2b968a0940 100644 --- a/frontend/src/components/ui/Input/CommonInput/Index.jsx +++ b/frontend/src/components/ui/Input/CommonInput/Index.jsx @@ -3,6 +3,7 @@ import NumberInput from './NumberInput'; import TextInput from './TextInput'; import { HelperMessage, InputLabel, ValidationMessage } from '../InputUtils/InputUtils'; import { ButtonSolid } from '../../../../_components/AppButton'; +import { generateCypressDataCy } from '../../../../modules/common/helpers/cypressHelpers.js'; const CommonInput = ({ label, helperText, disabled, required, onChange: change, ...restProps }) => { const { type, encrypted, validation, isValidatedMessages, isDisabled } = restProps; @@ -65,13 +66,14 @@ const CommonInput = ({ label, helperText, disabled, required, onChange: change, rel="noreferrer" disabled={isDisabled} onClick={toggleEditing} + data-cy={`button-${generateCypressDataCy(isEditing ? 'Cancel' : 'Edit')}`} > {isEditing ? 'Cancel' : 'Edit'}
- + Encrypted diff --git a/frontend/src/components/ui/Input/InputUtils/InputUtils.jsx b/frontend/src/components/ui/Input/InputUtils/InputUtils.jsx index 57128adf73..57e92a819b 100644 --- a/frontend/src/components/ui/Input/InputUtils/InputUtils.jsx +++ b/frontend/src/components/ui/Input/InputUtils/InputUtils.jsx @@ -3,6 +3,7 @@ import { Label } from '../../Label/Label'; import ValidationIcon from './ValidationIcon'; import { cn } from '@/lib/utils'; import HelperIcon from './HelperIcon'; +import { generateCypressDataCy } from '../../../../modules/common/helpers/cypressHelpers.js'; export const ValidationMessage = ({ response, validationMessage, className }) => (
@@ -14,7 +15,7 @@ export const ValidationMessage = ({ response, validationMessage, className }) => type="helper" size="default" className={`tw-font-normal ${response === true ? 'tw-text-text-success' : '!tw-text-text-warning'}`} - data-cy="validation-label" + data-cy={`${generateCypressDataCy(validationMessage)}-validation-label`} > {validationMessage} @@ -53,7 +54,7 @@ export const InputLabel = ({ disabled, label, required }) => ( type="label" size="default" className={`tw-font-medium tw-mb-[2px] ${disabled ? 'tw-text-text-disabled' : ''}`} - data-cy="input-field-label" + data-cy={`${generateCypressDataCy(label)}-field-label`} > {label} {required && } diff --git a/frontend/src/modules/common/helpers/cypressHelpers.js b/frontend/src/modules/common/helpers/cypressHelpers.js new file mode 100644 index 0000000000..36bb5d713f --- /dev/null +++ b/frontend/src/modules/common/helpers/cypressHelpers.js @@ -0,0 +1,6 @@ +export function generateCypressDataCy(text) { + return String(text) + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, ""); +} diff --git a/frontend/src/modules/dataSources/components/DataSourceManager/DataSourceManager.jsx b/frontend/src/modules/dataSources/components/DataSourceManager/DataSourceManager.jsx index 65ee96758b..ea556429dd 100644 --- a/frontend/src/modules/dataSources/components/DataSourceManager/DataSourceManager.jsx +++ b/frontend/src/modules/dataSources/components/DataSourceManager/DataSourceManager.jsx @@ -36,6 +36,7 @@ import './dataSourceManager.theme.scss'; import { canUpdateDataSource } from '@/_helpers'; import DataSourceSchemaManager from '@/_helpers/dataSourceSchemaManager'; import MultiEnvTabs from './MultiEnvTabs'; +import { generateCypressDataCy } from '../../../common/helpers/cypressHelpers'; class DataSourceManagerComponent extends React.Component { constructor(props) { @@ -1127,7 +1128,11 @@ class DataSourceManagerComponent extends React.Component {
{validationError.map((error, index) => ( -
+
{error}
))} diff --git a/server/data-migrations/1742369617678-EnforceNewBasicPlanLimits.ts b/server/data-migrations/1742369617678-EnforceNewBasicPlanLimits.ts index 7bc644b4c4..1e784a20bf 100644 --- a/server/data-migrations/1742369617678-EnforceNewBasicPlanLimits.ts +++ b/server/data-migrations/1742369617678-EnforceNewBasicPlanLimits.ts @@ -16,7 +16,9 @@ export class EnforceNewBasicPlanLimits1742369617678 implements MigrationInterfac } const manager = queryRunner.manager; const nestApp = await NestFactory.createApplicationContext(await AppModule.register({ IS_GET_CONTEXT: true })); - const { LicenseCountsService } = await import(`${await getImportPath(true, edition)}/licensing/services/count.service`); + const { LicenseCountsService } = await import( + `${await getImportPath(true, edition)}/licensing/services/count.service` + ); const licenseInitService = nestApp.get(LicenseInitService); const { isValid } = await licenseInitService.initForMigration(manager); @@ -170,6 +172,32 @@ export class EnforceNewBasicPlanLimits1742369617678 implements MigrationInterfac await manager.query(archiveViewersInstanceQuery); } } + + const superAdminCountQuery = ` + SELECT COUNT(*) FROM users + WHERE status = '${USER_STATUS.ACTIVE}' AND user_type = '${USER_TYPE.INSTANCE}' + `; + const superAdminCount = await manager.query(superAdminCountQuery); + + if (superAdminCount === 0) { + const superAdminsQuery = ` + SELECT * FROM users + WHERE user_type = '${USER_TYPE.INSTANCE}' + ORDER BY id ASC + `; + const superAdmins = await manager.query(superAdminsQuery); + + if (superAdmins.length > 0) { + const oneInstanceUser = superAdmins[0]; + console.log('Activating one instance user:', oneInstanceUser.id, oneInstanceUser.email); + const activateUserQuery = ` + UPDATE users + SET status = '${USER_STATUS.ACTIVE}' + WHERE id = '${oneInstanceUser.id}' + `; + await manager.query(activateUserQuery); + } + } } await nestApp.close(); } diff --git a/server/scripts/boot.sh b/server/scripts/boot.sh index 3c2bbad93e..a561f80987 100755 --- a/server/scripts/boot.sh +++ b/server/scripts/boot.sh @@ -1,8 +1,6 @@ #!/bin/bash set -e -service postgresql start - echo " _____ _ ___ _ |_ _| | | |_ | | | @@ -15,6 +13,7 @@ Everything you need to build internal tools! GitHub: https://github.com/ToolJet/ToolJet " +# Run setup npm run db:setup:prod -npm run db:seed:prod -npm run start:prod +# Start ToolJet +exec npm run start:prod diff --git a/server/scripts/preview.sh b/server/scripts/preview.sh new file mode 100644 index 0000000000..6664902874 --- /dev/null +++ b/server/scripts/preview.sh @@ -0,0 +1,22 @@ +#!/bin/bash +set -e + +# Fix ownership and permissions +chown -R postgres:postgres /var/lib/postgresql /var/run/postgresql +chmod 0700 /var/lib/postgresql/13/main + +# Initialize DB cluster if needed +if [ ! -s "/var/lib/postgresql/13/main/PG_VERSION" ]; then + echo "Initializing PostgreSQL..." + su - postgres -c "/usr/lib/postgresql/13/bin/initdb -D /var/lib/postgresql/13/main" +fi + +# Start PostgreSQL +echo "Starting PostgreSQL..." +su - postgres -c "/usr/lib/postgresql/13/bin/pg_ctl -D /var/lib/postgresql/13/main -w start" + +# Export the PORT variable to be used by the application +export PORT=${PORT:-80} + +# Start Supervisor +exec supervisord -c /etc/supervisor/conf.d/supervisord.conf