Merge branch 'main' into fix/external-epi

This commit is contained in:
Midhun G S 2025-05-13 11:36:36 +05:30 committed by GitHub
commit 3d697f08b1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
27 changed files with 550 additions and 415 deletions

View file

@ -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

View file

@ -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('[]')
}}

View file

@ -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('[]')
}}

View file

@ -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')

View file

@ -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" },

View file

@ -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"]',

View file

@ -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"]`;

View file

@ -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");
}

View file

@ -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

View file

@ -2127,7 +2127,7 @@
"encrypted": false
},
"host": {
"value": "35.238.9.114",
"value": "9.234.17.31",
"encrypted": false
},
"port": {

View file

@ -585,7 +585,7 @@
"encrypted": false
},
"host": {
"value": "35.238.9.114",
"value": "9.234.17.31",
"encrypted": false
},
"port": {

View file

@ -1862,7 +1862,7 @@
"encrypted": false
},
"host": {
"value": "35.238.9.114",
"value": "9.234.17.31",
"encrypted": false
},
"port": {

View file

@ -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": {

View file

@ -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"]

View file

@ -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"]

View file

@ -254,7 +254,7 @@ const RunButton = ({ buttonLoadingState }) => {
<ButtonComponent
size="medium"
variant="secondary"
onClick={() => 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}

View file

@ -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(() => {

View file

@ -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 = (
<label
className="form-label"
data-cy={`label-${String(label).toLowerCase().replace(/\s+/g, '-')}`}
data-cy={`label-${generateCypressDataCy(label)}`}
style={{ textDecoration: tooltip ? 'underline 2px dashed' : 'none', textDecorationColor: 'var(--slate8)' }}
>
{label}
@ -572,7 +573,7 @@ const DynamicForm = ({
'd-flex': isHorizontalLayout,
'dynamic-form-row': isHorizontalLayout,
})}
data-cy={`${key.replace(/_/g, '-')}-section`}
data-cy={`${generateCypressDataCy(key)}-section`}
key={key}
>
{!isSpecificComponent && (
@ -628,7 +629,7 @@ const DynamicForm = ({
<Element
{...getElementProps(obj[key])}
{...computedProps[propertyKey]}
data-cy={`${String(label).toLocaleLowerCase().replace(/\s+/g, '-')}-text-field`}
data-cy={`${generateCypressDataCy(label)}-text-field`}
dataCy={obj[key].key.replace(/_/g, '-')}
//to be removed after whole ui is same
isHorizontalLayout={isHorizontalLayout}
@ -663,15 +664,16 @@ const DynamicForm = ({
{(flipComponentDropdown.label || isHorizontalLayout) && (
<label
className={cx('form-label')}
data-cy={`${String(flipComponentDropdown.label)
.toLocaleLowerCase()
.replace(/\s+/g, '-')}-dropdown-label`}
data-cy={`${generateCypressDataCy(flipComponentDropdown.label)}-dropdown-label`}
>
{flipComponentDropdown.label}
</label>
)}
<div data-cy={`${String(flipComponentDropdown.label).toLocaleLowerCase().replace(/\s+/g, '-')}-select-dropdown`} className={cx({ 'flex-grow-1': isHorizontalLayout })}>
<div
data-cy={`${generateCypressDataCy(flipComponentDropdown.label)}-select-dropdown`}
className={cx({ 'flex-grow-1': isHorizontalLayout })}
>
<Select
{...getElementProps(flipComponentDropdown)}
styles={computeSelectStyles ? computeSelectStyles('100%') : {}}

View file

@ -14,6 +14,7 @@ import { canDeleteDataSource, canUpdateDataSource } from '@/_helpers';
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
import { orgEnvironmentVariableService, orgEnvironmentConstantService } from '../_services';
import { Constants } from '@/_helpers/utils';
import { generateCypressDataCy } from '../modules/common/helpers/cypressHelpers.js';
const DynamicFormV2 = ({
schema,
@ -437,7 +438,7 @@ const DynamicFormV2 = ({
const labelElement = (
<label
className="form-label"
data-cy={`label-${String(label).toLowerCase().replace(/\s+/g, '-')}`}
data-cy={`label-${generateCypressDataCy(label)}`}
style={{ textDecoration: tooltip ? 'underline 2px dashed' : 'none', textDecorationColor: 'var(--slate8)' }}
>
{label}
@ -475,6 +476,7 @@ const DynamicFormV2 = ({
'd-flex': isHorizontalLayout,
'dynamic-form-row': isHorizontalLayout,
})}
data-cy={`${generateCypressDataCy(key)}-section`}
key={key}
>
{!isSpecificComponent && (
@ -505,7 +507,8 @@ const DynamicFormV2 = ({
<Element
{...getElementProps(uiProperties[key])}
{...computedProps[propertyKey]}
data-cy={`${String(label).toLocaleLowerCase().replace(/\s+/g, '-')}-text-field`}
data-cy={`${generateCypressDataCy(label)}-text-field`}
dataCy={uiProperties[key].key.replace(/_/g, '-')}
//to be removed after whole ui is same
isHorizontalLayout={isHorizontalLayout}
/>
@ -539,15 +542,16 @@ const DynamicFormV2 = ({
{(flipComponentDropdown.label || isHorizontalLayout) && (
<label
className={cx('form-label')}
data-cy={`${String(flipComponentDropdown.label)
.toLocaleLowerCase()
.replace(/\s+/g, '-')}-dropdown-label`}
data-cy={`${generateCypressDataCy(flipComponentDropdown.label)}-dropdown-label`}
>
{flipComponentDropdown.label}
</label>
)}
<div data-cy={'query-select-dropdown'} className={cx({ 'flex-grow-1': isHorizontalLayout })}>
<div
data-cy={`${generateCypressDataCy(flipComponentDropdown.label)}-select-dropdown`}
className={cx({ 'flex-grow-1': isHorizontalLayout })}
>
<Select {...getElementProps(flipComponentDropdown)} styles={{}} useCustomStyles={false} />
</div>
{flipComponentDropdown.helpText && (

View file

@ -21,8 +21,7 @@ export default ({
return (
<div className="table-content-wrapper">
{options.length === 0 && (
<div className="empty-key-value"
data-cy="label-empty-key-value">
<div className="empty-key-value" data-cy="label-empty-key-value">
<InfoIcon style={{ width: '16px', marginRight: '5px' }} />
<span>There are no key value pairs added</span>
</div>
@ -86,7 +85,7 @@ export default ({
<div className="d-flex mb-2" style={{ height: '16px' }}>
<ButtonSolid
data-cy={`${dataCy}-add-more-button`}
data-cy={`${dataCy}-add-button`}
variant="ghostBlue"
size="sm"
onClick={() => addNewKeyValuePair(options)}

View file

@ -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'}
</ButtonSolid>
</div>
<div className="col-auto mb-2">
<small className="text-green">
<small className="text-green" data-cy="encrypted-text">
<img className="mx-2 encrypted-icon" src="assets/images/icons/padlock.svg" width="12" height="12" />
Encrypted
</small>

View file

@ -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 }) => (
<div className={cn('tw-flex tw-pl-[2px] tw-items-start tw-my-[2px]', 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}
</Label>
@ -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 && <RequiredIndicator disabled={disabled} />}

View file

@ -0,0 +1,6 @@
export function generateCypressDataCy(text) {
return String(text)
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
}

View file

@ -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 {
<div className="row w-100">
<div className="alert alert-danger" role="alert">
{validationError.map((error, index) => (
<div key={index} className="text-muted" data-cy="connection-alert-text">
<div
key={index}
className="text-muted"
data-cy={`${generateCypressDataCy(error)}-field-alert-text`}
>
{error}
</div>
))}

View file

@ -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();
}

View file

@ -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

22
server/scripts/preview.sh Normal file
View file

@ -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