Merge branch develop into appdef-2.0

This commit is contained in:
arpitnath 2023-08-18 17:23:19 +05:30
commit 19850ea69e
1123 changed files with 124924 additions and 5865 deletions

View file

@ -1 +1 @@
2.11.1
2.13.1

View file

@ -170,7 +170,7 @@ export const commonText = {
// iframeLinkLabel: "Get embeddable link for this application",
// ifameLinkCopyButton: "copy",
},
groupInputFieldLabel: "Select Group"
groupInputFieldLabel: "Select Group",
};
export const commonWidgetText = {
@ -199,7 +199,7 @@ export const commonWidgetText = {
codeMirrorInputTrue: codeMirrorInputLabel(true),
codeMirrorInputFalse: codeMirrorInputLabel("false"),
addEventHandlerLink: "+ Add event handler",
addEventHandlerLink: "Add handler",
inspectorComponentLabel: "components",
componentValueLabel: "Value",
labelDefaultValue: "Default Value",

View file

@ -2,10 +2,10 @@ export const dataSourceText = {
labelDataSources: "Datasources",
labelAddDataSource: "+ add data source",
allDataSources: "All Datasources (42)",
allDatabase: "Databases (19)",
allDataSources: "All Datasources (41)",
allDatabase: "Databases (17)",
allApis: "APIs (20)",
allCloudStorage: "Cloud Storage (3)",
allCloudStorage: "Cloud Storage (4)",
postgreSQL: "PostgreSQL",
labelHost: "Host",

View file

@ -15,7 +15,7 @@ export const multipageText = {
optionEventHandler: "Event Handlers",
eventModalTitle: "Page Events",
labelEvents: "Events",
addEventHandlerLink: "+ Add event handler",
addEventHandlerLink: "Add handler",
noEventHandlerInfo: "This page doesn't have any event handlers",
optionDeletePage: "Delete Page",

View file

@ -2,10 +2,10 @@ export const postgreSqlText = {
labelDataSources: "Datasources",
labelAddDataSource: "+ add data source",
allDataSources: "All Datasources (42)",
allDataSources: "All Datasources (43)",
allDatabase: "Databases (19)",
allApis: "APIs (20)",
allCloudStorage: "Cloud Storage (3)",
allCloudStorage: "Cloud Storage (4)",
postgreSQL: "PostgreSQL",
labelHost: "Host",

View file

@ -25,6 +25,7 @@ describe("Data sources", () => {
it("Should verify elements on connection form", () => {
cy.get(commonSelectors.globalDataSourceIcon).click();
cy.wait(1000);
cy.get(commonSelectors.addNewDataSourceButton)
.verifyVisibleElement("have.text", commonText.addNewDataSourceButton)
.click();

View file

@ -118,7 +118,7 @@ describe("Data sources", () => {
);
});
it("Should verify the functionality of PostgreSQL connection form.", () => {
it.skip("Should verify the functionality of Snowflake connection form.", () => {
selectDataSource("Snowflake");
cy.clearAndType(

View file

@ -193,7 +193,7 @@ describe("Multipage", () => {
multipageText.labelEvents
);
cy.get(multipageSelector.addEventHandlerLink).verifyVisibleElement(
"have.text",
"contain.text",
multipageText.addEventHandlerLink
);
cy.get(multipageSelector.noEventHandlerMessage).verifyVisibleElement(

View file

@ -33,7 +33,7 @@ import {
} from "Support/utils/events";
import {
selectQuery,
selectQueryFromLandingPage,
deleteQuery,
query,
changeQueryToggles,
@ -66,17 +66,15 @@ describe("RunJS", () => {
cy.createApp();
cy.viewport(1800, 1800);
cy.dragAndDropWidget("Button");
resizeQueryPanel("50");
resizeQueryPanel("80");
});
it("should verify basic runjs", () => {
const data = {};
data.customText = randomString(12);
selectQuery("Run JavaScript code");
selectQueryFromLandingPage("runjs", "JavaScript");
addInputOnQueryField("runjs", "return true");
query("create");
cy.verifyToastMessage(commonSelectors.toastMessage, "Query Added");
query("preview");
verifypreview("raw", "true");
query("run");
@ -92,7 +90,7 @@ describe("RunJS", () => {
const data = {};
data.customText = randomString(12);
selectQuery("Run JavaScript code");
selectQueryFromLandingPage("runjs", "JavaScript");
addInputOnQueryField(
"runjs",
`setTimeout(() => {
@ -101,7 +99,6 @@ describe("RunJS", () => {
}, [0]) `
);
query("run");
cy.verifyToastMessage(commonSelectors.toastMessage, "Query Added");
cy.get(commonWidgetSelector.sidebarinspector).click();
cy.get(".tooltip-inner").invoke("hide");
verifyNodeData("variables", "Object", "1 entry ");
@ -134,7 +131,6 @@ describe("RunJS", () => {
"actions.showAlert('success', 'alert from runjs');"
);
query("run");
cy.verifyToastMessage(commonSelectors.toastMessage, "Query Saved");
cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runjs");
cy.get(multipageSelector.sidebarPageButton).click();
@ -154,8 +150,6 @@ describe("RunJS", () => {
addInputOnQueryField("runjs", "actions.closeModal('modal1');");
query("run");
cy.intercept("GET", "api/data_queries?**").as("addQuery");
cy.wait("@addQuery");
cy.wait(200);
cy.notVisible('[data-cy="modal-title"]');
@ -164,7 +158,6 @@ describe("RunJS", () => {
"actions.copyToClipboard('data from runjs');"
);
query("run");
cy.wait("@addQuery");
cy.window().then((win) => {
win.navigator.clipboard.readText().then((text) => {
@ -176,7 +169,6 @@ describe("RunJS", () => {
"actions.setLocalStorage('localStorage','data from runjs');"
);
query("run");
cy.wait("@addQuery");
cy.getAllLocalStorage().then((result) => {
expect(result[Cypress.config().baseUrl].localStorage).to.deep.equal(
@ -189,8 +181,6 @@ describe("RunJS", () => {
"actions.generateFile('runjscsv', 'csv', [{ name: 'John', email: 'john@tooljet.com' }])"
);
query("run");
cy.wait("@addQuery");
cy.wait(3000);
cy.readFile("cypress/downloads/runjscsv.csv", "utf-8")
.should("contain", "name,email")
@ -201,11 +191,9 @@ describe("RunJS", () => {
// "actions.goToApp('111234')"
// );
// query("run");
// cy.wait("@addQuery");
addInputOnQueryField("runjs", "actions.logout()");
query("run");
cy.wait("@addQuery");
cy.wait(3000);
cy.get('[data-cy="sign-in-header"]').should("be.visible");
});
@ -213,10 +201,8 @@ describe("RunJS", () => {
const data = {};
data.customText = randomString(12);
selectQuery("Run JavaScript code");
selectQueryFromLandingPage("runjs", "JavaScript");
addInputOnQueryField("runjs", "return [page.handle,page.name]");
query("create");
cy.verifyToastMessage(commonSelectors.toastMessage, "Query Added");
query("preview");
verifypreview("raw", `["home","Home"]`);
@ -267,22 +253,20 @@ describe("RunJS", () => {
const data = {};
data.customText = randomString(12);
selectQuery("Run JavaScript code");
selectQueryFromLandingPage("runjs", "JavaScript");
addInputOnQueryField(
"runjs",
"actions.showAlert('success', 'alert from runjs');"
);
query("create");
cy.verifyToastMessage(commonSelectors.toastMessage, "Query Added");
query("run");
openEditorSidebar("button1");
selectEvent("On Click", "Run query", 1);
cy.get('[data-cy="query-selection-field"]').type("runjs1{enter}");
cy.get(commonWidgetSelector.draggableWidget("button1")).click();
cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runjs");
renameQueryFromEditor("newrunjs");
cy.wait(3000);
cy.waitForAutoSave();
cy.get('[data-cy="event-handler"]').click();
cy.get('[data-cy="query-selection-field"]').should("have.text", "newrunjs");
@ -291,26 +275,25 @@ describe("RunJS", () => {
});
it("should verify runjs toggle options", () => {
cy.intercept("PATCH", "api/data_queries/**").as("editQuery");
const data = {};
data.customText = randomString(12);
selectQuery("Run JavaScript code");
selectQueryFromLandingPage("runjs", "JavaScript");
addInputOnQueryField(
"runjs",
"actions.showAlert('success', 'alert from runjs');"
);
query("create");
cy.verifyToastMessage(commonSelectors.toastMessage, "Query Added");
changeQueryToggles("run-on-app-load");
query("save");
cy.wait(`@editQuery`);
cy.waitForAutoSave();
cy.reload();
cy.wait(3000);
cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runjs");
changeQueryToggles("confirmation-before-run");
query("save");
cy.wait(`@editQuery`);
cy.waitForAutoSave();
cy.reload();
cy.wait(3000);
cy.get('[data-cy="modal-message"]').verifyVisibleElement(
"have.text",
"Do you want to run this query - runjs1?"
@ -318,13 +301,15 @@ describe("RunJS", () => {
cy.get('[data-cy="modal-confirm-button"]').realClick();
cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runjs");
resizeQueryPanel("80");
changeQueryToggles("notification-on-success");
cy.get('[data-cy="success-message-input-field"]').clearAndTypeOnCodeMirror(
"Success alert"
);
query("save");
cy.get('[data-cy="runjs-input-field"]').realClick();
cy.wait(1000);
cy.waitForAutoSave();
cy.reload();
cy.wait(3000);
cy.get('[data-cy="modal-confirm-button"]').realClick();
cy.verifyToastMessage(commonSelectors.toastMessage, "Success alert");
cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runjs");

View file

@ -33,12 +33,12 @@ import {
} from "Support/utils/events";
import {
selectQuery,
deleteQuery,
selectQueryFromLandingPage,
query,
changeQueryToggles,
renameQueryFromEditor,
addInputOnQueryField,
waitForQueryAction,
} from "Support/utils/queries";
import {
@ -66,17 +66,17 @@ describe("runpy", () => {
cy.createApp();
cy.viewport(1800, 1800);
cy.dragAndDropWidget("Button");
resizeQueryPanel("50");
resizeQueryPanel("80");
cy.intercept("PATCH", "api/data_queries/**").as("editQuery");
});
it("should verify basic runpy", () => {
const data = {};
data.customText = randomString(12);
selectQuery("Run Python code");
selectQueryFromLandingPage("runpy", "Python");
addInputOnQueryField("runpy", "True");
query("create");
cy.verifyToastMessage(commonSelectors.toastMessage, "Query Added");
cy.waitForAutoSave();
query("preview");
verifypreview("raw", "true");
query("run");
@ -92,14 +92,14 @@ describe("runpy", () => {
const data = {};
data.customText = randomString(12);
selectQuery("Run Python code");
selectQueryFromLandingPage("runpy", "Python");
addInputOnQueryField(
"runpy",
`actions.setVariable('var', 'test')
actions.setPageVariable('pageVar', 'pageTest')`
);
cy.waitForAutoSave();
query("run");
cy.verifyToastMessage(commonSelectors.toastMessage, "Query Added");
cy.get(commonWidgetSelector.sidebarinspector).click();
cy.get(".tooltip-inner").invoke("hide");
verifyNodeData("variables", "Object", "1 entry ");
@ -130,9 +130,12 @@ actions.unsetPageVariable('pageVar')`
"actions.showAlert('success', 'alert from runpy')"
);
query("run");
// cy.verifyToastMessage(commonSelectors.toastMessage, "Query Added");
cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runpy");
cy.verifyToastMessage(
commonSelectors.toastMessage,
"alert from runpy",
false
);
cy.get(multipageSelector.sidebarPageButton).click();
addNewPage("test_page");
cy.url().should("contain", "/test-page");
@ -146,22 +149,21 @@ actions.unsetPageVariable('pageVar')`
cy.waitForAutoSave();
addInputOnQueryField("runpy", "actions.showModal('modal1')");
query("run");
cy.closeToastMessage();
cy.get('[data-cy="modal-title"]').should("be.visible");
cy.get('[data-cy="runpy-input-field"]').click({ force: true });
addInputOnQueryField("runpy", "actions.closeModal('modal1')");
cy.wait(2000);
cy.wait(`@editQuery`);
cy.waitForAutoSave();
query("run");
cy.intercept("GET", "api/data_queries?**").as("addQuery");
cy.wait("@addQuery");
cy.wait(10000);
waitForQueryAction("run");
cy.notVisible('[data-cy="modal-title"]');
addInputOnQueryField("runpy", "actions.copyToClipboard('data from runpy')");
cy.wait(`@editQuery`);
cy.waitForAutoSave();
query("run");
cy.wait("@addQuery");
cy.wait(10000);
waitForQueryAction("run");
cy.window().then((win) => {
win.navigator.clipboard.readText().then((text) => {
expect(text).to.eq("data from runpy");
@ -171,9 +173,10 @@ actions.unsetPageVariable('pageVar')`
"runpy",
"actions.setLocalStorage('localStorage','data from runpy')"
);
cy.wait(`@editQuery`);
cy.waitForAutoSave();
query("run");
cy.wait("@addQuery");
cy.wait(10000);
waitForQueryAction("run");
cy.getAllLocalStorage().then((result) => {
expect(result[Cypress.config().baseUrl].localStorage).to.deep.equal(
@ -186,7 +189,7 @@ actions.unsetPageVariable('pageVar')`
// "actions.generateFile('runpycsv', 'csv', [{ 'name': 'John', 'email': 'john@tooljet.com' }])"
// );
// query("run");
// cy.wait("@addQuery");
// cy.verifyToastMessage(
// commonSelectors.toastMessage,
// "Query (runpy1) completed."
@ -203,11 +206,13 @@ actions.unsetPageVariable('pageVar')`
// "actions.goToApp('111234')"
// );
// query("run");
// cy.wait("@addQuery");
addInputOnQueryField("runpy", "actions.logout()");
cy.wait(`@editQuery`);
cy.wait(200);
cy.waitForAutoSave();
query("run");
cy.wait("@addQuery");
cy.wait(3000);
waitForQueryAction("run");
cy.get('[data-cy="sign-in-header"]').should("be.visible");
});
@ -215,10 +220,9 @@ actions.unsetPageVariable('pageVar')`
const data = {};
data.customText = randomString(12);
selectQuery("Run Python code");
selectQueryFromLandingPage("runpy", "Python");
addInputOnQueryField("runpy", "tj_globals.theme");
query("create");
cy.verifyToastMessage(commonSelectors.toastMessage, "Query Added");
cy.waitForAutoSave();
query("preview");
verifypreview("raw", `{"name":"light"}`);
@ -236,8 +240,11 @@ actions.unsetPageVariable('pageVar')`
verifypreview("raw", `Developer`);
addInputOnQueryField("runpy", "tj_globals.currentUser.groups");
query("preview");
cy.verifyToastMessage(commonSelectors.toastMessage, "Query completed.");
cy.wait(10000);
cy.verifyToastMessage(
commonSelectors.toastMessage,
"Query (runpy1) completed."
);
waitForQueryAction("preview");
verifypreview("raw", `["all_users","admin"]`);
if (Cypress.env("environment") != "Community") {
addInputOnQueryField("runpy", "tj_globals.mode.value");
@ -261,21 +268,20 @@ actions.unsetPageVariable('pageVar')`
const data = {};
data.customText = randomString(12);
selectQuery("Run Python code");
selectQueryFromLandingPage("runpy", "Python");
addInputOnQueryField(
"runpy",
"actions.showAlert('success', 'alert from runpy');"
);
query("create");
cy.verifyToastMessage(commonSelectors.toastMessage, "Query Added");
cy.waitForAutoSave();
query("run");
openEditorSidebar("button1");
selectEvent("On Click", "Run query", 1);
cy.get('[data-cy="query-selection-field"]').type("runpy1{enter}");
cy.get(commonWidgetSelector.draggableWidget("button1")).click();
cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runpy");
renameQueryFromEditor("newrunpy");
cy.wait(3000);
cy.get('[data-cy="event-handler"]').click();
cy.get('[data-cy="query-selection-field"]').should("have.text", "newrunpy");
@ -287,23 +293,26 @@ actions.unsetPageVariable('pageVar')`
const data = {};
data.customText = randomString(12);
selectQuery("Run Python code");
selectQueryFromLandingPage("runpy", "Python");
cy.waitForAutoSave();
addInputOnQueryField(
"runpy",
"actions.showAlert('success', 'alert from runpy');"
);
query("create");
cy.verifyToastMessage(commonSelectors.toastMessage, "Query Added");
cy.wait("@editQuery");
cy.wait(200);
cy.waitForAutoSave();
changeQueryToggles("run-on-app-load");
query("save");
cy.wait("@editQuery");
cy.waitForAutoSave();
cy.reload();
cy.wait(3000);
cy.verifyToastMessage(commonSelectors.toastMessage, "alert from runpy");
changeQueryToggles("confirmation-before-run");
query("save");
cy.wait("@editQuery");
cy.wait(200);
cy.waitForAutoSave();
cy.reload();
cy.wait(3000);
cy.get('[data-cy="modal-message"]').verifyVisibleElement(
"have.text",
"Do you want to run this query - runpy1?"
@ -315,10 +324,12 @@ actions.unsetPageVariable('pageVar')`
cy.get('[data-cy="success-message-input-field"]').clearAndTypeOnCodeMirror(
"Success alert"
);
query("save");
cy.forceClickOnCanvas();
cy.wait("@editQuery");
cy.wait(200);
cy.waitForAutoSave();
cy.reload();
cy.wait(4000);
cy.get('[data-cy="modal-confirm-button"]').realClick();
cy.get('[data-cy="modal-confirm-button"]', { timeout: 10000 }).realClick();
cy.verifyToastMessage(commonSelectors.toastMessage, "Success alert", false);
cy.verifyToastMessage(
commonSelectors.toastMessage,

View file

@ -29,6 +29,7 @@ import {
commonWidgetText,
codeMirrorInputLabel,
} from "Texts/common";
import { resizeQueryPanel } from "Support/utils/dataSource";
describe("Basic components", () => {
const data = {};
@ -37,7 +38,6 @@ describe("Basic components", () => {
cy.appUILogin();
cy.createApp();
cy.modifyCanvasSize(900, 900);
cy.get('[data-tooltip-id="tooltip-for-hide-query-editor"]').click();
cy.renameApp(data.appName);
cy.intercept("GET", "/api/comments/*").as("loadComments");
});
@ -617,7 +617,9 @@ describe("Basic components", () => {
});
it("Should verify Tabs", () => {
cy.dragAndDropWidget("Tabs", 50, 50);
cy.viewport(1200, 1300);
resizeQueryPanel("0");
cy.dragAndDropWidget("Tabs", 100, 100);
verifyComponent("tabs1");
deleteComponentAndVerify("image1");

View file

@ -368,7 +368,7 @@ describe("Table", () => {
verifyAndEnterColumnOptionInput("Text color", "red");
verifyAndEnterColumnOptionInput(
"Cell Background Color",
"{backspace}{backspace}{backspace}{backspace}{backspace}yellow"
"{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}yellow"
);
cy.get(
'[data-cy="input-and-label-cell-background-color"] > .form-label'

View file

@ -137,10 +137,11 @@ describe("Bulk user upload", () => {
force: true,
});
cy.get(usersSelector.buttonUploadUsers).click();
cy.wait(1000);
cy.get(".go2072408551")
.should("be.visible")
.and("have.text", "250 users are being added");
cy.wait(500);
cy.wait(1000);
common.searchUser("test12@gmail.com");
cy.contains("td", "test12@gmail.com")
.parent()
@ -152,4 +153,4 @@ describe("Bulk user upload", () => {
cy.get(groupsSelector.usersLink).click();
cy.contains("test12@gmail.com").should("be.visible");
});
});
});

View file

@ -35,15 +35,15 @@ describe("Profile Settings", () => {
cy.get(profileSelector.updateButton).click();
cy.verifyToastMessage(
commonSelectors.toastMessage,
profileText.nameErrorToast
profileText.firstNameErrorToast
);
cy.clearAndType(profileSelector.firstNameInput, profileText.firstName);
cy.get(profileSelector.updateButton).click();
cy.verifyToastMessage(
commonSelectors.toastMessage,
profileText.lastNameNameErrorToast
);
// cy.clearAndType(profileSelector.firstNameInput, profileText.firstName);
// cy.get(profileSelector.updateButton).click();
// cy.verifyToastMessage(
// commonSelectors.toastMessage,
// profileText.lastNameNameErrorToast
// );
cy.clearAndType(profileSelector.firstNameInput, randomFirstName);
cy.clearAndType(profileSelector.lastNameInput, randomLastName);
@ -241,4 +241,4 @@ describe("Profile Settings", () => {
cy.login(commonText.email, profileText.password);
common.logout();
});
});
});

View file

@ -13,7 +13,7 @@ Cypress.Commands.add(
cy.get(commonSelectors.signInButton).click();
cy.intercept("GET", "api/library_apps").as("apps");
cy.wait("@apps");
cy.wait(2000);
cy.wait(4000);
cy.get(commonSelectors.homePageLogo).should("be.visible");
}
);

View file

@ -5,6 +5,7 @@ import { commonSelectors, commonWidgetSelector } from "Selectors/common";
import moment from "moment";
import { dashboardSelector } from "Selectors/dashboard";
import { groupsSelector } from "Selectors/manageGroups";
import { groupsText } from "Texts/manageGroups";
export const navigateToProfile = () => {
cy.get(commonSelectors.profileSettings).click();
@ -15,8 +16,8 @@ export const navigateToProfile = () => {
export const logout = () => {
cy.get(commonSelectors.profileSettings).click();
cy.get(commonSelectors.logoutLink).click();
cy.intercept('GET', '/api/metadata').as('publicConfig');
cy.wait('@publicConfig');
cy.intercept("GET", "/api/metadata").as("publicConfig");
cy.wait("@publicConfig");
cy.wait(500);
};
@ -51,7 +52,7 @@ export const navigateToAllUserGroup = () => {
cy.wait(2000);
}
});
}
};
export const navigateToWorkspaceVariable = () => {
cy.get(commonSelectors.workspaceSettingsIcon).click();
@ -112,8 +113,7 @@ export const navigateToAppEditor = (appName) => {
cy.intercept("GET", "/api/v2/data_sources").as("appDs");
cy.wait("@appDs", { timeout: 15000 });
cy.skipEditorPopover();
}
else {
} else {
cy.intercept("GET", "/api/app-environments/**").as("appDs");
cy.wait("@appDs", { timeout: 15000 });
cy.skipEditorPopover();
@ -237,17 +237,24 @@ export const verifyTooltip = (selector, message) => {
export const pinInspector = () => {
cy.get(commonWidgetSelector.sidebarinspector).click();
cy.get(commonSelectors.inspectorPinIcon).click();
cy.intercept("GET", "/api/v2/data_sources").as("editor");
cy.reload();
cy.wait("@editor");
cy.wait(500);
cy.get("body").then(($body) => {
if (!$body.find(commonSelectors.inspectorPinIcon).length > 0) {
cy.get(commonWidgetSelector.sidebarinspector).click();
cy.get(commonSelectors.inspectorPinIcon).click();
cy.wait(500);
cy.intercept("GET", "/api/v2/data_sources").as("editor");
cy.reload();
cy.wait("@editor");
}
});
cy.reload();
cy.waitForAppLoad();
};
export const createGroup = (groupName) => {
cy.get(groupsSelector.createNewGroupButton).click();
cy.clearAndType(groupsSelector.groupNameInput, groupName);
cy.get(groupsSelector.createGroupButton).click();
cy.verifyToastMessage(
commonSelectors.toastMessage,
groupsText.groupCreatedToast
);
};

View file

@ -67,7 +67,7 @@ export const verifyAndModifyToggleFx = (
export const addDefaultEventHandler = (message) => {
cy.get(commonWidgetSelector.addEventHandlerLink)
.should("have.text", commonWidgetText.addEventHandlerLink)
.should("contain.text", commonWidgetText.addEventHandlerLink)
.click();
cy.get(commonWidgetSelector.eventHandlerCard).click();
cy.get(commonWidgetSelector.alertMessageInputField)

View file

@ -14,7 +14,7 @@ export const manageGroupsElements = () => {
groupsText.admin
);
navigateToAllUserGroup()
navigateToAllUserGroup();
cy.get(groupsSelector.groupPageTitle("All Users")).verifyVisibleElement(
"have.text",
@ -158,7 +158,7 @@ export const manageGroupsElements = () => {
);
cy.get(groupsSelector.workspaceVarCheckbox).uncheck();
navigateToAllUserGroup()
navigateToAllUserGroup();
cy.get(groupsSelector.groupLink("Admin")).click();
cy.get(groupsSelector.groupLink("Admin")).verifyVisibleElement(
"have.text",
@ -234,3 +234,30 @@ export const manageGroupsElements = () => {
"be.disabled"
);
};
export const addAppToGroup = (appName) => {
cy.get(groupsSelector.appsLink).click();
cy.wait(500);
cy.get(groupsSelector.appSearchBox).realClick();
cy.wait(500);
cy.get(groupsSelector.searchBoxOptions).contains(appName).click();
cy.get(groupsSelector.selectAddButton).click();
cy.contains("tr", appName)
.parent()
.within(() => {
cy.get("td input").eq(1).check();
});
cy.verifyToastMessage(
commonSelectors.toastMessage,
"App permissions updated"
);
};
export const addUserToGroup = (groupName, email) => {
cy.get(groupsSelector.usersLink).click();
cy.get(".select-search__input").type(email);
cy.get(".item-renderer").within(() => {
cy.get("input").check();
});
cy.get(`[data-cy="${groupName}-group-add-button"]`).click();
};

View file

@ -1,12 +1,12 @@
import { postgreSqlSelector } from "Selectors/postgreSql";
export const selectQuery = (dbName) => {
cy.get(postgreSqlSelector.buttonAddNewQueries).click();
export const selectQueryFromLandingPage = (dbName, label) => {
cy.get(
`[data-cy="${dbName.toLowerCase().replace(/\s+/g, "-")}-add-query-card"]`
)
.should("contain", dbName)
.should("contain", label)
.click();
cy.waitForAutoSave();
};
export const deleteQuery = (queryName) => {
@ -34,4 +34,12 @@ export const addInputOnQueryField = (field, data) => {
.click()
.clearAndTypeOnCodeMirror(`{backSpace}`);
cy.get(`[data-cy="${field}-input-field"]`).clearAndTypeOnCodeMirror(data);
cy.forceClickOnCanvas();
};
export const waitForQueryAction = (action) => {
cy.get(`[data-cy="query-${action}-button"]`, { timeout: 20000 }).should(
"not.have.class",
"button-loading"
);
};

View file

@ -67,8 +67,10 @@ export const verifyAndEnterColumnOptionInput = (label, value) => {
cy.get(`[data-cy="input-and-label-${cyParamName(label)}"]`)
.realClick()
.realPress(["Meta", "A"])
.realType(`{backspace}{backspace}{backspace}{backspace}`)
.realPress(["Meta", "A"])
.realType(
`{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}{backspace}${value}`
`{backspace}{rightarrow}{backspace}{rightarrow}{backspace}{rightarrow}{backspace}{rightarrow}{backspace}{rightarrow}{backspace}{rightarrow}{backspace}{rightarrow}{backspace}{backspace}{rightarrow}{backspace}{rightarrow}{backspace}{rightarrow}{backspace}{rightarrow}{backspace}{rightarrow}{backspace}{rightarrow}{backspace}{rightarrow}{backspace}{rightarrow}{backspace}{rightarrow}${value}`
);
};

View file

@ -11,6 +11,7 @@ import { dashboardSelector } from "Selectors/dashboard";
export const adminLogin = () => {
common.logout();
cy.appUILogin();
cy.wait(2000);
common.navigateToManageGroups();
};

View file

@ -0,0 +1,79 @@
# Create .env from this example file and replace values for the environment.
# The application expects a separate .env.test for test environment configuration
# Get detailed information about each variable here: https://docs.tooljet.com/docs/setup/env-vars
TOOLJET_HOST=http://localhost:80
LOCKBOX_MASTER_KEY= #replace_with_lockbox_master_key
SECRET_KEY_BASE= #replace_with_secret_key_base
# DATABASE CONFIG
ORM_LOGGING=all
PG_DB= # Databse name
PG_USER= # Postgres database username
PG_HOST= # Postgres database host
PG_PASS= # Postgres database password
PG_PORT=5432
# The above postgres values is set to its default state. If necessary, kindly modify it according to your personal preference.
# TOOLJET DATABASE
ENABLE_TOOLJET_DB=true
TOOLJET_DB= # Database name
TOOLJET_DB_USER= # Postgres database username
TOOLJET_DB_HOST= # Postgres database host
TOOLJET_DB_PASS= # Postgres database password
PGRST_HOST=postgrest
PGRST_DB_URI=
PGRST_JWT_SECRET= # If you have openssl installed, you can run the following command openssl rand -hex 32 to generate the value for PGRST_JWT_SECRET.
# Checks every 24 hours to see if a new version of ToolJet is available
# (Enabled by default. Set false to disable)
CHECK_FOR_UPDATES=true
# Checks every 24 hours to update app telemetry data to ToolJet hub.
# (Telemetry is enabled by default. Set value to true to disable.)
# DISABLE_TOOLJET_TELEMETRY=false
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
# EMAIL CONFIGURATION
DEFAULT_FROM_EMAIL=hello@tooljet.io
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_DOMAIN=
SMTP_PORT=
# DISABLE USER SIGNUPS (true or false). only applicable if Multi-Workspace feature is enabled
DISABLE_SIGNUPS=
# OBSERVABILITY
APM_VENDOR=
SENTRY_DNS=
SENTRY_DEBUG=
# FEATURE TOGGLE
COMMENT_FEATURE_ENABLE=
ENABLE_MULTIPLAYER_EDITING=true
ENABLE_MARKETPLACE_FEATURE=
# SSO (Applicable only for Multi-Workspace)
SSO_GOOGLE_OAUTH2_CLIENT_ID=
SSO_GIT_OAUTH2_CLIENT_ID=
SSO_GIT_OAUTH2_CLIENT_SECRET=
SSO_GIT_OAUTH2_HOST=
SSO_ACCEPTED_DOMAINS=
SSO_DISABLE_SIGNUPS=
#ONBOARDING
ENABLE_ONBOARDING_QUESTIONS_FOR_ALL_SIGN_UPS=
#session expiry in minutes
USER_SESSION_EXPIRY=2880
#TELEMETRY
DEPLOYMENT_PLATFORM=docker

View file

@ -3,25 +3,28 @@
# Get detailed information about each variable here: https://docs.tooljet.com/docs/setup/env-vars
TOOLJET_HOST=http://localhost:80
LOCKBOX_MASTER_KEY=replace_with_lockbox_master_key
SECRET_KEY_BASE=replace_with_secret_key_base
LOCKBOX_MASTER_KEY= # replace_with_lockbox_master_key
SECRET_KEY_BASE= # replace_with_secret_key_base
# DATABASE CONFIG
ORM_LOGGING=all
PG_DB=tooljet_production
PG_USER=postgres
PG_HOST=postgres
PG_PASS=postgres
PG_HOST=postgresql
PG_PASS= # postgres database password
# The above postgres values is set to its default state. If necessary, kindly modify it according to your personal preference.
# TOOLJET DATABASE
ENABLE_TOOLJET_DB=
TOOLJET_DB=
TOOLJET_DB_USER=
TOOLJET_DB_HOST=
ENABLE_TOOLJET_DB=true
TOOLJET_DB=tooljet_db
TOOLJET_DB_USER=postgres
TOOLJET_DB_HOST=postgresql
TOOLJET_DB_PASS=
PGRST_HOST=
PGRST_JWT_SECRET=
PGRST_DB_URI= # postgres://<postgres_username>:<postgres_password><@postgres_hostname>/<database_name>
PGRST_HOST=postgrest
PGRST_JWT_SECRET= # If you have openssl installed, you can run the following command openssl rand -hex 32 to generate the value for PGRST_JWT_SECRET.
# Checks every 24 hours to see if a new version of ToolJet is available
# (Enabled by default. Set false to disable)

View file

@ -4,6 +4,7 @@ services:
tooljet:
tty: true
stdin_open: true
container_name: Tooljet-app
image: tooljet/tooljet-ce:latest
restart: always
env_file: .env
@ -17,24 +18,25 @@ services:
command: npm run start:prod
postgres:
container_name: ${PG_HOST}
image: postgres:13
restart: always
ports:
- 5432:5432
volumes:
- postgres:/var/lib/postgresql/data
env_file: .env
environment:
- POSTGRES_PASSWORD=postgres
- POSTGRES_USER=${PG_USER}
- POSTGRES_PASSWORD=${PG_PASS}
# Uncomment if ENABLE_TOOLJET_DB=true
# postgrest:
# image: postgrest/postgrest:v10.1.1.20221215
# restart: always
# depends_on:
# - postgres
# env_file: .env
# environment:
# - PGRST_SERVER_PORT=80
postgrest:
container_name: postgrest
image: postgrest/postgrest:v10.1.1.20221215
restart: always
depends_on:
- postgres
env_file: .env
environment:
- PGRST_SERVER_PORT=80
volumes:
postgres:

View file

@ -4,6 +4,7 @@ services:
tooljet:
tty: true
stdin_open: true
container_name: Tooljet-app
image: tooljet/tooljet-ce:latest
restart: always
env_file: .env
@ -13,11 +14,10 @@ services:
SERVE_CLIENT: "true"
PORT: "80"
command: npm run start:prod
# Uncomment if ENABLE_TOOLJET_DB=true
# postgrest:
# image: postgrest/postgrest:v10.1.1.20221215
# restart: always
# env_file: .env
# environment:
# - PGRST_SERVER_PORT=80
# Uncomment if ENABLE_TOOLJET_DB=true
postgrest:
image: postgrest/postgrest:v10.1.1.20221215
restart: always
env_file: .env
environment:
- PGRST_SERVER_PORT=80

124
deploy/docker/external.sh Normal file
View file

@ -0,0 +1,124 @@
#!/bin/bash
# Load the .env file
source .env
# Check if LOCKBOX_MASTER_KEY is present or empty
if [[ -z "$LOCKBOX_MASTER_KEY" ]]; then
# Generate LOCKBOX_MASTER_KEY
LOCKBOX_MASTER_KEY=$(openssl rand -hex 32)
# Update .env file
awk -v key="$LOCKBOX_MASTER_KEY" '
BEGIN { FS=OFS="=" }
/^LOCKBOX_MASTER_KEY=/ { $2=key; found=1 }
1
END { if (!found) print "LOCKBOX_MASTER_KEY="key }
' .env > temp.env && mv temp.env .env
echo "Generated a secure master key for the lockbox"
else
echo "The lockbox master key already exists."
fi
# Check if SECRET_KEY_BASE is present or empty
if [[ -z "$SECRET_KEY_BASE" ]]; then
# Generate SECRET_KEY_BASE
SECRET_KEY_BASE=$(openssl rand -hex 64)
# Update .env file
awk -v key="$SECRET_KEY_BASE" '
BEGIN { FS=OFS="=" }
/^SECRET_KEY_BASE=/ { $2=key; found=1 }
1
END { if (!found) print "SECRET_KEY_BASE="key }
' .env > temp.env && mv temp.env .env
echo "Created a secret key for secure operations."
else
echo "The secret key base is already in place."
fi
# Check if PGRST_JWT_SECRET is present or empty
if [[ -z "$PGRST_JWT_SECRET" ]]; then
# Generate PGRST_JWT_SECRET
PGRST_JWT_SECRET=$(openssl rand -hex 32)
# Update .env file
awk -v key="$PGRST_JWT_SECRET" '
BEGIN { FS=OFS="=" }
/^PGRST_JWT_SECRET=/ { $2=key; found=1 }
1
END { if (!found) print "PGRST_JWT_SECRET="key }
' .env > temp.env && mv temp.env .env
echo "Generated a unique secret for PGRST authentication."
else
echo "The PGRST JWT secret is already generated and in place."
fi
# Function to generate a random password
generate_password() {
openssl rand -base64 12 | tr -d '/+' | cut -c1-16
}
# Check if PG_USER, PG_HOST, PG_PASS, PG_DB are present or empty
if [[ -z "$PG_USER" ]] || [[ -z "$PG_HOST" ]] || [[ -z "$PG_PASS" ]] || [[ -z "$PG_DB" ]]; then
# Prompt user for values
read -p "Enter PostgreSQL database username: " PG_USER
read -p "Enter PostgreSQL database hostname: " PG_HOST
read -p "Enter PostgreSQL database password: " PG_PASS
read -p "Enter PostgreSQL database name: " PG_DB
# Update .env file
awk -v pg_user="$PG_USER" -v pg_host="$PG_HOST" -v pg_pass="$PG_PASS" -v pg_db="$PG_DB" '
BEGIN { FS=OFS="=" }
/^PG_USER=/ { $2=pg_user; found=1 }
/^PG_HOST=/ { $2=pg_host; found=1 }
/^PG_PASS=/ { $2=pg_pass; found=1 }
/^PG_DB=/ { $2=pg_db; found=1 }
1
END {
if (!found) {
print "PG_USER="pg_user
print "PG_HOST="pg_host
print "PG_PASS="pg_pass
print "PG_DB="pg_db
}
}
' .env > temp.env && mv temp.env .env
echo "Successfully updated postgresql database values .env file"
fi
# Copy values from PG to TOOLJET_DB
TOOLJET_DB_USER=$PG_USER
TOOLJET_DB_HOST=$PG_HOST
TOOLJET_DB_PASS=$PG_PASS
# Update .env file for TOOLJET_DB
awk -v tj_user="$TOOLJET_DB_USER" -v tj_host="$TOOLJET_DB_HOST" -v tj_pass="$TOOLJET_DB_PASS" '
BEGIN { FS=OFS="=" }
/^TOOLJET_DB_USER=/ { $2=tj_user; found=1 }
/^TOOLJET_DB_HOST=/ { $2=tj_host; found=1 }
/^TOOLJET_DB_PASS=/ { $2=tj_pass; found=1 }
1
END { if (!found) print "TOOLJET_DB_USER="tj_user ORS "TOOLJET_DB_HOST="tj_host ORS "TOOLJET_DB_PASS="tj_pass }
' .env > temp.env && mv temp.env .env
echo "Successfully updated tooljet database values in the .env file"
# Construct PGRST_DB_URI with user-provided values
PGRST_DB_URI="postgres://$PG_USER:$PG_PASS@$PG_HOST/tooljet_db"
# Update .env file for PGRST_DB_URI
awk -v uri="$PGRST_DB_URI" '
BEGIN { FS=OFS="=" }
/^PGRST_DB_URI=/ { $2=uri; found=1 }
1
END { if (!found) print "PGRST_DB_URI="uri }
' .env > temp.env && mv temp.env .env
echo "Successfully updated PGRST database URI"
exec "$@"

101
deploy/docker/internal.sh Normal file
View file

@ -0,0 +1,101 @@
#!/bin/bash
# Load the .env file
source .env
# Check if LOCKBOX_MASTER_KEY is present or empty
if [[ -z "$LOCKBOX_MASTER_KEY" ]]; then
# Generate LOCKBOX_MASTER_KEY
LOCKBOX_MASTER_KEY=$(openssl rand -hex 32)
# Update .env file
awk -v key="$LOCKBOX_MASTER_KEY" '
BEGIN { FS=OFS="=" }
/^LOCKBOX_MASTER_KEY=/ { $2=key; found=1 }
1
END { if (!found) print "LOCKBOX_MASTER_KEY="key }
' .env > temp.env && mv temp.env .env
echo "Generated a secure master key for the lockbox"
else
echo "The lockbox master key already exists."
fi
# Check if SECRET_KEY_BASE is present or empty
if [[ -z "$SECRET_KEY_BASE" ]]; then
# Generate SECRET_KEY_BASE
SECRET_KEY_BASE=$(openssl rand -hex 64)
# Update .env file
awk -v key="$SECRET_KEY_BASE" '
BEGIN { FS=OFS="=" }
/^SECRET_KEY_BASE=/ { $2=key; found=1 }
1
END { if (!found) print "SECRET_KEY_BASE="key }
' .env > temp.env && mv temp.env .env
echo "Created a secret key for secure operations."
else
echo "The secret key base is already in place."
fi
# Check if PGRST_JWT_SECRET is present or empty
if [[ -z "$PGRST_JWT_SECRET" ]]; then
# Generate PGRST_JWT_SECRET
PGRST_JWT_SECRET=$(openssl rand -hex 32)
# Update .env file
awk -v key="$PGRST_JWT_SECRET" '
BEGIN { FS=OFS="=" }
/^PGRST_JWT_SECRET=/ { $2=key; found=1 }
1
END { if (!found) print "PGRST_JWT_SECRET="key }
' .env > temp.env && mv temp.env .env
echo "Generated a unique secret for PGRST authentication."
else
echo "The PGRST JWT secret is already generated and in place."
fi
# Function to generate a random password
generate_password() {
openssl rand -base64 12 | tr -d '/+' | cut -c1-16
}
# Check if PG_PASS and TOOLJET_DB_PASS are present or empty
if [[ -z "$PG_PASS" ]] && [[ -z "$TOOLJET_DB_PASS" ]]; then
# Generate random passwords
PASSWORD=$(generate_password)
# Update .env file
awk -v pass="$PASSWORD" '
BEGIN { FS=OFS="=" }
/^(PG_PASS|TOOLJET_DB_PASS)=/ { $2=pass; found=1 }
1
END { if (!found) print "PG_PASS="pass ORS "TOOLJET_DB_PASS="pass }
' .env > temp.env && mv temp.env .env
echo "Successfully generated a secure password for the PostgreSQL database."
else
echo "Postgres password already exist"
fi
# Check if PGRST_DB_URI is present or empty
if [[ -z "$PGRST_DB_URI" ]]; then
# Construct PGRST_DB_URI with PG_PASS
PGRST_DB_URI="postgres://postgres:$PASSWORD@postgresql/tooljet_db"
# Update .env file for PGRST_DB_URI
awk -v uri="$PGRST_DB_URI" '
BEGIN { FS=OFS="=" }
/^PGRST_DB_URI=/ { $2=uri; found=1 }
1
END { if (!found) print "PGRST_DB_URI="uri }
' .env > temp.env && mv temp.env .env
echo "Successfully updated PGRST database URI"
else
echo "The PGRST DB URI is already configured and in use."
fi
exec "$@"

View file

@ -4,7 +4,7 @@ RUN apt-get update && apt-get install -y postgresql-client freetds-dev libaio1 w
# Install Instantclient Basic Light Oracle and Dependencies
WORKDIR /opt/oracle
RUN wget wget https://tooljet-plugins-production.s3.us-east-2.amazonaws.com/marketplace-assets/oracledb/instantclients/instantclient-basiclite-linuxx64.zip && \
RUN wget https://tooljet-plugins-production.s3.us-east-2.amazonaws.com/marketplace-assets/oracledb/instantclients/instantclient-basiclite-linuxx64.zip && \
wget https://tooljet-plugins-production.s3.us-east-2.amazonaws.com/marketplace-assets/oracledb/instantclients/instantclient-basiclite-linux.x64-11.2.0.4.0.zip && \
unzip instantclient-basiclite-linuxx64.zip && rm -f instantclient-basiclite-linuxx64.zip && \
unzip instantclient-basiclite-linux.x64-11.2.0.4.0.zip && rm -f instantclient-basiclite-linux.x64-11.2.0.4.0.zip && \

View file

@ -37,7 +37,7 @@ They are commonly used to provide additional information to the server or to mod
## Using RunJS query to switch page
Alternatively, the switch page command can be activated via a RunJS query using the following syntax:
Alternatively, the switch page action can be activated via a RunJS query using the following syntax:
```js
await actions.switchPage('<page-handle>')
```
@ -46,3 +46,10 @@ await actions.switchPage('<page-handle>')
For instructions on how to run actions from a RunJS query, refer to the how-to guide [Running Actions from RunJS Query](/docs/how-to/run-actions-from-runjs).
:::
### Switch page with query params
The switch page action can also be triggered along with query parameters using the following syntax:
```js
actions.switchPage('<pageHandle>', [['param1', 'value1'], ['param2', 'value2']])
```

View file

@ -3,21 +3,58 @@ id: query-panel
title: Query Panel
---
The Query Panel is present at the bottom of the app-builder, this is where you create queries to interact with connected **local** and **global** datasources. You can perform API requests, query **[databases](/docs/data-sources/overview)**, or **[transform](/docs/tutorial/transformations)** or manipulate data with **[JavaScript](/docs/data-sources/run-js)** & **[Python](/docs/data-sources/run-py)**.
The Query Panel, located at the bottom of the app-builder, allows you to create and manage queries for interacting with connected **Default** and **Global** datasources. It provides the capability to perform API requests, query **[databases](/docs/data-sources/overview)**, and apply **[transformations](/docs/tutorial/transformations)** or data manipulation using **[JavaScript](/docs/data-sources/run-js)** and **[Python](/docs/data-sources/run-py)**.
The Query Panel has two sections:
- **[Query Manager](#query-manager)** on the right that includes a list of all the created queries
- **[Query Editor](#query-editor)** is used to configure the selected query
The Query Panel consists of two sections:
- The **[Query Manager](#query-manager)** on the right side, which displays a list of all the created queries.
- The **[Query Editor](#query-editor)**, used to configure the selected query.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui/querypanel.png" alt="App Builder: Component library- right sidebar"/>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/querypanel.png" alt="App Builder: Query Panel"/>
</div>
## Query Manager
Query Manager will list all the queries that has been created in the application. Query Manager is used to:
Query Manager will list all the queries that has been created in the application. Query Manager helps in managing the queries that have been created, you can **add**, **edit**, **delete**, **duplicate**, **search**, **sort** and **filter** through them.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/querymanager.png" alt="App Builder: Query Panel"/>
</div>
### Add
Add button is used to add new query in the application. When Add button is clicked, a menu will open with a list of options for creating a query from **Default** datasources such as **Rest API**, **ToolJet Database**, **JavaScript Code**, **Python Code** or from connected **Global Datasources**.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/addquery.gif" alt="App Builder: Query Panel"/>
</div>
### Sort/Filter
On the top of Query Manager, there is button to Sort or Filter queries. The following options are there:
**Filter:**
- By Datasource
**Sort:**
- Name: A-Z
- Name: Z-A
- Type: A-Z
- Type: Z-A
- Last modified: oldest first
- Last modified: newest First
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/sortfilter.gif" alt="App Builder: Query Panel"/>
</div>
### Search
@ -25,37 +62,37 @@ On the top of the query manager is search box that can be used to search for a s
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/search.png" alt="App Builder: Component library- right sidebar"/>
</div>
### Add
Add button is used to add more queries in the application. When Add button is clicked, the Query Editor will show you a list of options for creating a query from: **Rest API**, connected **[datasources](/docs/data-sources/overview)**, **[ToolJet Database](/docs/tooljet-database)**, **[JavaScript Code](/docs/data-sources/run-js)**, **[Python Code](/docs/data-sources/run-py)** or Add a new datasource.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui/add.png" alt="App Builder: Component library- right sidebar"/>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/search.gif" alt="App Builder: Query Panel"/>
</div>
### Delete
Delete button will delete the selected query, the button will only show up when you hover over the query name.
Delete button will delete the selected query, the button will only show up when you hover over the query name. When you click on the delete button, a confirmation dialog will open to confirm the deletion of the query.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/delete.png" alt="App Builder: Component library- right sidebar"/>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/delete.png" alt="App Builder: Query Panel"/>
</div>
### Edit
### Duplicate
Edit button is used edit the name of the selected query, the button will only show up when you hover over the query name.
Duplicate button will duplicate the selected query, the button will only show up when you hover over the query name. The duplicate query will be named as `<query name>_copy`.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/edit.png" alt="App Builder: Component library- right sidebar"/>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/duplicate.png" alt="App Builder: Query Panel"/>
</div>
### Rename
Rename button is used to rename the selected query, the button will only show up when you hover over the query name. When you click on the rename button, the query name becomes editable and you can change the name of the query.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/rename.png" alt="App Builder: Query Panel"/>
</div>
@ -63,9 +100,13 @@ Edit button is used edit the name of the selected query, the button will only sh
Query editor used to configure the query parameters, preview or transform the data return by the query.
:::info
The changes made in the query panel will be saved automatically.
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/editor.png" alt="App Builder: Component library- right sidebar"/>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/editor.png" alt="App Builder: Query Panel"/>
</div>
@ -73,45 +114,25 @@ Query editor used to configure the query parameters, preview or transform the da
On the top of the query panel there are a few options:
#### Query Name editor
#### Query Name
Edit the name of the query by clicking on the edit button next to the default query name.
The name of query is displayed on the top of the query panel. You can click on it to make it editable and change the name of the query.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/nameedit.png" alt="App Builder: Component library- right sidebar"/>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/renameeditor.gif" alt="App Builder: Query Panel"/>
</div>
#### Preview
Preview gives you a quick look at the data returned by the query without triggering the query in the app.
Preview button is used to preview the data returned by the query. The data will be displayed on the preview section present at the bottom of the query panel. This helps in debugging the query and see the data returned by the query without triggering the query in the app.
The Preview of data is returned in two different formats:
**Raw**
The Preview of data is returned in two different formats: **Raw** & **JSON**. You can click on the clear button to clear the preview data.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/raw.png" alt="App Builder: Component library- right sidebar"/>
</div>
**JSON**
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/json.png" alt="App Builder: Component library- right sidebar"/>
</div>
#### Save
Save is used to save the changes whenever a change is made in query.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/save.png" alt="App Builder: Component library- right sidebar"/>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/preview.gif" alt="App Builder: Query Panel"/>
</div>
@ -121,17 +142,29 @@ Run is used to trigger the query, running the query will interact with the appli
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/run.png" alt="App Builder: Component library- right sidebar"/>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/run.gif" alt="App Builder: Query Panel"/>
</div>
### Query Parameters
Query Parameters are the values required for the query to return a response from the server. Parameters include **endpoints**, **methods**, or **operations**. Query Parameters are different for each datasource.
Query Parameters are essential values that must be provided in a query for the server to generate a response. These parameters encompass **endpoints**, **methods**, or **operations**. It's important to note that the specific set of Query Parameters varies for each datasource.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/params.png" alt="App Builder: Component library- right sidebar"/>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/params.png" alt="App Builder: Query Panel"/>
</div>
#### Datasource
The primary and default parameter found in all queries is **Datasource**. This option allows you to choose the appropriate datasource for your query.
In cases where multiple datasources of the same type are connected, you can easily switch the query's datasource using the dropdown menu.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/switch.png" alt="App Builder: Query Panel"/>
</div>
@ -141,27 +174,35 @@ Transformations can be enabled on queries to transform the query results. ToolJe
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/transform.png" alt="App Builder: Component library- right sidebar"/>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/transform.gif" alt="App Builder: Query Panel"/>
</div>
### Advanced options
### Settings
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/settings.png" alt="App Builder: Query Panel"/>
</div>
#### Run this query on application load?
Enabling this option will fire the query every time the app is loaded.
Enabling this option will execute the query every time the app is loaded.
#### Request confirmation before running the query?
Enabling this option show a confirmation modal to confirm `Yes` or `No` if you want to fire that query.
#### Run this query on application load?
#### Show notification on success?
Enabling this option show a success toast notification when the query is successfully triggered.
#### Event Handlers
You can provide a custom **success message** and **notification duration** in milliseconds.
Event Handler are used to add some action when a particular event happens. You can add event handlers to the query for the following events:
### Events
Event handlers can be added to queries for the following events:
- **Query Success**
- **Query Failure**
@ -172,17 +213,6 @@ Learn more about [Event Handlers and Actions](/docs/widgets/overview#component-e
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/advanced.png" alt="App Builder: Component library- right sidebar"/>
</div>
### Change Datasource
If more than one datasources are connected of same type then you can change the datasource of the query from this dropdown.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui/switch.png" alt="App Builder: Component library- right sidebar"/>
<img className="screenshot-full" src="/img/v2-beta/app-builder/querypanel/newui2/events.png" alt="App Builder: Query Panel"/>
</div>

View file

@ -0,0 +1,140 @@
---
id: azureblob
title: Azure Blob
---
ToolJet offers the capability to establish a connection with Azure Blob storage in order to read and store large objects.
## Connection
To connect ToolJet with the Azure Blob global datasource, you have two options:
1. Click on the `+Add new global datasource` button in the query panel.
2. Go to the **[Global Datasources](/docs/data-sources/overview)** page on the ToolJet dashboard.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/azureblob/gdsazure.gif" alt="Azure Blob - ToolJet" />
</div>
To successfully establish the connection, ToolJet requires the following details:
- **Connection String**: The connection string can be found on the dashboard of Azure Blob Storage.
Once you have entered the connection string, click on the **Test connection** button to verify the connection's success. To save the datasource, click on the **Save** button.
## Querying Azure Blob
Once you have connected to the Azure Blob global datasource, follow these steps to create queries and interact with a Azure Blob storage from the ToolJet application:
1. Open the ToolJet application and navigate to the query panel at the bottom of the app builder.
2. Click the `+Add` button to open the list of available `local` and `global datasources`.
3. Select **Azure Blob** from the global datasource section.
4. Select the desired **operation** from the dropdown and enter the required **parameters**.
5. **Rename**(optional) and **Create** the query.
6. Click **Preview** to view the data returned from the query or click **Run** to execute the query.
:::tip
Query results can be transformed using Transformation. For more information on transformations, please refer to our documentation at **[link](/docs/tutorial/transformations)**.
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/azureblob/queries.png" alt="Azure Blob - ToolJet" />
</div>
## Supported operations
1. **[Create container](#create-container)**
2. **[List containers](#list-containers)**
3. **[List blobs](#list-blobs)**
4. **[Upload blob](#upload-blob)**
5. **[Read blob](#read-blob)**
6. **[Delete blob](#delete-blob)**
### Create container
The create container operation enables the creation of new containers within Azure Blob storage. Containers serve as logical units for organizing and managing blob data. Users can provide a unique name for the container. Once created, the container is available for use in storing and organizing blob data. If the container with the same name already exists, the operation fails.
#### Required parameters:
- **Container Name:** Name of the container that you want to create.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/azureblob/createcontainer.png" alt="Azure blob: create container operation" />
</div>
### List containers
The list container operation allows you to retrieve a list of containers within Azure Blob storage.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/azureblob/listcon.png" alt="Azure blob: list container operation" />
</div>
### List blobs
The list blobs operation enables you to retrieve a list of blobs within a specific container in Azure Blob storage.
#### Required parameter:
- **Container:** Specify the name of the container from which you wish to retrieve a list of blobs.
- **Page Size:** Specify the maximum number of blobs to be returned per page or request.
#### Optional parameters:
- **Prefix:** Filter the list of blobs based on a specific prefix or prefix pattern, allowing you to narrow down the results to blobs with names that start with the specified prefix.
- **Continuation Token:** A marker or token used to retrieve the next page of results when there are more blobs available beyond the initial page.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/azureblob/listblobs.png" alt="Azure blob: list blobs operation" />
</div>
### Upload blob
The upload blob operation allows you to upload a new blob or update an existing blob in Azure Blob storage. It provides a convenient way to store data such as files, images, or documents in the specified container.
#### Required parameters:
- **Container**: Specify the name of the container where the blob will be uploaded or updated.
- **Blob Name**: Provide a unique name for the blob. This name is used to identify and access the blob within the specified container.
- **Content Type**: Set the content type of the blob, which indicates the type of data being stored. It helps clients interpret the blob content correctly when accessing it. example: **image/jpeg** for JPEG images or **image/png** for PNG image. You can also get the content type from the exposed variable of the file picker component.
- **Upload Data**: Select or provide the data to be uploaded as the content of the blob. This can be a file from your local system, binary data, or text content. You can also get the data from the exposed variable of the file picker component.
- **Encoding**: Choose the encoding format for the uploaded data if applicable. This parameter determines how the data is encoded before being stored as the blob content. If the value is left blank then it takes **UTF-8** by default.
### Read blob
The read blob operation allows you to retrieve the content of a specific blob stored in Azure Blob storage. It enables you to access and retrieve the data stored within the blob for further processing or display.
#### Required parameters:
- **Container**: Specify the name of the container where the blob is located.
- **Blob Name**: Provide the unique name of the blob you want to read. This identifies the specific blob within the specified container
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/azureblob/read.png" alt="Azure blob: read blob operation" />
</div>
### Delete blob
The delete blob operation allows you to remove a specific blob from Azure Blob storage. This operation permanently deletes the blob and its associated data, freeing up storage space and removing the blob from the container.
#### Required parameters:
- **Container**: Specify the name of the container from which you want to delete the blob.
- **Blob Name**: Provide the unique name of the blob you want to delete. This identifies the specific blob within the specified container.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/azureblob/delete.png" alt="Azure blob: delete blob operation" />
</div>

View file

@ -0,0 +1,11 @@
# azureblobstorage
ToolJet can connect to Azure Blob Storage databases to read and write data.
- [Connection](#connection)
- [Getting Started](#querying-azureblobstorage)
## Connection
## Querying Azure Blob Storage

View file

@ -71,6 +71,16 @@ To execute the query, click the 'Run' button. Note that the query must be saved
You can apply transformations to the query results. Refer to our transformations documentation for more information: [link](/docs/tutorial/transformations)
:::
- **[List Tables](#list-tables)**
- **[Get Item](#get-item)**
- **[Query Table](#query-table)**
- **[Scan Table](#scan-table)**
- **[Delete Item](#delete-item)**
- **[Update Item](#update-item)**
- **[Describe Table](#describe-table)**
- **[Create Table](#create-table)**
- **[Put Item](#put-item)**
### List Tables
Returns an array of table names associated with the current account and endpoint. The output from List Tables is paginated, with each page returning a maximum of 100 table names.
@ -187,3 +197,133 @@ Syntax for Key name:
<img className="screenshot-full" src="/img/datasource-reference/dynamodb/deleteitem.png" alt="ToolJet - DynamoDB operations" />
</div>
### Update Item
Update an item in DynamoDB by specifying the primary key and providing new attribute values. If the primary key does not exist in the table then instead of updating it will insert a new row.
**Required parameters:**
- **Update Condition**
Syntax for Update Condition:
```json
{
"TableName": "USER_DETAILS_with_local",
"Key": {
"USER_ID": 1,
"USER_NAME": "Nick"
},
"UpdateExpression": "set USER_AGE = :age, USER_FEE = :fee",
"ExpressionAttributeValues": {
":age": 40,
":fee": 230545
}
}
```
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/dynamodb/updateitem.png" alt="ToolJet - DynamoDB operations" />
</div>
### Describe Table
This operation in DynamoDB retrieves metadata and configuration details about a specific table. It provides information such as the table's name, primary key schema, provisioned throughput settings, and any secondary indexes defined on the table.
**Required parameters:**
- **Table**
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/dynamodb/describetable.png" alt="ToolJet - DynamoDB operations" />
</div>
### Create Table
This operation in DynamoDB enables you to create a new table by specifying its name, primary key schema, and optional configurations.
**Required parameters:**
- **Table Parameters**
Syntax for Table Parameters:
```json
{
"AttributeDefinitions": [
{
"AttributeName": "USER_ID",
"AttributeType": "N"
},
{
"AttributeName": "USER_FEE",
"AttributeType": "N"
}
],
"KeySchema": [
{
"AttributeName": "USER_ID",
"KeyType": "HASH"
}
],
"LocalSecondaryIndexes": [
{
"IndexName": "USER_FEE",
"KeySchema": [
{
"AttributeName": "USER_ID",
"KeyType": "HASH"
},
{
"AttributeName": "USER_FEE",
"KeyType": "RANGE"
}
],
"Projection": {
"ProjectionType": "KEYS_ONLY"
}
}
],
"ProvisionedThroughput": {
"ReadCapacityUnits": 1,
"WriteCapacityUnits": 1
},
"TableName": "USER_FEE_LOCAL",
"StreamSpecification": {
"StreamEnabled": false
}
}
```
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/dynamodb/createtable.png" alt="ToolJet - DynamoDB operations" />
</div>
### Put Item
This operation allows you to create or replace an item in a table. It enables you to specify the table name, provide the attribute values for the new item, and define the primary key attributes to uniquely identify the item.
**Required parameters:**
- **New Item Details**
Syntax for New Item Details:
```json
{
"TableName": "USER_DETAILS_with_localS",
"Item": {
"USER_ID": 1,
"USER_NAME": "NICK",
"USER_AGE": 34,
"USER_FEE": 1234.56,
}
}
```
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/dynamodb/updateitem.png" alt="ToolJet - DynamoDB operations" />
</div>

View file

@ -3,36 +3,31 @@ id: graphql
title: GraphQL
---
# GraphQL
ToolJet can connect to GraphQL endpoints to execute queries and mutations.
ToolJet can establish connections with GraphQL endpoints, enabling the execution of queries and mutations.
## Connection
To add a new GraphQL datasource, click the `+` button on data sources panel at the bottom-left corner of the app builder and then select GraphQL from the modal that pops up.
ToolJet requires the following to connect to a GraphQL datasource:
- **URL of the GraphQL endpoint**
The following optional parameters are also supported:
| Type | Description |
| ----------- | ----------- |
| URL params | Additional query string parameters|
| headers | Any headers the GraphQL source requires|
To establish a connection with the GraphQL global datasource, you can either click on the **Add new global datasource** button located on the query panel or navigate to the **[Global Datasources](/docs/data-sources/overview)** page through the ToolJet dashboard.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/graphql/graphql-ds.png" alt="ToolJet - Data source - GraphQl"/>
<img className="screenshot-full" src="/img/datasource-reference/graphql/graphgds.gif" alt="ToolJet - Data source - REST API" />
</div>
Click on the **Save** button to save the data source.
ToolJet requires the following to connect to a GraphQL datasource:
- **URL**: URL of the GraphQL endpoint
- **Headers**: Any headers the GraphQL source requires
- **URL parameters**: Additional query string parameters
- __Authentication Type__: The method of authentication to use with GraphQL requests. Supported Types: None, Basic, Bearer, and OAuth 2.0
- **Basic**: Requires Username and Password
- **Bearer**: Requires a token, typically a JSON Web Token (JWT), to grant access
- **OAuth 2.0**: The OAuth 2.0 protocol mandates the provision of the following parameters: access token URL, access token URL custom headers, client ID, client secret, scopes, custom query parameters, authorization URL, custom authentication parameters, and client authentication.
## Querying GraphQL
Click on `+` button of the query manager at the bottom panel of the editor and select the GraphQL endpoint added in the previous step as the data source.
Click on **`+Add`** button of the query manager at the bottom panel of the editor and select the GraphQL global datasource added in previous step.
### Required Parameters:
- **Query**
@ -48,7 +43,7 @@ Click on `+` button of the query manager at the bottom panel of the editor and s
</div>
Click on the 'Create' button to create the query or Click on the `Run` button to create and trigger the query.
Click on the **Create** button to create the query or Click on the **Run** button to create and trigger the query.
:::tip
Query results can be transformed using transformations. Read our transformations documentation to see how: [link](/docs/tutorial/transformations)

View file

@ -3,42 +3,85 @@ id: redis
title: Redis
---
# Redis
ToolJet can run Redis commands on your Redis instances.
ToolJet enables you to execute Redis commands on your Redis instances.
## Connection
## Connecting to Redis
ToolJet requires the following to connect to your Redis instances.
To establish a connection with the Redis global datasource, you have two options. You can either click on the **`+Add new global datasource`** button on the query panel or access the **[Global Datasources](/docs/data-sources/overview)** page from the ToolJet dashboard.
<img class="screenshot-full" src="/img/redis/connect.png" alt="ToolJet - Redis connection" height="250"/>
<div style={{textAlign: 'center'}}>
- **Host**
- **Port** - The default port for Redis server is 6379
- **Username**
- **Password**
<img className="screenshot-full" src="/img/datasource-reference/redis/gdsredis.gif" alt="Redis" />
Click on "Test" button to test the connection and click "Save" to save the data source.
</div>
**To connect ToolJet with Redis, you need to provide the following connection details:**
- **Host**: The address or hostname of the Redis server
- **Port**: The port number used by the Redis server (default is 6379)
- **Username**: The username used for authentication
- **Password**: The password used for authentication
:::info
Click on **Test connection** button to verify if the credentials are correct and that the Redis is accessible to ToolJet server. Click on **Save** button to save the data source.
:::
## Redis Queries
List of supported commands: [Redis Official Documentation](https://redis.io/commands)
Here are some examples of Redis commands and their usage. You can refer to the [Redis Official Documentation](https://redis.io/commands) for a complete list of supported commands.
### Examples
### PING Command
`PING` command to test the Redis connection. If the connection is ready, the Redis server will respond with `PONG`.
The `PING` command is used to test the connection to Redis. If the connection is successful, the Redis server will respond with `PONG`.
```shell
PING
```
`SET` command can be used to set the value for a key
### SET Command
The `SET` command is used in Redis to assign a value to a specific key.
```shell
SET key value
```
`GET` command can be used to retrieve the value of a key
**Example 1/2:**
When the input value contains spaces, you should encode the value before providing it as an input:
```shell
SET products {{encodeURI('John Doe')}}
```
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/redis/encode.png" alt="Redis" />
</div>
### GET Command
The `GET` command is used in Redis to retrieve the value associated with a specific key.
```shell
GET key
```
**Example 2/2:**
To retrieve a value that was previously encoded while setting, you can use transformations.
- Enter the GET command in the editor:
```shell
GET products
```
- Enable Transformations (JS) and use `decodeURI`:
```js
return JSON.parse(decodeURI(data));
```
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/datasource-reference/redis/decode.png" alt="Redis" />
</div>

View file

@ -0,0 +1,151 @@
---
id: delete-multiple-rows
title: Delete multiple rows in table
---
The table component in the ToolJet has the option for bulk selection of rows that can have various use cases such as **updating** or **deleting** records. However, the datasources does not support bulk delete or bulk update operations.
In this guide, we will learn how we can delete multiple rows in a table. We have assumed that you have successfully connected the data source. For this guide, we will be using the PostgreSQL data source as an example database, currently, this workaround can be used only for PostgreSQL and MySQL.
## 1. Create a query to fetch the data from the database
Create a new query, name it `getRecords` and use SQL mode:
```sql
SELECT * FROM tooljet // replace tooljet with your table name
```
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/delete-rows/getRecords.png" alt="How-to: Delete multiple rows in table" />
</div>
<br/>
Enable the **Run the query on application load?** option. This will ensure that the query is executed when the application is loaded.
## 2. Load the data on the table
Now, we will load the data on the table. For this, we will use the `getRecords` query that we created in the previous step. Drag the table component from the right sidebar and drop it on the canvas.
On table properties, go to the table data property and set the value to `{{queries.getRecords.data}}`. This will load the data from the `getRecords` query on the table.
Run the query and you should see the data loaded on the table.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/delete-rows/querydata.png" alt="How-to: Delete multiple rows in table" />
</div>
<br/>
## 3. Enable bulk row selection on table
Now, we will enable the bulk row selection on the table. For this, go to the table properties and enable the **Bulk selection** option. Enabling this option will allow you to select multiple rows on the table. This option is disabled by default.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/delete-rows/bulkselection.png" alt="How-to: Delete multiple rows in table" />
</div>
<br/>
## 4. Create a custom javascript query
Now, we will create a custom javascript query that will **generate a SQL statement** to delete the selected rows from the table component based on a list of selected IDs, assuming the IDs are stored in the **id** column and that the name of the table component is **table1**. The actual database name should be replaced with **tooljet** as indicated in the SQL statemnent in the code below:
```js
const uniqueIdentifier = "id";
const idsToDelete = Object.values(components.table1.selectedRows).map(dataUpdate => dataUpdate[uniqueIdentifier]);
const idsString = idsToDelete.map(id => `'${id}'`).join(', ');
const SQL = `DELETE FROM tooljet WHERE ${uniqueIdentifier} IN (${idsString});`;
return SQL;
```
If you click on the **Preview** button, you should see the SQL statement generated by the query:
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/delete-rows/runjs.png" alt="How-to: Delete multiple rows in table" />
</div>
<br/>
Now, let's select a few rows on the table and then preview the SQL query generated by the javascript query:
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/delete-rows/runjs1.png" alt="How-to: Delete multiple rows in table" />
</div>
<br/>
## 5. Create a new query to delete the rows
Now, we will create a new query to delete the rows from the table. Create a new query, name it `delete` and use SQL mode:
```sql
{{queries.runjs1.data}} // replace runjs1 with the name of the javascript query
```
In this query, we are dynamically loading the SQL statement generated by the javascript query.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/delete-rows/delete.png" alt="How-to: Delete multiple rows in table" />
</div>
<br/>
## 6. Add a button to delete the selected rows
Now, we will add a button to delete the selected rows from the table. Drag the button component from the right sidebar and drop it on the canvas. Edit its properties and set the **Button text** to **Delete**.
Add a new **Event** to the button on **On click** event to trigger the **Run Query** action and select the `runjs1` query that we created in the previously.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/delete-rows/button.png" alt="How-to: Delete multiple rows in table" />
</div>
<br/>
Optionally, we can add a loading state to the button whenever the `delete` or `getRecords` query is running:
```js
{{queries.delete.isLoading || queries.getRecords.isLoading}}
```
Now, whenever you click on the button, the javascript query will generate a SQL statement to delete the selected rows from the table but to delete the rows from the database, we need to add event handler to the **runjs1** query to trigger the **delete** query whenever the `runjs1` query is **executed and successfull**.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/delete-rows/eventrunjs.png" alt="How-to: Delete multiple rows in table" />
</div>
<br/>
Now, whenever you click on the button, the javascript query will generate a delete SQL statement with selected rows on the table and the `delete` query will delete the rows from the database.
Similarly, you can add an Event to the **delete** query to trigger the **getRecords** query whenever the `delete` query is executed and successful. This will ensure that the table is updated with the latest data from the database.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/delete-rows/eventdelete.png" alt="How-to: Delete multiple rows in table" />
</div>
<br/>
## 7. Preview the application
The application is now ready. Click on the **Preview** button on the topbar of the app builder to preview the application.

View file

@ -11,7 +11,7 @@ In this how-to guide, we will import a few JavaScript libraries and use it in th
You can import any of the available libraries using their **CDN**. Find free CDN of the open source projects at **[jsDelivr](https://www.jsdelivr.com/)**
:::
- Create a new application and then create a new RunPy query from the query panel.
- Create a new application and then create a new RunJS query from the query panel.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/how-to/import-js/newquery.png" alt="Import external libraries using RunJS" />

View file

@ -43,6 +43,11 @@ To perform queries on HarperDB, click the `+Add` button in the query manager loc
SQL mode enables you to perform various operations on the database using SQL statements.
- **[Select](#select)**
- **[Insert](#insert)**
- **[Update](#update)**
- **[Delete](#delete)**
#### Select
The SELECT statement is used to query data from the database.
@ -97,4 +102,171 @@ DELETE FROM sampleorg.people WHERE id = 5
<img className="screenshot-full" src="/img/marketplace/plugins/harperdb/delete.png" alt="Marketplace: HarperDB" />
</div>
</div>
### NoSQL mode
NoSQL mode enables you to perform schema-less storage and retrieval of JSON documents.
- **[Insert](#insert-nosql)**
- **[Update](#update-nosql)**
- **[Delete](#delete-nosql)**
- **[Search by hash](#search-by-hash)**
- **[Search by value](#search-by-value)**
- **[SeleSearch by conditions](#search-by-conditions)**
#### Insert (NoSQL)
Insert operation allows to add one or more rows of data to a database table.
| Parameters | Description |
| ---------- | ----------- |
| Schema (required) | schema where the table you are inserting records into lives |
| Table (required) | table name where you want to insert records |
| Records (required) | array of one or more records for insert |
**Example Records:**
```js
[{id: 22, name: "James Scott", age: 26, country:"Italy", hobby: "football"},...]
```
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/marketplace/plugins/harperdb/nosql_insert.png" alt="Marketplace: HarperDB" />
</div>
#### Update (NoSQL)
The Update operation modifies the values of specified attributes in one or more rows of a database table based on the hash attribute(primary key) that identifies the rows.
| Parameters | Description |
| ---------- | ----------- |
| Schema (required) | schema where the table you are updating records into lives |
| Table (required) | table name where you want to update records |
| Records (required) | array of one or more records for update |
**Example Records:**
```js
[{id:12, name:"Jeff Hannistor"},...] // Record having 12 as Primary key value will be updated
```
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/marketplace/plugins/harperdb/nosql_update.png" alt="Marketplace: HarperDB" />
</div>
#### Delete (NoSQL)
Removes one or more rows of data from a specified table.
| Parameters | Description |
| ---------- | ----------- |
| Schema (required) | schema where the table you are deleting records into lives |
| Table (required) | table name where you want to delete records |
| Hash Values (required) | array of one or more hash attribute (primary key) values, which identifies records to delete |
**Example Hash Values:**
```js
[6, 15] // Records having 6 and 15 as Primary key value will be deleted
```
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/marketplace/plugins/harperdb/nosql_delete.png" alt="Marketplace: HarperDB" />
</div>
#### Search by hash
Returns data from a table for one or more hash values.
| Parameters | Description |
| ---------- | ----------- |
| Schema (required) | schema where the table you are searching lives |
| Table (required) | table you wish to search |
| Hash Values (required) | array of hashes to retrieve |
| Table Attributes (required) | define which attributes you want returned. |
**Example Hash Values:**
```js
[124, 66] // Records having 6 and 15 as Primary key value will be retrieved
```
**Example Table Attributes:**
```js
['id', 'name', 'age', 'hobby', 'country'] // Only the provided columns will be retrieved from the table
```
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/marketplace/plugins/harperdb/searchbyhash.png" alt="Marketplace: HarperDB" />
</div>
#### Search by value
Returns data from a table for a matching value.
| Parameters | Description |
| ---------- | ----------- |
| Schema (required) | schema where the table you are searching lives |
| Table (required) | table you wish to search |
| Hash Values (required) | array of hashes to retrieve |
| Search Attribute (required) | attribute you wish to search can be any attribute |
| Search Value (required) | value you wish to search - wild cards are allowed. |
| Table Attributes (required) | define which attributes you want returned. |
**Example Search Attribute:**
```bash
name
```
**Example Search Value:**
```bash
John Doe
or
Joh* // using wild card
```
**Example Table Attributes:**
```js
['id', 'name', 'age', 'hobby', 'country'] // Only the provided columns will be retrieved from the table
```
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/marketplace/plugins/harperdb/searchbyvalue.png" alt="Marketplace: HarperDB" />
</div>
#### Search by conditions
Returns data from a table for one or more matching conditions.
| Parameters | Description |
| ---------- | ----------- |
| Schema (required) | schema where the table you are searching lives |
| Table (required) | table you wish to search |
| Operator inbetween each condition (optional) | the operator used between each condition - 'And', 'Or'. The default is 'And'. |
| Offset (optional) | the number of records that the query results will skip. The default is 0. |
| Limit (optional) | the number of records that the query results will include. The default is null, resulting in no limit. |
| Table Attributes (required) | define which attributes you want returned. |
| Conditions to filter (required) | the array of conditions objects, to filter by. Must include one or more object in the array. **search_attribute** (required) - the attribute you wish to search, can be any attribute. **search_type** (required) - the type of search to perform - 'equals', 'contains', 'starts_with', 'ends_with', 'greater_than', 'greater_than_equal', 'less_than', 'less_than_equal', 'between'. **search_value** (required) - case-sensitive value you wish to search. If the search_type is 'between' then use an array of two values to search between. Check the example below. |
**Example Table Attributes:**
```js
['id', 'name', 'age', 'hobby', 'country'] // Only the provided columns will be retrieved from the table
```
**Example Conditions to filter:**
```js
[{'search_attribute': 'age', 'search_type': 'between', 'search_value': [20, 28]}, {'search_attribute': 'name', 'search_type': 'contains', 'search_value': 'Ray'}]
```
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/marketplace/plugins/harperdb/searchbyconditions.png" alt="Marketplace: HarperDB" />
</div>

View file

@ -0,0 +1,83 @@
---
id: workspace-variables
title: Workspace Variables
---
:::caution
Workspace variables are currently marked as deprecated, indicating that it will be removed in future releases. In the current version, you are still able to delete existing variables and use it through out any ToolJet apps, but creating and updating variables are no longer supported.
Please use [Workspace Constants](/docs/org-management/workspaces/workspace_constants) instead.
:::
Workspace Variables are the variables with some value(usually tokens/secret keys/API keys) that can be used in different apps across the same Workspace.
:::note
Server variables will not resolve if you use bracket notation. This is because bracket notation is not supported on the server-side, where server variables are resolved. If you use bracket notation in a query that is executed on the server, the query will fail. To avoid this, use dot notation to resolve workspace variables in queries.
:::
## How can we add these variables to an Workspace?
Suppose there is an `API key` or a value that you want to use in the queries or widgets in the multiple apps of the same Workspace then the Workspace admin or the user with permissions can add an environment variable.
#### Adding the environment variable
- Go to the ToolJet Dashboard, and click on the dropdown on the navigation bar to show `Workspace` options
- Select `Manage Environment Variables`
- Click on `Add New Variable` button
- Give a `Name` to the variable, set the value, choose `Type`, toggle `Encryption`, and click **Add Variable** button
- Now this variable can be used inside any application of this Workspace
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/tutorial/use-env-org-vars/work-var2.gif" alt="add variable" />
</div>
### Types of variables
- **Client**: The client variable can be utilized in components, queries, and global datasources.
- **Server**: The server variables can be employed in all queries except for `RunJS` and the connection form for global datasources. The restriction on using server variables with components is due to their resolution occurring solely during runtime, ensuring a high level of security.
:::info
Variable Type cannot be changed once it has been created.
:::
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/tutorial/use-env-org-vars/variable-type.png" alt="variable-type" width="700"/>
</div>
### Encryption
This feature enables us to add a client variable with and without `encryption`. The server variables are always encrypted by default.
### Using variable in an app
Let's use the variable that we created [here](/docs/tutorial/workspace-variables/#adding-the-environment-variable). If you have used ToolJet before, then you know that for getting the values from any variable we use JS notation i.e. `{{}}` but for using the Workspace variables we have different opening and closing notation `%% %%`. The environment variables will not work inside js code `{{}}`.
So, the syntax for using the variable that we created before will be `%%client.pi%%`
**Example for client variable usage:**
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/tutorial/use-env-org-vars/variable-usage.png" alt="variable-usage" width="700"/>
</div>
**Example for server variable usage:**
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/tutorial/use-env-org-vars/server-variable-usage.png" alt="server-variable-usage" width="700" />
</div>
Starting from ToolJet version `2.10.0` and onwards, it is possible to utilize Server-type workspace variables in the global datasources connection form.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/tutorial/use-env-org-vars/varingds.gif" alt="server-variable-usage" />
</div>

View file

@ -0,0 +1,82 @@
---
id: workspace_constants
title: Workspace Constants
---
Workspace constants are predefined values(usually tokens/secret keys/API keys) that can be used across your application to maintain consistency and facilitate easy updates. They allow you to store important data or configurations that should remain unchanged during the application's runtime. This doc will guide you through the usage and management of workspace constants within your workspaces.
## Environment-Specific Configurations
Users can define environment-specific configurations by setting different values for constants across environments. It is useful for managing sensitive information such as API keys, database credentials, or external service endpoints. For Community edition only production environment is available and for Cloud/EE we will have multi environments (development, staging, production).
## Server-Side Resolution
Workspace constants are designed to be resolved on the server side only. This means that when you make network calls, the payload sent will not include the actual values of the constants. Instead, the server will resolve the constants and use their actual values while processing the requests. This ensures that the constants remain secure and are not exposed to the client-side.
## Access Control
Creating, updating, and deleting constants are exclusive privileges granted to **Admins** (workspaces). Only users with administrative rights can perform these operations. Workspace constants are specific to the workspace where they are created and cannot be utilized in other workspaces.
## Usage in App Builder and Global Datasource connection
All users with edit app permissions have access to consume and utilize constants in the app builder and global datasource connection forms. This enables you to use the same constant values across different components of your application, ensuring consistency and reducing duplication of effort.
## Syntax
To use a workspace constant, you need to follow the syntax: **`{{constants.constant_name}}`**. For example, if you have a constant named "psql_host", you can access its value by using `{{constants.psql_host}}`.
## Creating Workspace Constants
To create workspace constants, follow these steps:
- Access the ToolJet Dashboard and navigate to Workspace Settings.
- Select the Workspace Constants tab.
- Click on the **Create New Constant** button.
- A drawer will appear; enter the desired name and value for the constant.
- Click the **Add Constant** button to save the constant.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/workspace-const/createconstant.gif" alt="Workspace constants: create"/>
</div>
- If you are an admin, you have the privilege to edit or delete constants. However, if you are a user with edit app permissions in the workspace, you can only view the constants and consume them in the app builder and global datasource connection forms.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/workspace-const/editdelete.png" alt="Workspace constants: edit/delete"/>
</div>
## Using Workspace Constants
Workspace constants can be used in the app builder and the global datasource connection forms.
### Using Workspace Constants in Global Datasource Connection
You can use workspace constants in the **[global datasource connection](/docs/data-sources/overview#connecting-global-datasources)** form to store sensitive information like API keys, tokens, etc. This will ensure that the data remains secure and is not exposed to the client-side. You can use the syntax `{{constants.constant_name}}` to access the value of the constant.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/workspace-const/globaldatasource.png" alt="Workspace constants: global datasource"/>
</div>
### Using Workspace Constants in App Builder
Inside the App Builder, you will find the **[Inspector](/docs/app-builder/left-sidebar#inspector)** on the left sidebar. The inspector will have a Constants section which will be updated dynamically to display all the available constant values.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/workspace-const/inspector.png" alt="Workspace constants: inspector"/>
</div>
As you build the application, you can easily refer to the constants and incorporate them into different elements of your app.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/workspace-const/querypanel.png" alt="Workspace constants: querypanel"/>
</div>
With workspace constants, you can streamline your application's configuration and maintain a consistent experience for your users. By leveraging this feature, you can ensure that vital data remains secure while making it accessible for authorized users throughout the application building process.

View file

@ -0,0 +1,42 @@
---
id: workspace_overview
title: 'Workspace: Overview'
---
# Workspace: Overview
User can create their own workspaces, user who created workspace will be having admin privileges for the workspace.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/multiworkspace/multiwork2.gif" alt="multi workspace" />
</div>
## Hierarchy
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/multiworkspace/Tooljet-workspace.png" alt="tooljet workspace" />
</div>
## Permissions
:::tip
Please check the detailed doc on **[Permissions](/docs/org-management/permissions)**.
:::
- The administrator can manage [users and groups](/docs/tutorial/manage-users-groups) of each workspace
- Applications and settings can not be shared between workspaces
- A user authorised to login to ToolJet will not have access to all workspaces, Users should be invited or signed up to a workspace to log-in to it.
- When Multi-Workspace feature is enabled, user should login with username and password to log in to Tooljet.
- Administrator can configure authentication methods for their workspaces.
- If password login is enabled, switching to the workspace will happen without any other authorization since the user is already authorized with password login.
- User logged in to Tooljet and trying to switch to a workspace where SSO is enabled and password login is disabled, will be redirected to workspace login page and enabled SSO options will be shown
- User can directly login to a workspace using workspace login URL, Administrator can view the URL **Manage SSO -> General Settings -> Login URL**.
### When disabled (Super Admin)
- Only **[Super Admins](/docs/Enterprise/superadmin#restrict-creation-of-personal-workspace-of-users)** can disable the option for creating personal workspaces for a user.
- If creating personal workspaces is disabled, Create workspace feature wont be available.
- No separate login page for workspace and SSO configured for the workspace will be reflected to the main login page/login.

View file

@ -9,6 +9,8 @@ title: Azure container apps
Please note that you need to set up a PostgreSQL database manually to be used by ToolJet
:::
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
## Deploying ToolJet application
1. Open the Azure dashboard at https://portal.azure.com, navigate to Container Apps, and click on "Create container app".

View file

@ -12,6 +12,8 @@ You can build standalone client with the below command:
SERVE_CLIENT=false npm run build
```
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
## Deploying ToolJet client on Firebase
:::tip

View file

@ -7,6 +7,8 @@ title: DigitalOcean
Now you can quickly deploy ToolJet using the Deploy to DigitalOcean button.
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
## Deploying
#### Follow the steps below to deploy ToolJet on DigitalOcean:

View file

@ -11,8 +11,11 @@ import TabItem from '@theme/TabItem';
Follow the steps below to deploy ToolJet on a server using Docker Compose. ToolJet requires a PostgreSQL database to store applications definitions, (encrypted) credentials for datasources and user authentication data.
:::info
If you rather want to try out ToolJet on your local machine with Docker, you can follow the steps [here](/docs/setup/try-tooljet).
If you rather want to try out ToolJet on your local machine with Docker, you can follow the steps [here](/docs/setup/try-tooljet/).
:::
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
### Installing Docker and Docker Compose
Install docker and docker-compose on the server.
- Docs for [Docker Installation](https://docs.docker.com/engine/install/)
@ -21,74 +24,14 @@ Install docker and docker-compose on the server.
### Deployment options
There are two options to deploy ToolJet using Docker Compose:
1. **Using an external PostgreSQL database**. This setup is recommended if you want to use a managed PostgreSQL service such as AWS RDS or Google Cloud SQL.
2. **Using in-built PostgreSQL database**. This setup uses the official Docker image of PostgreSQL.
1. **With in-built PostgreSQL database (recommended)**. This setup uses the official Docker image of PostgreSQL.
2. **With external PostgreSQL database**. This setup is recommended if you want to use a managed PostgreSQL service such as AWS RDS or Google Cloud SQL.
Confused about which setup to select? Feel free to ask the community via Slack: https://tooljet.com/slack.
:::info
We recommend using the managed PostgreSQL service on production for ease of administration, security, and management (backups, monitoring, etc).
If you'd want to run postgres with persistent volume rather, curl for the alternate docker compose file shared in the next step.
:::
<Tabs>
<TabItem value="with-external-postgres" label="With external PostgreSQL" default>
1. Setup a PostgreSQL database and make sure that the database is accessible.
2. Download our production docker-compose file into the server.
```bash
curl -LO https://raw.githubusercontent.com/ToolJet/ToolJet/main/deploy/docker/docker-compose.yaml
```
3. Create `.env` file in the current directory (where the docker-compose.yaml file is downloaded):
```bash
curl -LO https://raw.githubusercontent.com/ToolJet/ToolJet/main/deploy/docker/.env.example
mv .env.example .env
```
Set up environment variables in `.env` file as explained in [environment variables reference](/docs/setup/env-vars)
`TOOLJET_HOST` environment variable can either be the public ipv4 address of your server or a custom domain that you want to use.
Examples:
`TOOLJET_HOST=http://12.34.56.78` or
`TOOLJET_HOST=https://yourdomain.com` or
`TOOLJET_HOST=https://tooljet.yourdomain.com`
:::info
Please make sure that `TOOLJET_HOST` starts with either `http://` or `https://`
:::
:::info
If there are self signed HTTPS endpoints that Tooljet needs to connect to, please make sure that `NODE_EXTRA_CA_CERTS` environment variable is set to the absolute path containing the certificates.
:::
4. Once you've populated the `.env` file, run
:::note
Kindly uncomment PostgREST service within the [docker-compose.yaml](https://raw.githubusercontent.com/tooljet/tooljet/main/deploy/docker/docker-compose.yaml) if you intend to use tooljet database.
:::
```bash
docker-compose up -d
```
to start all the required services.
:::info
If you're running a linux server, `docker` might need sudo permissions. In that case you can either run:
`sudo docker-compose up -d`
or
setup docker to run without root privileges by following the instructions written here https://docs.docker.com/engine/install/linux-postinstall/
:::
5. If you've set a custom domain for `TOOLJET_HOST`, add a `A record` entry in your DNS settings to point to the IP address of the server.
</TabItem>
<TabItem value="with-in-built-postgres" label="With in-built PostgreSQL">
<TabItem value="with-in-built-postgres" label="With in-built PostgreSQL" default>
1. Download our production docker-compose file into the server.
```bash
@ -97,50 +40,94 @@ If you'd want to run postgres with persistent volume rather, curl for the altern
mkdir postgres_data
```
2. Create `.env` file in the current directory (where the docker-compose.yaml file is downloaded):
2. Create `.env` file in the current directory (where the docker-compose.yaml file is downloaded as in step 1):
```bash
curl -LO https://raw.githubusercontent.com/ToolJet/ToolJet/main/deploy/docker/.env.example
mv .env.example .env
curl -LO https://raw.githubusercontent.com/ToolJet/ToolJet/main/deploy/docker/.env.internal.example
curl -LO https://raw.githubusercontent.com/ToolJet/ToolJet/main/deploy/docker/internal.sh && chmod +x internal.sh
mv .env.internal.example .env && ./internal.sh
```
Set up environment variables in `.env` file as explained in [environment variables reference](/docs/setup/env-vars)
`TOOLJET_HOST` environment variable can either be the public ipv4 address of your server or a custom domain that you want to use.
Examples:
`TOOLJET_HOST=http://12.34.56.78` or
`TOOLJET_HOST=https://yourdomain.com` or
`TOOLJET_HOST=https://tooljet.yourdomain.com`
:::info
Please make sure that `TOOLJET_HOST` starts with either `http://` or `https://`
:::
:::info
If there are self signed HTTPS endpoints that Tooljet needs to connect to, please make sure that `NODE_EXTRA_CA_CERTS` environment variable is set to the absolute path containing the certificates.
:::
3. Once you've populated the `.env` file, run
:::note
Kindly uncomment PostgREST service within the [docker-compose.yaml](https://raw.githubusercontent.com/ToolJet/ToolJet/main/deploy/docker/docker-compose-db.yaml) if you intend to use tooljet database.
:::
3. To start the docker container, use the following command:
```bash
docker-compose up -d
```
to start all the required services.
4. **(Optional)** `TOOLJET_HOST` environment variable can either be the public ipv4 address of your server or a custom domain that you want to use. Which can be modified in the .env file.
Also, for setting up additional environment variables in the .env file, please check our documentation on [environment variable](/docs/setup/env-vars)
Examples:
`TOOLJET_HOST=http://12.34.56.78` or
`TOOLJET_HOST=https://tooljet.yourdomain.com`
If you've set a custom domain for `TOOLJET_HOST`, add a `A record` entry in your DNS settings to point to the IP address of the server.
:::info
If you're running on a linux server, `docker` might need sudo permissions. In that case you can either run:
i. Please make sure that `TOOLJET_HOST` starts with either `http://` or `https://`
ii. Setup docker to run without root privileges by following the instructions written here https://docs.docker.com/engine/install/linux-postinstall/
iii. If you're running on a linux server, `docker` might need sudo permissions. In that case you can either run:
`sudo docker-compose up -d`
OR
Setup docker to run without root privileges by following the instructions written here https://docs.docker.com/engine/install/linux-postinstall/
:::
4. If you've set a custom domain for `TOOLJET_HOST`, add a `A record` entry in your DNS settings to point to the IP address of the server.
</TabItem>
<TabItem value="with-external-postgres" label="With external PostgreSQL">
1. Setup a PostgreSQL database and make sure that the database is accessible.
2. Download our production docker-compose file into the server.
```bash
curl -LO https://raw.githubusercontent.com/ToolJet/ToolJet/main/deploy/docker/docker-compose.yaml
```
3. Create `.env` file in the current directory (where the docker-compose.yaml file is downloaded as in step 1):
Kindly set the postgresql database credentials according to your external database. Please enter the database details with the help of the bash as shown below.
<div style={{textAlign: 'center'}}>
<img className="screenshot-full" src="/img/setup/docker/bash.gif"/>
</div>
```bash
curl -LO https://raw.githubusercontent.com/ToolJet/ToolJet/main/deploy/docker/.env.external.example
curl -LO https://raw.githubusercontent.com/ToolJet/ToolJet/main/deploy/docker/external.sh && chmod +x external.sh
mv .env.external.example .env && ./external.sh
```
4. To start the docker container, use the following command:
```bash
docker-compose up -d
```
5. **(Optional)** `TOOLJET_HOST` environment variable can either be the public ipv4 address of your server or a custom domain that you want to use. Which can be modified in the .env file.
Also, for setting up additional environment variables in the .env file, please check our documentation on [environment variable](/docs/setup/env-vars)
Examples:
`TOOLJET_HOST=http://12.34.56.78` or
`TOOLJET_HOST=https://tooljet.yourdomain.com`
If you've set a custom domain for `TOOLJET_HOST`, add a `A record` entry in your DNS settings to point to the IP address of the server.
:::info
i. Please make sure that `TOOLJET_HOST` starts with either `http://` or `https://`
ii. If there are self signed HTTPS endpoints that Tooljet needs to connect to, please make sure that `NODE_EXTRA_CA_CERTS` environment variable is set to the absolute path containing the certificates.
iii. If you're running a linux server, `docker` might need sudo permissions. In that case you can either run:
`sudo docker-compose up -d`
iv. Setup docker to run without root privileges by following the instructions written here https://docs.docker.com/engine/install/linux-postinstall/
:::

View file

@ -7,7 +7,9 @@ title: AWS EC2
:::info
You should setup a PostgreSQL database manually to be used by the ToolJet server.
:::
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
Follow the steps below to deploy ToolJet on AWS EC2 instances.

View file

@ -9,6 +9,8 @@ title: AWS ECS
You should setup a PostgreSQL database manually to be used by ToolJet.
:::
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
Follow the steps below to deploy ToolJet on a ECS cluster.
1. Setup a PostgreSQL database

View file

@ -7,6 +7,8 @@ title: Environment variables
Both the ToolJet server and client requires some environment variables to start running.
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
## ToolJet server
### ToolJet host ( required )

View file

@ -9,6 +9,8 @@ title: Google Cloud Run
You should setup a PostgreSQL database manually to be used by ToolJet.
:::
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
Follow the steps below to deploy ToolJet on Cloud run with `gcloud` CLI.

View file

@ -7,6 +7,8 @@ title: Helm
This repository contains Helm charts for deploying [ToolJet](https://github.com/ToolJet/helm-charts) on a Kubernetes Cluster using Helm v3. The charts include an integrated PostgreSQL server that is enabled by default. However, you have the option to disable it and configure a different PostgreSQL server by updating the `values.yml` file.
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
## Installation
### From Helm repo

View file

@ -7,6 +7,9 @@ title: Heroku
<iframe width="800" height="500" src="https://www.youtube.com/embed/ApDtwE1OXY0" frameborder="0" allowfullscreen width="100%"></iframe>
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
### Follow the steps below to deploy ToolJet on Heroku:
1. Click the button below to start one click deployment.

View file

@ -10,3 +10,5 @@ Server will connect to internet via the configured HTTP proxy when this environm
| variable | description |
| ----------------------- | ------------------------------------- |
| TOOLJET_HTTP_PROXY | used for both HTTP and HTTPS requests |
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*

View file

@ -8,5 +8,3 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
<DocsCardList list={useCurrentSidebarCategory().items} />
```
If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.

View file

@ -9,6 +9,8 @@ title: Kubernetes (AKS)
You should setup a PostgreSQL database manually to be used by ToolJet. We recommend using Azure Database for PostgreSQL since this guide is for deploying using AKS.
:::
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
Follow the steps below to deploy ToolJet on a AKS Kubernetes cluster.
1. Create an AKS cluster and connect to it to start with the deployment. You can follow the steps as mentioned on the [Azure's documentation](https://docs.microsoft.com/en-us/azure/aks/kubernetes-walkthrough-portal).

View file

@ -9,6 +9,8 @@ title: Kubernetes (GKE)
You should setup a PostgreSQL database manually to be used by ToolJet. We recommend using Cloud SQL since this guide is for deploying using GKE.
:::
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
Follow the steps below to deploy ToolJet on a GKE Kubernetes cluster.
1. Create an SSL certificate.

View file

@ -9,6 +9,8 @@ title: Kubernetes
You should setup a PostgreSQL database manually to be used by ToolJet.
:::
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
Follow the steps below to deploy ToolJet on a Kubernetes cluster.
1. Setup a PostgreSQL database
@ -41,6 +43,7 @@ If there are self signed HTTPS endpoints that Tooljet needs to connect to, pleas
5. Create a Kubernetes services to publish the Kubernetes deployment that you've created. This step varies with cloud providers. We have a [template](https://raw.githubusercontent.com/ToolJet/ToolJet/main/deploy/kubernetes/service.yaml) for exposing the ToolJet server as a service using an AWS loadbalancer.
**Examples:**
- [Application load balancing on Amazon EKS](https://docs.aws.amazon.com/eks/latest/userguide/alb-ingress.html)
- [GKE Ingress for HTTP(S) Load Balancing](https://cloud.google.com/kubernetes-engine/docs/concepts/ingress)

View file

@ -9,6 +9,8 @@ title: Openshift
You should setup a PostgreSQL database manually to be used by ToolJet.
:::
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
Follow the steps below to deploy ToolJet on Openshift.
1. Setup a PostgreSQL database ToolJet uses a postgres database as the persistent storage for storing data related to users and apps. We do not have plans to support other databases such as MySQL.

View file

@ -5,6 +5,8 @@ title: Deploying ToolJet on a subpath
ToolJet can now be deployed at a subpath rather than the root (`/`) of a public domain. Example subpath installation URL: **`http://www.yourcompany.com/apps/tooljet`**
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
You'll need to setup the following environment variables if ToolJet installation is on a domain subpath:
| variable | value |

View file

@ -4,6 +4,7 @@ title: Try ToolJet
---
# Try ToolJet
## On local with Docker
You can run the command below to have ToolJet up and running right away.
@ -16,6 +17,8 @@ docker run \
-v tooljet_data:/var/lib/postgresql/13/main \
tooljet/try:latest
```
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
#### Setup information

View file

@ -13,6 +13,7 @@ ToolJet version 2 comes with a bunch of exciting features, with the major ones b
Checkout the latest changelog for v2 [here](https://github.com/ToolJet/ToolJet/releases).
*If you have any questions feel free to join our [Slack Community](https://tooljet.com/slack) or send us an email at hello@tooljet.com.*
## Deployment
Based on your opted deployment method from our [setup doc](/docs/setup/), you can directly deploy v2 without any additional configuration for the default setup.

View file

@ -49,7 +49,7 @@ On file loaded event is triggered when a file is loaded in the browser.
On file selected event can be triggered when one or more files are removed from the picker.
:::info
Check [Action Reference](/docs/category/actions-reference) docs to get the detailed information about all the **Actions**.
Checkout **[this](/docs/how-to/loading-image-pdf-from-db)** guide to learn how to refer or display images/PDFs using base64 string
:::
## Properties

View file

@ -7,6 +7,10 @@ title: PDF
PDF widget can be used to embed the PDF file either by URL or as a Base64 encoded.
:::info
Checkout **[this](/docs/how-to/loading-image-pdf-from-db)** guide to learn how to display images/PDFs using base64 string
:::
## Properties
### File URL

View file

@ -63,15 +63,10 @@ Toggle on or off to display the widget in mobile view. You can programmatically
| Background Color | You can change the background color of the text component by entering the Hex color code or choosing a color of your choice from the color picker. |
| Text Color | You can change the color of the text by entering the Hex color code or choosing a color of your choice from the color picker. |
| Align Text | You can align the text inside the widget in following ways: left, right, center, justified |
| Visibility | This is to control the visibility of the widget. If `{{false}}` the widget will not visible after the app is deployed. It can only have boolean values i.e. either `{{true}}` or `{{false}}`. By default, it's set to `{{true}}`. |
| Disable | This property only accepts boolean values. If set to `{{true}}`, the widget will be locked and becomes non-functional. By default, its value is set to `{{false}}`. |
### Visibility
This is to control the visibility of the widget. If `{{false}}` the widget will not visible after the app is deployed. It can only have boolean values i.e. either `{{true}}` or `{{false}}`. By default, it's set to `{{true}}`.
### Disable
This property only accepts boolean values. If set to `{{true}}`, the widget will be locked and becomes non-functional. By default, its value is set to `{{false}}`.
:::info
Any property having `Fx` button next to its field can be **programmatically configured**.
:::
@ -90,4 +85,4 @@ Following actions of color picker component can be controlled using the componen
| Actions | Description |
| ----------- | ----------- |
| visibility | Set a visibility of the text via a component-specific action within any event handler. Additionally, you have the option to employ a RunJS query to execute component-specific actions such as `await components.text1.visibility(false)` |
| setText | Set a text value on the text component via a component-specific action within any event handler. Additionally, you have the option to employ a RunJS query to execute component-specific actions such as `await components.text1.setText('this is a text')` |
| setText | Set a text value on the text component via a component-specific action within any event handler. Additionally, you have the option to employ a RunJS query to execute component-specific actions such as `await components.text1.setText('this is a text')` |

View file

@ -121,7 +121,7 @@ module.exports = {
// Please change this to your repo.
editUrl: 'https://github.com/ToolJet/Tooljet/blob/develop/docs/',
includeCurrentVersion: false,
lastVersion: '2.11.0',
lastVersion: '2.13.0',
},
theme: {
customCss: require.resolve('./src/css/custom.css'),

View file

@ -56,6 +56,7 @@ const sidebars = {
'data-sources/amazonses',
'data-sources/appwrite',
'data-sources/athena',
'data-sources/azureblob',
'data-sources/baserow',
'data-sources/bigquery',
'data-sources/firestore',
@ -266,12 +267,13 @@ const sidebars = {
],
},
{
'type': 'category',
'label': 'Workspaces',
'items': [
'tutorial/workspace_overview',
'tutorial/workspace-variables',
],
"type": "category",
"label": "Workspaces",
"items": [
"org-management/workspaces/workspace_overview",
"org-management/workspaces/workspace-variables",
"org-management/workspaces/workspace_constants"
]
},
'org-management/permissions',
'tutorial/manage-users-groups',
@ -334,6 +336,7 @@ const sidebars = {
'how-to/use-form-component',
'how-to/access-cellvalue-rowdata',
'how-to/bulk-update-multiple-rows',
'how-to/delete-multiple-rows',
'how-to/use-server-side-pagination',
'how-to/access-currentuser',
'how-to/use-axios-in-runjs',

View file

@ -168,9 +168,9 @@ img {
height: 25px;
}
strong {
/* strong {
color: #4d72fa;
}
} */
.alert a {
color: inherit;
@ -266,9 +266,14 @@ strong {
border-radius: 5px !important;
}
.DocSearch-Search-Icon {
height: 13px;
width: 13px;
}
@media screen and (min-width: 768px) {
.DocSearch-Button-Container {
min-width: 200px;
/* Fixes #3856 */
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 913 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 798 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 MiB

After

Width:  |  Height:  |  Size: 598 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.6 MiB

After

Width:  |  Height:  |  Size: 801 KiB

View file

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 314 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Some files were not shown because too many files have changed in this diff Show more