diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index cc4c36825e..f27e6c04eb 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -4,15 +4,11 @@ on:
push:
branches: [develop, main]
pull_request:
- types: [labeled, opened, synchronize, reopened]
+ types: [labeled, unlabeled, closed]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
-concurrency:
- group: ${{ github.workflow }}-${{ (github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main') && github.run_number || github.ref }}
- cancel-in-progress: true
-
env:
FORCE_COLOR: true
NODE_OPTIONS: "--max-old-space-size=4096"
@@ -25,75 +21,287 @@ env:
PG_PASS: postgres
PG_DB: tooljet_test
-# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
build:
runs-on: ubuntu-latest
- if: |
- contains(github.event.pull_request.labels.*.name, 'run-ci') ||
- github.ref == 'refs/heads/main' ||
- github.ref == 'refs/heads/develop'
+ if: ${{ github.event.action == 'labeled' && github.event.label.name == 'run-ci' }}
steps:
- # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- - uses: actions/checkout@v3
+ - name: Checkout repository
+ uses: actions/checkout@v3
- name: Use Node.js 18.18.2
uses: actions/setup-node@v3
with:
node-version: 18.18.2
- - name: Cache node modules
+ # Cache server node modules to speed up subsequent builds
+ - name: Cache server node modules
uses: actions/cache@v3
- env:
- cache-name: cache-node-modules
with:
- # npm cache files are stored in `~/.npm` on Linux/macOS
- path: ~/.npm
- key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
+ path: server/node_modules
+ key: ${{ runner.os }}-node-server-${{ hashFiles('server/package-lock.json') }}
restore-keys: |
- ${{ runner.os }}-build-${{ env.cache-name }}-
- ${{ runner.os }}-build-
- ${{ runner.os }}-
+ ${{ runner.os }}-node-server-
- - run: npm run build
+ # Cache frontend node modules to speed up subsequent builds
+ - name: Cache frontend node modules
+ uses: actions/cache@v3
+ with:
+ path: frontend/node_modules
+ key: ${{ runner.os }}-node-frontend-${{ hashFiles('frontend/package-lock.json') }}
+ restore-keys: |
+ ${{ runner.os }}-node-frontend-
- lint:
+ # Cache plugins node modules to speed up subsequent builds
+ - name: Cache plugins node modules
+ uses: actions/cache@v3
+ with:
+ path: plugins/node_modules
+ key: ${{ runner.os }}-node-plugins-${{ hashFiles('plugins/package-lock.json') }}
+ restore-keys: |
+ ${{ runner.os }}-node-plugins-
+
+ - name: Setup Python 3
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.x
+
+ - name: Install Python dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install setuptools
+
+ - name: Install Node.js dependencies
+ run: npm ci
+
+ - name: Build
+ run: npm run build
+
+ - name: Frontend ci
+ run: |
+ npm --prefix frontend ci
+
+ - name: Server ci
+ run: npm --prefix server ci
+
+ - name: plugins ci
+ run: npm --prefix plugins ci
+
+ # Upload plugins build artifacts
+ - name: Archive specific plugins files and folders
+ uses: actions/upload-artifact@v4
+ with:
+ name: plugins-files
+ path: |
+ plugins/dist
+ plugins/client.js
+ plugins/node_modules
+ plugins/packages/common
+ plugins/package.json
+
+ # Upload server build artifacts
+ - name: Archive specific server files and folders
+ uses: actions/upload-artifact@v4
+ with:
+ name: server-files
+ path: |
+ server/dist
+
+ # Upload frontend build artifacts
+ - name: Archive specific frontend files and folders
+ uses: actions/upload-artifact@v4
+ with:
+ name: frontend-files
+ path: |
+ frontend/build
+
+ lint-for-plugins:
runs-on: ubuntu-latest
- if: |
- contains(github.event.pull_request.labels.*.name, 'run-ci') ||
- github.ref == 'refs/heads/main' ||
- github.ref == 'refs/heads/develop'
+ needs: build
+
steps:
- # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- - uses: actions/checkout@v3
+ - name: Checkout repository
+ uses: actions/checkout@v3
- name: Use Node.js 18.18.2
uses: actions/setup-node@v3
with:
node-version: 18.18.2
- - name: Cache node modules
+ # Cache server node modules to speed up subsequent builds
+ - name: Cache server node modules
uses: actions/cache@v3
- env:
- cache-name: cache-node-modules
with:
- # npm cache files are stored in `~/.npm` on Linux/macOS
- path: ~/.npm
- key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
+ path: server/node_modules
+ key: ${{ runner.os }}-node-server-${{ hashFiles('server/package-lock.json') }}
restore-keys: |
- ${{ runner.os }}-build-${{ env.cache-name }}-
- ${{ runner.os }}-build-
- ${{ runner.os }}-
+ ${{ runner.os }}-node-server-
- - run: npm run build:plugins
- - run: npm --prefix frontend ci && npm --prefix server ci && npm --prefix plugins ci
- - run: npm --prefix server run lint && npm --prefix frontend run lint && npm --prefix plugins run lint
+ # Cache frontend node modules to speed up subsequent builds
+ - name: Cache frontend node modules
+ uses: actions/cache@v3
+ with:
+ path: frontend/node_modules
+ key: ${{ runner.os }}-node-frontend-${{ hashFiles('frontend/package-lock.json') }}
+ restore-keys: |
+ ${{ runner.os }}-node-frontend-
+
+ # Cache plugins node modules to speed up subsequent builds
+ - name: Cache plugins node modules
+ uses: actions/cache@v3
+ with:
+ path: plugins/node_modules
+ key: ${{ runner.os }}-node-plugins-${{ hashFiles('plugins/package-lock.json') }}
+ restore-keys: |
+ ${{ runner.os }}-node-plugins-
+
+ - name: Setup Python 3
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.x
+
+ - name: Install Python dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install setuptools
+
+ # Download plugins build artifacts
+ - name: Download plugins files and folders
+ uses: actions/download-artifact@v4
+ with:
+ name: plugins-files
+
+ - name: Running for plugins
+ run: |
+ npm --prefix plugins run lint
+
+
+ lint-for-frontend:
+ runs-on: ubuntu-latest
+ needs: build
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Use Node.js 18.18.2
+ uses: actions/setup-node@v3
+ with:
+ node-version: 18.18.2
+
+ # Cache server node modules to speed up subsequent builds
+ - name: Cache server node modules
+ uses: actions/cache@v3
+ with:
+ path: server/node_modules
+ key: ${{ runner.os }}-node-server-${{ hashFiles('server/package-lock.json') }}
+ restore-keys: |
+ ${{ runner.os }}-node-server-
+
+ # Cache frontend node modules to speed up subsequent builds
+ - name: Cache frontend node modules
+ uses: actions/cache@v3
+ with:
+ path: frontend/node_modules
+ key: ${{ runner.os }}-node-frontend-${{ hashFiles('frontend/package-lock.json') }}
+ restore-keys: |
+ ${{ runner.os }}-node-frontend-
+
+ # Cache plugins node modules to speed up subsequent builds
+ - name: Cache plugins node modules
+ uses: actions/cache@v3
+ with:
+ path: plugins/node_modules
+ key: ${{ runner.os }}-node-plugins-${{ hashFiles('plugins/package-lock.json') }}
+ restore-keys: |
+ ${{ runner.os }}-node-plugins-
+
+ - name: Setup Python 3
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.x
+
+ - name: Install Python dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install setuptools
+
+ # Download frontend build artifacts
+ - name: Download frontend files and folders
+ uses: actions/download-artifact@v4
+ with:
+ name: frontend-files
+
+ - name: Running for frontend
+ run: |
+ npm --prefix frontend run lint
+
+ lint-for-server:
+ runs-on: ubuntu-latest
+ needs: build
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+
+ - name: Use Node.js 18.18.2
+ uses: actions/setup-node@v3
+ with:
+ node-version: 18.18.2
+
+ # Cache server node modules to speed up subsequent builds
+ - name: Cache server node modules
+ uses: actions/cache@v3
+ with:
+ path: server/node_modules
+ key: ${{ runner.os }}-node-server-${{ hashFiles('server/package-lock.json') }}
+ restore-keys: |
+ ${{ runner.os }}-node-server-
+
+ # Cache frontend node modules to speed up subsequent builds
+ - name: Cache frontend node modules
+ uses: actions/cache@v3
+ with:
+ path: frontend/node_modules
+ key: ${{ runner.os }}-node-frontend-${{ hashFiles('frontend/package-lock.json') }}
+ restore-keys: |
+ ${{ runner.os }}-node-frontend-
+
+ # Cache plugins node modules to speed up subsequent builds
+ - name: Cache plugins node modules
+ uses: actions/cache@v3
+ with:
+ path: plugins/node_modules
+ key: ${{ runner.os }}-node-plugins-${{ hashFiles('plugins/package-lock.json') }}
+ restore-keys: |
+ ${{ runner.os }}-node-plugins-
+
+ - name: Setup Python 3
+ uses: actions/setup-python@v2
+ with:
+ python-version: 3.x
+
+ - name: Install Python dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install setuptools
+
+ # Download server build artifacts
+ - name: Download server files and folders
+ uses: actions/download-artifact@v4
+ with:
+ name: server-files
+
+ - name: Running for server
+ run: |
+ npm --prefix server run lint
unit-test:
runs-on: ubuntu-latest
+ timeout-minutes: 30 # Set a timeout of 30 minutes
needs: build
- container: node:18.18.2-buster
+ container: node:18.18.2-bullseye
services:
postgres:
image: postgres
@@ -119,7 +327,7 @@ jobs:
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- - run: apt update && apt install -y postgresql
+ - run: apt update && apt install -y postgresql-client
- run: npm --prefix plugins ci
- run: npm --prefix plugins run create:client && npm --prefix plugins run create:server
- run: npm --prefix plugins run build:packages && npm --prefix plugins run build:server
@@ -130,8 +338,9 @@ jobs:
e2e-test:
runs-on: ubuntu-latest
+ timeout-minutes: 30 # Set a timeout of 30 minutes
needs: build
- container: node:18.18.2-buster
+ container: node:18.18.2-bullseye
services:
postgres:
image: postgres
@@ -157,11 +366,11 @@ jobs:
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- - run: apt update && apt install -y postgresql
+ - run: apt update && apt install -y postgresql-client
- run: npm --prefix plugins ci
- run: npm --prefix plugins run create:client && npm --prefix plugins run create:server
- run: npm --prefix plugins run build:packages && npm --prefix plugins run build:server
- run: npm --prefix server ci
- run: npm --prefix server run db:create
- run: npm --prefix server run db:migrate
- - run: NODE_OPTIONS=--max_old_space_size=8096 npm --prefix server run test:e2e -- --silent --testTimeout=20000
+ - run: NODE_OPTIONS=--max_old_space_size=8096 npm --prefix server run test:e2e -- --silent --testTimeout=20000
\ No newline at end of file
diff --git a/.github/workflows/update-test-system.yml b/.github/workflows/update-test-system.yml
index a33980956b..94bba1b3e9 100644
--- a/.github/workflows/update-test-system.yml
+++ b/.github/workflows/update-test-system.yml
@@ -97,11 +97,11 @@ jobs:
# Remove the existing tooljet/* images
sudo docker images -a | grep 'tooljet/' | awk '{print $3}' | xargs sudo docker rmi -f
- #checking images
+ # Check remaining images
sudo docker images
- # Update docker-compose.yml with the new image
- sed -i '/^[[:space:]]*tooljet:/,/^$/ s|^\([[:space:]]*image:[[:space:]]*\).*|\1tooljet/tj-osv:${{ env.SAFE_BRANCH_NAME }}|' docker-compose.yaml
+ # Update docker-compose.yml with the new image for tooljet service
+ sed -i '/^[[:space:]]*tooljet:/,/^[[:space:]]*[^:]*$/ { /^[[:space:]]*image:[[:space:]]*tooljet\/tj-osv/s|\(image:[[:space:]]*\).*|\1tooljet/tj-osv:'"${{ env.SAFE_BRANCH_NAME }}"'| }' docker-compose.yml
# Start the Docker containers
cat docker-compose.yaml
diff --git a/.version b/.version
index 6679a60439..6eba18ef8f 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-2.61.1
+2.61.3
diff --git a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/app-version/version.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/app-version/version.cy.js
index f4495f50e7..a4dc305d73 100644
--- a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/app-version/version.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/app-version/version.cy.js
@@ -71,13 +71,13 @@ describe("App Version Functionality", () => {
cy.openApp()
cy.get('[data-cy="widget-list-box-table"]').should("be.visible");
- cy.dragAndDropWidget("Toggle Switch", 50, 50);
- verifyComponent("toggleswitch1");
+ cy.dragAndDropWidget("Text", 50, 50);
+ verifyComponent("text1");
navigateToCreateNewVersionModal((currentVersion = "v1"));
createNewVersion((newVersion = ["v2"]), (versionFrom = "v1"));
- verifyComponent("toggleswitch1");
- deleteComponentAndVerify("toggleswitch1");
+ verifyComponent("text1");
+ deleteComponentAndVerify("text1");
cy.dragAndDropWidget(buttonText.defaultWidgetText);
verifyComponent("button1");
@@ -87,7 +87,7 @@ describe("App Version Functionality", () => {
navigateToCreateNewVersionModal((currentVersion = "v3"));
createNewVersion((newVersion = ["v4"]), (versionFrom = "v1"));
- verifyComponent("toggleswitch1");
+ verifyComponent("text1");
editVersionAndVerify(
(currentVersion = "v4"),
diff --git a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/manageSSO.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/manageSSO.cy.js
index cae0a6e2b2..95f4ad6304 100644
--- a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/manageSSO.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/manageSSO.cy.js
@@ -12,9 +12,13 @@ describe("Manage SSO for multi workspace", () => {
beforeEach(() => {
cy.defaultWorkspaceLogin();
SSO.deleteOrganisationSSO("My workspace", ["google", "git"]);
+ SSO.resetDomain();
+ });
+ after(() => {
+ cy.defaultWorkspaceLogin();
+ SSO.resetDomain();
});
it("Should verify General settings page elements", () => {
- SSO.resetDomain();
SSO.setSignupStatus(false);
SSO.defaultSSO(true);
diff --git a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/workspaceConstants.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/workspaceConstants.cy.js
index 8b83328ae8..2d480d4a2b 100644
--- a/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/workspaceConstants.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/platform/ceTestcases/workspace/workspaceConstants.cy.js
@@ -290,54 +290,38 @@ describe("Workspace constants", () => {
cy.apiCreateApp(data.appName);
- cy.getCookie("tj_auth_token").then((cookie) => {
- const headers = {
- "Tj-Workspace-Id": Cypress.env("workspaceId"),
- Cookie: `tj_auth_token=${cookie.value}`,
- };
- cy.request({
- method: "GET",
- url: `http://localhost:3000/api/app-environments/versions?app_id=${Cypress.env(
- "appId"
- )}`,
- headers: headers,
- }).then((response) => {
- const appVersions = response.body.appVersions;
- const appVersionId = appVersions[0].id;
- createDataQuery(
- appVersionId,
- data.restapilink,
- data.restapiHeaderKey,
- data.restapiHeaderValue
- );
- });
- });
-
+ cy.wait(1000);
+ createDataQuery(
+ data.appName,
+ data.restapilink,
+ data.restapiHeaderKey,
+ data.restapiHeaderValue
+ );
cy.openApp();
cy.get(".custom-toggle-switch>.switch>").eq(3).click();
cy.waitForAutoSave();
- cy.dragAndDropWidget("Text", 550, 650);
+ cy.dragAndDropWidget("Text Input", 550, 650);
editAndVerifyWidgetName(data.widgetName, []);
cy.waitForAutoSave();
cy.get(
- '[data-cy="textcomponenttextinput-input-field"]'
+ '[data-cy="default-value-input-field"]'
).clearAndTypeOnCodeMirror(`{{queries.restapi1.data.message`);
cy.forceClickOnCanvas();
cy.waitForAutoSave();
cy.get(dataSourceSelector.queryCreateAndRunButton).click();
cy.get(
commonWidgetSelector.draggableWidget(data.widgetName)
- ).verifyVisibleElement("have.text", "Production environment testing");
+ ).verifyVisibleElement("have.value", "Production environment testing");
cy.openInCurrentTab(commonWidgetSelector.previewButton);
cy.wait(4000);
cy.get(
commonWidgetSelector.draggableWidget(data.widgetName)
- ).verifyVisibleElement("have.text", "Production environment testing");
+ ).verifyVisibleElement("have.value", "Production environment testing");
});
it("should verify the constants resolving in datasource connection form", () => {
data.ds = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
diff --git a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/bulkUserUpload.cy.js b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/bulkUserUpload.cy.js
index dea24b3615..f42a82943b 100644
--- a/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/bulkUserUpload.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/platform/commonTestcases/workspace/bulkUserUpload.cy.js
@@ -143,10 +143,6 @@ describe("Bulk user upload", () => {
});
cy.get(usersSelector.buttonUploadUsers).click();
cy.wait(10000);
- cy.get(".go2072408551")
- .should("be.visible")
- .and("have.text", "250 users are being added");
- cy.wait(1000);
common.searchUser("test12@gmail.com");
cy.contains("td", "test12@gmail.com")
.parent()
diff --git a/cypress-tests/cypress/support/utils/dataSource.js b/cypress-tests/cypress/support/utils/dataSource.js
index 2f5605a241..a79f968b9c 100644
--- a/cypress-tests/cypress/support/utils/dataSource.js
+++ b/cypress-tests/cypress/support/utils/dataSource.js
@@ -159,36 +159,52 @@ export const selectDatasource = (datasourceName) => {
cy.get(`[data-cy="${cyParamName(datasourceName)}-button"]`).click();
};
-export const createDataQuery = (versionId, url, key, value) => {
- cy.getCookie("tj_auth_token").then((cookie) => {
- const headers = {
- "Tj-Workspace-Id": Cypress.env("workspaceId"),
- Cookie: `tj_auth_token=${cookie.value}`,
- };
- cy.request({
- method: "POST",
- url: "http://localhost:3000/api/data_queries",
- headers: headers,
- body: {
- app_id: Cypress.env("appId"),
- app_version_id: versionId,
- name: "restapi1",
- kind: "restapi",
- options: {
- method: "get",
- url: `{{constants.${url}}}`,
- url_params: [["", ""]],
- headers: [[`{{constants.${key}}}`, `{{constants.${value}}}`]],
- body: [["", ""]],
- json_body: null,
- body_toggle: false,
- transformationLanguage: "javascript",
- enableTransformation: false,
- },
- data_source_id: null,
- },
- }).then((response) => {
- expect(response.status).to.equal(201);
+export const createDataQuery = (appName, url, key, value) => {
+ let appId, versionId;
+ cy.task("updateId", {
+ dbconfig: Cypress.env("app_db"),
+ sql: `select id from apps where name='${appName}';`,
+ }).then((resp) => {
+ appId = resp.rows[0].id;
+
+ cy.task("updateId", {
+ dbconfig: Cypress.env("app_db"),
+ sql: `select id from app_versions where app_id='${appId}';`,
+ }).then((resp) => {
+ versionId = resp.rows[0].id;
+
+ cy.getCookie("tj_auth_token").then((cookie) => {
+ const headers = {
+ "Tj-Workspace-Id": Cypress.env("workspaceId"),
+ Cookie: `tj_auth_token=${cookie.value}`,
+ };
+
+ cy.request({
+ method: "POST",
+ url: "http://localhost:3000/api/data_queries",
+ headers: headers,
+ body: {
+ app_id: appId,
+ app_version_id: versionId,
+ name: "restapi1",
+ kind: "restapi",
+ options: {
+ method: "get",
+ url: `{{constants.${url}}}`,
+ url_params: [["", ""]],
+ headers: [[`{{constants.${key}}}`, `{{constants.${value}}}`]],
+ body: [["", ""]],
+ json_body: null,
+ body_toggle: false,
+ transformationLanguage: "javascript",
+ enableTransformation: false,
+ },
+ data_source_id: null,
+ },
+ }).then((response) => {
+ expect(response.status).to.equal(201);
+ });
+ });
});
});
};
diff --git a/frontend/.version b/frontend/.version
index 6679a60439..6eba18ef8f 100644
--- a/frontend/.version
+++ b/frontend/.version
@@ -1 +1 @@
-2.61.1
+2.61.3
diff --git a/frontend/src/Editor/Components/Modal.jsx b/frontend/src/Editor/Components/Modal.jsx
index 3c33021889..e9bddfab21 100644
--- a/frontend/src/Editor/Components/Modal.jsx
+++ b/frontend/src/Editor/Components/Modal.jsx
@@ -87,7 +87,7 @@ export const Modal = function Modal({
return;
}
const canShowModal = exposedVariables.show ?? false;
- fireEvent(canShowModal ? 'onOpen' : 'onClose');
+ fireEvent(!canShowModal && 'onClose');
setShowModal(exposedVariables.show ?? false);
const inputRef = document?.getElementsByClassName('tj-text-input-widget')?.[0];
inputRef?.blur();
@@ -223,6 +223,7 @@ export const Modal = function Modal({
event.stopPropagation();
setShowModal(true);
setExposedVariable('show', true);
+ fireEvent('onOpen');
}}
data-cy={`${dataCy}-launch-button`}
>
diff --git a/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/DropDownSelect.jsx b/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/DropDownSelect.jsx
index f28f31d9da..3c6d913528 100644
--- a/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/DropDownSelect.jsx
+++ b/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/DropDownSelect.jsx
@@ -242,7 +242,7 @@ const DropDownSelect = ({
style={{
position: 'relative',
left: '-10px',
- top: selected.label === null || selected.label === undefined ? '0px' : '2px',
+ top: selected.label === null || selected.label === undefined || isCellEdit ? '0px' : '2px',
paddingLeft: '10px',
paddingBottom: '4px',
}}
@@ -251,10 +251,11 @@ const DropDownSelect = ({
setShowMenu(true);
}}
>
-
{selected.label === null || selected.label === undefined ? 'Null' : selected.label}
-
+
) : (
diff --git a/frontend/src/TooljetDatabase/Table/styles.scss b/frontend/src/TooljetDatabase/Table/styles.scss
index 0e366a07ab..dd19ec7f4d 100644
--- a/frontend/src/TooljetDatabase/Table/styles.scss
+++ b/frontend/src/TooljetDatabase/Table/styles.scss
@@ -171,15 +171,15 @@
div {
padding-bottom: 0px !important;
- span {
- white-space: nowrap;
- overflow: hidden;
- text-overflow: ellipsis;
- width: 85%;
+ .cell-menu-text {
+ white-space: nowrap ;
+ overflow: hidden ;
+ text-overflow: ellipsis ;
+ width: 85% ;
color: var(--slate11) !important;
cursor: default;
border-radius: 6px;
- height: 20px;
+ // height: 20px;
font-size: 12px;
}
}
@@ -190,7 +190,7 @@
.tjdb-td-wrapper {
div {
padding-bottom: 0px !important;
- span {
+ .cell-menu-text {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
@@ -198,7 +198,7 @@
color: var(--slate11) !important;
cursor: default;
border-radius: 6px;
- height: 20px;
+ // height: 20px;
font-size: 12px;
}
}
diff --git a/frontend/src/_ui/AppButton/AppButton.scss b/frontend/src/_ui/AppButton/AppButton.scss
index c30b7e49b5..2fee7d996e 100644
--- a/frontend/src/_ui/AppButton/AppButton.scss
+++ b/frontend/src/_ui/AppButton/AppButton.scss
@@ -7,7 +7,7 @@
justify-content: center;
align-items: center;
padding: 10px 20px;
- gap: 6px;
+ gap: 8px;
border-radius: 6px;
font-weight: 600;
text-decoration: none;
@@ -51,7 +51,7 @@
.tj-small-btn {
height: 28px;
border-radius: 6px;
- padding: 4px 0px;
+ padding: 4px 16px 4px 16px;
font-size: 12px;
}
@@ -198,7 +198,6 @@
color: var(--indigo9);
border: none;
background: transparent;
- box-shadow: none !important;
&:hover {
color: var(--indigo10);
@@ -214,6 +213,15 @@
background: var(--base);
border: none;
outline: none;
+ box-shadow: 0px 0px 0px 4px var(--indigo6);
+ }
+
+ &:focus {
+ box-shadow: 0px 0px 0px 4px var(--indigo6);
+ }
+
+ &:focus:not(:focus-visible) {
+ box-shadow: none;
}
}
diff --git a/plugins/package-lock.json b/plugins/package-lock.json
index 6119977e79..f0cb5d4650 100644
--- a/plugins/package-lock.json
+++ b/plugins/package-lock.json
@@ -9573,14 +9573,15 @@
"license": "MIT"
},
"node_modules/follow-redirects": {
- "version": "1.15.3",
+ "version": "1.15.6",
+ "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
+ "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
"funding": [
{
"type": "individual",
"url": "https://github.com/sponsors/RubenVerborgh"
}
],
- "license": "MIT",
"engines": {
"node": ">=4.0"
},
@@ -13874,11 +13875,11 @@
}
},
"node_modules/node-appwrite": {
- "version": "5.1.0",
- "license": "BSD-3-Clause",
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-13.0.0.tgz",
+ "integrity": "sha512-O1g1RWcRYcvnVvR0Dykjoe/4UcL6ELVvZosb4B8/hhNvleuWfN2fOFxzeCaU8amGW6UtVtZqvNaUT3IF1mPBdA==",
"dependencies": {
- "axios": "^0.26.1",
- "form-data": "^4.0.0"
+ "node-fetch-native-with-agent": "1.7.2"
}
},
"node_modules/node-fetch": {
@@ -13899,6 +13900,11 @@
}
}
},
+ "node_modules/node-fetch-native-with-agent": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/node-fetch-native-with-agent/-/node-fetch-native-with-agent-1.7.2.tgz",
+ "integrity": "sha512-5MaOOCuJEvcckoz7/tjdx1M6OusOY6Xc5f459IaruGStWnKzlI1qpNgaAwmn4LmFYcsSlj+jBMk84wmmRxfk5g=="
+ },
"node_modules/node-fetch-npm": {
"version": "2.0.4",
"license": "MIT",
@@ -19944,7 +19950,7 @@
"version": "1.0.0",
"dependencies": {
"@tooljet-plugins/common": "file:../common",
- "node-appwrite": "^5.1.0",
+ "node-appwrite": "^13.0.0",
"react": "^17.0.2"
}
},
diff --git a/plugins/packages/appwrite/lib/index.ts b/plugins/packages/appwrite/lib/index.ts
index 6a1bad8ced..224ccbaa42 100644
--- a/plugins/packages/appwrite/lib/index.ts
+++ b/plugins/packages/appwrite/lib/index.ts
@@ -1,21 +1,27 @@
import { QueryError, QueryResult, QueryService, ConnectionTestResult } from '@tooljet-plugins/common';
-import { SourceOptions, QueryOptions } from './types';
+import { SourceOptions, QueryOptions, ReturnObject } from './types';
import sdk from 'node-appwrite';
-import { bulkUpdate, createDocument, deleteDocument, getDocument, queryCollection, updateDocument } from './operations';
+import { createDocument, deleteDocument, getDocument, queryCollection, updateDocument } from './operations';
const JSON5 = require('json5');
export default class Appwrite implements QueryService {
async run(sourceOptions: SourceOptions, queryOptions: QueryOptions, dataSourceId: string): Promise
{
const database = await this.getConnection(sourceOptions);
+ const { database_id } = sourceOptions;
const operation = queryOptions.operation;
const body = this.returnObject(queryOptions.body);
+ const isBodyEmpty = !Object.keys(body).length;
let result = {};
try {
+ if (!queryOptions.collectionId) {
+ throw new Error('Collection id is required.');
+ }
switch (operation) {
case 'list_docs':
result = await queryCollection(
database,
+ database_id,
queryOptions.collectionId,
queryOptions.limit,
queryOptions.order_fields,
@@ -26,24 +32,27 @@ export default class Appwrite implements QueryService {
);
break;
case 'get_document':
- result = await getDocument(database, queryOptions.collectionId, queryOptions.documentId);
+ if (!queryOptions.documentId) throw new Error('Document id is required');
+ result = await getDocument(database, database_id, queryOptions.collectionId, queryOptions.documentId);
break;
case 'add_document':
- result = await createDocument(database, queryOptions.collectionId, body);
+ if (isBodyEmpty) throw new Error('Body is required');
+ result = await createDocument(database, database_id, queryOptions.collectionId, body as object);
break;
case 'update_document':
- result = await updateDocument(database, queryOptions.collectionId, queryOptions.documentId, body);
+ if (!queryOptions.documentId) throw new Error('Document id is required');
+ if (isBodyEmpty) throw new Error('Body is required');
+ result = await updateDocument(
+ database,
+ database_id,
+ queryOptions.collectionId,
+ queryOptions.documentId,
+ body as object
+ );
break;
case 'delete_document':
- result = await deleteDocument(database, queryOptions.collectionId, queryOptions.documentId);
- break;
- case 'bulk_update':
- result = await bulkUpdate(
- database,
- queryOptions.collectionId,
- this.returnObject(queryOptions.records),
- queryOptions['document_id_key']
- );
+ if (!queryOptions.documentId) throw new Error('Document id is required');
+ result = await deleteDocument(database, database_id, queryOptions.collectionId, queryOptions.documentId);
break;
}
} catch (error) {
@@ -56,14 +65,14 @@ export default class Appwrite implements QueryService {
};
}
- private returnObject(data: any) {
+ returnObject(data: ReturnObject | string): ReturnObject {
if (!data) {
return {};
}
return typeof data === 'string' ? JSON5.parse(data) : data;
}
- async getConnection(sourceOptions: SourceOptions, _options?: object): Promise {
+ async getConnection(sourceOptions: SourceOptions, _options?: object): Promise {
const { host, secret_key, project_id } = sourceOptions;
const client = new sdk.Client();
@@ -72,20 +81,20 @@ export default class Appwrite implements QueryService {
.setProject(project_id) // Your project ID
.setKey(secret_key); // Your secret API key;
- return new sdk.Database(client);
+ return new sdk.Databases(client);
}
async testConnection(sourceOptions: SourceOptions): Promise {
const databaseClient = await this.getConnection(sourceOptions);
- if (!databaseClient) {
- throw new Error('Invalid credentials');
+ try {
+ await databaseClient.listCollections(sourceOptions.database_id);
+
+ return {
+ status: 'ok',
+ };
+ } catch (err) {
+ throw new Error(`Connection test failed - ${err.message}`);
}
-
- await databaseClient.listCollections();
-
- return {
- status: 'ok',
- };
}
}
diff --git a/plugins/packages/appwrite/lib/manifest.json b/plugins/packages/appwrite/lib/manifest.json
index fb04bd74b4..1f6260be80 100644
--- a/plugins/packages/appwrite/lib/manifest.json
+++ b/plugins/packages/appwrite/lib/manifest.json
@@ -32,6 +32,12 @@
"type": "text",
"description": "Appwrite project id"
},
+ "database_id": {
+ "label": "Database ID",
+ "key": "database_id",
+ "type": "text",
+ "description": "Appwrite Database id"
+ },
"secret_key": {
"label": "Secret Key",
"key": "secret_key",
diff --git a/plugins/packages/appwrite/lib/operations.json b/plugins/packages/appwrite/lib/operations.json
index eb9234f9cd..a906d0fd37 100644
--- a/plugins/packages/appwrite/lib/operations.json
+++ b/plugins/packages/appwrite/lib/operations.json
@@ -27,10 +27,6 @@
"value": "update_document",
"name": "Update Document"
},
- {
- "value": "bulk_update",
- "name": "Bulk update using document id"
- },
{
"value": "delete_document",
"name": "Delete Document"
@@ -121,6 +117,22 @@
"value": ">=",
"name": ">="
},
+ {
+ "value": "is",
+ "name": "Is"
+ },
+ {
+ "value": "startsWith",
+ "name": "Starts With"
+ },
+ {
+ "value": "endsWith",
+ "name": "Ends With"
+ },
+ {
+ "value": "search",
+ "name": "Search"
+ },
{
"value": "",
"name": "None"
@@ -177,7 +189,8 @@
"type": "codehinter",
"description": "Enter document body",
"height": "150px",
- "editorType": "extendedSingleLine"
+ "editorType": "extendedSingleLine",
+ "placeholder": "{'name': 'John', 'age': 30}"
}
},
"update_document": {
@@ -207,7 +220,8 @@
"type": "codehinter",
"description": "Enter document body",
"height": "150px",
- "editorType": "extendedSingleLine"
+ "editorType": "extendedSingleLine",
+ "placeholder": "{'name': 'John', 'age': 30}"
}
},
"delete_document": {
@@ -231,39 +245,6 @@
"className": "codehinter-plugins col-6",
"placeholder": "Enter document id"
}
- },
- "bulk_update": {
- "collectionId": {
- "label": "Collection ID",
- "key": "collectionId",
- "type": "codehinter",
- "lineNumbers": false,
- "description": "Enter collection id",
- "width": "320px",
- "height": "36px",
- "className": "codehinter-plugins",
- "placeholder": "Enter collection id"
- },
- "document_id_key": {
- "label": "Key for document Id",
- "key": "document_id_key",
- "type": "codehinter",
- "lineNumbers": false,
- "description": "Enter key for document Id",
- "width": "320px",
- "height": "36px",
- "className": "codehinter-plugins",
- "placeholder": "Enter key for document Id"
- },
- "records": {
- "label": "Records",
- "key": "records",
- "type": "codehinter",
- "mode": "javascript",
- "description": "Enter records",
- "height": "150px",
- "editorType": "extendedSingleLine"
- }
}
}
-}
\ No newline at end of file
+}
diff --git a/plugins/packages/appwrite/lib/operations.ts b/plugins/packages/appwrite/lib/operations.ts
index 53ed36042f..177570a1da 100644
--- a/plugins/packages/appwrite/lib/operations.ts
+++ b/plugins/packages/appwrite/lib/operations.ts
@@ -1,88 +1,150 @@
-import { Database, Query } from 'node-appwrite';
+import { Databases, Query } from 'node-appwrite';
+import { ParsedObject, AwModelDocument, AwModelDocumentList, AwQueryTypes } from './types';
+import { isEmpty } from 'lodash';
-function computeValue(value: string) {
- const numConverted = Number.parseInt(value);
- return isNaN(numConverted) ? value : numConverted;
+function parseValue(value: ParsedObject) {
+ try {
+ return typeof value === 'string' ? JSON.parse(value) : value;
+ } catch (err) {
+ return value;
+ }
+}
+
+function computeValue(value: AwQueryTypes | ParsedObject) {
+ const numConverted = Number.parseInt(value as string);
+ return isNaN(numConverted) ? parseValue(value as ParsedObject) : numConverted;
}
export async function queryCollection(
- db: Database,
+ db: Databases,
+ databaseId: string,
collection: string,
- limit: number,
- order_fields: string[],
- order_types: string[],
+ limit: string,
+ order_fields: string | string[],
+ order_types: string | string[],
where_field: string,
where_operation: string,
- where_value: any
-): Promise