mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-23 08:58:26 +00:00
merge base
This commit is contained in:
commit
3f201e9a28
227 changed files with 5733 additions and 2493 deletions
68
.github/workflows/cypress-appbuilder.yml
vendored
68
.github/workflows/cypress-appbuilder.yml
vendored
|
|
@ -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
|
||||
|
|
|
|||
19
.github/workflows/cypress-marketplace.yml
vendored
19
.github/workflows/cypress-marketplace.yml
vendored
|
|
@ -14,17 +14,19 @@ jobs:
|
|||
Cypress-Marketplace:
|
||||
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')
|
||||
if: contains(github.event.pull_request.labels.*.name, 'run-cypress') ||
|
||||
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('[]')
|
||||
}}
|
||||
|
||||
|
|
@ -159,13 +161,12 @@ jobs:
|
|||
"password": "password"
|
||||
}'
|
||||
|
||||
|
||||
- name: Create Cypress environment file
|
||||
id: create-json
|
||||
uses: jsdaniell/create-json@1.1.2
|
||||
with:
|
||||
name: "cypress.env.json"
|
||||
json: ${{ secrets.CYPRESS_SECRETS }}
|
||||
json: ${{ secrets.CYPRESS_SECRETS_MARKETPLACE }}
|
||||
dir: "./cypress-tests"
|
||||
|
||||
- name: Marketplace
|
||||
|
|
@ -185,8 +186,8 @@ jobs:
|
|||
Cypress-Marketplace-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-marketplace-subpath')
|
||||
if: contains(github.event.pull_request.labels.*.name, 'run-cypress') ||
|
||||
contains(github.event.pull_request.labels.*.name, 'run-cypress-marketplace-subpath')
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
|
|
|
|||
11
.github/workflows/cypress-platform.yml
vendored
11
.github/workflows/cypress-platform.yml
vendored
|
|
@ -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('[]')
|
||||
}}
|
||||
|
||||
|
|
|
|||
173
.github/workflows/render-preview-deploy.yml
vendored
173
.github/workflows/render-preview-deploy.yml
vendored
|
|
@ -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": "/var/lib/postgresql/13/main",
|
||||
"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')
|
||||
|
|
@ -389,6 +393,39 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
|
||||
- name: Sync repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Check if Forked Repository
|
||||
id: check_repo
|
||||
run: |
|
||||
if [[ "${{ github.event.pull_request.head.repo.fork }}" == "true" ]]; then
|
||||
echo "is_fork=true" >> $GITHUB_ENV
|
||||
echo "FORKED_OWNER=${{ github.event.pull_request.head.repo.owner.login }}" >> $GITHUB_ENV
|
||||
else
|
||||
echo "is_fork=false" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Set Repository URL
|
||||
run: |
|
||||
if [[ "$is_fork" == "true" ]]; then
|
||||
echo "REPO_URL=https://github.com/${FORKED_OWNER}/ToolJet" >> $GITHUB_ENV
|
||||
else
|
||||
echo "REPO_URL=https://github.com/ToolJet/ToolJet" >> $GITHUB_ENV
|
||||
fi
|
||||
|
||||
- name: Fetch and Checkout Forked Branch
|
||||
if: env.is_fork == 'true'
|
||||
run: |
|
||||
git fetch origin pull/${{ github.event.number }}/head:${{ env.BRANCH_NAME }}
|
||||
git checkout ${{ env.BRANCH_NAME }}
|
||||
|
||||
- name: Checkout Default Branch
|
||||
if: env.is_fork == 'false'
|
||||
uses: actions/checkout@v3
|
||||
|
||||
|
||||
- name: Creating deployment for Enterprise Edition
|
||||
id: create-ee-deployment
|
||||
run: |
|
||||
|
|
@ -404,7 +441,7 @@ jobs:
|
|||
"name": "ToolJet EE PR #${{ env.PR_NUMBER }}",
|
||||
"notifyOnFail": "default",
|
||||
"ownerId": "tea-caeo4bj19n072h3dddc0",
|
||||
"repo": "https://github.com/ToolJet/ToolJet",
|
||||
"repo": "'"$REPO_URL"'",
|
||||
"slug": "tooljet-ee-pr-${{ env.PR_NUMBER }}",
|
||||
"suspended": "not_suspended",
|
||||
"suspenders": [],
|
||||
|
|
@ -420,7 +457,7 @@ jobs:
|
|||
},
|
||||
{
|
||||
"key": "PG_USER",
|
||||
"value": "tooljet"
|
||||
"value": "postgres"
|
||||
},
|
||||
{
|
||||
"key": "PG_PASS",
|
||||
|
|
@ -440,7 +477,7 @@ jobs:
|
|||
},
|
||||
{
|
||||
"key": "TOOLJET_DB_USER",
|
||||
"value": "tooljet"
|
||||
"value": "postgres"
|
||||
},
|
||||
{
|
||||
"key": "TOOLJET_DB_PASS",
|
||||
|
|
@ -456,7 +493,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 +573,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 +590,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 +688,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 +726,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 +758,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')
|
||||
|
|
|
|||
2
.version
2
.version
|
|
@ -1 +1 @@
|
|||
3.11.0
|
||||
3.12.1
|
||||
|
|
|
|||
|
|
@ -39,11 +39,11 @@ module.exports = defineConfig({
|
|||
chromeWebSecurity: false,
|
||||
trashAssetsBeforeRuns: true,
|
||||
e2e: {
|
||||
setupNodeEvents(on, config) {
|
||||
setupNodeEvents (on, config) {
|
||||
config.baseUrl = environment.baseUrl;
|
||||
|
||||
on("task", {
|
||||
readPdf(pathToPdf) {
|
||||
readPdf (pathToPdf) {
|
||||
return new Promise((resolve) => {
|
||||
const pdfPath = path.resolve(pathToPdf);
|
||||
let dataBuffer = fs.readFileSync(pdfPath);
|
||||
|
|
@ -55,7 +55,7 @@ module.exports = defineConfig({
|
|||
});
|
||||
|
||||
on("task", {
|
||||
readXlsx(filePath) {
|
||||
readXlsx (filePath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
let dataBuffer = fs.readFileSync(filePath);
|
||||
|
|
@ -69,7 +69,7 @@ module.exports = defineConfig({
|
|||
});
|
||||
|
||||
on("task", {
|
||||
deleteFolder(folderName) {
|
||||
deleteFolder (folderName) {
|
||||
return new Promise((resolve, reject) => {
|
||||
rmdir(folderName, { maxRetries: 10, recursive: true }, (err) => {
|
||||
if (err) {
|
||||
|
|
@ -83,7 +83,7 @@ module.exports = defineConfig({
|
|||
});
|
||||
|
||||
on("task", {
|
||||
dbConnection({ dbconfig, sql }) {
|
||||
dbConnection ({ dbconfig, sql }) {
|
||||
const client = new pg.Pool(dbconfig);
|
||||
return client.query(sql);
|
||||
},
|
||||
|
|
@ -97,9 +97,9 @@ module.exports = defineConfig({
|
|||
baseUrl: environment.baseUrl,
|
||||
configFile: environment.configFile,
|
||||
specPattern: [
|
||||
"cypress/e2e/happyPath/platform/ceTestcases/userFlow/firstUserOnboarding.cy.js",
|
||||
"cypress/e2e/happyPath/platform/commonTestcases/workspace/dashboard.cy.js",
|
||||
"cypress/e2e/happyPath/platform/ceTestcases/!(userFlow)/**/*.cy.js",
|
||||
"cypress/e2e/happyPath/platform/firstUser/firstUserOnboarding.cy.js",
|
||||
"cypress/e2e/happyPath/platform/ceTestcases/apps/appSlug.cy.js",
|
||||
"cypress/e2e/happyPath/platform/ceTestcases/**/!(*appSlug).cy.js",
|
||||
"cypress/e2e/happyPath/platform/commonTestcases/**/*.cy.js",
|
||||
],
|
||||
numTestsKeptInMemory: 1,
|
||||
|
|
|
|||
|
|
@ -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" },
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { passwordInputText } from "Texts/passwordInput";
|
|||
import { importSelectors } from "Selectors/exportImport";
|
||||
import { importText } from "Texts/exportImport";
|
||||
import { onboardingSelectors } from "Selectors/onboarding";
|
||||
import { selectAppCardOption } from "Support/utils/common";
|
||||
|
||||
const API_ENDPOINT =
|
||||
Cypress.env("environment") === "Community"
|
||||
|
|
@ -160,13 +161,15 @@ Cypress.Commands.add(
|
|||
|
||||
Cypress.Commands.add("deleteApp", (appName) => {
|
||||
cy.intercept("DELETE", "/api/apps/*").as("appDeleted");
|
||||
cy.get(commonSelectors.appCard(appName))
|
||||
.realHover()
|
||||
.find(commonSelectors.appCardOptionsButton)
|
||||
.realHover()
|
||||
.click();
|
||||
cy.get(commonSelectors.deleteAppOption).click();
|
||||
selectAppCardOption(
|
||||
appName,
|
||||
commonSelectors.appCardOptions(commonText.deleteAppOption)
|
||||
);
|
||||
cy.get(commonSelectors.buttonSelector(commonText.modalYesButton)).click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
commonText.appDeletedToast
|
||||
);
|
||||
cy.wait("@appDeleted");
|
||||
});
|
||||
|
||||
|
|
@ -394,39 +397,37 @@ Cypress.Commands.add("getPosition", (componentName) => {
|
|||
});
|
||||
|
||||
Cypress.Commands.add("defaultWorkspaceLogin", () => {
|
||||
cy.apiLogin();
|
||||
cy.task("dbConnection", {
|
||||
dbconfig: Cypress.env("app_db"),
|
||||
sql: `
|
||||
SELECT id FROM organizations WHERE name = 'My workspace';`,
|
||||
}).then((resp) => {
|
||||
const workspaceId = resp.rows[0].id;
|
||||
|
||||
// cy.intercept("GET", API_ENDPOINT).as("library_apps");
|
||||
cy.visit("/my-workspace");
|
||||
cy.wait(2000)
|
||||
cy.get(commonSelectors.homePageLogo, { timeout: 10000 });
|
||||
// cy.wait("@library_apps");
|
||||
cy.apiLogin(
|
||||
"dev@tooljet.io",
|
||||
"password",
|
||||
workspaceId,
|
||||
"/my-workspace"
|
||||
).then(() => {
|
||||
cy.visit("/");
|
||||
cy.wait(2000);
|
||||
cy.get(commonSelectors.homePageLogo, { timeout: 10000 });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add(
|
||||
"visitSlug",
|
||||
({
|
||||
actualUrl,
|
||||
errorUrls = [
|
||||
`${Cypress.config("baseUrl")}/error/unknown`,
|
||||
`${Cypress.config("baseUrl")}/error/restricted`,
|
||||
],
|
||||
}) => {
|
||||
if (!actualUrl) {
|
||||
throw new Error("actualUrl is required for visitSlug command.");
|
||||
Cypress.Commands.add("visitSlug", ({ actualUrl }) => {
|
||||
cy.visit(actualUrl);
|
||||
cy.wait(1000);
|
||||
|
||||
cy.url().then((currentUrl) => {
|
||||
if (currentUrl !== actualUrl) {
|
||||
cy.visit(actualUrl);
|
||||
cy.wait(1000);
|
||||
}
|
||||
|
||||
cy.visit(actualUrl);
|
||||
|
||||
cy.url().then((url) => {
|
||||
if (errorUrls.includes(url)) {
|
||||
cy.log(`Navigation resulted in error URL: ${url}. Retrying...`);
|
||||
cy.visit(actualUrl);
|
||||
cy.wait(1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Cypress.Commands.add("releaseApp", () => {
|
||||
|
|
@ -513,13 +514,58 @@ Cypress.Commands.overwrite(
|
|||
}
|
||||
);
|
||||
|
||||
Cypress.Commands.add("installMarketplacePlugin", (pluginName) => {
|
||||
const MARKETPLACE_URL = `${Cypress.config("baseUrl")}/integrations/marketplace`;
|
||||
|
||||
cy.visit(MARKETPLACE_URL);
|
||||
cy.wait(1000);
|
||||
|
||||
cy.get('[data-cy="-list-item"]').eq(0).click();
|
||||
cy.wait(1000);
|
||||
|
||||
cy.get("body").then(($body) => {
|
||||
if ($body.find(".plugins-card").length === 0) {
|
||||
cy.log("No plugins found, proceeding to install...");
|
||||
installPlugin(pluginName);
|
||||
} else {
|
||||
cy.get(".plugins-card").then(($cards) => {
|
||||
const isInstalled = $cards.toArray().some((card) => {
|
||||
return (
|
||||
Cypress.$(card)
|
||||
.find(".font-weight-medium.text-capitalize")
|
||||
.text()
|
||||
.trim() === pluginName
|
||||
);
|
||||
});
|
||||
|
||||
if (isInstalled) {
|
||||
cy.log(`${pluginName} is already installed. Skipping installation.`);
|
||||
cy.get(commonSelectors.globalDataSourceIcon).click();
|
||||
} else {
|
||||
installPlugin(pluginName);
|
||||
cy.get(commonSelectors.globalDataSourceIcon).click();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function installPlugin (pluginName) {
|
||||
cy.get('[data-cy="-list-item"]').eq(1).click();
|
||||
cy.wait(1000);
|
||||
|
||||
cy.contains(".plugins-card", pluginName).within(() => {
|
||||
cy.get(".marketplace-install").click();
|
||||
cy.wait(1000);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Cypress.Commands.add("verifyElement", (selector, text, eqValue) => {
|
||||
const element =
|
||||
eqValue !== undefined ? cy.get(selector).eq(eqValue) : cy.get(selector);
|
||||
element.should("be.visible").and("have.text", text);
|
||||
});
|
||||
|
||||
|
||||
Cypress.Commands.add("getAppId", (appName) => {
|
||||
cy.task("dbConnection", {
|
||||
dbconfig: Cypress.env("app_db"),
|
||||
|
|
@ -529,3 +575,33 @@ Cypress.Commands.add("getAppId", (appName) => {
|
|||
return appId;
|
||||
});
|
||||
});
|
||||
|
||||
Cypress.Commands.add("uninstallMarketplacePlugin", (pluginName) => {
|
||||
const MARKETPLACE_URL = `${Cypress.config("baseUrl")}/integrations/marketplace`;
|
||||
|
||||
cy.visit(MARKETPLACE_URL);
|
||||
cy.wait(1000);
|
||||
|
||||
cy.get('[data-cy="-list-item"]').eq(0).click();
|
||||
cy.wait(1000);
|
||||
|
||||
cy.get(".plugins-card").each(($card) => {
|
||||
cy.wrap($card)
|
||||
.find(".font-weight-medium.text-capitalize")
|
||||
.invoke("text")
|
||||
.then((text) => {
|
||||
if (text.trim() === pluginName) {
|
||||
cy.wrap($card).find(".link-primary").contains("Remove").click();
|
||||
cy.wait(1000);
|
||||
|
||||
cy.get('[data-cy="delete-plugin-title"]').should("be.visible");
|
||||
cy.get('[data-cy="yes-button"]').click();
|
||||
cy.wait(2000);
|
||||
|
||||
cy.log(`${pluginName} has been successfully uninstalled.`);
|
||||
} else {
|
||||
cy.log(`${pluginName} is not installed. Skipping uninstallation.`);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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"]',
|
||||
|
|
|
|||
|
|
@ -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"]`;
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ export const postgreSqlText = {
|
|||
|
||||
allDataSources: () => {
|
||||
return Cypress.env("marketplace_action")
|
||||
? "All data sources (44)"
|
||||
: "All data sources (45)";
|
||||
? "All data sources (45)"
|
||||
: "All data sources (43)";
|
||||
},
|
||||
commonlyUsed: "Commonly used (5)",
|
||||
allDatabase: () => {
|
||||
|
|
|
|||
|
|
@ -8,11 +8,7 @@ export const editVersionText = {
|
|||
|
||||
export const deleteVersionText = {
|
||||
deleteModalText: (text) => {
|
||||
// return `Are you sure you want to delete this version - ${cyParamName(
|
||||
// text
|
||||
// )}?`;
|
||||
|
||||
return `Deleting a version will permanently remove it from all environments.Are you sure you want to delete this version - ${cyParamName(
|
||||
return `Are you sure you want to delete this version - ${cyParamName(
|
||||
text
|
||||
)}?`;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import {
|
|||
addSupportCSAData,
|
||||
} from "Support/utils/events";
|
||||
|
||||
describe("Editor- Test Button widget", () => {
|
||||
describe("Editor- Test Button widget ", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.apiCreateApp(`${fake.companyName}-button-App`);
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ describe("Add all Data sources to app", () => {
|
|||
cy.apiLogin();
|
||||
});
|
||||
|
||||
it("Should verify global data source page", () => {
|
||||
it.skip("Should verify global data source page", () => {
|
||||
cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug);
|
||||
cy.visit(`${data.workspaceSlug}`);
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ describe("Add all Data sources to app", () => {
|
|||
);
|
||||
});
|
||||
|
||||
it("Should add all data sources in data source page", () => {
|
||||
it.skip("Should add all data sources in data source page", () => {
|
||||
cy.visit(`${data.workspaceSlug}`);
|
||||
|
||||
dataSources.forEach((dsName) => {
|
||||
|
|
@ -109,7 +109,7 @@ describe("Add all Data sources to app", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("Should add all data sources in the app", () => {
|
||||
it.skip("Should add all data sources in the app", () => {
|
||||
cy.visit(`${data.workspaceSlug}`);
|
||||
cy.get(commonSelectors.dashboardIcon).click();
|
||||
cy.get(commonSelectors.appCreateButton).click();
|
||||
|
|
@ -135,7 +135,7 @@ describe("Add all Data sources to app", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("Should install all makretplace plugins and add them into the app", () => {
|
||||
it.skip("Should install all makretplace plugins and add them into the app", () => {
|
||||
cy.visit(`${data.workspaceSlug}`);
|
||||
const dataSourcesMarketplace = [
|
||||
"Plivo",
|
||||
|
|
@ -189,12 +189,15 @@ describe("Add all Data sources to app", () => {
|
|||
cy.wrap(dataSourcesMarketplace).each((dsName) => {
|
||||
cy.get(commonSelectors.globalDataSourceIcon).click();
|
||||
selectAndAddDataSource("databases", dsName, dsName);
|
||||
cy.wait(500);
|
||||
cy.wait(1000);
|
||||
});
|
||||
|
||||
cy.get(commonSelectors.dashboardIcon).click();
|
||||
cy.get(commonSelectors.appCreateButton).click();
|
||||
cy.get(commonSelectors.appNameInput).click().type(data.dsNamefake1);
|
||||
cy.get(commonSelectors.dashboardIcon).should("be.visible").click();
|
||||
cy.get(commonSelectors.appCreateButton).should("be.visible").click();
|
||||
cy.get(commonSelectors.appNameInput)
|
||||
.should("be.visible")
|
||||
.click()
|
||||
.type(data.dsNamefake1);
|
||||
cy.get(commonSelectors.createAppButton).click();
|
||||
cy.skipWalkthrough();
|
||||
|
||||
|
|
@ -203,7 +206,7 @@ describe("Add all Data sources to app", () => {
|
|||
cy.get(".css-4e90k9").type(
|
||||
`cypress-${cyParamName(dsName)}-${cyParamName(dsName)}`
|
||||
);
|
||||
cy.wait(500);
|
||||
cy.wait(1000);
|
||||
|
||||
cy.contains(
|
||||
`[id*="react-select-"]`,
|
||||
|
|
@ -212,7 +215,7 @@ describe("Add all Data sources to app", () => {
|
|||
.should("be.visible")
|
||||
.click();
|
||||
|
||||
cy.wait(500);
|
||||
cy.wait(1000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,13 +19,14 @@ import {
|
|||
import { dataSourceSelector } from "../../../../../constants/selectors/dataSource";
|
||||
|
||||
const data = {};
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
data.dsName1 = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
|
||||
data.queryName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
|
||||
describe("Data source Airtable", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.visit("/");
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("Should verify elements on connection AirTable form", () => {
|
||||
|
|
@ -199,7 +200,7 @@ describe("Data source Airtable", () => {
|
|||
cy.get('[data-cy="show-ds-popover-button"]').click();
|
||||
cy.get(".css-4e90k9").type(`${data.dsName}`);
|
||||
cy.contains(`[id*="react-select-"]`, data.dsName).click();
|
||||
cy.get('[data-cy="query-rename-input"]').clear().type(data.dsName1);
|
||||
cy.get('[data-cy="query-rename-input"]').clear().type(data.queryName);
|
||||
|
||||
cy.get(airTableSelector.operationSelectDropdown)
|
||||
.click()
|
||||
|
|
@ -225,7 +226,7 @@ describe("Data source Airtable", () => {
|
|||
cy.get(dataSourceSelector.queryPreviewButton).click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
`Query (${data.dsName1}) completed.`
|
||||
`Query (${data.queryName}) completed.`
|
||||
);
|
||||
|
||||
// Verify Delete record operation
|
||||
|
|
@ -277,7 +278,7 @@ describe("Data source Airtable", () => {
|
|||
cy.get(dataSourceSelector.queryPreviewButton).click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
`Query (${data.dsName1}) completed.`
|
||||
`Query (${data.queryName}) completed.`
|
||||
);
|
||||
deleteAppandDatasourceAfterExecution(
|
||||
data.dsName,
|
||||
|
|
@ -20,15 +20,15 @@ import {
|
|||
import { dataSourceSelector } from "../../../../../constants/selectors/dataSource";
|
||||
|
||||
const data = {};
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
|
||||
describe("Data source amazon athena", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.visit("/");
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("Should verify elements on amazon athena connection form", () => {
|
||||
it.skip("Should verify elements on amazon athena connection form", () => {
|
||||
const Accesskey = Cypress.env("amazonathena_accessKey");
|
||||
const Secretkey = Cypress.env("amazonathena_secretKey");
|
||||
const DbName = Cypress.env("amazonathena_DbName");
|
||||
|
|
@ -97,7 +97,7 @@ describe("Data source amazon athena", () => {
|
|||
deleteDatasource(`cypress-${data.dsName}-Amazon-Athena`);
|
||||
});
|
||||
|
||||
it("Should verify the functionality of amazon athena connection form.", () => {
|
||||
it.skip("Should verify the functionality of amazon athena connection form.", () => {
|
||||
const Accesskey = Cypress.env("amazonathena_accessKey");
|
||||
const Secretkey = Cypress.env("amazonathena_secretKey");
|
||||
const DbName = Cypress.env("amazonathena_DbName");
|
||||
|
|
@ -134,7 +134,7 @@ describe("Data source amazon athena", () => {
|
|||
deleteDatasource(`cypress-${data.dsName}-amazon-Athena`);
|
||||
});
|
||||
|
||||
it("Should able to run the query with valid conection", () => {
|
||||
it.skip("Should able to run the query with valid conection", () => {
|
||||
const Accesskey = Cypress.env("amazonathena_accessKey");
|
||||
const Secretkey = Cypress.env("amazonathena_secretKey");
|
||||
const DbName = Cypress.env("amazonathena_DbName");
|
||||
|
|
@ -188,11 +188,13 @@ describe("Data source amazon athena", () => {
|
|||
cy.get(".css-4e90k9").type(`${data.dsName}`);
|
||||
cy.contains(`[id*="react-select-"]`, data.dsName).click();
|
||||
cy.get('[data-cy="query-rename-input"]').clear().type(data.dsName);
|
||||
|
||||
cy.get(`[data-cy="list-query-${data.dsName}"]`).should("be.visible");
|
||||
cy.get('[data-cy="query-input-field"]').clearAndTypeOnCodeMirror(
|
||||
"SHOW DATABASES;"
|
||||
);
|
||||
|
||||
cy.get(
|
||||
'[data-cy="query-input-field"] >>> .cm-editor >> .cm-content > .cm-line'
|
||||
).should("have.text", "SHOW DATABASES;");
|
||||
cy.get(dataSourceSelector.queryPreviewButton).click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
|
|
@ -20,15 +20,15 @@ import {
|
|||
import { dataSourceSelector } from "../../../../../constants/selectors/dataSource";
|
||||
|
||||
const data = {};
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
|
||||
describe("Data source amazon ses", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.visit("/");
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("Should verify elements on amazonses connection form", () => {
|
||||
it.skip("Should verify elements on amazonses connection form", () => {
|
||||
const Accesskey = Cypress.env("amazonSes_accessKey");
|
||||
const Secretkey = Cypress.env("amazonSes_secretKey");
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ describe("Data source amazon ses", () => {
|
|||
deleteDatasource(`cypress-${data.dsName}-Amazon-ses`);
|
||||
});
|
||||
|
||||
it("Should verify the functionality of amazonses connection form.", () => {
|
||||
it.skip("Should verify the functionality of amazonses connection form.", () => {
|
||||
selectAndAddDataSource("databases", amazonSesText.AmazonSES, data.dsName);
|
||||
|
||||
cy.get(".react-select__dropdown-indicator").eq(1).click();
|
||||
|
|
@ -112,7 +112,7 @@ describe("Data source amazon ses", () => {
|
|||
deleteDatasource(`cypress-${data.dsName}-amazon-ses`);
|
||||
});
|
||||
|
||||
it("Should able to run the query with valid conection", () => {
|
||||
it.skip("Should able to run the query with valid conection", () => {
|
||||
const email = "adish" + "@" + "tooljet.com";
|
||||
selectAndAddDataSource("databases", amazonSesText.AmazonSES, data.dsName);
|
||||
|
||||
|
|
@ -20,15 +20,15 @@ import {
|
|||
import { dataSourceSelector } from "../../../../../constants/selectors/dataSource";
|
||||
|
||||
const data = {};
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
|
||||
describe("Data source AppWrite", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.visit("/");
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("Should verify elements on appwrite connection form", () => {
|
||||
it.skip("Should verify elements on appwrite connection form", () => {
|
||||
const Host = Cypress.env("appwrite_host");
|
||||
const ProjectID = Cypress.env("appwrite_projectID");
|
||||
const DatabaseID = Cypress.env("appwrite_databaseID");
|
||||
|
|
@ -100,7 +100,7 @@ describe("Data source AppWrite", () => {
|
|||
deleteDatasource(`cypress-${data.dsName}-Appwrite`);
|
||||
});
|
||||
|
||||
it("Should verify the functionality of appwrite connection form.", () => {
|
||||
it.skip("Should verify the functionality of appwrite connection form.", () => {
|
||||
const Host = Cypress.env("appwrite_host");
|
||||
const ProjectID = Cypress.env("appwrite_projectID");
|
||||
const DatabaseID = Cypress.env("appwrite_databaseID");
|
||||
|
|
@ -150,7 +150,7 @@ describe("Data source AppWrite", () => {
|
|||
deleteDatasource(`cypress-${data.dsName}-Appwrite`);
|
||||
});
|
||||
|
||||
it("Should be able to run the query with a valid connection", () => {
|
||||
it.skip("Should be able to run the query with a valid connection", () => {
|
||||
const Host = Cypress.env("appwrite_host");
|
||||
const ProjectID = Cypress.env("appwrite_projectID");
|
||||
const DatabaseID = Cypress.env("appwrite_databaseID");
|
||||
|
|
@ -20,15 +20,15 @@ import {
|
|||
import { dataSourceSelector } from "../../../../../constants/selectors/dataSource";
|
||||
|
||||
const data = {};
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
|
||||
describe("Data source AWS Lambda", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.visit("/");
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("Should verify elements on AWS Lambda connection form", () => {
|
||||
it.skip("Should verify elements on AWS Lambda connection form", () => {
|
||||
const Accesskey = Cypress.env("awslamda_access");
|
||||
const Secretkey = Cypress.env("awslamda_secret");
|
||||
|
||||
|
|
@ -80,9 +80,10 @@ describe("Data source AWS Lambda", () => {
|
|||
);
|
||||
|
||||
deleteDatasource(`cypress-${data.dsName}-aws-lambda`);
|
||||
cy.uninstallMarketplacePlugin("AWS Lambda");
|
||||
});
|
||||
|
||||
it("Should verify the functionality of AWS Lambda connection form", () => {
|
||||
it.skip("Should verify the functionality of AWS Lambda connection form", () => {
|
||||
const Accesskey = Cypress.env("awslamda_access");
|
||||
const Secretkey = Cypress.env("awslamda_secret");
|
||||
|
||||
|
|
@ -113,9 +114,10 @@ describe("Data source AWS Lambda", () => {
|
|||
);
|
||||
|
||||
deleteDatasource(`cypress-${data.dsName}-aws-lambda`);
|
||||
cy.uninstallMarketplacePlugin("AWS Lambda");
|
||||
});
|
||||
|
||||
it("Should able to run the query with valid conection", () => {
|
||||
it.skip("Should able to run the query with valid conection", () => {
|
||||
const Accesskey = Cypress.env("awslamda_access");
|
||||
const Secretkey = Cypress.env("awslamda_secret");
|
||||
|
||||
|
|
@ -21,15 +21,15 @@ import {
|
|||
import { dataSourceSelector } from "../../../../../constants/selectors/dataSource";
|
||||
|
||||
const data = {};
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
|
||||
describe("Data source AWS Textract", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.visit("/");
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("Should verify elements on AWS Textract connection form", () => {
|
||||
it.skip("Should verify elements on AWS Textract connection form", () => {
|
||||
const Accesskey = Cypress.env("awstextract_access");
|
||||
const Secretkey = Cypress.env("awstextract_secret");
|
||||
|
||||
|
|
@ -87,7 +87,7 @@ describe("Data source AWS Textract", () => {
|
|||
deleteDatasource(`cypress-${data.dsName}-aws-textract`);
|
||||
});
|
||||
|
||||
it("Should verify functionality of AWS Textract connection form", () => {
|
||||
it.skip("Should verify functionality of AWS Textract connection form", () => {
|
||||
const Accesskey = Cypress.env("awstextract_access");
|
||||
const Secretkey = Cypress.env("awstextract_secret");
|
||||
|
||||
|
|
@ -122,9 +122,10 @@ describe("Data source AWS Textract", () => {
|
|||
);
|
||||
|
||||
deleteDatasource(`cypress-${data.dsName}-aws-textract`);
|
||||
cy.uninstallMarketplacePlugin("AWS Textract");
|
||||
});
|
||||
|
||||
it("Should able to run the query with valid conection", () => {
|
||||
it.skip("Should able to run the query with valid conection", () => {
|
||||
const Accesskey = Cypress.env("awstextract_access");
|
||||
const Secretkey = Cypress.env("awstextract_secret");
|
||||
|
||||
|
|
@ -17,8 +17,8 @@ data.customText = fake.randomSentence;
|
|||
|
||||
describe("Data source Azure Blob Storage", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
|
|||
|
|
@ -20,15 +20,15 @@ import {
|
|||
import { dataSourceSelector } from "../../../../../constants/selectors/dataSource";
|
||||
|
||||
const data = {};
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
|
||||
describe("Data source baserow", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.visit("/");
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("Should verify elements on baserow connection form", () => {
|
||||
it.skip("Should verify elements on baserow connection form", () => {
|
||||
const Apikey = Cypress.env("baserow_apikey");
|
||||
|
||||
cy.get(commonSelectors.globalDataSourceIcon).click();
|
||||
|
|
@ -78,7 +78,7 @@ describe("Data source baserow", () => {
|
|||
deleteDatasource(`cypress-${data.dsName}-baserow`);
|
||||
});
|
||||
|
||||
it("Should verify the functionality of baserow connection form.", () => {
|
||||
it.skip("Should verify the functionality of baserow connection form.", () => {
|
||||
const Apikey = Cypress.env("baserow_apikey");
|
||||
|
||||
selectAndAddDataSource("databases", baseRowText.baserow, data.dsName);
|
||||
|
|
@ -103,7 +103,7 @@ describe("Data source baserow", () => {
|
|||
deleteDatasource(`cypress-${data.dsName}-baserow`);
|
||||
});
|
||||
|
||||
it("Should be able to run the query with a valid connection", () => {
|
||||
it.skip("Should be able to run the query with a valid connection", () => {
|
||||
const baserowTableID = Cypress.env("baserow_tableid");
|
||||
const baserowRowID = Cypress.env("baserow_rowid");
|
||||
const Apikey = Cypress.env("baserow_apikey");
|
||||
|
|
@ -16,8 +16,8 @@ const data = {};
|
|||
|
||||
describe("Data source BigQuery", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
@ -19,8 +19,8 @@ const data = {};
|
|||
|
||||
describe("Data sources", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ const data = {};
|
|||
|
||||
describe("Data sources", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ const data = {};
|
|||
|
||||
describe("Data sources", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ const data = {};
|
|||
|
||||
describe("Data source DynamoDB", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
|
|||
|
|
@ -17,9 +17,12 @@ import {
|
|||
const data = {};
|
||||
describe("Data source Elasticsearch", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
data.lastName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("Should verify elements on Elasticsearch connection form", () => {
|
||||
|
|
@ -123,14 +126,14 @@ describe("Data source Elasticsearch", () => {
|
|||
"have.text",
|
||||
elasticsearchText.errorConnectionRefused
|
||||
);
|
||||
deleteDatasource(`cypress-${data.lastName}-elasticsearch`);
|
||||
deleteDatasource(`cypress-${data.dataSourceName}-elasticsearch`);
|
||||
});
|
||||
|
||||
it("Should verify the functionality of Elasticsearch connection form.", () => {
|
||||
selectAndAddDataSource(
|
||||
"databases",
|
||||
elasticsearchText.elasticSearch,
|
||||
data.lastName
|
||||
data.dataSourceName
|
||||
);
|
||||
|
||||
fillDataSourceTextField(
|
||||
|
|
@ -210,12 +213,12 @@ describe("Data source Elasticsearch", () => {
|
|||
);
|
||||
|
||||
cy.get(
|
||||
`[data-cy="cypress-${data.lastName}-elasticsearch-button"]`
|
||||
`[data-cy="cypress-${data.dataSourceName}-elasticsearch-button"]`
|
||||
).verifyVisibleElement(
|
||||
"have.text",
|
||||
`cypress-${data.lastName}-elasticsearch`
|
||||
`cypress-${data.dataSourceName}-elasticsearch`
|
||||
);
|
||||
|
||||
deleteDatasource(`cypress-${data.lastName}-elasticsearch`);
|
||||
deleteDatasource(`cypress-${data.dataSourceName}-elasticsearch`);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ const data = {};
|
|||
|
||||
describe("Data source Firestore", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
|
|||
|
|
@ -19,12 +19,12 @@ import {
|
|||
import { dataSourceSelector } from "../../../../../constants/selectors/dataSource";
|
||||
|
||||
const data = {};
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
|
||||
describe("Data source GraphQL", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.visit("/");
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("Should verify elements on GraphQL connection form", () => {
|
||||
|
|
@ -20,13 +20,13 @@ import {
|
|||
import { dataSourceSelector } from "../../../../../constants/selectors/dataSource";
|
||||
|
||||
const data = {};
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
data.dsName1 = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
|
||||
describe("Data source HarperDB", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.visit("/");
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("Should verify elements on HarperDB connection form", () => {
|
||||
|
|
@ -102,6 +102,7 @@ describe("Data source HarperDB", () => {
|
|||
);
|
||||
|
||||
deleteDatasource(`cypress-${data.dsName}-HarperDB`);
|
||||
cy.uninstallMarketplacePlugin("HarperDB");
|
||||
});
|
||||
|
||||
it("Should verify functionality of HarperDB connection form", () => {
|
||||
|
|
@ -156,9 +157,10 @@ describe("Data source HarperDB", () => {
|
|||
);
|
||||
|
||||
deleteDatasource(`cypress-${data.dsName}-HarperDB`);
|
||||
cy.uninstallMarketplacePlugin("HarperDB");
|
||||
});
|
||||
|
||||
it("Should be able to run the query with a valid connection", () => {
|
||||
it.skip("Should be able to run the query with a valid connection", () => {
|
||||
const Host = Cypress.env("harperdb_host");
|
||||
const Port = Cypress.env("harperdb_port");
|
||||
const Username = Cypress.env("harperdb_username");
|
||||
|
|
@ -23,8 +23,8 @@ const data = {};
|
|||
|
||||
describe("Data sources", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ const data = {};
|
|||
|
||||
describe("Data sources", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ import {
|
|||
import { dataSourceSelector } from "../../../../../constants/selectors/dataSource";
|
||||
|
||||
const data = {};
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
|
||||
describe("Data source minio", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.visit("/");
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("Should verify elements on minio connection form", () => {
|
||||
|
|
@ -157,7 +157,7 @@ describe("Data source minio", () => {
|
|||
deleteDatasource(`cypress-${data.dsName}-minio`);
|
||||
});
|
||||
|
||||
it("Should be able to run the query with a valid connection", () => {
|
||||
it.skip("Should be able to run the query with a valid connection", () => {
|
||||
const Host = Cypress.env("minio_host");
|
||||
const Port = Cypress.env("minio_port");
|
||||
const AccessKey = Cypress.env("minio_accesskey");
|
||||
|
|
@ -27,8 +27,8 @@ const data = {};
|
|||
|
||||
describe("Data source MongoDB", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
@ -133,7 +133,7 @@ describe("Data source MongoDB", () => {
|
|||
"have.text",
|
||||
mongoDbText.errorConnectionRefused
|
||||
);
|
||||
cy.get('[data-cy="query-select-dropdown"]').type(
|
||||
cy.get('[data-cy="connection-type-select-dropdown"]').type(
|
||||
mongoDbText.optionConnectUsingConnectionString
|
||||
);
|
||||
cy.get('[data-cy="label-connection-string"]').verifyVisibleElement(
|
||||
|
|
@ -26,8 +26,8 @@ const data = {};
|
|||
|
||||
describe("Data sources MySql", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
@ -15,7 +15,7 @@ import {
|
|||
|
||||
describe("Data sources", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.apiLogin();
|
||||
// cy.createApp();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -20,14 +20,14 @@ const data = {};
|
|||
|
||||
describe("Data sources", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("Should verify elements on connection form", () => {
|
||||
it.skip("Should verify elements on connection form", () => {
|
||||
cy.log(process.env.NODE_ENV);
|
||||
cy.log(postgreSqlText.allDatabase());
|
||||
cy.get(commonSelectors.globalDataSourceIcon).click();
|
||||
|
|
@ -140,7 +140,7 @@ describe("Data sources", () => {
|
|||
deleteDatasource(`cypress-${data.dataSourceName}-postgresql`);
|
||||
});
|
||||
|
||||
it("Should verify the functionality of PostgreSQL connection form.", () => {
|
||||
it.skip("Should verify the functionality of PostgreSQL connection form.", () => {
|
||||
selectAndAddDataSource(
|
||||
"databases",
|
||||
postgreSqlText.postgreSQL,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
|||
describe("Data source Redis", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.visit("/");
|
||||
});
|
||||
|
||||
it("Should verify elements on connection Redis form", () => {
|
||||
|
|
@ -215,7 +215,7 @@ describe("Data source Redis", () => {
|
|||
deleteDatasource(`cypress-${data.dsName}-redis`);
|
||||
});
|
||||
|
||||
it("Should able to run the query with valid conection", () => {
|
||||
it.skip("Should able to run the query with valid conection", () => {
|
||||
selectAndAddDataSource("databases", redisText.redis, data.dsName);
|
||||
|
||||
fillDataSourceTextField(
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const clientAuthenticationDropdown =
|
|||
describe("Data source Rest API", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
@ -332,7 +332,6 @@ describe("Data source Rest API", () => {
|
|||
deleteDatasource(`cypress-${data.dataSourceName}-restapi`);
|
||||
});
|
||||
it("Should verify basic connection for Rest API", () => {
|
||||
const storedId = Cypress.env("storedId");
|
||||
cy.apiCreateGDS(
|
||||
`${Cypress.env("server_host")}/api/data-sources`,
|
||||
`cypress-${data.dataSourceName}-restapi`,
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ const data = {};
|
|||
|
||||
describe("Data sources", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const data = {};
|
|||
describe("Data sources AWS S3", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {
|
|||
|
||||
describe("Data sources", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.apiLogin();
|
||||
// cy.createApp();
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ const data = {};
|
|||
|
||||
describe("Data source SMTP", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
|
|||
|
|
@ -20,8 +20,8 @@ import {
|
|||
const data = {};
|
||||
describe("Data sources", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ const data = {};
|
|||
|
||||
describe("Data sources", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.apiLogin();
|
||||
cy.visit("/");
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
@ -21,15 +21,15 @@ import { dataSourceSelector } from "../../../../../constants/selectors/dataSourc
|
|||
import { pluginSelectors } from "Selectors/plugins";
|
||||
|
||||
const data = {};
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
|
||||
describe("Data source Twilio", () => {
|
||||
beforeEach(() => {
|
||||
cy.apiLogin();
|
||||
cy.defaultWorkspaceLogin();
|
||||
cy.visit("/");
|
||||
data.dsName = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||
});
|
||||
|
||||
it("Should verify elements on Twilio connection form", () => {
|
||||
it.skip("Should verify elements on Twilio connection form", () => {
|
||||
const AuthToken = Cypress.env("twilio_auth_token");
|
||||
const AccountSID = Cypress.env("twilio_account_SID");
|
||||
const MessageSID = Cypress.env("twilio_messaging_service_SID");
|
||||
|
|
@ -89,7 +89,7 @@ describe("Data source Twilio", () => {
|
|||
deleteDatasource(`cypress-${data.dsName}-twilio`);
|
||||
});
|
||||
|
||||
it("Should verify functionality of Twilio connection form", () => {
|
||||
it.skip("Should verify functionality of Twilio connection form", () => {
|
||||
const AuthToken = Cypress.env("twilio_auth_token");
|
||||
const AccountSID = Cypress.env("twilio_account_SID");
|
||||
const MessageSID = Cypress.env("twilio_messaging_service_SID");
|
||||
|
|
@ -128,7 +128,7 @@ describe("Data source Twilio", () => {
|
|||
deleteDatasource(`cypress-${data.dsName}-twilio`);
|
||||
});
|
||||
|
||||
it("Should be able to run the query with a valid connection", () => {
|
||||
it.skip("Should be able to run the query with a valid connection", () => {
|
||||
const AuthToken = Cypress.env("twilio_auth_token");
|
||||
const AccountSID = Cypress.env("twilio_account_SID");
|
||||
const MessageSID = Cypress.env("twilio_messaging_service_SID");
|
||||
|
|
@ -20,7 +20,7 @@ const data = {};
|
|||
|
||||
describe("Data sources", () => {
|
||||
beforeEach(() => {
|
||||
cy.appUILogin();
|
||||
cy.apiLogin();
|
||||
data.dataSourceName = fake.lastName
|
||||
.toLowerCase()
|
||||
.replaceAll("[^A-Za-z]", "");
|
||||
|
|
|
|||
|
|
@ -46,9 +46,7 @@ describe("App Export", () => {
|
|||
});
|
||||
|
||||
it("Verify the elements of export dialog box", () => {
|
||||
cy.window({ log: false }).then((win) => {
|
||||
win.localStorage.setItem("walkthroughCompleted", "true");
|
||||
});
|
||||
cy.skipWalkthrough()
|
||||
|
||||
cy.apiLogin();
|
||||
cy.visit(`${data.workspaceSlug}`);
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ describe("App Import Functionality", () => {
|
|||
cy.apiLogin();
|
||||
cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug);
|
||||
cy.apiLogout();
|
||||
cy.skipWalkthrough()
|
||||
});
|
||||
|
||||
it("should verify app import functionality", () => {
|
||||
|
|
@ -100,12 +101,13 @@ describe("App Import Functionality", () => {
|
|||
.and("have.text", importText.appImportedToastMessage);
|
||||
|
||||
// Verify imported app
|
||||
cy.get(".driver-close-btn").click();
|
||||
cy.get(commonSelectors.toastCloseButton).click();
|
||||
cy.wait(500);
|
||||
cy.get(commonSelectors.appNameInput).verifyVisibleElement(
|
||||
"contain.value",
|
||||
"three-versions"
|
||||
);
|
||||
cy.get(appVersionSelectors.currentVersionField("v3")).should("be.visible");
|
||||
|
||||
// Configure app
|
||||
cy.skipEditorPopover();
|
||||
|
|
|
|||
|
|
@ -27,17 +27,21 @@ describe("App Slug", () => {
|
|||
});
|
||||
|
||||
it("Verify app slug cases in global settings", () => {
|
||||
cy.apiLogin();
|
||||
const workspaceId = Cypress.env("workspaceId");
|
||||
const appId = Cypress.env("appId");
|
||||
const appUrl = `${host}/${Cypress.env("workspaceId")}/apps/${Cypress.env("appId")}/`;
|
||||
|
||||
cy.visit("/my-workspace");
|
||||
cy.wait(1000);
|
||||
cy.apiLogin();
|
||||
cy.skipWalkthrough();
|
||||
|
||||
cy.window({ log: false }).then((win) => {
|
||||
win.localStorage.setItem("walkthroughCompleted", "true");
|
||||
cy.visit(appUrl);
|
||||
cy.url().then((url) => {
|
||||
if (url !== appUrl) {
|
||||
cy.visit(appUrl);
|
||||
}
|
||||
});
|
||||
cy.visit(`/${Cypress.env("workspaceId")}/apps/${Cypress.env("appId")}/`);
|
||||
cy.url().should("eq", appUrl);
|
||||
|
||||
cy.wait(1000);
|
||||
|
||||
cy.get(commonSelectors.leftSideBarSettingsButton).click();
|
||||
|
|
|
|||
|
|
@ -78,11 +78,11 @@ describe("Private and Public apps", {
|
|||
|
||||
// Test private access
|
||||
logout();
|
||||
cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should("be.visible");
|
||||
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`,
|
||||
});
|
||||
|
||||
cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should("be.visible");
|
||||
cy.wait(2000);
|
||||
cy.appUILogin();
|
||||
|
|
@ -116,6 +116,9 @@ describe("Private and Public apps", {
|
|||
|
||||
inviteUserToWorkspace(data.firstName, data.email);
|
||||
logout();
|
||||
cy.visit("/");
|
||||
cy.wait(2000);
|
||||
cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should("be.visible");
|
||||
|
||||
// Test private access
|
||||
cy.visitSlug({
|
||||
|
|
@ -141,6 +144,8 @@ describe("Private and Public apps", {
|
|||
cy.wait(1000);
|
||||
cy.apiMakeAppPublic();
|
||||
logout();
|
||||
cy.wait(1000);
|
||||
cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should("be.visible");
|
||||
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`,
|
||||
|
|
@ -177,6 +182,9 @@ describe("Private and Public apps", {
|
|||
cy.apiMakeAppPublic();
|
||||
logout();
|
||||
|
||||
cy.wait(1000);
|
||||
cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should("be.visible");
|
||||
|
||||
cy.visitSlug({
|
||||
actualUrl: `${Cypress.config("baseUrl")}/applications/${data.slug}`,
|
||||
});
|
||||
|
|
@ -229,6 +237,8 @@ describe("Private and Public apps", {
|
|||
|
||||
cy.get('[data-cy="viewer-page-logo"]').click();
|
||||
logout();
|
||||
cy.wait(1000);
|
||||
cy.get(onboardingSelectors.signInButton, { timeout: 20000 }).should("be.visible");
|
||||
|
||||
// Setup new workspace and app
|
||||
cy.defaultWorkspaceLogin();
|
||||
|
|
@ -123,7 +123,7 @@ describe("App Version", () => {
|
|||
releasedVersionAndVerify("v2");
|
||||
});
|
||||
|
||||
it.only("should verify version management with components and queries", () => {
|
||||
it("should verify version management with components and queries", () => {
|
||||
// Initial setup with component and datasource
|
||||
cy.apiAddComponentToApp(
|
||||
data.appName,
|
||||
|
|
@ -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");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,164 @@ describe("dashboard", () => {
|
|||
cy.visit(`${data.workspaceSlug}`);
|
||||
});
|
||||
|
||||
// it("Should verify app card elements and app card operations", () => {
|
||||
// const customLayout = {
|
||||
// desktop: { top: 100, left: 20 },
|
||||
// mobile: { width: 8, height: 50 },
|
||||
// };
|
||||
|
||||
// cy.apiCreateApp(data.appName);
|
||||
// cy.visit(`${data.workspaceSlug}`);
|
||||
|
||||
// cy.wait(2000);
|
||||
// cy.get(commonSelectors.appCreationDetails).should("be.visible");
|
||||
// cy.get(commonSelectors.appCard(data.appName)).should("be.visible");
|
||||
// cy.get(commonSelectors.appTitle(data.appName)).verifyVisibleElement(
|
||||
// "have.text",
|
||||
// data.appName
|
||||
// );
|
||||
|
||||
// viewAppCardOptions(data.appName);
|
||||
// cy.get(
|
||||
// commonSelectors.appCardOptions(commonText.changeIconOption)
|
||||
// ).verifyVisibleElement("have.text", commonText.changeIconOption);
|
||||
// cy.get(
|
||||
// commonSelectors.appCardOptions(commonText.addToFolderOption)
|
||||
// ).verifyVisibleElement("have.text", commonText.addToFolderOption);
|
||||
// cy.get(
|
||||
// commonSelectors.appCardOptions(commonText.cloneAppOption)
|
||||
// ).verifyVisibleElement("have.text", commonText.cloneAppOption);
|
||||
// cy.get(
|
||||
// commonSelectors.appCardOptions(commonText.exportAppOption)
|
||||
// ).verifyVisibleElement("have.text", commonText.exportAppOption);
|
||||
// cy.get(
|
||||
// commonSelectors.appCardOptions(commonText.deleteAppOption)
|
||||
// ).verifyVisibleElement("have.text", commonText.deleteAppOption);
|
||||
|
||||
// modifyAndVerifyAppCardIcon(data.appName);
|
||||
// createFolder(data.folderName);
|
||||
|
||||
// viewAppCardOptions(data.appName);
|
||||
// cy.get(
|
||||
// commonSelectors.appCardOptions(commonText.addToFolderOption)
|
||||
// ).click();
|
||||
// verifyModal(
|
||||
// dashboardText.addToFolderTitle,
|
||||
// dashboardText.addToFolderButton,
|
||||
// dashboardSelector.selectFolder
|
||||
// );
|
||||
// cy.get(dashboardSelector.moveAppText).verifyVisibleElement(
|
||||
// "have.text",
|
||||
// dashboardText.moveAppText(data.appName)
|
||||
// );
|
||||
|
||||
// cy.get(dashboardSelector.selectFolder).click();
|
||||
// cy.get(commonSelectors.folderList).contains(data.folderName).click();
|
||||
// cy.get(dashboardSelector.addToFolderButton).click();
|
||||
// cy.verifyToastMessage(
|
||||
// commonSelectors.toastMessage,
|
||||
// commonText.AddedToFolderToast,
|
||||
// false
|
||||
// );
|
||||
|
||||
// cy.get(dashboardSelector.folderName(data.folderName)).verifyVisibleElement(
|
||||
// "have.text",
|
||||
// dashboardText.folderName(`${data.folderName} (1)`)
|
||||
// );
|
||||
|
||||
// cy.get(dashboardSelector.folderName(data.folderName)).click();
|
||||
// cy.get(commonSelectors.appCard(data.appName))
|
||||
// .contains(data.appName)
|
||||
// .should("be.visible");
|
||||
|
||||
// viewAppCardOptions(data.appName);
|
||||
|
||||
// cy.get(commonSelectors.appCardOptions(commonText.removeFromFolderOption))
|
||||
// .verifyVisibleElement("have.text", commonText.removeFromFolderOption)
|
||||
// .click();
|
||||
// verifyConfirmationModal(commonText.appRemovedFromFolderMessage);
|
||||
|
||||
// cancelModal(commonText.cancelButton);
|
||||
|
||||
// viewAppCardOptions(data.appName);
|
||||
// cy.get(
|
||||
// commonSelectors.appCardOptions(commonText.removeFromFolderOption)
|
||||
// ).click();
|
||||
// cy.get(commonSelectors.buttonSelector(commonText.modalYesButton)).click();
|
||||
// cy.verifyToastMessage(
|
||||
// commonSelectors.toastMessage,
|
||||
// commonText.appRemovedFromFolderTaost,
|
||||
// false
|
||||
// );
|
||||
// cy.get(commonSelectors.modalComponent).should("not.exist");
|
||||
// cy.get(commonSelectors.empytyFolderImage).should("be.visible");
|
||||
// cy.get(commonSelectors.emptyFolderText).verifyVisibleElement(
|
||||
// "have.text",
|
||||
// commonText.emptyFolderText
|
||||
// );
|
||||
// cy.get(commonSelectors.allApplicationsLink).click();
|
||||
// deleteFolder(data.folderName);
|
||||
|
||||
// cy.get(commonSelectors.allApplicationsLink).click();
|
||||
|
||||
// cy.wait(1000);
|
||||
// viewAppCardOptions(data.appName);
|
||||
// cy.wait(2000);
|
||||
// cy.get(commonSelectors.appCardOptions(commonText.exportAppOption)).click();
|
||||
// cy.get(commonSelectors.exportAllButton).click();
|
||||
|
||||
// cy.exec("ls ./cypress/downloads/").then((result) => {
|
||||
// const downloadedAppExportFileName = result.stdout.split("\n")[0];
|
||||
// expect(downloadedAppExportFileName).to.contain.string("app");
|
||||
// });
|
||||
|
||||
// viewAppCardOptions(data.appName);
|
||||
// cy.get(commonSelectors.appCardOptions(commonText.cloneAppOption)).click();
|
||||
// cy.get('[data-cy="clone-app"]').click();
|
||||
// cy.get(".go3958317564")
|
||||
// .should("be.visible")
|
||||
// .and("have.text", dashboardText.appClonedToast);
|
||||
// cy.wait(3000);
|
||||
|
||||
// cy.renameApp(data.cloneAppName);
|
||||
// cy.apiAddComponentToApp(data.cloneAppName, "button", 25, 25);
|
||||
// cy.backToApps();
|
||||
// cy.wait("@appLibrary");
|
||||
// cy.wait(1000);
|
||||
|
||||
// cy.get(commonSelectors.appCard(data.cloneAppName)).should("be.visible");
|
||||
|
||||
// cy.wait(1000);
|
||||
|
||||
// viewAppCardOptions(data.cloneAppName);
|
||||
// cy.get(commonSelectors.deleteAppOption).click();
|
||||
// cy.get(commonSelectors.modalMessage).verifyVisibleElement(
|
||||
// "have.text",
|
||||
// commonText.deleteAppModalMessage(data.cloneAppName)
|
||||
// );
|
||||
// cy.get(
|
||||
// commonSelectors.buttonSelector(commonText.cancelButton)
|
||||
// ).verifyVisibleElement("have.text", commonText.cancelButton);
|
||||
// cy.get(
|
||||
// commonSelectors.buttonSelector(commonText.modalYesButton)
|
||||
// ).verifyVisibleElement("have.text", commonText.modalYesButton);
|
||||
// cancelModal(commonText.cancelButton);
|
||||
|
||||
// viewAppCardOptions(data.cloneAppName);
|
||||
// cy.get(commonSelectors.deleteAppOption).click();
|
||||
// cy.get(commonSelectors.buttonSelector(commonText.modalYesButton)).click();
|
||||
// cy.verifyToastMessage(
|
||||
// commonSelectors.toastMessage,
|
||||
// commonText.appDeletedToast,
|
||||
// false
|
||||
// );
|
||||
// verifyAppDelete(data.cloneAppName);
|
||||
// cy.wait("@appLibrary");
|
||||
|
||||
// cy.deleteApp(data.appName);
|
||||
// verifyAppDelete(data.appName);
|
||||
// });
|
||||
|
||||
it("should verify the elements on empty dashboard", () => {
|
||||
cy.intercept("GET", "/api/metadata", {
|
||||
body: {
|
||||
|
|
@ -171,181 +329,6 @@ describe("dashboard", () => {
|
|||
verifyTooltip(dashboardSelector.modeToggle, "Mode");
|
||||
});
|
||||
|
||||
it.skip("Should verify app card elements and app card operations", () => {
|
||||
const customLayout = {
|
||||
desktop: { top: 100, left: 20 },
|
||||
mobile: { width: 8, height: 50 },
|
||||
};
|
||||
|
||||
cy.apiCreateApp(data.appName);
|
||||
cy.openApp();
|
||||
cy.apiAddComponentToApp(data.appName, "text1", customLayout);
|
||||
|
||||
cy.backToApps();
|
||||
|
||||
cy.wait(500);
|
||||
cy.get(commonSelectors.appCard(data.appName))
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get(commonSelectors.appCard(data.appName)).should("be.visible");
|
||||
cy.get(commonSelectors.appTitle(data.appName)).verifyVisibleElement(
|
||||
"have.text",
|
||||
data.appName
|
||||
);
|
||||
cy.get(commonSelectors.appCreationDetails).should("be.visible");
|
||||
|
||||
//Add the edited details
|
||||
});
|
||||
|
||||
viewAppCardOptions(data.appName);
|
||||
cy.get(
|
||||
commonSelectors.appCardOptions(commonText.changeIconOption)
|
||||
).verifyVisibleElement("have.text", commonText.changeIconOption);
|
||||
cy.get(
|
||||
commonSelectors.appCardOptions(commonText.addToFolderOption)
|
||||
).verifyVisibleElement("have.text", commonText.addToFolderOption);
|
||||
cy.get(
|
||||
commonSelectors.appCardOptions(commonText.cloneAppOption)
|
||||
).verifyVisibleElement("have.text", commonText.cloneAppOption);
|
||||
cy.get(
|
||||
commonSelectors.appCardOptions(commonText.exportAppOption)
|
||||
).verifyVisibleElement("have.text", commonText.exportAppOption);
|
||||
cy.get(
|
||||
commonSelectors.appCardOptions(commonText.deleteAppOption)
|
||||
).verifyVisibleElement("have.text", commonText.deleteAppOption);
|
||||
|
||||
modifyAndVerifyAppCardIcon(data.appName);
|
||||
createFolder(data.folderName);
|
||||
|
||||
viewAppCardOptions(data.appName);
|
||||
cy.get(
|
||||
commonSelectors.appCardOptions(commonText.addToFolderOption)
|
||||
).click();
|
||||
verifyModal(
|
||||
dashboardText.addToFolderTitle,
|
||||
dashboardText.addToFolderButton,
|
||||
dashboardSelector.selectFolder
|
||||
);
|
||||
cy.get(dashboardSelector.moveAppText).verifyVisibleElement(
|
||||
"have.text",
|
||||
dashboardText.moveAppText(data.appName)
|
||||
);
|
||||
|
||||
cy.get(dashboardSelector.selectFolder).click();
|
||||
cy.get(commonSelectors.folderList).contains(data.folderName).click();
|
||||
cy.get(dashboardSelector.addToFolderButton).click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
commonText.AddedToFolderToast
|
||||
);
|
||||
|
||||
cy.get(dashboardSelector.folderName(data.folderName)).verifyVisibleElement(
|
||||
"have.text",
|
||||
dashboardText.folderName(`${data.folderName} (1)`)
|
||||
);
|
||||
|
||||
cy.get(dashboardSelector.folderName(data.folderName)).click();
|
||||
cy.get(commonSelectors.appCard(data.appName))
|
||||
.contains(data.appName)
|
||||
.should("be.visible");
|
||||
|
||||
cy.wait(2000);
|
||||
viewAppCardOptions(data.appName);
|
||||
|
||||
cy.get(commonSelectors.appCardOptions(commonText.removeFromFolderOption))
|
||||
.verifyVisibleElement("have.text", commonText.removeFromFolderOption)
|
||||
.click();
|
||||
verifyConfirmationModal(commonText.appRemovedFromFolderMessage);
|
||||
|
||||
cancelModal(commonText.cancelButton);
|
||||
|
||||
cy.wait(3000);
|
||||
viewAppCardOptions(data.appName);
|
||||
cy.get(
|
||||
commonSelectors.appCardOptions(commonText.removeFromFolderOption)
|
||||
).click();
|
||||
cy.get(commonSelectors.buttonSelector(commonText.modalYesButton)).click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
commonText.appRemovedFromFolderTaost
|
||||
);
|
||||
cy.get(commonSelectors.modalComponent).should("not.exist");
|
||||
cy.get(commonSelectors.empytyFolderImage).should("be.visible");
|
||||
cy.get(commonSelectors.emptyFolderText).verifyVisibleElement(
|
||||
"have.text",
|
||||
commonText.emptyFolderText
|
||||
);
|
||||
cy.get(commonSelectors.allApplicationsLink).click();
|
||||
deleteFolder(data.folderName);
|
||||
|
||||
cy.get(commonSelectors.allApplicationsLink).click();
|
||||
|
||||
cy.wait(3000);
|
||||
viewAppCardOptions(data.appName);
|
||||
cy.get(commonSelectors.appCardOptions(commonText.cloneAppOption)).click();
|
||||
cy.get('[data-cy="clone-app"]').click();
|
||||
cy.get(".go3958317564")
|
||||
.should("be.visible")
|
||||
.and("have.text", dashboardText.appClonedToast);
|
||||
cy.wait(3000);
|
||||
cy.renameApp(data.cloneAppName);
|
||||
cy.apiAddComponentToApp(data.cloneAppName, "button", 25, 25);
|
||||
cy.backToApps();
|
||||
cy.wait("@appLibrary");
|
||||
cy.wait(1000);
|
||||
cy.reloadAppForTheElement(data.cloneAppName);
|
||||
|
||||
cy.get(commonSelectors.appCard(data.cloneAppName)).should("be.visible");
|
||||
|
||||
cy.get(commonSelectors.globalDataSourceIcon).click();
|
||||
cy.get(commonSelectors.dashboardIcon).click();
|
||||
cy.wait(3000);
|
||||
cy.reloadAppForTheElement(data.cloneAppName);
|
||||
viewAppCardOptions(data.cloneAppName);
|
||||
cy.get(commonSelectors.appCardOptions(commonText.exportAppOption)).click();
|
||||
cy.get(commonSelectors.exportAllButton).click();
|
||||
|
||||
cy.exec("ls ./cypress/downloads/").then((result) => {
|
||||
const downloadedAppExportFileName = result.stdout.split("\n")[0];
|
||||
expect(downloadedAppExportFileName).to.contain.string("app");
|
||||
});
|
||||
|
||||
cy.wait(3000);
|
||||
cy.reloadAppForTheElement(data.cloneAppName);
|
||||
viewAppCardOptions(data.cloneAppName);
|
||||
cy.get(commonSelectors.deleteAppOption).click();
|
||||
cy.get(commonSelectors.modalMessage).verifyVisibleElement(
|
||||
"have.text",
|
||||
commonText.deleteAppModalMessage(data.cloneAppName)
|
||||
);
|
||||
cy.get(
|
||||
commonSelectors.buttonSelector(commonText.cancelButton)
|
||||
).verifyVisibleElement("have.text", commonText.cancelButton);
|
||||
cy.get(
|
||||
commonSelectors.buttonSelector(commonText.modalYesButton)
|
||||
).verifyVisibleElement("have.text", commonText.modalYesButton);
|
||||
cancelModal(commonText.cancelButton);
|
||||
|
||||
cy.wait(3000);
|
||||
cy.reloadAppForTheElement(data.cloneAppName);
|
||||
viewAppCardOptions(data.cloneAppName);
|
||||
cy.get(commonSelectors.deleteAppOption).click();
|
||||
cy.get(commonSelectors.buttonSelector(commonText.modalYesButton)).click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
commonText.appDeletedToast
|
||||
);
|
||||
verifyAppDelete(data.cloneAppName);
|
||||
cy.wait("@appLibrary");
|
||||
|
||||
cy.deleteApp(data.appName);
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
commonText.appDeletedToast
|
||||
);
|
||||
verifyAppDelete(data.appName);
|
||||
});
|
||||
|
||||
it("Should verify the app CRUD operation", () => {
|
||||
const customLayout = {
|
||||
desktop: { top: 100, left: 20 },
|
||||
|
|
@ -369,10 +352,7 @@ describe("dashboard", () => {
|
|||
cy.wait("@appLibrary");
|
||||
|
||||
cy.deleteApp(data.appName);
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
commonText.appDeletedToast
|
||||
);
|
||||
|
||||
verifyAppDelete(data.appName);
|
||||
});
|
||||
|
||||
|
|
@ -493,10 +473,7 @@ describe("dashboard", () => {
|
|||
|
||||
cy.get(commonSelectors.allApplicationsLink).click();
|
||||
cy.deleteApp(data.appName);
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
commonText.appDeletedToast
|
||||
);
|
||||
|
||||
verifyAppDelete(data.appName);
|
||||
logout();
|
||||
});
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -204,10 +204,7 @@ describe("Manage Groups", () => {
|
|||
|
||||
cy.wait(2500);
|
||||
cy.deleteApp(data.appName);
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
commonText.appDeletedToast
|
||||
);
|
||||
|
||||
|
||||
// Folder operations
|
||||
createFolder(data.folderName);
|
||||
|
|
|
|||
|
|
@ -2127,7 +2127,7 @@
|
|||
"encrypted": false
|
||||
},
|
||||
"host": {
|
||||
"value": "35.238.9.114",
|
||||
"value": "9.234.17.31",
|
||||
"encrypted": false
|
||||
},
|
||||
"port": {
|
||||
|
|
|
|||
|
|
@ -585,7 +585,7 @@
|
|||
"encrypted": false
|
||||
},
|
||||
"host": {
|
||||
"value": "35.238.9.114",
|
||||
"value": "9.234.17.31",
|
||||
"encrypted": false
|
||||
},
|
||||
"port": {
|
||||
|
|
|
|||
|
|
@ -1862,7 +1862,7 @@
|
|||
"encrypted": false
|
||||
},
|
||||
"host": {
|
||||
"value": "35.238.9.114",
|
||||
"value": "9.234.17.31",
|
||||
"encrypted": false
|
||||
},
|
||||
"port": {
|
||||
|
|
|
|||
|
|
@ -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": {
|
||||
|
|
|
|||
|
|
@ -101,11 +101,14 @@ export const navigateToAppEditor = (appName) => {
|
|||
|
||||
export const viewAppCardOptions = (appName) => {
|
||||
cy.wait(1000);
|
||||
cy.reloadAppForTheElement(appName);
|
||||
cy.get(commonSelectors.appCard(appName))
|
||||
.realHover()
|
||||
.find(commonSelectors.appCardOptionsButton)
|
||||
.realHover()
|
||||
cy.contains("div", appName)
|
||||
.parent()
|
||||
.within(() => {
|
||||
cy.get(commonSelectors.appCardOptionsButton).invoke("click");
|
||||
cy.get(commonSelectors.appCardOptionsButton).click();
|
||||
});
|
||||
};
|
||||
|
||||
|
|
@ -185,8 +188,9 @@ export const searchUser = (email) => {
|
|||
};
|
||||
|
||||
export const selectAppCardOption = (appName, appCardOption) => {
|
||||
cy.wait(1000);
|
||||
viewAppCardOptions(appName);
|
||||
cy.get(appCardOption).should("be.visible").click({ force: true });
|
||||
cy.get(appCardOption).should("be.visible").click();
|
||||
};
|
||||
|
||||
export const navigateToDatabase = () => {
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ export const modifyAndVerifyAppCardIcon = (appName) => {
|
|||
};
|
||||
|
||||
export const verifyAppDelete = (appName) => {
|
||||
cy.get("body").should("exist").and("be.visible");
|
||||
cy.get('[data-cy="dashboard-section-header"]').should("be.visible");
|
||||
cy.get("body").then(($title) => {
|
||||
if (!$title.text().includes(commonText.introductionMessage)) {
|
||||
cy.clearAndType(commonSelectors.homePageSearchBar, appName);
|
||||
|
|
|
|||
|
|
@ -239,7 +239,8 @@ export const createRestAPIQuery = (
|
|||
key = "",
|
||||
value = "",
|
||||
url = "",
|
||||
run = true
|
||||
run = true,
|
||||
kind = "restapi"
|
||||
) => {
|
||||
cy.getCookie("tj_auth_token").then((cookie) => {
|
||||
const headers = {
|
||||
|
|
@ -247,7 +248,6 @@ export const createRestAPIQuery = (
|
|||
Cookie: `tj_auth_token=${cookie.value}`,
|
||||
};
|
||||
|
||||
cy.log(Cypress.env("appId"));
|
||||
cy.request({
|
||||
method: "GET",
|
||||
url: `${Cypress.env("server_host")}/api/apps/${Cypress.env("appId")}`,
|
||||
|
|
@ -255,13 +255,13 @@ export const createRestAPIQuery = (
|
|||
}).then((response) => {
|
||||
const editingVersionId = response.body.editing_version.id;
|
||||
|
||||
const data_source_id = Cypress.env(`${dsName}-id`);
|
||||
const data_source_id = Cypress.env(kind);
|
||||
|
||||
const requestBody = {
|
||||
app_id: Cypress.env("appId"),
|
||||
app_version_id: editingVersionId,
|
||||
name: queryName,
|
||||
kind: "restapi",
|
||||
kind: kind,
|
||||
options: {
|
||||
method: "get",
|
||||
url: url,
|
||||
|
|
|
|||
|
|
@ -18,79 +18,97 @@ export const createAndRunRestAPIQuery = (
|
|||
method: "GET",
|
||||
url: `${Cypress.env("server_host")}/api/apps/${Cypress.env("appId")}`,
|
||||
headers,
|
||||
}).then((response) => {
|
||||
const editingVersionId = response.body.editing_version.id;
|
||||
const data_source_id = Cypress.env(`${dsName}-id`);
|
||||
const useJsonBody =
|
||||
["POST", "PATCH", "PUT"].includes(method.toUpperCase()) &&
|
||||
jsonBody !== null;
|
||||
|
||||
const queryOptions = {
|
||||
method: method.toLowerCase(),
|
||||
url: url + urlSuffix,
|
||||
url_params: [["", ""]],
|
||||
headers: headersList.length ? headersList : [["", ""]],
|
||||
body: !useJsonBody && bodyList.length ? bodyList : [["", ""]],
|
||||
json_body: useJsonBody ? jsonBody : null,
|
||||
body_toggle: useJsonBody,
|
||||
runOnPageLoad: run,
|
||||
transformationLanguage: "javascript",
|
||||
enableTransformation: false,
|
||||
};
|
||||
|
||||
const requestBody = {
|
||||
app_id: Cypress.env("appId"),
|
||||
app_version_id: editingVersionId,
|
||||
name: queryName,
|
||||
kind: "restapi",
|
||||
options: queryOptions,
|
||||
data_source_id,
|
||||
plugin_id: null,
|
||||
};
|
||||
}).then((appResponse) => {
|
||||
const currentEnvironmentId = appResponse.body.editorEnvironment.id;
|
||||
const editingVersionId = appResponse.body.editing_version.id;
|
||||
|
||||
cy.request({
|
||||
method: "POST",
|
||||
url: `${Cypress.env("server_host")}/api/data-queries/data-sources/${data_source_id}/versions/${editingVersionId}`,
|
||||
method: "GET",
|
||||
url: `${Cypress.env("server_host")}/api/data-sources/${Cypress.env("workspaceId")}/environments/${currentEnvironmentId}/versions/${editingVersionId}`,
|
||||
headers,
|
||||
body: requestBody,
|
||||
}).then((createResponse) => {
|
||||
expect(createResponse.status).to.equal(201);
|
||||
const queryId = createResponse.body.id;
|
||||
cy.log("Query created successfully:", queryId);
|
||||
}).then((dsResponse) => {
|
||||
expect(dsResponse.status).to.eq(200);
|
||||
|
||||
const createdOptions = createResponse.body.options;
|
||||
expect(createdOptions.method).to.equal(queryOptions.method);
|
||||
expect(createdOptions.url).to.equal(queryOptions.url);
|
||||
expect(createdOptions.headers).to.deep.equal(queryOptions.headers);
|
||||
const dataSource = dsResponse.body.data_sources.find(
|
||||
(ds) => ds.name === dsName
|
||||
);
|
||||
|
||||
if (useJsonBody) {
|
||||
expect(createdOptions.json_body).to.deep.equal(
|
||||
queryOptions.json_body
|
||||
);
|
||||
expect(createdOptions.body_toggle).to.equal(true);
|
||||
} else {
|
||||
expect(createdOptions.body).to.deep.equal(queryOptions.body);
|
||||
expect(createdOptions.body_toggle).to.equal(false);
|
||||
if (!dataSource) {
|
||||
throw new Error(`Data source '${dsName}' not found.`);
|
||||
}
|
||||
|
||||
expect(createdOptions.runOnPageLoad).to.equal(run);
|
||||
cy.log("Metadata verified successfully");
|
||||
if (run) {
|
||||
cy.request({
|
||||
method: "POST",
|
||||
url: `${Cypress.env("server_host")}/api/data-queries/${queryId}/run`,
|
||||
headers,
|
||||
}).then((runResponse) => {
|
||||
expect([200, 201]).to.include(runResponse.status);
|
||||
cy.log("Query executed successfully:", runResponse.body);
|
||||
if (runResponse.body?.data.id) {
|
||||
cy.writeFile("cypress/fixtures/restAPI/storedId.json", {
|
||||
id: runResponse.body.data.id,
|
||||
});
|
||||
cy.log("Stored ID:", runResponse.body.data.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
const data_source_id = dataSource.id;
|
||||
const useJsonBody =
|
||||
["POST", "PATCH", "PUT"].includes(method.toUpperCase()) &&
|
||||
jsonBody !== null;
|
||||
|
||||
const queryOptions = {
|
||||
method: method.toLowerCase(),
|
||||
url: url + urlSuffix,
|
||||
url_params: [["", ""]],
|
||||
headers: headersList.length ? headersList : [["", ""]],
|
||||
body: !useJsonBody && bodyList.length ? bodyList : [["", ""]],
|
||||
json_body: useJsonBody ? jsonBody : null,
|
||||
body_toggle: useJsonBody,
|
||||
runOnPageLoad: run,
|
||||
transformationLanguage: "javascript",
|
||||
enableTransformation: false,
|
||||
};
|
||||
|
||||
const requestBody = {
|
||||
app_id: Cypress.env("appId"),
|
||||
app_version_id: editingVersionId,
|
||||
name: queryName,
|
||||
kind: "restapi",
|
||||
options: queryOptions,
|
||||
data_source_id,
|
||||
plugin_id: null,
|
||||
};
|
||||
|
||||
cy.request({
|
||||
method: "POST",
|
||||
url: `${Cypress.env("server_host")}/api/data-queries/data-sources/${data_source_id}/versions/${editingVersionId}`,
|
||||
headers,
|
||||
body: requestBody,
|
||||
}).then((createResponse) => {
|
||||
expect(createResponse.status).to.equal(201);
|
||||
const queryId = createResponse.body.id;
|
||||
cy.log("Query created successfully:", queryId);
|
||||
|
||||
const createdOptions = createResponse.body.options;
|
||||
expect(createdOptions.method).to.equal(queryOptions.method);
|
||||
expect(createdOptions.url).to.equal(queryOptions.url);
|
||||
expect(createdOptions.headers).to.deep.equal(queryOptions.headers);
|
||||
|
||||
if (useJsonBody) {
|
||||
expect(createdOptions.json_body).to.deep.equal(
|
||||
queryOptions.json_body
|
||||
);
|
||||
expect(createdOptions.body_toggle).to.equal(true);
|
||||
} else {
|
||||
expect(createdOptions.body).to.deep.equal(queryOptions.body);
|
||||
expect(createdOptions.body_toggle).to.equal(false);
|
||||
}
|
||||
|
||||
expect(createdOptions.runOnPageLoad).to.equal(run);
|
||||
cy.log("Metadata verified successfully");
|
||||
if (run) {
|
||||
cy.request({
|
||||
method: "POST",
|
||||
url: `${Cypress.env("server_host")}/api/data-queries/${queryId}/run`,
|
||||
headers,
|
||||
}).then((runResponse) => {
|
||||
expect([200, 201]).to.include(runResponse.status);
|
||||
cy.log("Query executed successfully:", runResponse.body);
|
||||
if (runResponse.body?.data.id) {
|
||||
cy.writeFile("cypress/fixtures/restAPI/storedId.json", {
|
||||
id: runResponse.body.data.id,
|
||||
});
|
||||
cy.log("Stored ID:", runResponse.body.data.id);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -115,8 +115,8 @@ export const verifyDuplicateVersion = (newVersion = [], version) => {
|
|||
cy.get(appVersionSelectors.createNewVersionButton).click();
|
||||
cy.verifyToastMessage(
|
||||
commonSelectors.toastMessage,
|
||||
// appVersionText.versionNameAlreadyExists
|
||||
"Already exists!"
|
||||
appVersionText.versionNameAlreadyExists
|
||||
// "Already exists!"
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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,64 @@ 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
|
||||
|
||||
# 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 \
|
||||
|
|
@ -110,4 +139,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"]
|
||||
|
|
|
|||
|
|
@ -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"]
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
3.11.0
|
||||
3.12.1
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit 518f3334b12a83785fd37dd53b0245d72848211a
|
||||
Subproject commit 1b77a556709211daed8924821383db9dccc95eb5
|
||||
|
|
@ -58,6 +58,7 @@
|
|||
"dotenv": "^16.0.3",
|
||||
"draft-js": "^0.11.7",
|
||||
"draft-js-export-html": "^1.4.1",
|
||||
"draft-js-import-html": "^1.4.1",
|
||||
"driver.js": "^0.9.8",
|
||||
"emoji-mart": "^5.5.2",
|
||||
"file-loader": "^6.2.0",
|
||||
|
|
|
|||
|
|
@ -232,6 +232,7 @@ export const getAllChildComponents = (allComponents, parentId) => {
|
|||
const childTabId = componentParentId.split('-').at(-1);
|
||||
if (componentParentId === `${parentId}-${childTabId}`) {
|
||||
childComponent.isParentTabORCalendar = true;
|
||||
childComponent.events = useStore.getState().eventsSlice.getEventsByComponentsId(componentId);
|
||||
childComponents.push(childComponent);
|
||||
// Recursively find children of the current child component
|
||||
const childrenOfChild = getAllChildComponents(allComponents, componentId);
|
||||
|
|
@ -242,6 +243,7 @@ export const getAllChildComponents = (allComponents, parentId) => {
|
|||
if (componentParentId === parentId) {
|
||||
let childComponent = deepClone(allComponents[componentId]);
|
||||
childComponent.id = componentId;
|
||||
childComponent.events = useStore.getState().eventsSlice.getEventsByComponentsId(componentId);
|
||||
childComponents.push(childComponent);
|
||||
|
||||
// Recursively find children of the current child component
|
||||
|
|
|
|||
|
|
@ -0,0 +1,27 @@
|
|||
/* eslint-disable import/no-unresolved */
|
||||
import React from 'react';
|
||||
import { openSearchPanel } from '@codemirror/search';
|
||||
import './SearchBox.scss';
|
||||
import { Button as ButtonComponent } from '@/components/ui/Button/Button.jsx';
|
||||
|
||||
export const CodeHinterBtns = ({ view, isPanelOpen, renderCopilot }) => {
|
||||
return (
|
||||
<div
|
||||
className="d-flex tw-flex-col align-items-end tw-gap-[2.5px] w-100 position-absolute tw-pt-[4px] tw-pr-[4px]"
|
||||
style={{ top: isPanelOpen ? '44px' : 0 }}
|
||||
>
|
||||
{!isPanelOpen && (
|
||||
<ButtonComponent
|
||||
iconOnly
|
||||
trailingIcon="search01"
|
||||
size="small"
|
||||
variant="outline"
|
||||
ariaLabel="Open search panel"
|
||||
className="codehinter-search-btn"
|
||||
onClick={() => openSearchPanel(view)}
|
||||
/>
|
||||
)}
|
||||
{renderCopilot && renderCopilot()}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
@ -20,10 +20,12 @@ import { PreviewBox } from './PreviewBox';
|
|||
import { removeNestedDoubleCurlyBraces } from '@/_helpers/utils';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { syntaxTree } from '@codemirror/language';
|
||||
import { search, searchKeymap, searchPanelOpen } from '@codemirror/search';
|
||||
import { handleSearchPanel, SearchBtn } from './SearchBox';
|
||||
import { handleSearchPanel } from './SearchBox';
|
||||
import { useQueryPanelKeyHooks } from './useQueryPanelKeyHooks';
|
||||
import { isInsideParent } from './utils';
|
||||
import { CodeHinterBtns } from './CodehinterOverlayTriggers';
|
||||
|
||||
const langSupport = Object.freeze({
|
||||
javascript: javascript(),
|
||||
|
|
@ -66,7 +68,7 @@ const MultiLineCodeEditor = (props) => {
|
|||
|
||||
const context = useContext(CodeHinterContext);
|
||||
|
||||
const { suggestionList } = createReferencesLookup(context, true);
|
||||
const { suggestionList: paramList } = createReferencesLookup(context, true);
|
||||
|
||||
const currentValueRef = useRef(initialValue);
|
||||
|
||||
|
|
@ -74,6 +76,7 @@ const MultiLineCodeEditor = (props) => {
|
|||
|
||||
const [editorView, setEditorView] = React.useState(null);
|
||||
|
||||
const [isSearchPanelOpen, setIsSearchPanelOpen] = React.useState(false);
|
||||
const { queryPanelKeybindings } = useQueryPanelKeyHooks(onChange, currentValueRef, 'multiline');
|
||||
|
||||
const handleOnBlur = () => {
|
||||
|
|
@ -146,8 +149,29 @@ const MultiLineCodeEditor = (props) => {
|
|||
return suggestion.hint.includes(nearestSubstring);
|
||||
});
|
||||
|
||||
const localVariables = new Set();
|
||||
|
||||
// Traverse the syntax tree to extract variable declarations
|
||||
syntaxTree(context.state).iterate({
|
||||
enter: (node) => {
|
||||
// JavaScript: Detect variable declarations (var, let, const)
|
||||
if (node.name === 'VariableDefinition') {
|
||||
const varName = context.state.sliceDoc(node.from, node.to);
|
||||
if (varName && varName.startsWith(nearestSubstring)) localVariables.add(varName);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// Convert Set to an array of completion suggestions
|
||||
const localVariableSuggestions = [...localVariables].map((varName) => ({
|
||||
hint: varName,
|
||||
type: 'variable',
|
||||
}));
|
||||
|
||||
const suggestionList = paramList.filter((paramSuggestion) => paramSuggestion.hint.includes(nearestSubstring));
|
||||
|
||||
const suggestions = generateHints(
|
||||
[...JSLangHints, ...autoSuggestionList, ...suggestionList],
|
||||
[...localVariableSuggestions, ...JSLangHints, ...autoSuggestionList, ...suggestionList],
|
||||
null,
|
||||
nearestSubstring
|
||||
).map((hint) => {
|
||||
|
|
@ -204,6 +228,7 @@ const MultiLineCodeEditor = (props) => {
|
|||
return {
|
||||
from: context.pos,
|
||||
options: [...suggestions],
|
||||
filter: false,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -237,7 +262,7 @@ const MultiLineCodeEditor = (props) => {
|
|||
]);
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const overRideFunction = React.useCallback((context) => autoCompleteExtensionConfig(context), []);
|
||||
const overRideFunction = React.useCallback((context) => autoCompleteExtensionConfig(context), [paramList]);
|
||||
const { handleTogglePopupExapand, isOpen, setIsOpen, forceUpdate } = portalProps;
|
||||
let cyLabel = paramLabel ? paramLabel.toLowerCase().trim().replace(/\s+/g, '-') : props.cyLabel;
|
||||
|
||||
|
|
@ -258,7 +283,7 @@ const MultiLineCodeEditor = (props) => {
|
|||
ref={wrapperRef}
|
||||
>
|
||||
<div className={`${className} ${darkMode && 'cm-codehinter-dark-themed'}`}>
|
||||
<SearchBtn view={editorView} />
|
||||
<CodeHinterBtns view={editorView} isPanelOpen={isSearchPanelOpen} renderCopilot={renderCopilot} />
|
||||
<CodeHinter.PopupIcon
|
||||
callback={handleTogglePopupExapand}
|
||||
icon="portal-open"
|
||||
|
|
@ -266,7 +291,6 @@ const MultiLineCodeEditor = (props) => {
|
|||
isMultiEditor={true}
|
||||
isQueryManager={isInsideQueryPane}
|
||||
/>
|
||||
{renderCopilot && renderCopilot()}
|
||||
|
||||
<CodeHinter.Portal
|
||||
isCopilotEnabled={false}
|
||||
|
|
@ -326,12 +350,7 @@ const MultiLineCodeEditor = (props) => {
|
|||
readOnly={readOnly}
|
||||
editable={editable} //for transformations in query manager
|
||||
onCreateEditor={(view) => setEditorView(view)}
|
||||
onUpdate={(view) => {
|
||||
const icon = document.querySelector('.codehinter-search-btn');
|
||||
if (searchPanelOpen(view.state)) {
|
||||
icon.style.display = 'none';
|
||||
} else icon.style.display = 'block';
|
||||
}}
|
||||
onUpdate={(view) => setIsSearchPanelOpen(searchPanelOpen(view.state))}
|
||||
/>
|
||||
</div>
|
||||
{showPreview && (
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import {
|
|||
findPrevious,
|
||||
replaceNext,
|
||||
replaceAll,
|
||||
openSearchPanel,
|
||||
} from '@codemirror/search';
|
||||
import './SearchBox.scss';
|
||||
import InputComponent from '@/components/ui/Input/Index.jsx';
|
||||
|
|
@ -162,22 +161,3 @@ function SearchPanel({ view }) {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const SearchBtn = ({ view }) => {
|
||||
return (
|
||||
<div
|
||||
className="d-flex justify-content-end w-100 position-absolute tw-pt-[3px] tw-pr-[4px] codehinter-search-btn-wrapper"
|
||||
style={{ top: 0 }}
|
||||
>
|
||||
<ButtonComponent
|
||||
iconOnly
|
||||
trailingIcon="search01"
|
||||
size="small"
|
||||
variant="outline"
|
||||
ariaLabel="Open search panel"
|
||||
className="codehinter-search-btn"
|
||||
onClick={() => openSearchPanel(view)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -44,7 +44,5 @@
|
|||
}
|
||||
|
||||
.code-hinter-wrapper .codehinter-search-btn {
|
||||
display: block;
|
||||
padding-top: 1px;
|
||||
z-index: 10000;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
|
@ -1,12 +1,18 @@
|
|||
/* eslint-disable import/no-unresolved */
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import React, { useEffect, useMemo, useRef, useState, useContext } from 'react';
|
||||
import { PreviewBox } from './PreviewBox';
|
||||
import { ToolTip } from '@/Editor/Inspector/Elements/Components/ToolTip';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { camelCase, isEmpty, noop, get } from 'lodash';
|
||||
import CodeMirror from '@uiw/react-codemirror';
|
||||
import { javascript } from '@codemirror/lang-javascript';
|
||||
import { autocompletion, completionKeymap, completionStatus, acceptCompletion } from '@codemirror/autocomplete';
|
||||
import {
|
||||
autocompletion,
|
||||
completionKeymap,
|
||||
completionStatus,
|
||||
acceptCompletion,
|
||||
startCompletion,
|
||||
} from '@codemirror/autocomplete';
|
||||
import { defaultKeymap } from '@codemirror/commands';
|
||||
import { keymap } from '@codemirror/view';
|
||||
import FxButton from '../CodeBuilder/Elements/FxButton';
|
||||
|
|
@ -22,6 +28,8 @@ import CodeHinter from './CodeHinter';
|
|||
import { removeNestedDoubleCurlyBraces } from '@/_helpers/utils';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import { CodeHinterContext } from '../CodeBuilder/CodeHinterContext';
|
||||
import { createReferencesLookup } from '@/_stores/utils';
|
||||
import { useQueryPanelKeyHooks } from './useQueryPanelKeyHooks';
|
||||
|
||||
const SingleLineCodeEditor = ({ componentName, fieldMeta = {}, componentId, ...restProps }) => {
|
||||
|
|
@ -73,6 +81,7 @@ const SingleLineCodeEditor = ({ componentName, fieldMeta = {}, componentId, ...r
|
|||
if (typeof initialValue === 'string' && (initialValue?.includes('components') || initialValue?.includes('queries'))) {
|
||||
newInitialValue = replaceIdsWithName(initialValue);
|
||||
}
|
||||
|
||||
//! Re render the component when the componentName changes as the initialValue is not updated
|
||||
|
||||
// const { variablesExposedForPreview } = useContext(EditorContext) || {};
|
||||
|
|
@ -199,9 +208,14 @@ const EditorInput = ({
|
|||
wrapperRef,
|
||||
showSuggestions,
|
||||
}) => {
|
||||
const getServerSideGlobalSuggestions = useStore((state) => state.getServerSideGlobalSuggestions, shallow);
|
||||
const codeHinterContext = useContext(CodeHinterContext);
|
||||
const { suggestionList: paramHints } = createReferencesLookup(codeHinterContext, true);
|
||||
|
||||
const getSuggestions = useStore((state) => state.getSuggestions, shallow);
|
||||
const [codeMirrorView, setCodeMirrorView] = useState(undefined);
|
||||
|
||||
const getServerSideGlobalSuggestions = useStore((state) => state.getServerSideGlobalSuggestions, shallow);
|
||||
|
||||
const { queryPanelKeybindings } = useQueryPanelKeyHooks(onBlurUpdate, currentValue, 'singleline');
|
||||
|
||||
const isInsideQueryManager = useMemo(
|
||||
|
|
@ -209,16 +223,16 @@ const EditorInput = ({
|
|||
[wrapperRef.current]
|
||||
);
|
||||
function autoCompleteExtensionConfig(context) {
|
||||
const hints = getSuggestions();
|
||||
const hintsWithoutParamHints = getSuggestions();
|
||||
const serverHints = getServerSideGlobalSuggestions(isInsideQueryManager);
|
||||
|
||||
const allHints = {
|
||||
...hints,
|
||||
appHints: [...hints.appHints, ...serverHints],
|
||||
};
|
||||
|
||||
let word = context.matchBefore(/\w*/);
|
||||
|
||||
const hints = {
|
||||
...hintsWithoutParamHints,
|
||||
appHints: [...hintsWithoutParamHints.appHints, ...serverHints, ...paramHints],
|
||||
};
|
||||
|
||||
const totalReferences = (context.state.doc.toString().match(/{{/g) || []).length;
|
||||
|
||||
let queryInput = context.state.doc.toString();
|
||||
|
|
@ -247,17 +261,18 @@ const EditorInput = ({
|
|||
queryInput = '{{' + currentWord + '}}';
|
||||
}
|
||||
|
||||
let completions = getAutocompletion(queryInput, validationType, allHints, totalReferences, originalQueryInput);
|
||||
let completions = getAutocompletion(queryInput, validationType, hints, totalReferences, originalQueryInput);
|
||||
|
||||
return {
|
||||
from: word.from,
|
||||
options: completions,
|
||||
validFor: /^\{\{.*\}\}$/,
|
||||
filter: false,
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
const overRideFunction = React.useCallback((context) => autoCompleteExtensionConfig(context), [isInsideQueryManager]);
|
||||
const overRideFunction = React.useCallback((context) => autoCompleteExtensionConfig(context), [isInsideQueryManager, paramHints]);
|
||||
|
||||
const autoCompleteConfig = autocompletion({
|
||||
override: [overRideFunction],
|
||||
|
|
@ -424,6 +439,9 @@ const EditorInput = ({
|
|||
ref={previewRef}
|
||||
>
|
||||
<CodeMirror
|
||||
onCreateEditor={(view) => {
|
||||
setCodeMirrorView(view);
|
||||
}}
|
||||
value={currentValue}
|
||||
placeholder={placeholder}
|
||||
height={isInsideQueryPane ? '100%' : showLineNumbers ? '400px' : '100%'}
|
||||
|
|
@ -460,11 +478,16 @@ const EditorInput = ({
|
|||
theme={theme}
|
||||
indentWithTab={false}
|
||||
readOnly={disabled}
|
||||
onKeyDown={(event) => {
|
||||
if (event.key === 'Backspace') {
|
||||
startCompletion(codeMirrorView);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</ErrorBoundary>
|
||||
</CodeHinter.Portal>
|
||||
</div>
|
||||
</ErrorBoundary >
|
||||
</CodeHinter.Portal >
|
||||
</div >
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -67,7 +67,8 @@ export const getAutocompletion = (input, fieldType, hints, totalReferences = 1,
|
|||
originalQueryInput,
|
||||
searchInput
|
||||
);
|
||||
return orderSuggestions(suggestions, fieldType);
|
||||
|
||||
return suggestions;
|
||||
};
|
||||
|
||||
function orderSuggestions(suggestions, validationType) {
|
||||
|
|
@ -90,10 +91,18 @@ export const generateHints = (hints, totalReferences = 1, input, searchText) =>
|
|||
const hasDepth = currentWord.includes('.');
|
||||
const lastDepth = getLastSubstring(currentWord);
|
||||
|
||||
const displayLabel = getLastDepth(displayedHint);
|
||||
let displayLabel = getLastDepth(displayedHint);
|
||||
|
||||
if (type != 'js_method') {
|
||||
const currentWordDepth = currentWord.split('.').length;
|
||||
displayLabel = hint
|
||||
.split('.')
|
||||
.slice(currentWordDepth - 1)
|
||||
.join('.');
|
||||
}
|
||||
|
||||
return {
|
||||
displayLabel: lastDepth === '' ? displayedHint : displayLabel,
|
||||
displayLabel,
|
||||
label: displayedHint,
|
||||
info: displayedHint,
|
||||
type: type === 'js_method' ? 'js_methods' : type?.toLowerCase(),
|
||||
|
|
@ -154,40 +163,24 @@ export const generateHints = (hints, totalReferences = 1, input, searchText) =>
|
|||
};
|
||||
|
||||
function filterHintsByDepth(input, hints) {
|
||||
if (input === '') return hints;
|
||||
const inputParts = input.split('.');
|
||||
const inputDepth = inputParts.length + 1;
|
||||
|
||||
const inputDepth = input.includes('.') ? input.split('.').length : 0;
|
||||
|
||||
const filteredHints = hints.filter((cm) => {
|
||||
const hintParts = cm.hint.split('.');
|
||||
|
||||
let shouldInclude =
|
||||
(cm.hint.startsWith(input) && hintParts.length === inputDepth + 1) ||
|
||||
(cm.hint.startsWith(input) && hintParts.length === inputDepth);
|
||||
|
||||
const shouldFuzzyMatch = !shouldInclude ? hintParts.length > inputDepth : false;
|
||||
|
||||
if (shouldFuzzyMatch) {
|
||||
// fuzzy match
|
||||
let matchedDepth = -1;
|
||||
for (let i = 0; i < hintParts.length; i++) {
|
||||
if (hintParts[i].includes(input)) {
|
||||
matchedDepth = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (matchedDepth !== -1) {
|
||||
shouldInclude = hintParts.length === matchedDepth + 1;
|
||||
}
|
||||
} else if (input.endsWith('.')) {
|
||||
shouldInclude = cm.hint.startsWith(input) && hintParts.length === inputDepth;
|
||||
}
|
||||
|
||||
return shouldInclude;
|
||||
const hintsWithDepth = hints.map((hint) => {
|
||||
const hintParts = hint.hint.split('.');
|
||||
return {
|
||||
...hint,
|
||||
depth: hintParts.length,
|
||||
};
|
||||
});
|
||||
|
||||
return filteredHints;
|
||||
const filteredHints = hintsWithDepth.filter((hint) => {
|
||||
return hint.depth <= inputDepth;
|
||||
});
|
||||
|
||||
const sortedHints = filteredHints.sort((hint1, hint2) => hint1.depth - hint2.depth);
|
||||
|
||||
return sortedHints;
|
||||
}
|
||||
|
||||
export function findNearestSubstring(inputStr, currentCurosorPos) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,20 @@
|
|||
import React from 'react';
|
||||
import React, { useState } from 'react';
|
||||
import Select from '@/_ui/Select';
|
||||
import { decodeEntities } from '@/_helpers/utils';
|
||||
import usePopoverObserver from '@/AppBuilder/_hooks/usePopoverObserver';
|
||||
|
||||
export const ChangeDataSource = ({ dataSources, onChange, value, isVersionReleased }) => {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
|
||||
usePopoverObserver(
|
||||
document.getElementsByClassName('query-details')[0],
|
||||
document.querySelector('.change-data-source-select.react-select__control'),
|
||||
document.querySelector('.change-data-source-select.react-select__menu'),
|
||||
isMenuOpen,
|
||||
() => (document.querySelector('.change-data-source-select.react-select__menu').style.display = 'block'),
|
||||
() => (document.querySelector('.change-data-source-select.react-select__menu').style.display = 'none')
|
||||
);
|
||||
|
||||
return (
|
||||
<Select
|
||||
className="w-100"
|
||||
|
|
@ -14,6 +26,13 @@ export const ChangeDataSource = ({ dataSources, onChange, value, isVersionReleas
|
|||
}}
|
||||
useMenuPortal={true}
|
||||
isDisabled={isVersionReleased}
|
||||
customClassPrefix="change-data-source-select"
|
||||
onMenuOpen={() => {
|
||||
setIsMenuOpen(true);
|
||||
}}
|
||||
onMenuClose={() => {
|
||||
setIsMenuOpen(false);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,9 @@ class Restapi extends React.Component {
|
|||
codeHinterHeight: 32, // Default height
|
||||
};
|
||||
this.codeHinterRef = React.createRef();
|
||||
this.isMenuOpenRef = React.createRef();
|
||||
this.prevIsMenuOpenRef = React.createRef(false);
|
||||
this.intersectionObserver = null;
|
||||
this.resizeObserver = null;
|
||||
}
|
||||
|
||||
|
|
@ -47,6 +50,9 @@ class Restapi extends React.Component {
|
|||
if (this.codeHinterRef.current && !this.resizeObserver) {
|
||||
this.setupResizeObserver();
|
||||
}
|
||||
if (!this.intersectionObserver) {
|
||||
this.setupIntersectionObserver();
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
|
@ -75,6 +81,7 @@ class Restapi extends React.Component {
|
|||
}, 1000);
|
||||
|
||||
this.setupResizeObserver();
|
||||
this.setupIntersectionObserver();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
|
@ -84,6 +91,9 @@ class Restapi extends React.Component {
|
|||
if (this.resizeObserver) {
|
||||
this.resizeObserver.disconnect();
|
||||
}
|
||||
if (this.intersectionObserver) {
|
||||
this.intersectionObserver.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
setupResizeObserver() {
|
||||
|
|
@ -132,6 +142,33 @@ class Restapi extends React.Component {
|
|||
this.resizeObserver.observe(element);
|
||||
}
|
||||
|
||||
setupIntersectionObserver() {
|
||||
const container = document.getElementsByClassName('query-details')[0];
|
||||
const trigger = document.querySelector('.restapi-method-select.react-select__control');
|
||||
|
||||
if (this.intersectionObserver) {
|
||||
this.intersectionObserver.disconnect();
|
||||
}
|
||||
|
||||
this.intersectionObserver = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
const popover = document.querySelector('.restapi-method-select.react-select__menu');
|
||||
if (entry.isIntersecting) {
|
||||
if (this.prevIsMenuOpenRef.current) {
|
||||
popover.style.display = 'block';
|
||||
this.prevIsMenuOpenRef.current = false;
|
||||
}
|
||||
} else if (this.isMenuOpenRef.current) {
|
||||
popover.style.display = 'none';
|
||||
this.prevIsMenuOpenRef.current = true;
|
||||
}
|
||||
},
|
||||
{ root: container, threshold: [0.5] }
|
||||
);
|
||||
|
||||
this.intersectionObserver.observe(trigger);
|
||||
}
|
||||
|
||||
initizalizeRetryNetworkErrorsToggle = () => {
|
||||
const isRetryNetworkErrorToggleUnused = this.props.options.retry_network_errors === null;
|
||||
if (isRetryNetworkErrorToggleUnused) {
|
||||
|
|
@ -287,6 +324,13 @@ class Restapi extends React.Component {
|
|||
height={32}
|
||||
styles={this.customSelectStyles(this.props.darkMode, 91)}
|
||||
useCustomStyles={true}
|
||||
customClassPrefix="restapi-method-select"
|
||||
onMenuOpen={() => {
|
||||
this.isMenuOpenRef.current = true;
|
||||
}}
|
||||
onMenuClose={() => {
|
||||
this.isMenuOpenRef.current = false;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -53,7 +53,11 @@ export const BulkUploadPrimaryKey = () => {
|
|||
<div className="field flex-grow-1 minw-400-w-400">
|
||||
<CodeHinter
|
||||
type="basic"
|
||||
initialValue={`{{${JSON.stringify(bulkUpdatePrimaryKey?.rows_update ?? [])}}}`}
|
||||
initialValue={
|
||||
bulkUpdatePrimaryKey?.rows_update
|
||||
? `{{${JSON.stringify(bulkUpdatePrimaryKey?.rows_update ?? [])}}}`
|
||||
: null
|
||||
}
|
||||
className="codehinter-plugins"
|
||||
placeholder="{{ [ { 'column1': 'value', ... } ] }}"
|
||||
onChange={(newValue) => {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
import React, { useContext, useEffect } from 'react';
|
||||
import { TooljetDatabaseContext } from '@/TooljetDatabase/index';
|
||||
import { resolveReferences } from '@/AppBuilder/CodeEditor/utils';
|
||||
import CodeHinter from '@/AppBuilder/CodeEditor';
|
||||
|
||||
export const BulkUpsertPrimaryKey = () => {
|
||||
const {
|
||||
columns,
|
||||
bulkUpsertPrimaryKey,
|
||||
handleBulkUpsertRowsOptionChanged,
|
||||
handlePrimaryKeyOptionChangedForBulkUpsert,
|
||||
} = useContext(TooljetDatabaseContext);
|
||||
|
||||
useEffect(() => {
|
||||
const primaryKeys = columns.reduce((acc, column) => {
|
||||
if (column?.keytype === 'PRIMARY KEY' || column?.isPrimaryKey) {
|
||||
acc.push(column?.accessor);
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
|
||||
if (primaryKeys.length > 0) {
|
||||
handlePrimaryKeyOptionChangedForBulkUpsert(primaryKeys);
|
||||
}
|
||||
}, [columns]);
|
||||
|
||||
const handleRowsChange = (value) => {
|
||||
handleBulkUpsertRowsOptionChanged(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="tab-content-wrapper tj-db-field-wrapper mt-2 d-flex flex-column custom-gap-16">
|
||||
<div className="field-container d-flex tooljetdb-worflow-operations">
|
||||
<label className="form-label flex-shrink-0">Primary key</label>
|
||||
<div
|
||||
className="field flex-grow-1 minw-400-w-400 px-1"
|
||||
style={{ height: '28px', background: 'var(--controls-switch-tag)', borderRadius: '6px' }}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value={bulkUpsertPrimaryKey?.primary_key?.join(', ') || ''}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
border: '0',
|
||||
color: 'var(--text-placeholder)',
|
||||
background: 'transparent',
|
||||
}}
|
||||
disabled
|
||||
placeholder={''}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="field-container d-flex tooljetdb-worflow-operations">
|
||||
<label className="form-label flex-shrink-0" data-cy="">
|
||||
Rows to upsert
|
||||
</label>
|
||||
<div className="field flex-grow-1 minw-400-w-400">
|
||||
<CodeHinter
|
||||
type="basic"
|
||||
initialValue={
|
||||
bulkUpsertPrimaryKey?.rows
|
||||
? typeof bulkUpsertPrimaryKey?.rows === 'string'
|
||||
? bulkUpsertPrimaryKey?.rows
|
||||
: JSON.stringify(bulkUpsertPrimaryKey?.rows)
|
||||
: bulkUpsertPrimaryKey?.rows
|
||||
}
|
||||
className="codehinter-plugins"
|
||||
placeholder="{{ [ { 'column1': 'value', ... } ] }}"
|
||||
onChange={handleRowsChange}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default BulkUpsertPrimaryKey;
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import CodeHinter from '@/AppBuilder/CodeEditor';
|
||||
import { resolveReferences } from '@/Editor/CodeEditor/utils';
|
||||
import { ButtonSolid } from '@/_ui/AppButton/AppButton';
|
||||
import Trash from '@/_ui/Icon/solidIcons/Trash';
|
||||
import React from 'react';
|
||||
|
|
@ -43,8 +42,7 @@ const RenderColumnUI = ({
|
|||
placeholder="key"
|
||||
onChange={(newValue) => {
|
||||
if (isJSonTypeColumn) {
|
||||
const [_, __, resolvedValue] = resolveReferences(`{{${newValue}}}`);
|
||||
handleValueChange(resolvedValue);
|
||||
handleValueChange(newValue);
|
||||
} else {
|
||||
handleValueChange(newValue);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -220,7 +220,9 @@ function DataSourceSelect({
|
|||
if (isFirstPageLoaded && offset >= totalRecords) return;
|
||||
if (foreignKeys.length < 1) return;
|
||||
setIsLoadingFKDetails(true);
|
||||
const referencedColumns = foreignKeys.find((item) => item.column_names[0] === cellColumnName);
|
||||
const referencedColumns = Array.isArray(foreignKeys)
|
||||
? foreignKeys.find((item) => item.column_names[0] === cellColumnName)
|
||||
: undefined;
|
||||
if (!referencedColumns?.referenced_column_names?.length) return;
|
||||
|
||||
const selectQuery = new PostgrestQueryBuilder();
|
||||
|
|
@ -709,7 +711,8 @@ const MenuList = ({
|
|||
...props
|
||||
}) => {
|
||||
const menuListStyles = getStyles('menuList', props);
|
||||
const referencedColumnDetails = foreignKeys?.find((item) => item.column_names[0] === cellColumnName);
|
||||
const referencedColumnDetails =
|
||||
Array.isArray(foreignKeys) && foreignKeys?.find((item) => item?.column_names[0] === cellColumnName);
|
||||
|
||||
const handleNavigateToReferencedTable = () => {
|
||||
const data = {
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import { getPrivateRoute } from '@/_helpers/routes';
|
|||
import { useNavigate } from 'react-router-dom';
|
||||
import { deepClone } from '@/_helpers/utilities/utils.helpers';
|
||||
import { BulkUploadPrimaryKey } from './BulkUploadPrimaryKey';
|
||||
import BulkUpsertPrimaryKey from './BulkUpsertPrimaryKey';
|
||||
|
||||
import './styles.scss';
|
||||
import CodeHinter from '@/AppBuilder/CodeEditor';
|
||||
|
|
@ -46,6 +47,7 @@ const ToolJetDbOperations = ({ optionchanged, options, darkMode, isHorizontalLay
|
|||
const [tableForeignKeyInfo, setTableForeignKeyInfo] = useState({});
|
||||
|
||||
const [bulkUpdatePrimaryKey, setBulkUpdatePrimaryKey] = useState(() => options['bulk_update_with_primary_key'] || {});
|
||||
const [bulkUpsertPrimaryKey, setBulkUpsertPrimaryKey] = useState(() => options['bulk_upsert_with_primary_key'] || {});
|
||||
|
||||
const joinOptions = options['join_table']?.['joins'] || [
|
||||
{ conditions: { conditionsList: [{ leftField: { table: selectedTableId } }] } },
|
||||
|
|
@ -196,6 +198,11 @@ const ToolJetDbOperations = ({ optionchanged, options, darkMode, isHorizontalLay
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [bulkUpdatePrimaryKey]);
|
||||
|
||||
useEffect(() => {
|
||||
mounted && optionchanged('bulk_upsert_with_primary_key', bulkUpsertPrimaryKey);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [bulkUpsertPrimaryKey]);
|
||||
|
||||
useEffect(() => {
|
||||
mounted && optionchanged('update_rows', updateRowsOptions);
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
|
|
@ -234,10 +241,18 @@ const ToolJetDbOperations = ({ optionchanged, options, darkMode, isHorizontalLay
|
|||
setBulkUpdatePrimaryKey((prev) => ({ ...prev, rows_update: value }));
|
||||
};
|
||||
|
||||
const handleBulkUpsertRowsOptionChanged = (value) => {
|
||||
setBulkUpsertPrimaryKey((prev) => ({ ...prev, rows: value }));
|
||||
};
|
||||
|
||||
const handlePrimaryKeyOptionChangedForBulkUpdate = (value) => {
|
||||
setBulkUpdatePrimaryKey((prev) => ({ ...prev, primary_key: value }));
|
||||
};
|
||||
|
||||
const handlePrimaryKeyOptionChangedForBulkUpsert = (value) => {
|
||||
setBulkUpsertPrimaryKey((prev) => ({ ...prev, primary_key: value }));
|
||||
};
|
||||
|
||||
const loadTableInformation = async (tableId, isNewTableAdded) => {
|
||||
const tableDetails = findTableDetails(tableId);
|
||||
if (tableDetails?.table_name && !tableInfo[tableDetails?.table_name]) {
|
||||
|
|
@ -340,8 +355,11 @@ const ToolJetDbOperations = ({ optionchanged, options, darkMode, isHorizontalLay
|
|||
tableForeignKeyInfo,
|
||||
setTableForeignKeyInfo,
|
||||
bulkUpdatePrimaryKey,
|
||||
bulkUpsertPrimaryKey,
|
||||
handleBulkUpdateWithPrimaryKeysRowsUpdateOptionChanged,
|
||||
handleBulkUpsertRowsOptionChanged,
|
||||
handlePrimaryKeyOptionChangedForBulkUpdate,
|
||||
handlePrimaryKeyOptionChangedForBulkUpsert,
|
||||
}),
|
||||
[
|
||||
organizationId,
|
||||
|
|
@ -357,6 +375,7 @@ const ToolJetDbOperations = ({ optionchanged, options, darkMode, isHorizontalLay
|
|||
joinOrderByOptions,
|
||||
selectedTableId,
|
||||
bulkUpdatePrimaryKey,
|
||||
bulkUpsertPrimaryKey,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
@ -517,6 +536,8 @@ const ToolJetDbOperations = ({ optionchanged, options, darkMode, isHorizontalLay
|
|||
return JoinTable;
|
||||
case 'bulk_update_with_primary_key':
|
||||
return BulkUploadPrimaryKey;
|
||||
case 'bulk_upsert_with_primary_key':
|
||||
return BulkUpsertPrimaryKey;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -527,6 +548,7 @@ const ToolJetDbOperations = ({ optionchanged, options, darkMode, isHorizontalLay
|
|||
{ label: 'Delete rows', value: 'delete_rows' },
|
||||
{ label: 'Join tables', value: 'join_tables' },
|
||||
{ label: 'Bulk update with primary key', value: 'bulk_update_with_primary_key' },
|
||||
{ label: 'Bulk upsert with primary key', value: 'bulk_upsert_with_primary_key' },
|
||||
];
|
||||
|
||||
const ComponentToRender = getComponent(operation);
|
||||
|
|
|
|||
|
|
@ -5,14 +5,25 @@ import CodeHinter from '@/AppBuilder/CodeEditor';
|
|||
import './workflows-query.scss';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import usePopoverObserver from '@/AppBuilder/_hooks/usePopoverObserver';
|
||||
|
||||
export function Workflows({ options, optionsChanged, currentState }) {
|
||||
const [workflowOptions, setWorkflowOptions] = useState([]);
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const [_selectedWorkflowId, setSelectedWorkflowId] = useState(undefined);
|
||||
const [params, setParams] = useState([...(options.params ?? [{ key: '', value: '' }])]);
|
||||
|
||||
const appId = useStore((state) => state.app.appId);
|
||||
|
||||
usePopoverObserver(
|
||||
document.getElementsByClassName('query-details')[0],
|
||||
document.querySelector('.workflow-select.react-select__control'),
|
||||
document.querySelector('.workflow-select.react-select__menu'),
|
||||
isMenuOpen,
|
||||
() => (document.querySelector('.workflow-select.react-select__menu').style.display = 'block'),
|
||||
() => (document.querySelector('.workflow-select.react-select__menu').style.display = 'none')
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
appsService.getWorkflows(appId).then(({ workflows }) => {
|
||||
setWorkflowOptions(
|
||||
|
|
@ -50,6 +61,13 @@ export function Workflows({ options, optionsChanged, currentState }) {
|
|||
customWrap={true}
|
||||
width="300px"
|
||||
menuPlacement="bottom"
|
||||
customClassPrefix="workflow-select"
|
||||
onMenuOpen={() => {
|
||||
setIsMenuOpen(true);
|
||||
}}
|
||||
onMenuClose={() => {
|
||||
setIsMenuOpen(false);
|
||||
}}
|
||||
/>
|
||||
<label className="my-2">Params</label>
|
||||
<div className="grid"></div>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,538 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import Accordion from '@/_ui/Accordion';
|
||||
import { EventManager } from '../EventManager';
|
||||
import { renderElement } from '../Utils';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
import Popover from 'react-bootstrap/Popover';
|
||||
import List from '@/ToolJetUI/List/List';
|
||||
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
import CodeHinter from '@/AppBuilder/CodeEditor';
|
||||
import AddNewButton from '@/ToolJetUI/Buttons/AddNewButton/AddNewButton';
|
||||
import ListGroup from 'react-bootstrap/ListGroup';
|
||||
import { ButtonSolid } from '@/_ui/AppButton/AppButton';
|
||||
import SortableList from '@/_components/SortableList';
|
||||
import Trash from '@/_ui/Icon/solidIcons/Trash';
|
||||
import { shallow } from 'zustand/shallow';
|
||||
import Switch from '@/Editor/CodeBuilder/Elements/Switch';
|
||||
import { usePrevious } from '@dnd-kit/utilities';
|
||||
|
||||
export function Steps({ componentMeta, darkMode, ...restProps }) {
|
||||
const {
|
||||
layoutPropertyChanged,
|
||||
component,
|
||||
dataQueries,
|
||||
paramUpdated,
|
||||
currentState,
|
||||
eventsChanged,
|
||||
apps,
|
||||
allComponents,
|
||||
pages,
|
||||
} = restProps;
|
||||
const getResolvedValue = useStore((state) => state.getResolvedValue, shallow);
|
||||
|
||||
const isDynamicOptionsEnabled = getResolvedValue(component?.component?.definition?.properties?.advanced?.value);
|
||||
const variant = component?.component?.definition?.properties?.variant?.value;
|
||||
const prevVariant = usePrevious(variant)
|
||||
console.log("variant", component?.component?.definition);
|
||||
|
||||
|
||||
const [options, setOptions] = useState([]);
|
||||
const [hoveredOptionIndex, setHoveredOptionIndex] = useState(null);
|
||||
let properties = [];
|
||||
let additionalActions = [];
|
||||
let optionsProperties = [];
|
||||
|
||||
for (const [key] of Object.entries(componentMeta?.properties)) {
|
||||
if (componentMeta?.properties[key]?.section === 'additionalActions') {
|
||||
additionalActions.push(key);
|
||||
} else if (componentMeta?.properties[key]?.accordian === 'Options') {
|
||||
optionsProperties.push(key);
|
||||
} else {
|
||||
properties.push(key);
|
||||
}
|
||||
}
|
||||
|
||||
// the default style of "number" & "titles" type are different for completed label
|
||||
// TODO: Need to revisit this logic when text custom themes are implemented
|
||||
useEffect(() => {
|
||||
const completedLabelColor = component?.component?.definition?.styles?.completedLabel?.value;
|
||||
if (variant !== prevVariant) {
|
||||
if (variant === "numbers" && completedLabelColor === "#1B1F24") {
|
||||
paramUpdated({ name: 'completedLabel' }, 'value', "#FFFFFF", 'styles', false, {});
|
||||
} else if (variant === "titles" && completedLabelColor === "#FFFFFF") {
|
||||
paramUpdated({ name: 'completedLabel' }, 'value', "#1B1F24", 'styles', false, {});
|
||||
}
|
||||
}
|
||||
|
||||
}, [variant])
|
||||
|
||||
const getItemStyle = (isDragging, draggableStyle) => ({
|
||||
userSelect: 'none',
|
||||
...draggableStyle,
|
||||
});
|
||||
|
||||
const updateAllOptionsParams = (options, props) => {
|
||||
paramUpdated({ name: 'steps' }, 'value', options, 'properties', false, props);
|
||||
};
|
||||
|
||||
const generateNewOptions = () => {
|
||||
let found = false;
|
||||
let label = '';
|
||||
let currentNumber = options.length + 1;
|
||||
while (!found) {
|
||||
label = `step ${currentNumber}`;
|
||||
if (options.find((option) => option.name === label) === undefined) {
|
||||
found = true;
|
||||
}
|
||||
currentNumber += 1;
|
||||
}
|
||||
return {
|
||||
name: label,
|
||||
id: currentNumber - 1,
|
||||
tooltip: label,
|
||||
visible: { value: '{{true}}' },
|
||||
disabled: { value: '{{false}}' },
|
||||
};
|
||||
};
|
||||
|
||||
const handleAddOption = () => {
|
||||
let _option = generateNewOptions();
|
||||
const _items = [...options, _option];
|
||||
setOptions(_items);
|
||||
updateAllOptionsParams(_items);
|
||||
};
|
||||
const handleDeleteOption = (index) => {
|
||||
const _items = options.filter((option, i) => i !== index);
|
||||
setOptions(_items);
|
||||
updateAllOptionsParams(_items, { isParamFromDropdownOptions: true });
|
||||
};
|
||||
|
||||
const handleLabelChange = (propertyName, value, index) => {
|
||||
const _options = options.map((option, i) => {
|
||||
if (i === index) {
|
||||
return {
|
||||
...option,
|
||||
[propertyName]: value,
|
||||
};
|
||||
}
|
||||
return option;
|
||||
});
|
||||
setOptions(_options);
|
||||
updateAllOptionsParams(_options);
|
||||
};
|
||||
|
||||
const reorderOptions = async (startIndex, endIndex) => {
|
||||
const result = [...options];
|
||||
const [removed] = result.splice(startIndex, 1);
|
||||
result.splice(endIndex, 0, removed);
|
||||
setOptions(result);
|
||||
updateAllOptionsParams(result);
|
||||
};
|
||||
|
||||
const onDragEnd = ({ source, destination }) => {
|
||||
if (!destination || source?.index === destination?.index) {
|
||||
return;
|
||||
}
|
||||
reorderOptions(source.index, destination.index);
|
||||
};
|
||||
|
||||
const handleOnFxPress = (active, index, key) => {
|
||||
const _options = options.map((option, i) => {
|
||||
if (i === index) {
|
||||
return {
|
||||
...option,
|
||||
[key]: {
|
||||
...option[key],
|
||||
fxActive: active,
|
||||
},
|
||||
};
|
||||
}
|
||||
return option;
|
||||
});
|
||||
setOptions(_options);
|
||||
updateAllOptionsParams(_options);
|
||||
};
|
||||
|
||||
const _renderOverlay = (item, index) => {
|
||||
return (
|
||||
<Popover className={`${darkMode && 'dark-theme theme-dark'}`} style={{ minWidth: '248px' }}>
|
||||
<Popover.Body>
|
||||
<div className="field mb-3" data-cy={`input-and-label-column-name`}>
|
||||
<label data-cy={`label-column-name`} className="font-weight-500 mb-1 font-size-12">
|
||||
{'Id'}
|
||||
</label>
|
||||
<CodeHinter
|
||||
type={'basic'}
|
||||
initialValue={item?.id + ''}
|
||||
theme={darkMode ? 'monokai' : 'default'}
|
||||
mode="javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={'Option label'}
|
||||
onChange={(value) => handleLabelChange('id', value, index)}
|
||||
/>
|
||||
</div>
|
||||
<div className="field mb-3" data-cy={`input-and-label-column-name`}>
|
||||
<label data-cy={`label-column-name`} className="font-weight-500 mb-1 font-size-12">
|
||||
{'Label'}
|
||||
</label>
|
||||
<CodeHinter
|
||||
type={'basic'}
|
||||
initialValue={item?.name}
|
||||
theme={darkMode ? 'monokai' : 'default'}
|
||||
mode="javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={'Option label'}
|
||||
onChange={(value) => handleLabelChange('name', value, index)}
|
||||
/>
|
||||
</div>
|
||||
<div className="field mb-3" data-cy={`input-and-label-column-name`}>
|
||||
<label data-cy={`label-column-name`} className="font-weight-500 mb-1 font-size-12">
|
||||
{'Tooltip'}
|
||||
</label>
|
||||
<CodeHinter
|
||||
type={'basic'}
|
||||
initialValue={item?.tooltip + ''}
|
||||
theme={darkMode ? 'monokai' : 'default'}
|
||||
mode="javascript"
|
||||
lineNumbers={false}
|
||||
placeholder={'Tooltip'}
|
||||
onChange={(value) => handleLabelChange('tooltip', value, index)}
|
||||
/>
|
||||
</div>
|
||||
<div className="field mb-2" data-cy={`input-and-label-column-name`}>
|
||||
<CodeHinter
|
||||
initialValue={item?.visible?.value}
|
||||
theme={darkMode ? 'monokai' : 'default'}
|
||||
mode="javascript"
|
||||
lineNumbers={false}
|
||||
component={component}
|
||||
type={'fxEditor'}
|
||||
paramLabel={'Visibility'}
|
||||
onChange={(value) =>
|
||||
handleLabelChange(
|
||||
'visible',
|
||||
{
|
||||
value,
|
||||
},
|
||||
index
|
||||
)
|
||||
}
|
||||
paramName={'visible'}
|
||||
onFxPress={(active) => handleOnFxPress(active, index, 'visible')}
|
||||
fxActive={item?.visible?.fxActive}
|
||||
fieldMeta={{
|
||||
type: 'toggle',
|
||||
displayName: 'Make editable',
|
||||
}}
|
||||
paramType={'toggle'}
|
||||
/>
|
||||
</div>
|
||||
<div className="field" data-cy={`input-and-label-column-name`}>
|
||||
<CodeHinter
|
||||
initialValue={item?.disabled?.value}
|
||||
theme={darkMode ? 'monokai' : 'default'}
|
||||
mode="javascript"
|
||||
lineNumbers={false}
|
||||
component={component}
|
||||
type={'fxEditor'}
|
||||
paramLabel={'Disable'}
|
||||
paramName={'disable'}
|
||||
onChange={(value) => handleLabelChange('disabled', { value }, index)}
|
||||
onFxPress={(active) => handleOnFxPress(active, index, 'disabled')}
|
||||
fxActive={item?.disabled?.fxActive}
|
||||
fieldMeta={{
|
||||
type: 'toggle',
|
||||
displayName: 'Make editable',
|
||||
}}
|
||||
paramType={'toggle'}
|
||||
/>
|
||||
</div>
|
||||
</Popover.Body>
|
||||
</Popover>
|
||||
);
|
||||
};
|
||||
const _renderOptions = () => {
|
||||
return (
|
||||
<List style={{ marginBottom: '20px' }}>
|
||||
<DragDropContext
|
||||
onDragEnd={(result) => {
|
||||
onDragEnd(result);
|
||||
}}
|
||||
>
|
||||
<Droppable droppableId="droppable">
|
||||
{({ innerRef, droppableProps, placeholder }) => (
|
||||
<div className="w-100" {...droppableProps} ref={innerRef}>
|
||||
{options?.map((item, index) => {
|
||||
return (
|
||||
<Draggable key={item.name} draggableId={item.name} index={index}>
|
||||
{(provided, snapshot) => (
|
||||
<div
|
||||
key={index}
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
style={getItemStyle(snapshot.isDragging, provided.draggableProps.style)}
|
||||
>
|
||||
<OverlayTrigger
|
||||
trigger="click"
|
||||
placement="left"
|
||||
rootClose
|
||||
overlay={_renderOverlay(item, index)}
|
||||
>
|
||||
<div key={item.name + item.id}>
|
||||
<ListGroup.Item
|
||||
style={{ marginBottom: '8px', backgroundColor: 'var(--slate3)' }}
|
||||
onMouseEnter={() => setHoveredOptionIndex(index)}
|
||||
onMouseLeave={() => setHoveredOptionIndex(null)}
|
||||
{...restProps}
|
||||
>
|
||||
<div className="row">
|
||||
<div className="col-auto d-flex align-items-center">
|
||||
<SortableList.DragHandle show />
|
||||
</div>
|
||||
<div className="col text-truncate cursor-pointer" style={{ padding: '0px' }}>
|
||||
{getResolvedValue(item.name)}
|
||||
</div>
|
||||
<div className="col-auto">
|
||||
{index === hoveredOptionIndex && (
|
||||
<ButtonSolid
|
||||
variant="danger"
|
||||
size="xs"
|
||||
className={'delete-icon-btn'}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleDeleteOption(index);
|
||||
}}
|
||||
>
|
||||
<span className="d-flex">
|
||||
<Trash fill={'var(--tomato9)'} width={12} />
|
||||
</span>
|
||||
</ButtonSolid>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</ListGroup.Item>
|
||||
</div>
|
||||
</OverlayTrigger>
|
||||
</div>
|
||||
)}
|
||||
</Draggable>
|
||||
);
|
||||
})}
|
||||
{placeholder}
|
||||
</div>
|
||||
)}
|
||||
</Droppable>
|
||||
</DragDropContext>
|
||||
<AddNewButton onClick={handleAddOption} dataCy="add-new-dropdown-option" className="mt-0">
|
||||
Add new option
|
||||
</AddNewButton>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
const isDynamicStepsEnabled = getResolvedValue(component?.component?.definition?.properties?.advanced?.value);
|
||||
useEffect(() => {
|
||||
setOptions(constructSteps());
|
||||
}, [component?.id, isDynamicStepsEnabled]);
|
||||
|
||||
const constructSteps = () => {
|
||||
try {
|
||||
let optionsValue = isDynamicOptionsEnabled
|
||||
? component?.component?.definition?.properties?.schema?.value
|
||||
: component?.component?.definition?.properties?.steps?.value;
|
||||
let options = [];
|
||||
|
||||
if (isDynamicOptionsEnabled || typeof optionsValue === 'string') {
|
||||
options = getResolvedValue(optionsValue);
|
||||
} else {
|
||||
options = optionsValue?.map((option) => option);
|
||||
}
|
||||
return options.map((option) => {
|
||||
const newOption = { ...option };
|
||||
|
||||
Object.keys(option).forEach((key) => {
|
||||
if (typeof option[key]?.value === 'boolean') {
|
||||
newOption[key]['value'] = `{{${option[key]?.value}}}`;
|
||||
}
|
||||
});
|
||||
|
||||
if (!('visible' in newOption)) {
|
||||
newOption['visible'] = { value: '{{true}}' };
|
||||
}
|
||||
return newOption;
|
||||
});
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
let items = [];
|
||||
|
||||
items.push({
|
||||
title: 'Steps',
|
||||
isOpen: true,
|
||||
children: (
|
||||
<>
|
||||
{properties
|
||||
.filter((property) => !optionsProperties.includes(property))
|
||||
?.map((property) => {
|
||||
if (property === 'steps') {
|
||||
return (
|
||||
<>
|
||||
{renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
'advanced',
|
||||
'properties',
|
||||
currentState,
|
||||
allComponents
|
||||
)}
|
||||
{isDynamicStepsEnabled
|
||||
? renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
'schema',
|
||||
'properties',
|
||||
currentState,
|
||||
allComponents
|
||||
)
|
||||
: _renderOptions()}
|
||||
</>
|
||||
);
|
||||
}
|
||||
// else if (property === 'variant') {
|
||||
// return renderTest(
|
||||
// component,
|
||||
// componentMeta,
|
||||
// paramUpdated,
|
||||
// dataQueries,
|
||||
// 'variant',
|
||||
// 'properties',
|
||||
// currentState,
|
||||
// allComponents,
|
||||
// handleLabelChange
|
||||
// );
|
||||
// }
|
||||
return renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
property,
|
||||
'properties',
|
||||
currentState,
|
||||
allComponents,
|
||||
darkMode
|
||||
);
|
||||
})}
|
||||
</>
|
||||
),
|
||||
});
|
||||
|
||||
items.push({
|
||||
title: 'Events',
|
||||
isOpen: true,
|
||||
children: (
|
||||
<EventManager
|
||||
sourceId={component?.id}
|
||||
eventSourceType="component"
|
||||
eventMetaDefinition={componentMeta}
|
||||
dataQueries={dataQueries}
|
||||
components={allComponents}
|
||||
eventsChanged={eventsChanged}
|
||||
apps={apps}
|
||||
darkMode={darkMode}
|
||||
pages={pages}
|
||||
/>
|
||||
),
|
||||
});
|
||||
items.push({
|
||||
title: `Additional Actions`,
|
||||
isOpen: true,
|
||||
children: additionalActions.map((property) => {
|
||||
return renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
property,
|
||||
'properties',
|
||||
currentState,
|
||||
allComponents,
|
||||
darkMode,
|
||||
componentMeta.properties?.[property]?.placeholder
|
||||
);
|
||||
}),
|
||||
});
|
||||
|
||||
items.push({
|
||||
title: 'Devices',
|
||||
isOpen: true,
|
||||
children: (
|
||||
<>
|
||||
{renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
layoutPropertyChanged,
|
||||
dataQueries,
|
||||
'showOnDesktop',
|
||||
'others',
|
||||
currentState,
|
||||
allComponents
|
||||
)}
|
||||
{renderElement(
|
||||
component,
|
||||
componentMeta,
|
||||
layoutPropertyChanged,
|
||||
dataQueries,
|
||||
'showOnMobile',
|
||||
'others',
|
||||
currentState,
|
||||
allComponents
|
||||
)}
|
||||
</>
|
||||
),
|
||||
});
|
||||
|
||||
return <Accordion items={items} />;
|
||||
}
|
||||
|
||||
function renderTest(...props) {
|
||||
const [
|
||||
component,
|
||||
componentMeta,
|
||||
paramUpdated,
|
||||
dataQueries,
|
||||
param,
|
||||
paramType,
|
||||
currentState,
|
||||
components = {},
|
||||
darkMode = false,
|
||||
placeholder = '',
|
||||
validationFn,
|
||||
] = props;
|
||||
const value = componentMeta?.definition?.properties?.variant?.value;
|
||||
return (
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<Switch
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
paramUpdated({ name: 'variant' }, 'value', e, 'properties', false, props);
|
||||
}}
|
||||
meta={{
|
||||
...componentMeta.properties[param],
|
||||
fullWidth: true,
|
||||
}}
|
||||
paramName={param}
|
||||
isIcon={false}
|
||||
component={component.component.definition.name}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -255,7 +255,7 @@ export const PropertiesTabElements = ({
|
|||
paramType="properties"
|
||||
/>
|
||||
</div>
|
||||
{resolveReferences(column?.isEditable) && (
|
||||
{(column?.fxActiveFields?.includes('isEditable') || resolveReferences(column?.isEditable)) && (
|
||||
<ValidationProperties
|
||||
column={column}
|
||||
index={index}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useState, useEffect, useContext } from 'react';
|
||||
import React, { useState, useEffect, useContext, useRef } from 'react';
|
||||
|
||||
import { ActionTypes } from '@/Editor/ActionTypes';
|
||||
import OverlayTrigger from 'react-bootstrap/OverlayTrigger';
|
||||
|
|
@ -32,6 +32,9 @@ import useStore from '@/AppBuilder/_stores/store';
|
|||
import { useEventActions, useEvents } from '@/AppBuilder/_stores/slices/eventsSlice';
|
||||
import ToggleGroup from '@/ToolJetUI/SwitchGroup/ToggleGroup';
|
||||
import ToggleGroupItem from '@/ToolJetUI/SwitchGroup/ToggleGroupItem';
|
||||
import usePopoverObserver from '@/AppBuilder/_hooks/usePopoverObserver';
|
||||
import SolidIcon from '@/_ui/Icon/SolidIcons';
|
||||
import { components as selectComponents } from 'react-select';
|
||||
|
||||
export const EventManager = ({
|
||||
sourceId,
|
||||
|
|
@ -82,6 +85,8 @@ export const EventManager = ({
|
|||
|
||||
const [events, setEvents] = useState([]);
|
||||
const [focusedEventIndex, setFocusedEventIndex] = useState(null);
|
||||
const lastFocusedEventIndex = useRef(null);
|
||||
const shouldSkipOnToggle = useRef(null);
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
|
@ -101,8 +106,23 @@ export const EventManager = ({
|
|||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [JSON.stringify(currentEvents)]);
|
||||
|
||||
let actionOptions = ActionTypes.map((action) => {
|
||||
return { name: action.name, value: action.id };
|
||||
let groupedOptions = ActionTypes.reduce((acc, action) => {
|
||||
const groupName = action.group;
|
||||
|
||||
if (!acc[groupName]) {
|
||||
acc[groupName] = [];
|
||||
}
|
||||
|
||||
acc[groupName].push({
|
||||
label: action.name,
|
||||
value: action.id,
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
let actionOptions = Object.keys(groupedOptions).map((groupName) => {
|
||||
return { label: groupName, options: groupedOptions[groupName] };
|
||||
});
|
||||
|
||||
let checkIfClicksAreInsideOf = document.querySelector('.cm-completionListIncompleteBottom');
|
||||
|
|
@ -124,6 +144,46 @@ export const EventManager = ({
|
|||
}),
|
||||
};
|
||||
|
||||
const actionStyles = {
|
||||
...styles,
|
||||
menuList: (base) => ({
|
||||
...base,
|
||||
padding: '8px 0 8px 8px',
|
||||
'&::-webkit-scrollbar': {
|
||||
width: '10px',
|
||||
},
|
||||
'&::-webkit-scrollbar-track': {
|
||||
background: 'transparent',
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb': {
|
||||
background: '#E4E7EB',
|
||||
border: '1px solid transparent',
|
||||
backgroundClip: 'content-box',
|
||||
},
|
||||
'&::-webkit-scrollbar-thumb:hover': {
|
||||
background: '#E4E7EB !important',
|
||||
border: '1px solid transparent !important',
|
||||
backgroundClip: 'content-box !important',
|
||||
},
|
||||
'&:hover': {
|
||||
'&::-webkit-scrollbar-thumb': {
|
||||
background: '#E4E7EB !important',
|
||||
border: '1px solid transparent !important',
|
||||
backgroundClip: 'content-box !important',
|
||||
},
|
||||
},
|
||||
}),
|
||||
group: (base) => ({
|
||||
...base,
|
||||
padding: 0,
|
||||
}),
|
||||
groupHeading: (base) => ({
|
||||
...base,
|
||||
margin: 0,
|
||||
padding: '0',
|
||||
}),
|
||||
};
|
||||
|
||||
const actionLookup = Object.fromEntries(ActionTypes.map((actionType) => [actionType.id, actionType]));
|
||||
|
||||
let alertTypes = [
|
||||
|
|
@ -394,6 +454,29 @@ export const EventManager = ({
|
|||
return defaultValue;
|
||||
};
|
||||
|
||||
const formatGroupLabel = (data) => {
|
||||
if (data.label === 'run-action') return;
|
||||
return (
|
||||
<div
|
||||
className="tw-border-x-0 tw-border-t-0 tw-border-b-[0.5px] tw-border-solid tw-my-[4px]"
|
||||
style={{ borderColor: 'var(--border-weak)' }}
|
||||
></div>
|
||||
);
|
||||
};
|
||||
|
||||
const CustomOption = (props) => {
|
||||
return (
|
||||
<selectComponents.Option {...props}>
|
||||
<div className="d-flex align-items-center">
|
||||
<div style={{ width: '16px', marginRight: '6px' }}>
|
||||
{props.isSelected && <SolidIcon name="tickv3" width="16px" height="16px" />}
|
||||
</div>
|
||||
<span>{props.label}</span>
|
||||
</div>
|
||||
</selectComponents.Option>
|
||||
);
|
||||
};
|
||||
|
||||
function eventPopover(event, index) {
|
||||
return (
|
||||
<Popover
|
||||
|
|
@ -433,13 +516,17 @@ export const EventManager = ({
|
|||
<Select
|
||||
className={`${darkMode ? 'select-search-dark' : 'select-search'} w-100`}
|
||||
options={actionOptions}
|
||||
value={event.actionId}
|
||||
value={actionOptions
|
||||
.flatMap((group) => group.options)
|
||||
.find((option) => option.value === event.actionId)}
|
||||
components={{ Option: CustomOption }}
|
||||
search={false}
|
||||
onChange={(value) => handlerChanged(index, 'actionId', value)}
|
||||
placeholder={t('globals.select', 'Select') + '...'}
|
||||
styles={styles}
|
||||
styles={actionStyles}
|
||||
useMenuPortal={false}
|
||||
useCustomStyles={true}
|
||||
formatGroupLabel={formatGroupLabel}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1006,10 +1093,21 @@ export const EventManager = ({
|
|||
placement={popoverPlacement || 'left'}
|
||||
rootClose={true}
|
||||
overlay={eventPopover(event.event, index)}
|
||||
onHide={() => setFocusedEventIndex(null)}
|
||||
onToggle={(showing) => {
|
||||
// If the toggle action should be skipped (e.g., due to a previous state change), reset the flag and exit early.
|
||||
if (shouldSkipOnToggle.current) {
|
||||
shouldSkipOnToggle.current = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// If there is already a focused event, set the skip flag to prevent unnecessary state updates.
|
||||
if (focusedEventIndex !== null && showing) {
|
||||
shouldSkipOnToggle.current = true;
|
||||
}
|
||||
|
||||
if (showing) {
|
||||
setFocusedEventIndex(index);
|
||||
lastFocusedEventIndex.current = index;
|
||||
} else {
|
||||
setFocusedEventIndex(null);
|
||||
}
|
||||
|
|
@ -1018,6 +1116,7 @@ export const EventManager = ({
|
|||
>
|
||||
<div
|
||||
key={index}
|
||||
id={`${sourceId}-${index}`}
|
||||
ref={provided.innerRef}
|
||||
{...provided.draggableProps}
|
||||
{...provided.dragHandleProps}
|
||||
|
|
@ -1061,6 +1160,17 @@ export const EventManager = ({
|
|||
);
|
||||
};
|
||||
|
||||
const shouldUsePopoverObserver = events.length !== 0 && eventSourceType === 'data_query';
|
||||
|
||||
usePopoverObserver(
|
||||
shouldUsePopoverObserver ? document.getElementsByClassName('query-details')[0] : null,
|
||||
document.getElementById(`${sourceId}-${lastFocusedEventIndex.current}`),
|
||||
document.getElementById('popover-basic'),
|
||||
focusedEventIndex !== null,
|
||||
() => (document.getElementById('popover-basic').style.display = 'block'),
|
||||
() => (document.getElementById('popover-basic').style.display = 'none')
|
||||
);
|
||||
|
||||
if (events.length === 0) {
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ import Inspect from '@/_ui/Icon/solidIcons/Inspect';
|
|||
import classNames from 'classnames';
|
||||
import { EMPTY_ARRAY } from '@/_stores/editorStore';
|
||||
import { Select } from './Components/Select';
|
||||
import { Steps } from './Components/Steps.jsx';
|
||||
import { deepClone } from '@/_helpers/utilities/utils.helpers';
|
||||
import useStore from '@/AppBuilder/_stores/store';
|
||||
// import { componentTypes } from '@/Editor/WidgetManager/components';
|
||||
|
|
@ -90,6 +91,7 @@ const NEW_REVAMPED_COMPONENTS = [
|
|||
'VerticalDivider',
|
||||
'ModalV2',
|
||||
'Link',
|
||||
'Steps',
|
||||
];
|
||||
|
||||
export const Inspector = ({ componentDefinitionChanged, darkMode, pages, selectedComponentId }) => {
|
||||
|
|
@ -539,8 +541,8 @@ export const Inspector = ({ componentDefinitionChanged, darkMode, pages, selecte
|
|||
componentMeta.displayName === 'Toggle Switch (Legacy)'
|
||||
? 'Toggle (Legacy)'
|
||||
: componentMeta.displayName === 'Toggle Switch'
|
||||
? 'Toggle Switch'
|
||||
: componentMeta.component,
|
||||
? 'Toggle Switch'
|
||||
: componentMeta.component,
|
||||
})}
|
||||
</small>
|
||||
</span>
|
||||
|
|
@ -740,6 +742,8 @@ const GetAccordion = React.memo(
|
|||
case 'DatePickerV2':
|
||||
case 'TimePicker':
|
||||
return <DatetimePickerV2 {...restProps} componentName={componentName} />;
|
||||
case 'Steps':
|
||||
return <Steps {...restProps} />;
|
||||
case 'PhoneInput':
|
||||
return <PhoneInput {...restProps} />;
|
||||
case 'CurrencyInput':
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ const NEW_WIDGETS = [
|
|||
'TimePicker',
|
||||
'ModalV2',
|
||||
'TextArea',
|
||||
'EmailInput',
|
||||
'PhoneInput',
|
||||
'CurrencyInput',
|
||||
];
|
||||
|
||||
export const WidgetBox = ({ component, darkMode }) => {
|
||||
|
|
|
|||
|
|
@ -168,6 +168,7 @@ export const Viewer = ({ id: appId, darkMode, moduleId = 'canvas', switchDarkMod
|
|||
showViewerNavigation={!isPagesSidebarHidden}
|
||||
handleAppEnvironmentChanged={handleAppEnvironmentChanged}
|
||||
changeToDarkMode={changeToDarkMode}
|
||||
switchPage={switchPage}
|
||||
/>
|
||||
)}
|
||||
<div className="sub-section">
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ export const containerConfig = {
|
|||
displayName: 'Container',
|
||||
description: 'Group components',
|
||||
defaultSize: {
|
||||
width: 10,
|
||||
height: 200,
|
||||
width: 13,
|
||||
height: 480,
|
||||
},
|
||||
component: 'Container',
|
||||
others: {
|
||||
|
|
|
|||
|
|
@ -4,25 +4,38 @@ export const stepsConfig = {
|
|||
description: 'Step-by-step navigation aid',
|
||||
component: 'Steps',
|
||||
properties: {
|
||||
variant: {
|
||||
type: 'switch',
|
||||
displayName: 'Variant',
|
||||
validation: { schema: { type: 'string' }, defaultValue: 'titles' },
|
||||
options: [
|
||||
{ displayName: 'Label', value: 'titles' },
|
||||
{ displayName: 'Number', value: 'numbers' },
|
||||
{ displayName: 'Plain', value: 'plain' },
|
||||
],
|
||||
accordian: 'label',
|
||||
},
|
||||
schema: {
|
||||
type: 'code',
|
||||
displayName: 'Schema',
|
||||
conditionallyRender: {
|
||||
key: 'advanced',
|
||||
value: true,
|
||||
},
|
||||
accordian: 'Options',
|
||||
},
|
||||
steps: {
|
||||
type: 'code',
|
||||
displayName: 'Steps',
|
||||
displayName: '',
|
||||
showLabel: false,
|
||||
validation: {
|
||||
schema: {
|
||||
type: 'array',
|
||||
element: { type: 'object', object: { id: { type: 'number' } } },
|
||||
element: { type: 'object' },
|
||||
},
|
||||
defaultValue: `[{ name: 'step 1'}, {name: 'step 2'}]`,
|
||||
},
|
||||
},
|
||||
currentStep: {
|
||||
type: 'code',
|
||||
displayName: 'Current step',
|
||||
validation: {
|
||||
schema: { type: 'number' },
|
||||
defaultValue: 1,
|
||||
},
|
||||
},
|
||||
stepsSelectable: {
|
||||
type: 'toggle',
|
||||
displayName: 'Steps selectable',
|
||||
|
|
@ -30,7 +43,38 @@ export const stepsConfig = {
|
|||
schema: { type: 'boolean' },
|
||||
defaultValue: false,
|
||||
},
|
||||
section: 'additionalActions',
|
||||
},
|
||||
disabledState: {
|
||||
type: 'toggle',
|
||||
displayName: 'Disable',
|
||||
validation: { schema: { type: 'boolean' } },
|
||||
section: 'additionalActions',
|
||||
},
|
||||
visibility: {
|
||||
type: 'toggle',
|
||||
displayName: 'Visibility',
|
||||
validation: { schema: { type: 'boolean' }, defaultValue: true },
|
||||
section: 'additionalActions',
|
||||
},
|
||||
advanced: {
|
||||
type: 'toggle',
|
||||
displayName: 'Dynamic options',
|
||||
validation: {
|
||||
schema: { type: 'boolean' },
|
||||
defaultValue: true,
|
||||
},
|
||||
accordian: 'Options',
|
||||
},
|
||||
currentStep: {
|
||||
type: 'code',
|
||||
displayName: 'Current step',
|
||||
validation: {
|
||||
schema: { type: 'number' },
|
||||
defaultValue: 1,
|
||||
},
|
||||
},
|
||||
|
||||
},
|
||||
defaultSize: {
|
||||
width: 22,
|
||||
|
|
@ -40,46 +84,126 @@ export const stepsConfig = {
|
|||
showOnDesktop: { type: 'toggle', displayName: 'Show on desktop' },
|
||||
showOnMobile: { type: 'toggle', displayName: 'Show on mobile' },
|
||||
},
|
||||
actions: [
|
||||
{
|
||||
handle: 'setStep',
|
||||
displayName: 'Set step',
|
||||
params: [
|
||||
{
|
||||
handle: 'option',
|
||||
displayName: 'Option',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
handle: 'setVisibility',
|
||||
displayName: 'Set visibility',
|
||||
params: [{ handle: 'visible', displayName: 'Value', defaultValue: '{{false}}', type: 'toggle' }],
|
||||
},
|
||||
{
|
||||
handle: 'setDisabled',
|
||||
displayName: 'Set disabled',
|
||||
params: [{ handle: 'disable', displayName: 'Value', defaultValue: '{{true}}', type: 'toggle' }],
|
||||
},
|
||||
{
|
||||
handle: 'resetSteps',
|
||||
displayName: 'Reset steps',
|
||||
params: [],
|
||||
},
|
||||
{
|
||||
handle: 'setStepVisible',
|
||||
displayName: 'Set step visible',
|
||||
params: [
|
||||
{
|
||||
handle: 'id',
|
||||
displayName: 'Step id',
|
||||
},
|
||||
{
|
||||
handle: 'visibility',
|
||||
displayName: 'visibility',
|
||||
defaultValue: '{{false}}',
|
||||
type: 'toggle',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
handle: 'setStepDisable',
|
||||
displayName: 'Set step disable',
|
||||
params: [
|
||||
{
|
||||
handle: 'id',
|
||||
displayName: 'Step id',
|
||||
},
|
||||
{
|
||||
handle: 'disabled',
|
||||
displayName: 'disabled',
|
||||
defaultValue: '{{true}}',
|
||||
type: 'toggle',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
events: {
|
||||
onSelect: { displayName: 'On select' },
|
||||
},
|
||||
styles: {
|
||||
color: {
|
||||
incompletedAccent: {
|
||||
type: 'colorSwatches',
|
||||
displayName: 'colorSwatches',
|
||||
displayName: 'Incompleted accent',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: '#CCD1D5',
|
||||
},
|
||||
accordian: 'steps',
|
||||
},
|
||||
incompletedLabel: {
|
||||
type: 'colorSwatches',
|
||||
displayName: 'Incompleted label',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: '#1B1F24',
|
||||
},
|
||||
accordian: 'steps',
|
||||
},
|
||||
completedAccent: {
|
||||
type: 'colorSwatches',
|
||||
displayName: 'Completed accent',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: 'var(--primary-brand)',
|
||||
},
|
||||
accordian: 'steps',
|
||||
},
|
||||
textColor: {
|
||||
completedLabel: {
|
||||
type: 'colorSwatches',
|
||||
displayName: 'Text color',
|
||||
displayName: 'Completed label',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: '#000000',
|
||||
defaultValue: '#1B1F24',
|
||||
},
|
||||
accordian: 'steps',
|
||||
},
|
||||
theme: {
|
||||
type: 'select',
|
||||
displayName: 'Theme',
|
||||
currentStepLabel: {
|
||||
type: 'colorSwatches',
|
||||
displayName: 'Current step label',
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: '#1B1F24',
|
||||
},
|
||||
accordian: 'steps',
|
||||
},
|
||||
padding: {
|
||||
type: 'switch',
|
||||
displayName: 'Padding',
|
||||
validation: {
|
||||
schema: { type: 'union', schemas: [{ type: 'string' }, { type: 'number' }] },
|
||||
defaultValue: 'default',
|
||||
},
|
||||
options: [
|
||||
{ name: 'titles', value: 'titles' },
|
||||
{ name: 'numbers', value: 'numbers' },
|
||||
{ name: 'plain', value: 'plain' },
|
||||
{ displayName: 'Default', value: 'default' },
|
||||
{ displayName: 'None', value: 'none' },
|
||||
],
|
||||
validation: {
|
||||
schema: { type: 'string' },
|
||||
defaultValue: 'titles',
|
||||
},
|
||||
},
|
||||
visibility: {
|
||||
type: 'toggle',
|
||||
displayName: 'Visibility',
|
||||
validation: {
|
||||
schema: { type: 'boolean' },
|
||||
defaultValue: true,
|
||||
},
|
||||
accordian: 'container',
|
||||
},
|
||||
},
|
||||
exposedVariables: {
|
||||
|
|
@ -92,17 +216,35 @@ export const stepsConfig = {
|
|||
},
|
||||
properties: {
|
||||
steps: {
|
||||
value: `{{ [{ name: 'step 1', tooltip: 'some tooltip', id: 1},{ name: 'step 2', tooltip: 'some tooltip', id: 2},{ name: 'step 3', tooltip: 'some tooltip', id: 3},{ name: 'step 4', tooltip: 'some tooltip', id: 4},{ name: 'step 5', tooltip: 'some tooltip', id: 5}]}}`,
|
||||
value: [
|
||||
{ name: 'step 1', tooltip: '', id: 1, visible: { value: true }, disabled: { value: false } },
|
||||
{ name: 'step 2', tooltip: '', id: 2, visible: { value: true }, disabled: { value: false } },
|
||||
{ name: 'step 3', tooltip: '', id: 3, visible: { value: true }, disabled: { value: false } },
|
||||
{ name: 'step 4', tooltip: '', id: 4, visible: { value: true }, disabled: { value: false } },
|
||||
{ name: 'step 5', tooltip: '', id: 5, visible: { value: true }, disabled: { value: false } },
|
||||
],
|
||||
},
|
||||
schema: {
|
||||
value: `{{ [{ name: 'step 1', tooltip: '', id: 1,visible: true, disabled: false},{ name: 'step 2', tooltip: '', id: 2,visible: true, disabled: false},{ name: 'step 3', tooltip: '', id: 3,visible: true, disabled: false},{ name: 'step 4', tooltip: '', id: 4,visible: true, disabled: false},{ name: 'step 5', tooltip: '', id: 5,visible: true, disabled: false}]}}`,
|
||||
},
|
||||
disabledState: { value: '{{false}}' },
|
||||
variant: { value: 'titles' },
|
||||
currentStep: { value: '{{3}}' },
|
||||
stepsSelectable: { value: true },
|
||||
advanced: { value: `{{false}}` },
|
||||
visibility: { value: '{{true}}' },
|
||||
},
|
||||
events: [],
|
||||
styles: {
|
||||
visibility: { value: '{{true}}' },
|
||||
theme: { value: 'titles' },
|
||||
color: { value: 'var(--primary-brand)' },
|
||||
textColor: { value: '' },
|
||||
// color: { value: '' },
|
||||
// textColor: { value: '' },
|
||||
padding: { value: 'default' },
|
||||
incompletedAccent: { value: '#E4E7EB' },
|
||||
incompletedLabel: { value: '#1B1F24' },
|
||||
completedAccent: { value: 'var(--primary-brand)' },
|
||||
completedLabel: { value: '#1B1F24' },
|
||||
currentStepLabel: { value: '#1B1F24' },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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(() => {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue