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 { - const limitProvided = isNaN(limit) !== true; + where_value: AwQueryTypes +): Promise { + const limitProvided = isNaN(Number.parseInt(limit)); let queryString: string; - where_value = computeValue(where_value); + if (where_field || where_operation || where_value) { + let filterErrorStr: string | string[] = []; + if (!where_field) filterErrorStr.push('Field'); + if (!where_operation) filterErrorStr.push('Operator'); + if (!where_value) filterErrorStr.push('Value'); + if (filterErrorStr.length) { + const suffix = ` ${filterErrorStr.length > 1 ? 'fields are' : 'field is'} required`; + filterErrorStr = filterErrorStr.join(' & '); + filterErrorStr += suffix; + throw new Error(filterErrorStr); + } + where_value = computeValue(where_value); - switch (where_operation) { - case '==': - queryString = Query.equal(where_field, where_value); - break; - case '!=': - queryString = Query.notEqual(where_field, where_value); - break; - case '<': - queryString = Query.lesser(where_field, where_value); - break; - case '>': - queryString = Query.greater(where_field, where_value); - break; - case '>=': - queryString = Query.greaterEqual(where_field, where_value); - break; - case '<=': - queryString = Query.lesserEqual(where_field, where_value); - break; + switch (where_operation) { + case '==': + queryString = Query.equal(where_field, where_value); + break; + case '!=': + queryString = Query.notEqual(where_field, where_value); + break; + case '<': + queryString = Query.lessThan(where_field, where_value); + break; + case '>': + queryString = Query.greaterThan(where_field, where_value); + break; + case '>=': + queryString = Query.greaterThanEqual(where_field, where_value); + break; + case '<=': + queryString = Query.lessThanEqual(where_field, where_value); + break; + case 'is': + if (String(where_value).toLowerCase() === 'not null') { + queryString = Query.isNotNull(where_field); + } else if (String(where_value).toLowerCase() === 'null') { + queryString = Query.isNull(where_field); + } + break; + case 'startsWith': + queryString = Query.startsWith(where_field, where_value as string); + break; + case 'endsWith': + queryString = Query.endsWith(where_field, where_value as string); + break; + case 'search': + queryString = Query.search(where_field, where_value as string); + break; + default: + break; + } } - return await db.listDocuments( - collection, - queryString ? [queryString] : [], - limitProvided ? limit : 25, - 0, - null, - null, - order_fields, - order_types - ); + const queries = [Query.limit(!limitProvided ? Number(limit) : 25)]; + if (!isEmpty(queryString)) { + queries.push(queryString); + } + + if (order_fields || order_types) { + if (!order_fields) throw new Error('Order fields field is required.'); + if (!order_types) throw new Error('Order types field is required.'); + order_fields = parseValue(order_fields); + order_types = parseValue(order_types); + if (typeof order_fields === 'string' || typeof order_types === 'string') { + throw new Error('Stringify order fields & order types values if not passing as {{["VALUE"]}}'); + } + if (order_types.length !== order_fields.length) { + throw new Error('Size of order types & order fields should be same'); + } + + for (let loop = 0; loop < order_fields.length; loop++) { + if (order_types[loop].toLowerCase() === 'asc') { + queries.push(Query.orderAsc(order_fields[loop])); + } + if (order_types[loop].toLowerCase() === 'desc') { + queries.push(Query.orderDesc(order_fields[loop])); + } + } + } + + return await db.listDocuments(databaseId, collection, queries); } -export async function getDocument(db: Database, collectionId: string, documentId: string): Promise { - return await db.getDocument(collectionId, documentId); +export async function getDocument( + db: Databases, + databaseId: string, + collectionId: string, + documentId: string +): Promise { + return await db.getDocument(databaseId, collectionId, documentId); } -export async function createDocument(db: Database, collectionId: string, body: object): Promise { - return await db.createDocument(collectionId, 'unique()', body); +export async function createDocument( + db: Databases, + databaseId: string, + collectionId: string, + body: object +): Promise { + return await db.createDocument(databaseId, collectionId, 'unique()', body); } export async function updateDocument( - db: Database, + db: Databases, + databaseId: string, collectionId: string, documentId: string, body: object -): Promise { - return await db.updateDocument(collectionId, documentId, body); +): Promise { + return await db.updateDocument(databaseId, collectionId, documentId, body); } -export async function deleteDocument(db: Database, collectionId: string, documentId: string): Promise { - return await db.deleteDocument(collectionId, documentId); -} - -export async function bulkUpdate( - db: Database, +export async function deleteDocument( + db: Databases, + databaseId: string, collectionId: string, - records: Array, - documentIdKey: string + documentId: string ): Promise { - for (const record of records) { - const documentId = record[documentIdKey]; - await db.updateDocument(collectionId, documentId, record); - } - - return { message: 'Docs are being updated' }; + await db.deleteDocument(databaseId, collectionId, documentId); + return { deleted: true }; } diff --git a/plugins/packages/appwrite/lib/types.ts b/plugins/packages/appwrite/lib/types.ts index 2227a29af3..2c55105224 100644 --- a/plugins/packages/appwrite/lib/types.ts +++ b/plugins/packages/appwrite/lib/types.ts @@ -1,12 +1,15 @@ +import { QueryTypes, Models } from 'node-appwrite'; + export type SourceOptions = { host: string; project_id: string; secret_key: string; + database_id: string; }; export type QueryOptions = { operation: string; collectionId: string; - limit: number; + limit: string; documentId: string; body: any; document_id_key: string; @@ -17,3 +20,13 @@ export type QueryOptions = { where_operation: string; where_value: string; }; + +export type ParsedObject = string | number | object | []; + +export type ReturnObject = object[] | object; + +export type AwQueryTypes = QueryTypes; + +export type AwModelDocumentList = Models.DocumentList; + +export type AwModelDocument = Models.Document; diff --git a/plugins/packages/appwrite/package-lock.json b/plugins/packages/appwrite/package-lock.json index 95e186ff91..a25d4970f7 100644 --- a/plugins/packages/appwrite/package-lock.json +++ b/plugins/packages/appwrite/package-lock.json @@ -9,15 +9,20 @@ "version": "1.0.0", "dependencies": { "@tooljet-plugins/common": "file:../common", - "node-appwrite": "^5.0.0", + "node-appwrite": "^11.1.1", "react": "^17.0.2" } }, "../common": { + "name": "@tooljet-plugins/common", "version": "1.0.0", "dependencies": { "react": "^17.0.2", - "rimraf": "^3.0.2" + "rimraf": "^3.0.2", + "tough-cookie": "^4.1.3" + }, + "devDependencies": { + "@types/tough-cookie": "^4.0.2" } }, "node_modules/@tooljet-plugins/common": { @@ -30,11 +35,13 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "node_modules/axios": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", - "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", "dependencies": { - "follow-redirects": "^1.14.7" + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "node_modules/combined-stream": { @@ -57,9 +64,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==", + "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", @@ -124,11 +131,11 @@ } }, "node_modules/node-appwrite": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-5.0.0.tgz", - "integrity": "sha512-VJ9e5+ra+ycQS17C0aJMbVXK4Gcja6at+f2EzlRlsjxAzTMetb79QJBOO6ktMtmVrUkAieMnaMZcV1hPppERmg==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-11.1.1.tgz", + "integrity": "sha512-j9QTHfsDx/RoLq9shVtpDWofCG8u/izy6uQrlzet7+UV14OMikaPUNZpQhtZGi5KjLy5R+JvJfx6HHXrfdcIvQ==", "dependencies": { - "axios": "^0.25.0", + "axios": "^1.4.0", "form-data": "^4.0.0" } }, @@ -140,6 +147,11 @@ "node": ">=0.10.0" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/react": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", @@ -157,8 +169,10 @@ "@tooljet-plugins/common": { "version": "file:../common", "requires": { + "@types/tough-cookie": "^4.0.2", "react": "^17.0.2", - "rimraf": "^3.0.2" + "rimraf": "^3.0.2", + "tough-cookie": "^4.1.3" } }, "asynckit": { @@ -167,11 +181,13 @@ "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" }, "axios": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.25.0.tgz", - "integrity": "sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", + "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", "requires": { - "follow-redirects": "^1.14.7" + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, "combined-stream": { @@ -188,9 +204,9 @@ "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" }, "follow-redirects": { - "version": "1.14.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz", - "integrity": "sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==" + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" }, "form-data": { "version": "4.0.0", @@ -229,11 +245,11 @@ } }, "node-appwrite": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-5.0.0.tgz", - "integrity": "sha512-VJ9e5+ra+ycQS17C0aJMbVXK4Gcja6at+f2EzlRlsjxAzTMetb79QJBOO6ktMtmVrUkAieMnaMZcV1hPppERmg==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/node-appwrite/-/node-appwrite-11.1.1.tgz", + "integrity": "sha512-j9QTHfsDx/RoLq9shVtpDWofCG8u/izy6uQrlzet7+UV14OMikaPUNZpQhtZGi5KjLy5R+JvJfx6HHXrfdcIvQ==", "requires": { - "axios": "^0.25.0", + "axios": "^1.4.0", "form-data": "^4.0.0" } }, @@ -242,6 +258,11 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "react": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", diff --git a/plugins/packages/appwrite/package.json b/plugins/packages/appwrite/package.json index 9f8220ad2d..db7da779a3 100644 --- a/plugins/packages/appwrite/package.json +++ b/plugins/packages/appwrite/package.json @@ -18,7 +18,7 @@ "homepage": "https://github.com/tooljet/tooljet#readme", "dependencies": { "@tooljet-plugins/common": "file:../common", - "node-appwrite": "^5.1.0", + "node-appwrite": "^13.0.0", "react": "^17.0.2" } } diff --git a/server/.version b/server/.version index 6679a60439..6eba18ef8f 100644 --- a/server/.version +++ b/server/.version @@ -1 +1 @@ -2.61.1 +2.61.3 diff --git a/server/src/services/import_export_resources.service.ts b/server/src/services/import_export_resources.service.ts index af643fe9db..9a8067b031 100644 --- a/server/src/services/import_export_resources.service.ts +++ b/server/src/services/import_export_resources.service.ts @@ -55,13 +55,13 @@ export class ImportExportResourcesService { const importingVersion = importResourcesDto.tooljet_version; const isTJDBEnabled = process.env.ENABLE_TOOLJET_DB === 'true'; - if (isTJDBEnabled && importResourcesDto.tooljet_database) { + if (isTJDBEnabled && !isEmpty(importResourcesDto.tooljet_database)) { const res = await this.tooljetDbImportExportService.bulkImport(importResourcesDto, importingVersion, cloning); tableNameMapping = res.tableNameMapping; imports.tooljet_database = res.tooljet_database; } - if (importResourcesDto.app) { + if (!isEmpty(importResourcesDto.app)) { for (const appImportDto of importResourcesDto.app) { user.organizationId = importResourcesDto.organization_id; const createdApp = await this.appImportExportService.import(