diff --git a/.github/workflows/cypress-marketplace.yml b/.github/workflows/cypress-marketplace.yml
index 4d34523219..7193798ec4 100644
--- a/.github/workflows/cypress-marketplace.yml
+++ b/.github/workflows/cypress-marketplace.yml
@@ -14,9 +14,9 @@ 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-ce-cypress-marketplace') ||
+ contains(github.event.pull_request.labels.*.name, 'run-ee-cypress-marketplace')
strategy:
matrix:
@@ -159,13 +159,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 +184,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
diff --git a/.version b/.version
index afad818663..92536a9e48 100644
--- a/.version
+++ b/.version
@@ -1 +1 @@
-3.11.0
+3.12.0
diff --git a/cypress-tests/cypress/commands/commands.js b/cypress-tests/cypress/commands/commands.js
index 10c849d807..0acb76c866 100644
--- a/cypress-tests/cypress/commands/commands.js
+++ b/cypress-tests/cypress/commands/commands.js
@@ -167,6 +167,10 @@ Cypress.Commands.add("deleteApp", (appName) => {
.click();
cy.get(commonSelectors.deleteAppOption).click();
cy.get(commonSelectors.buttonSelector(commonText.modalYesButton)).click();
+ cy.verifyToastMessage(
+ commonSelectors.toastMessage,
+ commonText.appDeletedToast
+ );
cy.wait("@appDeleted");
});
@@ -223,9 +227,9 @@ Cypress.Commands.add(
.invoke("text")
.then((text) => {
cy.wrap(subject).realType(createBackspaceText(text)),
- {
- delay: 0,
- };
+ {
+ delay: 0,
+ };
});
}
);
@@ -398,7 +402,7 @@ Cypress.Commands.add("defaultWorkspaceLogin", () => {
// cy.intercept("GET", API_ENDPOINT).as("library_apps");
cy.visit("/my-workspace");
- cy.wait(2000)
+ cy.wait(2000);
cy.get(commonSelectors.homePageLogo, { timeout: 10000 });
// cy.wait("@library_apps");
});
@@ -428,7 +432,6 @@ Cypress.Commands.add(
}
);
-
Cypress.Commands.add("releaseApp", () => {
if (Cypress.env("environment") !== "Community") {
cy.get(commonEeSelectors.promoteButton).click();
@@ -513,13 +516,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 +577,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.`);
+ }
+ });
+ });
+});
diff --git a/cypress-tests/cypress/constants/texts/postgreSql.js b/cypress-tests/cypress/constants/texts/postgreSql.js
index f7c9baf984..9db745b58d 100644
--- a/cypress-tests/cypress/constants/texts/postgreSql.js
+++ b/cypress-tests/cypress/constants/texts/postgreSql.js
@@ -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: () => {
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/addAllPluginsToApp.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/addAllPluginsToApp.cy.js
index 46f6339e11..51b65aeb60 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/addAllPluginsToApp.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/addAllPluginsToApp.cy.js
@@ -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);
});
});
});
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/airTable.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/airTableHappyPath.cy.skip.js
similarity index 96%
rename from cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/airTable.cy.js
rename to cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/airTableHappyPath.cy.skip.js
index 1ae1290180..0733373ece 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/airTable.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/airTableHappyPath.cy.skip.js
@@ -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,
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonAthena.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonAthenaHappyPath.cy.skip.js
similarity index 92%
rename from cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonAthena.cy.js
rename to cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonAthenaHappyPath.cy.skip.js
index 167ecdb2c5..f207f62058 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonAthena.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonAthenaHappyPath.cy.skip.js
@@ -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,
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonses.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonsesHappyPath.cy.skip.js
similarity index 95%
rename from cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonses.cy.js
rename to cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonsesHappyPath.cy.skip.js
index 674c694d28..9c3864c9fd 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonses.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/amazonsesHappyPath.cy.skip.js
@@ -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);
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/appWrite.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/appWriteHappyPath.cy.skip.js
similarity index 96%
rename from cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/appWrite.cy.js
rename to cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/appWriteHappyPath.cy.skip.js
index 8ed50754ad..27f52fd99d 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/appWrite.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/appWriteHappyPath.cy.skip.js
@@ -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");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsLambda.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsLambdaHappyPath.cy.skip.js
similarity index 92%
rename from cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsLambda.cy.js
rename to cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsLambdaHappyPath.cy.skip.js
index f127beb854..17a3f0426c 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsLambda.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsLambdaHappyPath.cy.skip.js
@@ -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");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsTextract.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsTextractHappyPath.cy.skip.js
similarity index 94%
rename from cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsTextract.cy.js
rename to cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsTextractHappyPath.cy.skip.js
index 7af21cf467..1a01ecc757 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsTextract.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/awsTextractHappyPath.cy.skip.js
@@ -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");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/azureBlobStorageHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/azureBlobStorageHappyPath.cy.js
index b148f92735..0c505cc417 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/azureBlobStorageHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/azureBlobStorageHappyPath.cy.js
@@ -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]", "");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/baseRow.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/baseRowHappyPath.cy.skip.js
similarity index 95%
rename from cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/baseRow.cy.js
rename to cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/baseRowHappyPath.cy.skip.js
index 05c250d01c..4b03148e9e 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/baseRow.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/baseRowHappyPath.cy.skip.js
@@ -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");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/bigqueryHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/bigqueryHappyPath.cy.skip.js
similarity index 98%
rename from cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/bigqueryHappyPath.cy.js
rename to cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/bigqueryHappyPath.cy.skip.js
index cee23b0807..24a39a3acc 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/bigqueryHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/bigqueryHappyPath.cy.skip.js
@@ -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]", "");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/clickHouseHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/clickHouseHappyPath.cy.js
index f221ed3c16..6ac8a3c8d1 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/clickHouseHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/clickHouseHappyPath.cy.js
@@ -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]", "");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/cosmosDbHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/cosmosDbHappyPath.cy.js
index 53fab94f67..bb5923ec7b 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/cosmosDbHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/cosmosDbHappyPath.cy.js
@@ -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]", "");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/couchDbHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/couchDbHappyPath.cy.js
index e16c6d5314..8e4a17d173 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/couchDbHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/couchDbHappyPath.cy.js
@@ -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]", "");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/dynamoDbHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/dynamoDbHappyPath.cy.js
index eb9a030963..5ff912d2d8 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/dynamoDbHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/dynamoDbHappyPath.cy.js
@@ -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]", "");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/elasticsearchHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/elasticsearchHappyPath.cy.js
index c7a1f242fa..88627284ad 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/elasticsearchHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/elasticsearchHappyPath.cy.js
@@ -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`);
});
});
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/fireStoreHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/fireStoreHappyPath.cy.js
index 6e703fc895..674501b2db 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/fireStoreHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/fireStoreHappyPath.cy.js
@@ -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]", "");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/graphQL.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/graphQLHappyPath.cy.js
similarity index 97%
rename from cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/graphQL.cy.js
rename to cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/graphQLHappyPath.cy.js
index 008121b863..7ad8e5484b 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/graphQL.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/graphQLHappyPath.cy.js
@@ -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", () => {
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/harperDb.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/harperDbHappyPath.cy.js
similarity index 97%
rename from cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/harperDb.cy.js
rename to cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/harperDbHappyPath.cy.js
index 9ec327cf71..12ad23efd9 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/harperDb.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/harperDbHappyPath.cy.js
@@ -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");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/influxDbHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/influxDbHappyPath.cy.js
index 36b39572d4..24dc92359c 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/influxDbHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/influxDbHappyPath.cy.js
@@ -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]", "");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mariaDbHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mariaDbHappyPath.cy.js
index 4c6c57d596..58c8c30705 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mariaDbHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mariaDbHappyPath.cy.js
@@ -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]", "");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/minio.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/minioHappyPath.cy.js
similarity index 97%
rename from cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/minio.cy.js
rename to cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/minioHappyPath.cy.js
index 7ea22b8693..6572724e3d 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/minio.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/minioHappyPath.cy.js
@@ -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");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mongoDbHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mongoDbHappyPath.cy.skip.js
similarity index 99%
rename from cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mongoDbHappyPath.cy.js
rename to cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mongoDbHappyPath.cy.skip.js
index 77d2e2ffa4..5729ea18c8 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mongoDbHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mongoDbHappyPath.cy.skip.js
@@ -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(
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mysqlHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mysqlHappyPath.cy.skip.js
similarity index 99%
rename from cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mysqlHappyPath.cy.js
rename to cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mysqlHappyPath.cy.skip.js
index 38e221ba0a..d54e15c933 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mysqlHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/mysqlHappyPath.cy.skip.js
@@ -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]", "");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/oracleDbHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/oracleDbHappyPath.cy.skip.js
index e7ae4f296b..0cd7ea4223 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/oracleDbHappyPath.cy.skip.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/oracleDbHappyPath.cy.skip.js
@@ -15,7 +15,7 @@ import {
describe("Data sources", () => {
beforeEach(() => {
- cy.appUILogin();
+ cy.apiLogin();
// cy.createApp();
});
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/postgresHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/postgresHappyPath.cy.js
index a6ff7595e5..2da4902edd 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/postgresHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/postgresHappyPath.cy.js
@@ -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,
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/redisHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/redisHappyPath.cy.js
index 8217a9db60..ddceea1b52 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/redisHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/redisHappyPath.cy.js
@@ -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(
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/restAPIHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/restAPIHappyPath.cy.js
index 0d45f8af89..825439e687 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/restAPIHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/restAPIHappyPath.cy.js
@@ -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`,
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/rethinkDbHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/rethinkDbHappyPath.cy.skip.js
index 267eedea1f..abdde2ba00 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/rethinkDbHappyPath.cy.skip.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/rethinkDbHappyPath.cy.skip.js
@@ -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]", "");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/s3HappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/s3HappyPath.cy.js
index 12b3817f16..73ccc703c3 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/s3HappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/s3HappyPath.cy.js
@@ -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]", "");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sapHanaHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sapHanaHappyPath.cy.skip.js
index c240286274..25ec2e4a9c 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sapHanaHappyPath.cy.skip.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sapHanaHappyPath.cy.skip.js
@@ -15,7 +15,7 @@ import {
describe("Data sources", () => {
beforeEach(() => {
- cy.appUILogin();
+ cy.apiLogin();
// cy.createApp();
});
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/smtpHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/smtpHappyPath.cy.js
index 4e824aeda5..506fe5d660 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/smtpHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/smtpHappyPath.cy.js
@@ -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]", "");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/snowflakeHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/snowflakeHappyPath.cy.js
index 4409c0577b..7c1fb5b588 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/snowflakeHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/snowflakeHappyPath.cy.js
@@ -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]", "");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sqlServerHappyPath.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sqlServerHappyPath.cy.skip.js
similarity index 99%
rename from cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sqlServerHappyPath.cy.js
rename to cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sqlServerHappyPath.cy.skip.js
index 9501fdabbb..954fb659b5 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sqlServerHappyPath.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/sqlServerHappyPath.cy.skip.js
@@ -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]", "");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/twilio.cy.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/twilioHappyPath.cy.skip.js
similarity index 94%
rename from cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/twilio.cy.js
rename to cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/twilioHappyPath.cy.skip.js
index ee2fa9a6e3..4ac2575266 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/twilio.cy.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/twilioHappyPath.cy.skip.js
@@ -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");
diff --git a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/typeSenseHappyPath.cy.skip.js b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/typeSenseHappyPath.cy.skip.js
index ff15053f09..4b4fe20e62 100644
--- a/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/typeSenseHappyPath.cy.skip.js
+++ b/cypress-tests/cypress/e2e/happyPath/marketplace/commonTestcases/data-source/typeSenseHappyPath.cy.skip.js
@@ -20,7 +20,7 @@ const data = {};
describe("Data sources", () => {
beforeEach(() => {
- cy.appUILogin();
+ cy.apiLogin();
data.dataSourceName = fake.lastName
.toLowerCase()
.replaceAll("[^A-Za-z]", "");
diff --git a/cypress-tests/cypress/support/utils/dashboard.js b/cypress-tests/cypress/support/utils/dashboard.js
index 0809909297..dbc090735b 100644
--- a/cypress-tests/cypress/support/utils/dashboard.js
+++ b/cypress-tests/cypress/support/utils/dashboard.js
@@ -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);
diff --git a/cypress-tests/cypress/support/utils/restAPI.js b/cypress-tests/cypress/support/utils/restAPI.js
index f5a44e3fd5..2dfc225a65 100644
--- a/cypress-tests/cypress/support/utils/restAPI.js
+++ b/cypress-tests/cypress/support/utils/restAPI.js
@@ -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);
+ }
+ });
+ }
+ });
});
});
});
diff --git a/frontend/.version b/frontend/.version
index afad818663..92536a9e48 100644
--- a/frontend/.version
+++ b/frontend/.version
@@ -1 +1 @@
-3.11.0
+3.12.0
diff --git a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/BulkUploadPrimaryKey.jsx b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/BulkUploadPrimaryKey.jsx
index d1a9dfa3aa..3a74038367 100644
--- a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/BulkUploadPrimaryKey.jsx
+++ b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/BulkUploadPrimaryKey.jsx
@@ -53,7 +53,11 @@ export const BulkUploadPrimaryKey = () => {
{
diff --git a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/BulkUpsertPrimaryKey.jsx b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/BulkUpsertPrimaryKey.jsx
new file mode 100644
index 0000000000..07ddc48742
--- /dev/null
+++ b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/BulkUpsertPrimaryKey.jsx
@@ -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 (
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default BulkUpsertPrimaryKey;
diff --git a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/RenderColumnUI.jsx b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/RenderColumnUI.jsx
index 89323c1c3c..e273e2ace8 100644
--- a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/RenderColumnUI.jsx
+++ b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/RenderColumnUI.jsx
@@ -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);
}
diff --git a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/SelectBox.jsx b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/SelectBox.jsx
index 208f4df816..0ea9538017 100644
--- a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/SelectBox.jsx
+++ b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/SelectBox.jsx
@@ -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 = {
diff --git a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/ToolJetDbOperations.jsx b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/ToolJetDbOperations.jsx
index e873228888..f25e72cb59 100644
--- a/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/ToolJetDbOperations.jsx
+++ b/frontend/src/AppBuilder/QueryManager/QueryEditors/TooljetDatabase/ToolJetDbOperations.jsx
@@ -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);
diff --git a/frontend/src/AppBuilder/_stores/slices/queryPanelSlice.js b/frontend/src/AppBuilder/_stores/slices/queryPanelSlice.js
index 5ca9429693..ddb67a5445 100644
--- a/frontend/src/AppBuilder/_stores/slices/queryPanelSlice.js
+++ b/frontend/src/AppBuilder/_stores/slices/queryPanelSlice.js
@@ -425,6 +425,7 @@ export const createQueryPanelSlice = (set, get) => ({
isLoading: false,
...(query.kind === 'restapi'
? {
+ metadata: data.metadata,
request: data.data.requestObject,
response: data.data.responseObject,
responseHeaders: data.data.responseHeaders,
diff --git a/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/SelectBox.jsx b/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/SelectBox.jsx
index 92772ffea1..d480f2c698 100644
--- a/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/SelectBox.jsx
+++ b/frontend/src/Editor/QueryManager/QueryEditors/TooljetDatabase/SelectBox.jsx
@@ -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();
@@ -715,7 +717,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 = {
diff --git a/frontend/src/MarketplacePage/InstalledPlugins.jsx b/frontend/src/MarketplacePage/InstalledPlugins.jsx
index 260b16c189..d0da8512a5 100644
--- a/frontend/src/MarketplacePage/InstalledPlugins.jsx
+++ b/frontend/src/MarketplacePage/InstalledPlugins.jsx
@@ -68,7 +68,7 @@ export const InstalledPlugins = () => {
})}
{!fetching && installedPlugins?.length === 0 && (
-
No results found
+
No plugins added. Please add a plugin from the Marketplace.
)}
diff --git a/frontend/src/TooljetDatabase/Forms/ColumnForm.jsx b/frontend/src/TooljetDatabase/Forms/ColumnForm.jsx
index 41cedf6bc4..d847f4238c 100644
--- a/frontend/src/TooljetDatabase/Forms/ColumnForm.jsx
+++ b/frontend/src/TooljetDatabase/Forms/ColumnForm.jsx
@@ -342,8 +342,39 @@ const ColumnForm = ({
)}
-
- Default value
+
+
+ Default value
+
+ {foreignKeyDetails?.length > 0 && isForeignKey && (
+
0 && isForeignKey}
+ >
+
+ Set default value to Null
+
+
+
+ )}
-
+
{isTimestamp ? (
) : (
-
-
- No data found
-
- }
- loader={
- <>
-
-
-
- >
- }
- isLoading={true}
- value={foreignKeyDefaultValue}
- foreignKeyAccessInRowForm={true}
- disabled={dataType === 'serial'}
- topPlaceHolder={dataType === 'serial' ? 'Auto-generated' : 'Enter a value'}
- onChange={(value) => {
- setForeignKeyDefaultValue(value);
- setDefaultValue(value?.value);
- }}
- onAdd={true}
- addBtnLabel={'Open referenced table'}
- foreignKeys={foreignKeyDetails}
- setReferencedColumnDetails={setReferencedColumnDetails}
- scrollEventForColumnValues={true}
- cellColumnName={columnName}
- columnDataType={dataType?.value}
- isCreateColumn={true}
- />
+ <>
+
+
+ No data found
+
+ }
+ loader={
+ <>
+
+
+
+ >
+ }
+ isLoading={true}
+ value={foreignKeyDefaultValue}
+ foreignKeyAccessInRowForm={true}
+ disabled={dataType === 'serial'}
+ topPlaceHolder={
+ dataType === 'serial'
+ ? 'Auto-generated'
+ : foreignKeyDefaultValue?.value === null
+ ? 'Null'
+ : 'Enter a value'
+ }
+ onChange={(value) => {
+ setForeignKeyDefaultValue(value);
+ setDefaultValue(value?.value);
+ }}
+ onAdd={true}
+ addBtnLabel={'Open referenced table'}
+ foreignKeys={foreignKeyDetails}
+ setReferencedColumnDetails={setReferencedColumnDetails}
+ scrollEventForColumnValues={true}
+ cellColumnName={columnName}
+ columnDataType={dataType?.value}
+ isCreateColumn={true}
+ />
+ {defaultValue === null && Null
}
+ >
)}
- {isNotNull === true && dataType?.value !== 'serial' && rows.length > 0 && defaultValue.length <= 0 ? (
+ {isNotNull === true && dataType?.value !== 'serial' && defaultValue?.length <= 0 ? (
Default value is required to populate this field in existing rows as NOT NULL constraint is added
@@ -546,6 +596,10 @@ const ColumnForm = ({
checked={isNotNull}
onChange={(e) => {
setIsNotNull(e.target.checked);
+ if (e.target.checked && defaultValue === null) {
+ setForeignKeyDefaultValue({ label: '', value: '' });
+ setDefaultValue('');
+ }
}}
disabled={dataType?.value === 'serial'}
/>
@@ -602,7 +656,7 @@ const ColumnForm = ({
shouldDisableCreateBtn={
isEmpty(columnName) ||
isEmpty(dataType) ||
- (isNotNull === true && rows.length > 0 && isEmpty(defaultValue) && dataType?.value !== 'serial') ||
+ (dataType?.value !== 'serial' && isNotNull === true && isEmpty(defaultValue)) ||
disabledSaveButton
}
showToolTipForFkOnReadDocsSection={true}
diff --git a/frontend/src/TooljetDatabase/Forms/EditColumnForm.jsx b/frontend/src/TooljetDatabase/Forms/EditColumnForm.jsx
index 3943964a16..071faf5e3e 100644
--- a/frontend/src/TooljetDatabase/Forms/EditColumnForm.jsx
+++ b/frontend/src/TooljetDatabase/Forms/EditColumnForm.jsx
@@ -31,6 +31,8 @@ import DateTimePicker from '@/Editor/QueryManager/QueryEditors/TooljetDatabase/D
import { getLocalTimeZone, timeZonesWithOffsets } from '@/Editor/QueryManager/QueryEditors/TooljetDatabase/util';
import CodeHinter from '@/AppBuilder/CodeEditor';
import { resolveReferences } from '@/AppBuilder/CodeEditor/utils';
+import Switch from '@/AppBuilder/CodeBuilder/Elements/Switch';
+import PostgrestQueryBuilder from '@/_helpers/postgrestQueryBuilder';
const ColumnForm = ({
onClose,
@@ -102,6 +104,68 @@ const ColumnForm = ({
const [foreignKeyDetails, setForeignKeyDetails] = useState([]);
+ // Add function to validate default value
+ const validateDefaultValue = async () => {
+ if (!isMatchingForeignKeyColumn(selectedColumn?.Header)) return;
+
+ try {
+ const referencedColumns = foreignKeys.find((item) => item.column_names[0] === selectedColumn?.Header);
+
+ if (!referencedColumns?.referenced_column_names?.length) {
+ setForeignKeyDefaultValue({
+ value: '',
+ label: '',
+ });
+ setDefaultValue('');
+ return;
+ }
+
+ const selectQuery = new PostgrestQueryBuilder();
+ selectQuery.select(referencedColumns.referenced_column_names[0]);
+ selectQuery.eq(referencedColumns.referenced_column_names[0], defaultValue);
+
+ const query = selectQuery.url.toString();
+
+ const { data = [], error } = await tooljetDatabaseService.findOne(
+ organizationId,
+ referencedColumns.referenced_table_id,
+ query
+ );
+
+ if (error) {
+ toast.error(error?.message ?? `Failed to validate default value`);
+ setForeignKeyDefaultValue({
+ value: '',
+ label: '',
+ });
+ setDefaultValue('');
+ return;
+ }
+
+ if (data.length === 0) {
+ setForeignKeyDefaultValue({
+ value: '',
+ label: '',
+ });
+ setDefaultValue('');
+ }
+ } catch (error) {
+ console.error('Error validating default value:', error);
+ setForeignKeyDefaultValue({
+ value: '',
+ label: '',
+ });
+ setDefaultValue('');
+ }
+ };
+
+ // Add useEffect to validate on mount
+ useEffect(() => {
+ if (isMatchingForeignKeyColumn(selectedColumn?.Header) && defaultValue) {
+ validateDefaultValue();
+ }
+ }, []);
+
useEffect(() => {
toast.dismiss();
setForeignKeyDetails(
@@ -471,6 +535,11 @@ const ColumnForm = ({
setDisabledSaveButton(columnName === '');
}, [columnName]);
+ useEffect(() => {
+ const shouldDisableForNullValue = dataType?.value !== 'serial' && isNotNull === true && isEmpty(defaultValue);
+ setDisabledSaveButton(shouldDisableForNullValue);
+ }, [isNotNull, defaultValue, dataType]);
+
const handleInputError = (bool = false) => {
setDisabledSaveButton(bool);
};
@@ -621,16 +690,52 @@ const ColumnForm = ({
)}
-
- Default value
+
+
+ Default value
+
+ {isMatchingForeignKeyColumn(selectedColumn?.Header) && (
+
+
+ Set default value to Null
+
+
+
+ )}
+
-
+
{isTimestamp ? (
) : (
-
-
- No data found
-
- }
- loader={
- <>
-
-
-
- >
- }
- isLoading={true}
- value={foreignKeyDefaultValue}
- foreignKeyAccessInRowForm={true}
- disabled={
- selectedColumn?.dataType === 'serial' || selectedColumn.constraints_type.is_primary_key === true
- }
- topPlaceHolder={selectedColumn?.dataType === 'serial' ? 'Auto-generated' : 'Enter a value'}
- onChange={(value) => {
- setForeignKeyDefaultValue(value);
- setDefaultValue(value?.value);
- }}
- onAdd={true}
- addBtnLabel={'Open referenced table'}
- foreignKeys={foreignKeys}
- setReferencedColumnDetails={setReferencedColumnDetails}
- scrollEventForColumnValues={true}
- cellColumnName={selectedColumn?.Header}
- columnDataType={dataType}
- isEditColumn={true}
- />
+ <>
+
+
+ No data found
+
+ }
+ loader={
+ <>
+
+
+
+ >
+ }
+ isLoading={true}
+ value={foreignKeyDefaultValue}
+ foreignKeyAccessInRowForm={true}
+ disabled={
+ selectedColumn?.dataType === 'serial' || selectedColumn.constraints_type.is_primary_key === true
+ }
+ topPlaceHolder={
+ selectedColumn?.dataType === 'serial'
+ ? 'Auto-generated'
+ : foreignKeyDefaultValue?.value === null || defaultValue === null
+ ? 'Null'
+ : 'Enter a value'
+ }
+ onChange={(value) => {
+ setForeignKeyDefaultValue(value);
+ setDefaultValue(value?.value);
+ }}
+ onAdd={true}
+ addBtnLabel={'Open referenced table'}
+ foreignKeys={foreignKeys}
+ setReferencedColumnDetails={setReferencedColumnDetails}
+ scrollEventForColumnValues={true}
+ cellColumnName={selectedColumn?.Header}
+ columnDataType={dataType}
+ isEditColumn={true}
+ />
+ {defaultValue === null && Null
}
+ >
)}
+ {isNotNull === true && dataType?.value !== 'serial' && defaultValue?.length <= 0 ? (
+
+ Default value is required to populate this field in existing rows as NOT NULL constraint is added
+
+ ) : null}
{isNotNull === true &&
selectedColumn?.dataType !== 'serial' &&
rows.length > 0 &&
@@ -866,6 +990,10 @@ const ColumnForm = ({
checked={isNotNull}
onChange={(e) => {
setIsNotNull(e.target.checked);
+ if (e.target.checked && defaultValue === null) {
+ setForeignKeyDefaultValue({ label: '', value: '' });
+ setDefaultValue('');
+ }
}}
disabled={selectedColumn?.dataType === 'serial' || selectedColumn?.constraints_type?.is_primary_key}
/>
diff --git a/frontend/src/TooljetDatabase/Forms/EditRowForm.jsx b/frontend/src/TooljetDatabase/Forms/EditRowForm.jsx
index 5451cc353f..98ff603242 100644
--- a/frontend/src/TooljetDatabase/Forms/EditRowForm.jsx
+++ b/frontend/src/TooljetDatabase/Forms/EditRowForm.jsx
@@ -177,7 +177,9 @@ const EditRowForm = ({
}
function isMatchingForeignKeyColumnDetails(columnHeader) {
- const matchingColumn = foreignKeys.find((foreignKey) => foreignKey.column_names[0] === columnHeader);
+ const matchingColumn = Array.isArray(foreignKeys)
+ ? foreignKeys.find((foreignKey) => foreignKey.column_names[0] === columnHeader)
+ : undefined;
return matchingColumn;
}
diff --git a/frontend/src/TooljetDatabase/Forms/RowForm.jsx b/frontend/src/TooljetDatabase/Forms/RowForm.jsx
index 8fcc9ef152..58dd3d1000 100644
--- a/frontend/src/TooljetDatabase/Forms/RowForm.jsx
+++ b/frontend/src/TooljetDatabase/Forms/RowForm.jsx
@@ -151,7 +151,9 @@ const RowForm = ({
}
function isMatchingForeignKeyColumnDetails(columnHeader) {
- const matchingColumn = foreignKeys.find((foreignKey) => foreignKey.column_names[0] === columnHeader);
+ const matchingColumn = Array.isArray(foreignKeys)
+ ? foreignKeys.find((foreignKey) => foreignKey.column_names[0] === columnHeader)
+ : undefined;
return matchingColumn;
}
diff --git a/frontend/src/TooljetDatabase/Table/index.jsx b/frontend/src/TooljetDatabase/Table/index.jsx
index 5d98ce70fe..3a1cafa460 100644
--- a/frontend/src/TooljetDatabase/Table/index.jsx
+++ b/frontend/src/TooljetDatabase/Table/index.jsx
@@ -958,7 +958,9 @@ const Table = ({ collapseSidebar }) => {
}
function isMatchingForeignKeyColumnDetails(columnHeader) {
- const matchingColumn = foreignKeys.find((foreignKey) => foreignKey.column_names[0] === columnHeader);
+ const matchingColumn = Array.isArray(foreignKeys)
+ ? foreignKeys.find((foreignKey) => foreignKey.column_names[0] === columnHeader)
+ : undefined;
return matchingColumn;
}
diff --git a/frontend/src/_components/DynamicFormV2.jsx b/frontend/src/_components/DynamicFormV2.jsx
index 1a6269976f..bada082e16 100644
--- a/frontend/src/_components/DynamicFormV2.jsx
+++ b/frontend/src/_components/DynamicFormV2.jsx
@@ -30,6 +30,8 @@ const DynamicFormV2 = ({
validationMessages,
setValidationMessages,
clearValidationMessages,
+ showValidationErrors,
+ clearValidationErrorBanner,
}) => {
const uiProperties = schema['tj:ui:properties'] || {};
const dsm = React.useMemo(() => new DataSourceSchemaManager(schema), [schema]);
@@ -89,18 +91,97 @@ const DynamicFormV2 = ({
React.useEffect(() => {
if (!hasUserInteracted) return;
- const { valid, errors } = dsm.validateData(options);
- if (valid) {
- clearValidationMessages();
- } else {
- setValidationMessages(errors, schema);
- const requiredFields = errors
- .filter((error) => error.keyword === 'required')
- .map((error) => error.params.missingProperty);
- setConditionallyRequiredProperties(requiredFields);
+ const timeout = setTimeout(() => {
+ validateOptions();
+ }, 300);
+
+ return () => clearTimeout(timeout);
+ }, [options, hasUserInteracted, validateOptions]);
+
+ const validateOptions = React.useCallback(async () => {
+ try {
+ const { valid, errors } = await dsm.validateData(options);
+
+ const conditionallyRequiredFields = processAllOfConditions(schema, options);
+ setConditionallyRequiredProperties(conditionallyRequiredFields);
+
+ if (valid) {
+ clearValidationMessages();
+ clearValidationErrorBanner();
+ } else {
+ setValidationMessages(errors, schema, interactedFields);
+ }
+ } catch (error) {
+ console.error('Validation error:', error);
}
- }, [options]);
+ }, [
+ dsm,
+ options,
+ processAllOfConditions,
+ schema,
+ clearValidationMessages,
+ clearValidationErrorBanner,
+ setValidationMessages,
+ interactedFields,
+ ]);
+
+ const processAllOfConditions = React.useCallback((schema, options, path = []) => {
+ let requiredFields = [];
+
+ if (schema.allOf) {
+ schema.allOf.forEach((condition) => {
+ if (condition.if && condition.then) {
+ const conditionMatches = Object.entries(condition.if.properties || {}).every(([propName, propCondition]) => {
+ const propertyPath = [...path, propName];
+
+ let currentValue = options;
+ for (const segment of propertyPath) {
+ if (!currentValue || typeof currentValue !== 'object') {
+ return false;
+ }
+ currentValue = currentValue[segment]?.value;
+ }
+
+ return propCondition.const === currentValue;
+ });
+
+ if (conditionMatches) {
+ if (condition.then.required) {
+ requiredFields = [...requiredFields, ...condition.then.required];
+ }
+
+ if (condition.then.allOf) {
+ const nestedRequired = processAllOfConditions({ allOf: condition.then.allOf }, options, path);
+ requiredFields = [...requiredFields, ...nestedRequired];
+ }
+
+ if (condition.then.properties) {
+ Object.entries(condition.then.properties).forEach(([propName, propSchema]) => {
+ if (propSchema.allOf) {
+ const nestedRequired = processAllOfConditions({ allOf: propSchema.allOf }, options, [
+ ...path,
+ propName,
+ ]);
+ requiredFields = [...requiredFields, ...nestedRequired];
+ }
+ });
+ }
+ }
+ }
+ });
+ }
+
+ return requiredFields;
+ }, []);
+
+ React.useEffect(() => {
+ if (showValidationErrors) {
+ setHasUserInteracted(true);
+ const allFieldKeys = Object.keys(options);
+ setInteractedFields(new Set(allFieldKeys));
+ }
+ }, [showValidationErrors, options]);
React.useEffect(() => {
const prevDataSourceId = prevDataSourceIdRef.current;
@@ -189,6 +270,7 @@ const DynamicFormV2 = ({
return Input;
case 'password-v3':
case 'text-v3':
+ case 'password-v3-textarea':
return InputV3;
case 'textarea':
return Textarea;
@@ -210,8 +292,10 @@ const DynamicFormV2 = ({
const isRequired = required || conditionallyRequiredProperties.includes(key);
const isEncrypted = widget === 'password-v3' || encryptedProperties.includes(key);
const currentValue = options?.[key]?.value;
+ const skipValidation =
+ (!hasUserInteracted && !showValidationErrors) || (!interactedFields.has(key) && !showValidationErrors);
- const handleOptionChange = (key, value, flag) => {
+ const handleOptionChange = (key, value, flag = true) => {
if (!hasUserInteracted) {
setHasUserInteracted(true);
}
@@ -243,6 +327,7 @@ const DynamicFormV2 = ({
};
}
case 'password-v3':
+ case 'password-v3-textarea':
case 'text-v3': {
return {
key,
@@ -262,14 +347,13 @@ const DynamicFormV2 = ({
encrypted: isEncrypted,
onBlur,
isRequired: isRequired,
- isValidatedMessages:
- !hasUserInteracted || !interactedFields.has(key)
- ? { valid: null, message: '' } // skip validation for initial render and untouched elements
- : validationMessages[key]
- ? { valid: false, message: validationMessages[key] }
- : isRequired && !isEncrypted
- ? { valid: true, message: '' }
- : { valid: null, message: '' }, // handle optional && encrypted fields
+ isValidatedMessages: skipValidation
+ ? { valid: null, message: '' } // skip validation for initial render and untouched elements
+ : validationMessages[key]
+ ? { valid: false, message: validationMessages[key] }
+ : isRequired
+ ? { valid: true, message: '' }
+ : { valid: null, message: '' }, // handle optional && encrypted fields
isDisabled: !canUpdateDataSource(selectedDataSource?.id) && !canDeleteDataSource(),
};
}
@@ -285,7 +369,7 @@ const DynamicFormV2 = ({
options: isRenderedAsQueryEditor
? options?.[key] ?? schema?.defaults?.[key]
: options?.[key]?.value ?? schema?.defaults?.[key]?.value,
- optionchanged,
+ handleOptionChange,
isRenderedAsQueryEditor,
workspaceConstants: currentOrgEnvironmentConstants,
isDisabled: !canUpdateDataSource(selectedDataSource?.id) && !canDeleteDataSource(),
@@ -298,14 +382,14 @@ const DynamicFormV2 = ({
return {
defaultChecked: currentValue,
checked: currentValue,
- onChange: (e) => optionchanged(key, e.target.checked),
+ onChange: (e) => handleOptionChange(key, e.target.checked, true),
};
case 'dropdown':
case 'dropdown-component-flip':
return {
options: list,
value: options?.[key]?.value || options?.[key],
- onChange: (value) => optionchanged(key, value),
+ onChange: (value) => handleOptionChange(key, value, true),
width: width || '100%',
encrypted: options?.[key]?.encrypted,
};
@@ -404,6 +488,7 @@ const DynamicFormV2 = ({
{label &&
widget !== 'text-v3' &&
widget !== 'password-v3' &&
+ widget !== 'password-v3-textarea' &&
renderLabel(label, uiProperties[key].tooltip)}
)}
diff --git a/frontend/src/_helpers/dataSourceSchemaManager.js b/frontend/src/_helpers/dataSourceSchemaManager.js
index abf2f3c9bb..6ce4f6f9a7 100644
--- a/frontend/src/_helpers/dataSourceSchemaManager.js
+++ b/frontend/src/_helpers/dataSourceSchemaManager.js
@@ -1,3 +1,4 @@
+import { datasourceService } from '@/_services';
import Ajv2020 from 'ajv';
const ajvOptions = {
@@ -18,13 +19,18 @@ export default class DataSourceSchemaManager {
this.validate = this.ajv.compile(this.schema);
}
- validateData(options) {
- const data = this._convertDataSourceOptionsToData(options);
- const valid = this.validate(data);
- if (!valid) {
- return { valid: false, errors: this.validate.errors };
+ async validateData(options) {
+ const decryptedOptions = await datasourceService.getDecryptedOptions(options);
+ const data = this._convertDataSourceOptionsToData(decryptedOptions);
+ try {
+ const valid = this.validate(data);
+ if (!valid) {
+ return { valid: false, errors: this.validate.errors };
+ }
+ return { valid: true, errors: [] };
+ } catch (error) {
+ console.log('Validtion error: ', error);
}
- return { valid: true, errors: [] };
}
getDefaults(options = {}) {
@@ -95,10 +101,6 @@ export default class DataSourceSchemaManager {
result[key] = value;
}
- // Add a dummy value to pass validation for encrypted keys
- if (this.getEncryptedProperties().includes(key)) {
- result[key] = 'REDACTED';
- }
return result;
}, {});
}
diff --git a/frontend/src/_services/datasource.service.js b/frontend/src/_services/datasource.service.js
index ef1a878db7..9ea24e99d0 100644
--- a/frontend/src/_services/datasource.service.js
+++ b/frontend/src/_services/datasource.service.js
@@ -11,8 +11,19 @@ export const datasourceService = {
save,
fetchOauth2BaseUrl,
testSampleDb,
+ getDecryptedOptions,
};
+function getDecryptedOptions(options) {
+ const requestOptions = {
+ method: 'POST',
+ headers: authHeader(),
+ credentials: 'include',
+ body: JSON.stringify(options),
+ };
+ return fetch(`${config.apiUrl}/data-sources/decrypt`, requestOptions).then(handleResponse);
+}
+
function getAll(appVersionId, environment_id, includeStaticSources = false) {
const requestOptions = { method: 'GET', headers: authHeader(), credentials: 'include' };
let searchParams = new URLSearchParams(
diff --git a/frontend/src/_styles/theme.scss b/frontend/src/_styles/theme.scss
index f293731a4a..4889a2f4e6 100644
--- a/frontend/src/_styles/theme.scss
+++ b/frontend/src/_styles/theme.scss
@@ -12367,6 +12367,17 @@ tbody {
}
}
+ .design-component-inputs textarea {
+
+ &.valid-textarea {
+ border: 1.5px solid #519b62 !important;
+ }
+
+ &.invalid-textarea {
+ border: 1.5px solid #e26367 !important;
+ }
+ }
+
}
.tj-app-input-wrapper {
@@ -13163,6 +13174,10 @@ tbody {
background-color: var(--slate3) !important;
}
+ textarea:disabled {
+ background-color: var(--slate3) !important;
+ }
+
.react-select__control--is-disabled {
background-color: var(--slate3) !important;
}
diff --git a/frontend/src/_ui/HttpHeaders/index.js b/frontend/src/_ui/HttpHeaders/index.js
index 04bbd8e3b5..3f254d1497 100644
--- a/frontend/src/_ui/HttpHeaders/index.js
+++ b/frontend/src/_ui/HttpHeaders/index.js
@@ -1,13 +1,14 @@
-import React from "react";
-import _ from "lodash";
-import QueryEditor from "./QueryEditor";
-import SourceEditor from "./SourceEditor";
-import { deepClone } from "@/_helpers/utilities/utils.helpers";
+import React from 'react';
+import _ from 'lodash';
+import QueryEditor from './QueryEditor';
+import SourceEditor from './SourceEditor';
+import { deepClone } from '@/_helpers/utilities/utils.helpers';
export default ({
getter,
- options = [["", ""]],
+ options = [['', '']],
optionchanged,
+ handleOptionChange,
isRenderedAsQueryEditor,
workspaceConstants,
isDisabled,
@@ -16,27 +17,46 @@ export default ({
dataCy,
}) => {
function addNewKeyValuePair(options) {
- const newPairs = [...options, ["", ""]];
- optionchanged(getter, newPairs);
+ const newPairs = [...options, ['', '']];
+
+ if (handleOptionChange) {
+ handleOptionChange(getter, newPairs, true);
+ } else {
+ optionchanged(getter, newPairs);
+ }
}
function removeKeyValuePair(index) {
const newOptions = [...options];
newOptions.splice(index, 1);
- optionchanged(getter, newOptions);
+ if (handleOptionChange) {
+ handleOptionChange(getter, newOptions, true);
+ } else {
+ optionchanged(getter, newOptions);
+ }
}
function keyValuePairValueChanged(value, keyIndex, index) {
if (!isRenderedAsQueryEditor) {
const newOptions = deepClone(options);
newOptions[index][keyIndex] = value;
- options.length - 1 === index
- ? addNewKeyValuePair(newOptions)
- : optionchanged(getter, newOptions);
+ if (options.length - 1 === index) {
+ addNewKeyValuePair(newOptions);
+ } else {
+ if (handleOptionChange) {
+ handleOptionChange(getter, newOptions, true);
+ } else {
+ optionchanged(getter, newOptions);
+ }
+ }
} else {
let newOptions = deepClone(options);
newOptions[index][keyIndex] = value;
- optionchanged(getter, newOptions);
+ if (handleOptionChange) {
+ handleOptionChange(getter, newOptions, true);
+ } else {
+ optionchanged(getter, newOptions);
+ }
}
}
@@ -53,10 +73,6 @@ export default ({
return isRenderedAsQueryEditor ? (
) : (
-
+
);
};
diff --git a/frontend/src/_ui/Input-V3/index.js b/frontend/src/_ui/Input-V3/index.js
index abc819c80a..71b2b3cf2a 100644
--- a/frontend/src/_ui/Input-V3/index.js
+++ b/frontend/src/_ui/Input-V3/index.js
@@ -43,7 +43,7 @@ const InputV3 = ({ helpText, ...props }) => {
required={props.isRequired}
/>
)}
- {(widget === 'password-v3' || encrypted) && (
+ {(widget === 'password-v3' || widget === 'password-v3-textarea' || encrypted) && (
{
label={props.label}
placeholder={props.placeholder}
required={props.isRequired}
+ multiline={widget === 'password-v3-textarea'}
/>
)}
diff --git a/frontend/src/components/ui/Input/CommonInput/Index.jsx b/frontend/src/components/ui/Input/CommonInput/Index.jsx
index 0710876ac3..baee3ad5d8 100644
--- a/frontend/src/components/ui/Input/CommonInput/Index.jsx
+++ b/frontend/src/components/ui/Input/CommonInput/Index.jsx
@@ -32,21 +32,28 @@ const CommonInput = ({ label, helperText, disabled, required, onChange: change,
}
}, [isValidatedMessages]);
+ useEffect(() => {
+ if (isValid === true && (!isValidatedMessages || isValidatedMessages.valid === null)) {
+ setIsValid(true);
+ }
+ }, [isValid, isValidatedMessages]);
+
const toggleEditing = () => {
if (isDisabled) return;
const willBeInEditMode = !isEditing;
setIsEditing(willBeInEditMode);
-
- if (willBeInEditMode) {
- change({ target: { value: '' } });
- }
+ change({ target: { value: '' } });
};
return (
- {label &&
}
+ {label && (
+
+
+
+ )}
{type === 'password' && (
diff --git a/frontend/src/components/ui/Input/CommonInput/TextInput.jsx b/frontend/src/components/ui/Input/CommonInput/TextInput.jsx
index 1834d4799d..978e69798f 100644
--- a/frontend/src/components/ui/Input/CommonInput/TextInput.jsx
+++ b/frontend/src/components/ui/Input/CommonInput/TextInput.jsx
@@ -14,14 +14,14 @@ const TextInput = ({
readOnly,
...restProps
}) => {
- const inputStyle = `tw-border-border-default placeholder:tw-text-text-placeholder tw-font-normal disabled:tw-bg-[#CCD1D5]/30 ${
+ const inputStyle = `placeholder:tw-text-text-placeholder tw-font-normal disabled:tw-bg-[#CCD1D5]/30 ${
leadingIcon ? (size === 'small' ? 'tw-pl-[32px]' : 'tw-pl-[34px]') : 'tw-pl-[12px]'
} ${trailingAction ? (size === 'small' ? 'tw-pr-[40px]' : 'tw-pr-[44px]') : 'tw-pr-[12px]'} ${
response === true
? '!tw-border-border-success-strong focus-visible:!tw-ring-0 focus-visible:!tw-ring-offset-0 focus-visible:!tw-border-border-success-strong'
: response === false
? '!tw-border-border-danger-strong focus-visible:!tw-ring-0 focus-visible:!tw-ring-offset-0 focus-visible:!tw-border-border-danger-strong'
- : ''
+ : 'tw-border-border-default'
}`;
return (
@@ -39,6 +39,7 @@ const TextInput = ({
size={size}
placeholder={disabled && readOnly ? readOnly : placeholder}
disabled={disabled}
+ response={response}
{...restProps}
className={inputStyle}
/>
diff --git a/frontend/src/components/ui/Input/Input.jsx b/frontend/src/components/ui/Input/Input.jsx
index 33c3194f0b..9d09b97009 100644
--- a/frontend/src/components/ui/Input/Input.jsx
+++ b/frontend/src/components/ui/Input/Input.jsx
@@ -3,7 +3,7 @@ import { cn } from '@/lib/utils';
import { inputVariants } from './InputUtils/Variants';
import SolidIcon from '../../../_ui/Icon/SolidIcons';
-const Input = React.forwardRef(({ className, size, type, ...props }, ref) => {
+const Input = React.forwardRef(({ className, size, type, multiline, response, rows = 3, ...props }, ref) => {
const [isPasswordVisible, setIsPasswordVisible] = React.useState(false);
const isPasswordField = type === 'password';
@@ -13,19 +13,34 @@ const Input = React.forwardRef(({ className, size, type, ...props }, ref) => {
}
};
+ const validationClass = response === true ? 'valid-textarea' : response === false ? 'invalid-textarea' : '';
+
return (
- <>
-
- {isPasswordField && (
+
+ {multiline ? (
+
+ ) : (
+
+ )}
+ {isPasswordField && !multiline && (
{isPasswordVisible ? (
@@ -34,7 +49,7 @@ const Input = React.forwardRef(({ className, size, type, ...props }, ref) => {
)}
)}
- >
+
);
});
Input.displayName = 'Input';
diff --git a/frontend/src/modules/dataSources/components/DataSourceManager/DataSourceManager.jsx b/frontend/src/modules/dataSources/components/DataSourceManager/DataSourceManager.jsx
index dd19801dfd..65ee96758b 100644
--- a/frontend/src/modules/dataSources/components/DataSourceManager/DataSourceManager.jsx
+++ b/frontend/src/modules/dataSources/components/DataSourceManager/DataSourceManager.jsx
@@ -84,6 +84,7 @@ class DataSourceManagerComponent extends React.Component {
creatingApp: false,
validationError: [],
validationMessages: {},
+ showValidationErrors: false,
};
}
@@ -219,11 +220,13 @@ class DataSourceManagerComponent extends React.Component {
dataSourceMeta,
dataSourceSchema,
validationMessages,
+ validationError,
} = this.state;
if (!isEmpty(validationMessages)) {
const validationMessageArray = Object.values(validationMessages);
- this.setState({ validationError: validationMessageArray });
+ this.setState({ validationError: validationMessageArray, showValidationErrors: true });
+
toast.error(
this.props.t(
'editor.queryManager.dataSourceManager.toast.error.validationFailed',
@@ -362,7 +365,7 @@ class DataSourceManagerComponent extends React.Component {
this.setState({ suggestingDatasources: true, activeDatasourceList: '#' });
};
- setValidationMessages = (errors, schema) => {
+ setValidationMessages = (errors, schema, interactedFields) => {
const errorMap = errors.reduce((acc, error) => {
// Get property name from either required error or dataPath
const property =
@@ -379,10 +382,19 @@ class DataSourceManagerComponent extends React.Component {
return acc;
}, {});
this.setState({ validationMessages: errorMap });
+ const filteredValidationBanner = interactedFields
+ ? Object.keys(this.state.validationMessages)
+ .filter((key) => interactedFields.has(key))
+ .reduce((result, key) => {
+ result.push(this.state.validationMessages[key]);
+ return result;
+ }, [])
+ : Object.values(this.state.validationMessages);
+ this.setState({ validationError: filteredValidationBanner });
};
renderSourceComponent = (kind, isPlugin = false) => {
- const { options, isSaving } = this.state;
+ const { options, isSaving, showValidationErrors } = this.state;
const sourceComponentName = kind?.charAt(0).toUpperCase() + kind?.slice(1);
const ComponentToRender = isPlugin ? SourceComponent : SourceComponents[sourceComponentName] || SourceComponent;
@@ -402,6 +414,8 @@ class DataSourceManagerComponent extends React.Component {
setValidationMessages={this.setValidationMessages}
clearValidationMessages={() => this.setState({ validationMessages: {} })}
setDefaultOptions={this.setDefaultOptions}
+ showValidationErrors={showValidationErrors}
+ clearValidationErrorBanner={() => this.setState({ validationError: [] })}
/>
);
};
@@ -901,6 +915,7 @@ class DataSourceManagerComponent extends React.Component {
addingDataSource,
datasourceName,
validationError,
+ validationMessages,
} = this.state;
const isPlugin = dataSourceSchema ? true : false;
const createSelectedDataSource = (dataSource) => {
@@ -910,7 +925,9 @@ class DataSourceManagerComponent extends React.Component {
const sampleDBmodalBodyStyle = isSampleDb ? { paddingBottom: '0px', borderBottom: '1px solid #E6E8EB' } : {};
const sampleDBmodalFooterStyle = isSampleDb ? { paddingTop: '8px' } : {};
const isSaveDisabled = selectedDataSource
- ? deepEqual(options, selectedDataSource?.options, ['encrypted']) && selectedDataSource?.name === datasourceName
+ ? (deepEqual(options, selectedDataSource?.options, ['encrypted']) &&
+ selectedDataSource?.name === datasourceName) ||
+ !isEmpty(validationMessages)
: true;
this.props.setGlobalDataSourceStatus({ isEditing: !isSaveDisabled });
const docLink = isSampleDb
diff --git a/marketplace/plugins/azurerepos/lib/manifest.json b/marketplace/plugins/azurerepos/lib/manifest.json
index fd15e29b9f..f14ec918df 100644
--- a/marketplace/plugins/azurerepos/lib/manifest.json
+++ b/marketplace/plugins/azurerepos/lib/manifest.json
@@ -4,7 +4,7 @@
"description": "A schema defining Azurerepos datasource",
"type": "api",
"source": {
- "name": "Azurerepos",
+ "name": "Azure Repos",
"kind": "azurerepos",
"exposedVariables": {
"isLoading": false,
diff --git a/marketplace/plugins/pinecone/lib/icon.svg b/marketplace/plugins/pinecone/lib/icon.svg
index 2bddce89fd..8681b2a90c 100644
--- a/marketplace/plugins/pinecone/lib/icon.svg
+++ b/marketplace/plugins/pinecone/lib/icon.svg
@@ -1,72 +1,10 @@
-
-
-