mirror of
https://github.com/ToolJet/ToolJet
synced 2026-05-24 09:28:31 +00:00
Merge branch 'main' into appbuilder/sprint-14
This commit is contained in:
commit
ca09b8df9c
20 changed files with 1276 additions and 308 deletions
2
.github/workflows/docker-release.yml
vendored
2
.github/workflows/docker-release.yml
vendored
|
|
@ -139,6 +139,7 @@ jobs:
|
||||||
context: .
|
context: .
|
||||||
build-args: |
|
build-args: |
|
||||||
CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
||||||
|
BRANCH_NAME=main
|
||||||
file: docker/ee/ee-production.Dockerfile
|
file: docker/ee/ee-production.Dockerfile
|
||||||
push: true
|
push: true
|
||||||
tags: tooljet/tooljet-ee:${{ github.event.release.tag_name }},tooljet/tooljet-ee:ee-lts-latest,tooljet/tooljet:ee-lts-latest,tooljet/tooljet:${{ github.event.release.tag_name }}
|
tags: tooljet/tooljet-ee:${{ github.event.release.tag_name }},tooljet/tooljet-ee:ee-lts-latest,tooljet/tooljet:ee-lts-latest,tooljet/tooljet:${{ github.event.release.tag_name }}
|
||||||
|
|
@ -155,6 +156,7 @@ jobs:
|
||||||
context: .
|
context: .
|
||||||
build-args: |
|
build-args: |
|
||||||
CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
||||||
|
BRANCH_NAME=lts
|
||||||
file: docker/ee/ee-production.Dockerfile
|
file: docker/ee/ee-production.Dockerfile
|
||||||
push: true
|
push: true
|
||||||
tags: tooljet/tooljet-ee:${{ github.event.release.tag_name }},tooljet/tooljet-ee:ee-lts-latest,tooljet/tooljet:ee-lts-latest,tooljet/tooljet:${{ github.event.release.tag_name }}
|
tags: tooljet/tooljet-ee:${{ github.event.release.tag_name }},tooljet/tooljet-ee:ee-lts-latest,tooljet/tooljet:ee-lts-latest,tooljet/tooljet:${{ github.event.release.tag_name }}
|
||||||
|
|
|
||||||
47
.github/workflows/manual-docker-build.yml
vendored
Normal file
47
.github/workflows/manual-docker-build.yml
vendored
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
name: Manual Docker Build and Push
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
branch_name:
|
||||||
|
description: 'Git branch to build from'
|
||||||
|
required: true
|
||||||
|
default: 'main'
|
||||||
|
dockerfile_path:
|
||||||
|
description: 'Path to Dockerfile'
|
||||||
|
required: true
|
||||||
|
default: './Dockerfile'
|
||||||
|
docker_tag:
|
||||||
|
description: 'Docker tag suffix (e.g., pre-release-14)'
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-push:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: ${{ github.event.inputs.branch_name }}
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Log in to DockerHub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ secrets.DOCKER_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Build and Push Docker image
|
||||||
|
uses: docker/build-push-action@v4
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ${{ github.event.inputs.dockerfile_path }}
|
||||||
|
push: true
|
||||||
|
tags: tooljet/tj-osv:${{ github.event.inputs.docker_tag }}
|
||||||
|
platforms: linux/amd64
|
||||||
|
build-args: |
|
||||||
|
CUSTOM_GITHUB_TOKEN=${{ secrets.CUSTOM_GITHUB_TOKEN }}
|
||||||
|
|
@ -98,8 +98,10 @@ module.exports = defineConfig({
|
||||||
configFile: environment.configFile,
|
configFile: environment.configFile,
|
||||||
specPattern: [
|
specPattern: [
|
||||||
"cypress/e2e/happyPath/platform/firstUser/firstUserOnboarding.cy.js",
|
"cypress/e2e/happyPath/platform/firstUser/firstUserOnboarding.cy.js",
|
||||||
|
"cypress/e2e/happyPath/platform/ceTestcases/apps/appSlug.cy.js",
|
||||||
|
"cypress/e2e/happyPath/platform/ceTestcases/apps/!(*appSlug).cy.js",
|
||||||
"cypress/e2e/happyPath/platform/commonTestcases/userManagment/*.cy.js",
|
"cypress/e2e/happyPath/platform/commonTestcases/userManagment/*.cy.js",
|
||||||
"cypress/e2e/happyPath/platform/eeTestcases/**/*.cy.js",
|
"cypress/e2e/happyPath/platform/eeTestcases/workspace/*.cy.js",
|
||||||
],
|
],
|
||||||
numTestsKeptInMemory: 1,
|
numTestsKeptInMemory: 1,
|
||||||
redirectionLimit: 15,
|
redirectionLimit: 15,
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ RUN git checkout ${BRANCH_NAME}
|
||||||
RUN git submodule update --init --recursive
|
RUN git submodule update --init --recursive
|
||||||
|
|
||||||
# Checkout the same branch in submodules if it exists, otherwise stay on default branch
|
# Checkout the same branch in submodules if it exists, otherwise stay on default branch
|
||||||
RUN git submodule foreach 'git checkout ${BRANCH_NAME} || true'
|
RUN git submodule foreach 'git checkout main'
|
||||||
|
|
||||||
# Scripts for building
|
# Scripts for building
|
||||||
COPY ./package.json ./package.json
|
COPY ./package.json ./package.json
|
||||||
|
|
@ -54,7 +54,7 @@ RUN npm install -g @nestjs/cli
|
||||||
RUN npm install -g copyfiles
|
RUN npm install -g copyfiles
|
||||||
RUN npm --prefix server run build
|
RUN npm --prefix server run build
|
||||||
|
|
||||||
FROM node:22.15.1
|
FROM node:22.15.1-bullseye
|
||||||
|
|
||||||
RUN apt-get update -yq \
|
RUN apt-get update -yq \
|
||||||
&& apt-get install curl wget gnupg zip -yq \
|
&& apt-get install curl wget gnupg zip -yq \
|
||||||
|
|
|
||||||
|
|
@ -479,24 +479,22 @@ Cypress.Commands.add("apiMakeAppPublic", (appId = Cypress.env("appId")) => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Cypress.Commands.add("apiDeleteGranularPermission", (groupName) => {
|
Cypress.Commands.add("apiDeleteGranularPermission", (groupName, typesToDelete = []) => {
|
||||||
cy.getAuthHeaders().then((headers) => {
|
cy.getAuthHeaders().then((headers) => {
|
||||||
// Fetch group permissions
|
// Step 1: Get the group by name
|
||||||
cy.request({
|
cy.request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions`,
|
url: `${Cypress.env("server_host")}/api/v2/group-permissions`,
|
||||||
headers: headers,
|
headers,
|
||||||
log: false,
|
log: false,
|
||||||
}).then((response) => {
|
}).then((response) => {
|
||||||
expect(response.status).to.equal(200);
|
expect(response.status).to.equal(200);
|
||||||
const group = response.body.groupPermissions.find(
|
const group = response.body.groupPermissions.find((g) => g.name === groupName);
|
||||||
(g) => g.name === groupName
|
|
||||||
);
|
|
||||||
if (!group) throw new Error(`Group with name ${groupName} not found`);
|
if (!group) throw new Error(`Group with name ${groupName} not found`);
|
||||||
|
|
||||||
const groupId = group.id;
|
const groupId = group.id;
|
||||||
|
|
||||||
// Fetch granular permissions for the specific group
|
// Step 2: Get all granular permissions for the group
|
||||||
cy.request({
|
cy.request({
|
||||||
method: "GET",
|
method: "GET",
|
||||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions/${groupId}/granular-permissions`,
|
url: `${Cypress.env("server_host")}/api/v2/group-permissions/${groupId}/granular-permissions`,
|
||||||
|
|
@ -504,22 +502,31 @@ Cypress.Commands.add("apiDeleteGranularPermission", (groupName) => {
|
||||||
log: false,
|
log: false,
|
||||||
}).then((granularResponse) => {
|
}).then((granularResponse) => {
|
||||||
expect(granularResponse.status).to.equal(200);
|
expect(granularResponse.status).to.equal(200);
|
||||||
const granularPermissionId = granularResponse.body[0].id;
|
const granularPermissions = granularResponse.body;
|
||||||
|
|
||||||
// Delete the granular permission
|
// Step 3: Filter if typesToDelete is specified
|
||||||
cy.request({
|
const permissionsToDelete = typesToDelete.length
|
||||||
method: "DELETE",
|
? granularPermissions.filter((perm) => typesToDelete.includes(perm.type))
|
||||||
url: `${Cypress.env("server_host")}/api/v2/group-permissions/granular-permissions/app/${granularPermissionId}`,
|
: granularPermissions;
|
||||||
headers,
|
|
||||||
log: false,
|
// Step 4: Delete each granular permission
|
||||||
}).then((deleteResponse) => {
|
permissionsToDelete.forEach((permission) => {
|
||||||
expect(deleteResponse.status).to.equal(200);
|
cy.request({
|
||||||
|
method: "DELETE",
|
||||||
|
url: `${Cypress.env("server_host")}/api/v2/group-permissions/granular-permissions/app/${permission.id}`,
|
||||||
|
headers,
|
||||||
|
log: false,
|
||||||
|
}).then((deleteResponse) => {
|
||||||
|
expect(deleteResponse.status).to.equal(200);
|
||||||
|
cy.log(`Deleted granular permission: ${permission.name}`);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
Cypress.Commands.add(
|
Cypress.Commands.add(
|
||||||
"apiCreateGranularPermission",
|
"apiCreateGranularPermission",
|
||||||
(
|
(
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ describe("App Import Functionality", () => {
|
||||||
cy.apiLogin();
|
cy.apiLogin();
|
||||||
cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug);
|
cy.apiCreateWorkspace(data.workspaceName, data.workspaceSlug);
|
||||||
cy.apiLogout();
|
cy.apiLogout();
|
||||||
cy.skipWalkthrough()
|
cy.skipWalkthrough();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should verify app import functionality", () => {
|
it("should verify app import functionality", () => {
|
||||||
|
|
@ -151,23 +151,49 @@ describe("App Import Functionality", () => {
|
||||||
|
|
||||||
cy.visit(`${data.workspaceSlug}/data-sources`);
|
cy.visit(`${data.workspaceSlug}/data-sources`);
|
||||||
cy.get('[data-cy="postgresql-button"]').should("be.visible");
|
cy.get('[data-cy="postgresql-button"]').should("be.visible");
|
||||||
cy.apiUpdateDataSource("postgresql", "production", {
|
|
||||||
options: [
|
cy.ifEnv("Community", () => {
|
||||||
{
|
cy.apiUpdateDataSource("postgresql", "production", {
|
||||||
key: "password",
|
options: [
|
||||||
value: `${Cypress.env("pg_password")}`,
|
{
|
||||||
encrypted: true,
|
key: "password",
|
||||||
},
|
value: `${Cypress.env("pg_password")}`,
|
||||||
],
|
encrypted: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
cy.ifEnv("Enterprise", () => {
|
||||||
|
cy.apiUpdateDataSource("postgresql", "development", {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
key: "password",
|
||||||
|
value: `${Cypress.env("pg_password")}`,
|
||||||
|
encrypted: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
cy.apiCreateWsConstant(
|
cy.ifEnv("Community", () => {
|
||||||
"pageHeader",
|
cy.apiCreateWsConstant(
|
||||||
"Import and Export",
|
"pageHeader",
|
||||||
["Global"],
|
"Import and Export",
|
||||||
["production"]
|
["Global"],
|
||||||
);
|
["production"]
|
||||||
cy.apiCreateWsConstant("db_name", "persons", ["Secret"], ["production"]);
|
);
|
||||||
|
cy.apiCreateWsConstant("db_name", "persons", ["Secret"], ["production"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.ifEnv("Enterprise", () => {
|
||||||
|
cy.apiCreateWsConstant(
|
||||||
|
"pageHeader",
|
||||||
|
"Import and Export",
|
||||||
|
["Global"],
|
||||||
|
["development"]
|
||||||
|
);
|
||||||
|
cy.apiCreateWsConstant("db_name", "persons", ["Secret"], ["development"]);
|
||||||
|
});
|
||||||
|
|
||||||
// Verify app after setup
|
// Verify app after setup
|
||||||
cy.wait("@importApp").then((interception) => {
|
cy.wait("@importApp").then((interception) => {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import {
|
||||||
verifyURLs,
|
verifyURLs,
|
||||||
resolveHost,
|
resolveHost,
|
||||||
} from "Support/utils/apps";
|
} from "Support/utils/apps";
|
||||||
|
import { appPromote } from "Support/utils/platform/multiEnv";
|
||||||
|
|
||||||
describe("App Slug", () => {
|
describe("App Slug", () => {
|
||||||
const data = {};
|
const data = {};
|
||||||
|
|
@ -153,6 +154,7 @@ describe("App Slug", () => {
|
||||||
cy.visit("/my-workspace");
|
cy.visit("/my-workspace");
|
||||||
cy.apiCreateApp(data.slug);
|
cy.apiCreateApp(data.slug);
|
||||||
cy.openApp("my-workspace");
|
cy.openApp("my-workspace");
|
||||||
|
|
||||||
releaseApp();
|
releaseApp();
|
||||||
cy.get(commonWidgetSelector.shareAppButton).click();
|
cy.get(commonWidgetSelector.shareAppButton).click();
|
||||||
cy.clearAndType(commonWidgetSelector.appNameSlugInput, data.slug);
|
cy.clearAndType(commonWidgetSelector.appNameSlugInput, data.slug);
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,8 @@ import {
|
||||||
verifyRestrictedAccess,
|
verifyRestrictedAccess,
|
||||||
onboardUserFromAppLink,
|
onboardUserFromAppLink,
|
||||||
} from "Support/utils/apps";
|
} from "Support/utils/apps";
|
||||||
|
import { appPromote } from "Support/utils/platform/multiEnv";
|
||||||
|
import { InstanceSSO } from "Support/utils/platform/eeCommon";
|
||||||
|
|
||||||
describe(
|
describe(
|
||||||
"Private and Public apps",
|
"Private and Public apps",
|
||||||
|
|
@ -183,6 +185,9 @@ describe(
|
||||||
setupAppWithSlug(data.appName, data.slug);
|
setupAppWithSlug(data.appName, data.slug);
|
||||||
|
|
||||||
cy.apiLogout();
|
cy.apiLogout();
|
||||||
|
cy.ifEnv("Enterprise", () => {
|
||||||
|
InstanceSSO(true, true, true);
|
||||||
|
});
|
||||||
userSignUp(data.firstName, data.email, data.workspaceName);
|
userSignUp(data.firstName, data.email, data.workspaceName);
|
||||||
cy.wait(1000);
|
cy.wait(1000);
|
||||||
cy.visitSlug({
|
cy.visitSlug({
|
||||||
|
|
@ -312,7 +317,9 @@ describe(
|
||||||
cy.apiLogout();
|
cy.apiLogout();
|
||||||
cy.apiLogin();
|
cy.apiLogin();
|
||||||
cy.visit(`${data.workspaceSlug}`);
|
cy.visit(`${data.workspaceSlug}`);
|
||||||
cy.apiDeleteGranularPermission("end-user");
|
|
||||||
|
cy.apiDeleteGranularPermission("end-user", ["app", "workflow"]);
|
||||||
|
|
||||||
setSignupStatus(true, data.workspaceName);
|
setSignupStatus(true, data.workspaceName);
|
||||||
|
|
||||||
setupAppWithSlug(data.appName, data.slug);
|
setupAppWithSlug(data.appName, data.slug);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||||
import { fake } from "Fixtures/fake";
|
import { fake } from "Fixtures/fake";
|
||||||
import { commonText } from "Texts/common";
|
import { commonText } from "Texts/common";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
editVersionAndVerify,
|
editVersionAndVerify,
|
||||||
deleteVersionAndVerify,
|
deleteVersionAndVerify,
|
||||||
|
|
@ -13,25 +12,20 @@ import {
|
||||||
navigateToEditVersionModal,
|
navigateToEditVersionModal,
|
||||||
switchVersionAndVerify,
|
switchVersionAndVerify,
|
||||||
} from "Support/utils/version";
|
} from "Support/utils/version";
|
||||||
|
|
||||||
import { appVersionSelectors } from "Selectors/exportImport";
|
import { appVersionSelectors } from "Selectors/exportImport";
|
||||||
import { editVersionSelectors } from "Selectors/version";
|
import { editVersionSelectors } from "Selectors/version";
|
||||||
import { editVersionText } from "Texts/version";
|
import { editVersionText } from "Texts/version";
|
||||||
import { createNewVersion } from "Support/utils/exportImport";
|
import { createNewVersion } from "Support/utils/exportImport";
|
||||||
|
|
||||||
import { verifyModal, closeModal } from "Support/utils/common";
|
import { verifyModal, closeModal } from "Support/utils/common";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
verifyComponent,
|
verifyComponent,
|
||||||
verifyComponentinrightpannel,
|
verifyComponentinrightpannel,
|
||||||
deleteComponentAndVerify,
|
deleteComponentAndVerify,
|
||||||
} from "Support/utils/basicComponents";
|
} from "Support/utils/basicComponents";
|
||||||
|
|
||||||
import { deleteVersionText, onlydeleteVersionText } from "Texts/version";
|
import { deleteVersionText, onlydeleteVersionText } from "Texts/version";
|
||||||
|
|
||||||
import { createRestAPIQuery } from "Support/utils/dataSource";
|
import { createRestAPIQuery } from "Support/utils/dataSource";
|
||||||
import { deleteQuery } from "Support/utils/queries";
|
import { deleteQuery } from "Support/utils/queries";
|
||||||
|
import { selectEnv, appPromote } from "Support/utils/platform/multiEnv";
|
||||||
describe("App Version", () => {
|
describe("App Version", () => {
|
||||||
let data;
|
let data;
|
||||||
|
|
||||||
|
|
@ -120,7 +114,15 @@ describe("App Version", () => {
|
||||||
|
|
||||||
// Preview and release verification
|
// Preview and release verification
|
||||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||||
cy.url().should("include", "/home?version=v2");
|
|
||||||
|
cy.ifEnv("Community", () => {
|
||||||
|
cy.url().should("include", "/home?version=v2");
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.ifEnv("Enterprise", () => {
|
||||||
|
cy.url().should("include", "/home?env=development&version=v2");
|
||||||
|
});
|
||||||
|
|
||||||
cy.openApp(
|
cy.openApp(
|
||||||
"",
|
"",
|
||||||
Cypress.env("workspaceId"),
|
Cypress.env("workspaceId"),
|
||||||
|
|
@ -149,7 +151,11 @@ describe("App Version", () => {
|
||||||
|
|
||||||
createRestAPIQuery(data.query1, data.datasourceName, "", "", "/1", true);
|
createRestAPIQuery(data.query1, data.datasourceName, "", "", "/1", true);
|
||||||
|
|
||||||
// Version v2 creation and verification
|
cy.ifEnv("Enterprise", () => {
|
||||||
|
appPromote("development", "production");
|
||||||
|
});
|
||||||
|
|
||||||
|
// Version v2 creation and verification and v2 is created from v1 production environment
|
||||||
navigateToCreateNewVersionModal("v1");
|
navigateToCreateNewVersionModal("v1");
|
||||||
createNewVersion(["v2"], "v1");
|
createNewVersion(["v2"], "v1");
|
||||||
cy.get(commonWidgetSelector.draggableWidget("text1")).verifyVisibleElement(
|
cy.get(commonWidgetSelector.draggableWidget("text1")).verifyVisibleElement(
|
||||||
|
|
@ -201,7 +207,8 @@ describe("App Version", () => {
|
||||||
versionChecks.forEach((check) => {
|
versionChecks.forEach((check) => {
|
||||||
navigateToCreateNewVersionModal(check.create.from);
|
navigateToCreateNewVersionModal(check.create.from);
|
||||||
createNewVersion([check.create.version], check.create.from);
|
createNewVersion([check.create.version], check.create.from);
|
||||||
|
cy.waitForAutoSave();
|
||||||
|
cy.wait(1000);
|
||||||
if (check.verify.component.value) {
|
if (check.verify.component.value) {
|
||||||
cy.get(
|
cy.get(
|
||||||
commonWidgetSelector.draggableWidget(check.verify.component.selector)
|
commonWidgetSelector.draggableWidget(check.verify.component.selector)
|
||||||
|
|
@ -224,6 +231,9 @@ describe("App Version", () => {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Version switching and component verification
|
// Version switching and component verification
|
||||||
|
cy.ifEnv("Enterprise", () => {
|
||||||
|
selectEnv("development");
|
||||||
|
});
|
||||||
cy.get(appVersionSelectors.currentVersionField("v5")).click();
|
cy.get(appVersionSelectors.currentVersionField("v5")).click();
|
||||||
cy.contains(`[id*="react-select-"]`, "v4").click();
|
cy.contains(`[id*="react-select-"]`, "v4").click();
|
||||||
cy.get(appVersionSelectors.currentVersionField("v4")).should(
|
cy.get(appVersionSelectors.currentVersionField("v4")).should(
|
||||||
|
|
@ -238,7 +248,14 @@ describe("App Version", () => {
|
||||||
|
|
||||||
// Preview and version switching verification
|
// Preview and version switching verification
|
||||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||||
cy.url().should("include", "/home?version=v4");
|
|
||||||
|
cy.ifEnv("Community", () => {
|
||||||
|
cy.url().should("include", "/home?version=v4");
|
||||||
|
});
|
||||||
|
cy.ifEnv("Enterprise", () => {
|
||||||
|
cy.url().should("include", "/home?env=development&version=v4");
|
||||||
|
});
|
||||||
|
|
||||||
cy.get(commonWidgetSelector.draggableWidget("text1")).verifyVisibleElement(
|
cy.get(commonWidgetSelector.draggableWidget("text1")).verifyVisibleElement(
|
||||||
"have.text",
|
"have.text",
|
||||||
"Leanne Graham"
|
"Leanne Graham"
|
||||||
|
|
@ -250,8 +267,74 @@ describe("App Version", () => {
|
||||||
cy.get(
|
cy.get(
|
||||||
commonWidgetSelector.draggableWidget("textInput")
|
commonWidgetSelector.draggableWidget("textInput")
|
||||||
).verifyVisibleElement("have.value", "Ervin Howell");
|
).verifyVisibleElement("have.value", "Ervin Howell");
|
||||||
//url validation should be added after bug fix
|
|
||||||
|
|
||||||
// cy.url().should("include", "/home?version=v5");
|
cy.ifEnv("Enterprise", () => {
|
||||||
|
cy.openApp(
|
||||||
|
"",
|
||||||
|
Cypress.env("workspaceId"),
|
||||||
|
Cypress.env("appId"),
|
||||||
|
commonWidgetSelector.draggableWidget("textInput")
|
||||||
|
);
|
||||||
|
|
||||||
|
navigateToCreateNewVersionModal("v5");
|
||||||
|
createNewVersion(["v6"], "v5");
|
||||||
|
cy.waitForAutoSave();
|
||||||
|
cy.wait(1000);
|
||||||
|
|
||||||
|
appPromote("development", "staging");
|
||||||
|
cy.get(
|
||||||
|
commonWidgetSelector.draggableWidget("textInput")
|
||||||
|
).verifyVisibleElement("have.value", "Ervin Howell");
|
||||||
|
cy.get(`[data-cy="list-query-${data.query2}"]`).should("be.visible");
|
||||||
|
|
||||||
|
appPromote("staging", "production");
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
commonWidgetSelector.draggableWidget("textInput")
|
||||||
|
).verifyVisibleElement("have.value", "Ervin Howell");
|
||||||
|
cy.get(`[data-cy="list-query-${data.query2}"]`).should("be.visible");
|
||||||
|
|
||||||
|
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||||
|
cy.get(
|
||||||
|
commonWidgetSelector.draggableWidget("textInput")
|
||||||
|
).verifyVisibleElement("have.value", "Ervin Howell");
|
||||||
|
cy.url().should("include", "/home?env=production&version=v6");
|
||||||
|
|
||||||
|
cy.wait(1000);
|
||||||
|
|
||||||
|
cy.get('[data-cy="preview-settings"]').click();
|
||||||
|
switchVersionAndVerify("v6", "v1");
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
commonWidgetSelector.draggableWidget("text1")
|
||||||
|
).verifyVisibleElement("have.text", "Leanne Graham");
|
||||||
|
// url bug
|
||||||
|
// cy.url().should("include", "/home?env=production&version=v1");
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.get('[data-cy="preview-settings"]').click();
|
||||||
|
switchVersionAndVerify("v1", "v6");
|
||||||
|
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.get('[data-cy="preview-settings"]').click();
|
||||||
|
selectEnv("staging");
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
commonWidgetSelector.draggableWidget("textInput")
|
||||||
|
).verifyVisibleElement("have.value", "Ervin Howell");
|
||||||
|
// cy.url().should("include", "/home?env=staging&version=v6");
|
||||||
|
|
||||||
|
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.get('[data-cy="preview-settings"]').click();
|
||||||
|
selectEnv("development");
|
||||||
|
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.get('[data-cy="preview-settings"]').click();
|
||||||
|
switchVersionAndVerify("v6", "v1");
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
commonWidgetSelector.draggableWidget("text1")
|
||||||
|
).verifyVisibleElement("have.text", "Leanne Graham");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,662 @@
|
||||||
|
import { fake } from "Fixtures/fake";
|
||||||
|
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||||
|
import { commonEeText, ssoEeText } from "Texts/eeCommon";
|
||||||
|
import { commonEeSelectors, multiEnvSelector } from "Selectors/eeCommon";
|
||||||
|
import {
|
||||||
|
verifyPromoteModalUI,
|
||||||
|
verifyTooltipDisabled,
|
||||||
|
} from "Support/utils/platform/eeCommon";
|
||||||
|
import { dataSourceSelector } from "Selectors/dataSource";
|
||||||
|
import {
|
||||||
|
navigateToAppEditor,
|
||||||
|
pinInspector,
|
||||||
|
verifyTooltip,
|
||||||
|
} from "Support/utils/common";
|
||||||
|
import { addQuery, selectDatasource } from "Support/utils/dataSource";
|
||||||
|
import {
|
||||||
|
appPromote,
|
||||||
|
createNewVersion,
|
||||||
|
selectVersion,
|
||||||
|
selectEnv,
|
||||||
|
} from "Support/utils/platform/multiEnv";
|
||||||
|
import { appVersionSelectors } from "Selectors/exportImport";
|
||||||
|
|
||||||
|
import { editAndVerifyWidgetName } from "Support/utils/commonWidget";
|
||||||
|
import { deleteVersionAndVerify } from "Support/utils/version";
|
||||||
|
import { deleteVersionText } from "Texts/version";
|
||||||
|
|
||||||
|
describe("Multi env", () => {
|
||||||
|
const data = {};
|
||||||
|
data.appName = `${fake.companyName} App`;
|
||||||
|
data.ds = fake.lastName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||||
|
data.constName = fake.firstName.toLowerCase().replaceAll("[^A-Za-z]", "");
|
||||||
|
const slug = data.appName.toLowerCase().replace(/\s+/g, "-");
|
||||||
|
let currentVersion = "";
|
||||||
|
let newVersion = [];
|
||||||
|
let versionFrom = "";
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
cy.apiLogin();
|
||||||
|
cy.viewport(1800, 1800);
|
||||||
|
cy.skipWalkthrough();
|
||||||
|
});
|
||||||
|
|
||||||
|
it.only("Verify the datasource configuration and data on each env", () => {
|
||||||
|
cy.apiCreateGDS(
|
||||||
|
`${Cypress.env("server_host")}/api/data-sources`,
|
||||||
|
data.ds,
|
||||||
|
"restapi",
|
||||||
|
[
|
||||||
|
{ key: "url", value: "" },
|
||||||
|
{ key: "auth_type", value: "none" },
|
||||||
|
{ key: "grant_type", value: "authorization_code" },
|
||||||
|
{ key: "add_token_to", value: "header" },
|
||||||
|
{ key: "header_prefix", value: "Bearer " },
|
||||||
|
{ key: "access_token_url", value: "" },
|
||||||
|
{ key: "client_ide", value: "" },
|
||||||
|
{ key: "client_secret", value: "", encrypted: true },
|
||||||
|
{ key: "scopes", value: "read, write" },
|
||||||
|
{ key: "username", value: "", encrypted: false },
|
||||||
|
{ key: "password", value: "", encrypted: true },
|
||||||
|
{ key: "bearer_token", value: "", encrypted: true },
|
||||||
|
{ key: "auth_url", value: "" },
|
||||||
|
{ key: "client_auth", value: "header" },
|
||||||
|
{ key: "headers", value: [["", ""]] },
|
||||||
|
{ key: "custom_query_params", value: [["", ""]], encrypted: false },
|
||||||
|
{ key: "custom_auth_params", value: [["", ""]] },
|
||||||
|
{
|
||||||
|
key: "access_token_custom_headers",
|
||||||
|
value: [["", ""]],
|
||||||
|
encrypted: false,
|
||||||
|
},
|
||||||
|
{ key: "multiple_auth_enabled", value: false, encrypted: false },
|
||||||
|
{ key: "ssl_certificate", value: "none", encrypted: false },
|
||||||
|
]
|
||||||
|
);
|
||||||
|
cy.apiCreateApp(data.appName);
|
||||||
|
cy.visit("/my-workspace");
|
||||||
|
cy.get(commonSelectors.globalDataSourceIcon).click();
|
||||||
|
selectDatasource(data.ds);
|
||||||
|
cy.get('[data-cy="development-label"]').click();
|
||||||
|
cy.clearAndType(
|
||||||
|
'[data-cy="base-url-text-field"]',
|
||||||
|
"https://reqres.in/api/users?page=1"
|
||||||
|
);
|
||||||
|
cy.get(dataSourceSelector.buttonSave).click();
|
||||||
|
cy.wait(2000);
|
||||||
|
|
||||||
|
cy.get(commonSelectors.dashboardIcon).click();
|
||||||
|
|
||||||
|
cy.openApp();
|
||||||
|
// cy.waitForAppLoad();
|
||||||
|
cy.wait(2000);
|
||||||
|
cy.get(`[data-cy="${data.ds}-add-query-card"] > .text-truncate`).click();
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.get(dataSourceSelector.queryCreateAndRunButton).click();
|
||||||
|
cy.get('[data-cy="query-tab-settings"]').click();
|
||||||
|
cy.get(':nth-child(1) > .custom-toggle-switch > .switch > .slider').click();
|
||||||
|
cy.waitForAutoSave();
|
||||||
|
|
||||||
|
cy.dragAndDropWidget("Text Input", 550, 650);
|
||||||
|
editAndVerifyWidgetName(data.constName, []);
|
||||||
|
cy.waitForAutoSave();
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
'[data-cy="default-value-input-field"]'
|
||||||
|
).clearAndTypeOnCodeMirror(`{{queries.restapi1.data.data[0].email`);
|
||||||
|
cy.wait(1000);
|
||||||
|
cy.forceClickOnCanvas();
|
||||||
|
cy.waitForAutoSave();
|
||||||
|
cy.get(dataSourceSelector.queryCreateAndRunButton).click();
|
||||||
|
cy.get(
|
||||||
|
commonWidgetSelector.draggableWidget(data.constName)
|
||||||
|
).verifyVisibleElement("have.value", "george.bluth@reqres.in");
|
||||||
|
|
||||||
|
pinInspector();
|
||||||
|
cy.get(commonWidgetSelector.sidebarinspector).click();
|
||||||
|
cy.get(commonWidgetSelector.inspectorNodeComponents).click();
|
||||||
|
cy.get(commonWidgetSelector.nodeComponent(data.constName)).click();
|
||||||
|
cy.get('[data-cy="inspector-node-value"] > .mx-2').verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
`"george.bluth@reqres.in"`
|
||||||
|
);
|
||||||
|
cy.get('[style="height: 13px; width: 13px;"] > img').should("exist");
|
||||||
|
cy.get('[data-cy="inspector-node-globals"] > .node-key').click();
|
||||||
|
cy.get('[data-cy="inspector-node-environment"] > .node-key').click();
|
||||||
|
cy.get('[data-cy="inspector-node-name"] > .mx-2').verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
`"development"`
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||||
|
cy.wait(4000);
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
commonWidgetSelector.draggableWidget(data.constName)
|
||||||
|
).verifyVisibleElement("have.value", "george.bluth@reqres.in");
|
||||||
|
|
||||||
|
cy.go("back");
|
||||||
|
cy.waitForAppLoad();
|
||||||
|
cy.wait(3000);
|
||||||
|
cy.get(commonEeSelectors.promoteButton).click();
|
||||||
|
cy.get(commonEeSelectors.promoteButton).eq(1).click();
|
||||||
|
cy.waitForAppLoad();
|
||||||
|
cy.wait(3000);
|
||||||
|
|
||||||
|
cy.get(dataSourceSelector.queryCreateAndRunButton, {
|
||||||
|
timeout: 20000,
|
||||||
|
}).click();
|
||||||
|
cy.verifyToastMessage(
|
||||||
|
commonSelectors.toastMessage,
|
||||||
|
"Query could not be completed"
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.backToApps();
|
||||||
|
cy.get(commonSelectors.globalDataSourceIcon).click();
|
||||||
|
selectDatasource(data.ds);
|
||||||
|
cy.get('[data-cy="staging-label"]').click();
|
||||||
|
cy.clearAndType(
|
||||||
|
'[data-cy="base-url-text-field"]',
|
||||||
|
"https://reqres.in/api/users?page=2"
|
||||||
|
);
|
||||||
|
cy.get(dataSourceSelector.buttonSave).click();
|
||||||
|
cy.wait(2000);
|
||||||
|
|
||||||
|
cy.get(commonSelectors.dashboardIcon).click();
|
||||||
|
navigateToAppEditor(data.appName);
|
||||||
|
cy.get(dataSourceSelector.queryCreateAndRunButton).click();
|
||||||
|
cy.get(
|
||||||
|
commonWidgetSelector.draggableWidget(data.constName)
|
||||||
|
).verifyVisibleElement("have.value", "michael.lawson@reqres.in");
|
||||||
|
|
||||||
|
cy.get(commonWidgetSelector.sidebarinspector).click();
|
||||||
|
cy.get(commonWidgetSelector.inspectorNodeComponents).click();
|
||||||
|
cy.get(commonWidgetSelector.nodeComponent(data.constName)).click();
|
||||||
|
cy.get('[data-cy="inspector-node-value"] > .mx-2').verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
`"michael.lawson@reqres.in"`
|
||||||
|
);
|
||||||
|
cy.get('[style="height: 13px; width: 13px;"] > img').should("not.exist");
|
||||||
|
cy.get('[data-cy="inspector-node-globals"] > .node-key').click();
|
||||||
|
cy.get('[data-cy="inspector-node-environment"] > .node-key').click();
|
||||||
|
cy.get('[data-cy="inspector-node-name"] > .mx-2').verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
`"staging"`
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||||
|
cy.wait(4000);
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
commonWidgetSelector.draggableWidget(data.constName)
|
||||||
|
).verifyVisibleElement("have.value", "michael.lawson@reqres.in");
|
||||||
|
|
||||||
|
cy.go("back");
|
||||||
|
cy.waitForAppLoad();
|
||||||
|
cy.wait(3000);
|
||||||
|
cy.get(commonEeSelectors.promoteButton).click();
|
||||||
|
cy.get(commonEeSelectors.promoteButton).eq(1).click();
|
||||||
|
cy.waitForAppLoad();
|
||||||
|
cy.wait(3000);
|
||||||
|
|
||||||
|
cy.get(dataSourceSelector.queryCreateAndRunButton, {
|
||||||
|
timeout: 20000,
|
||||||
|
}).click();
|
||||||
|
cy.verifyToastMessage(
|
||||||
|
commonSelectors.toastMessage,
|
||||||
|
"Query could not be completed"
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.backToApps();
|
||||||
|
cy.get(commonSelectors.globalDataSourceIcon).click();
|
||||||
|
selectDatasource(data.ds);
|
||||||
|
cy.get('[data-cy="production-label"]').click();
|
||||||
|
cy.clearAndType(
|
||||||
|
'[data-cy="base-url-text-field"]',
|
||||||
|
"https://reqres.in/api/users?page=1"
|
||||||
|
);
|
||||||
|
cy.get(dataSourceSelector.buttonSave).click();
|
||||||
|
cy.wait(2000);
|
||||||
|
|
||||||
|
cy.get(commonSelectors.dashboardIcon).click();
|
||||||
|
navigateToAppEditor(data.appName);
|
||||||
|
cy.get(dataSourceSelector.queryCreateAndRunButton).click();
|
||||||
|
cy.get(
|
||||||
|
commonWidgetSelector.draggableWidget(data.constName)
|
||||||
|
).verifyVisibleElement("have.value", "george.bluth@reqres.in");
|
||||||
|
|
||||||
|
cy.get(commonWidgetSelector.sidebarinspector).click();
|
||||||
|
cy.get(commonWidgetSelector.inspectorNodeComponents).click();
|
||||||
|
cy.get(commonWidgetSelector.nodeComponent(data.constName)).click();
|
||||||
|
cy.get('[data-cy="inspector-node-value"] > .mx-2').verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
`"george.bluth@reqres.in"`
|
||||||
|
);
|
||||||
|
cy.get('[style="height: 13px; width: 13px;"] > img').should("not.exist");
|
||||||
|
cy.get('[data-cy="inspector-node-globals"] > .node-key').click();
|
||||||
|
cy.get('[data-cy="inspector-node-environment"] > .node-key').click();
|
||||||
|
cy.get('[data-cy="inspector-node-name"] > .mx-2').verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
`"production"`
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||||
|
cy.wait(4000);
|
||||||
|
|
||||||
|
cy.get(
|
||||||
|
commonWidgetSelector.draggableWidget(data.constName)
|
||||||
|
).verifyVisibleElement("have.value", "george.bluth@reqres.in");
|
||||||
|
|
||||||
|
cy.go("back");
|
||||||
|
cy.waitForAppLoad();
|
||||||
|
cy.wait(3000);
|
||||||
|
cy.get(commonSelectors.releaseButton).click();
|
||||||
|
cy.get(commonSelectors.yesButton).click();
|
||||||
|
cy.verifyToastMessage(commonSelectors.toastMessage, "Version v1 released");
|
||||||
|
cy.wait(4000);
|
||||||
|
|
||||||
|
cy.get(commonWidgetSelector.shareAppButton).click();
|
||||||
|
cy.clearAndType(commonWidgetSelector.appNameSlugInput, `${slug}`);
|
||||||
|
cy.wait(2000);
|
||||||
|
cy.get(commonWidgetSelector.modalCloseButton).click();
|
||||||
|
|
||||||
|
cy.visit(`/applications/${slug}`);
|
||||||
|
cy.get(
|
||||||
|
commonWidgetSelector.draggableWidget(data.constName)
|
||||||
|
).verifyVisibleElement("have.value", "george.bluth@reqres.in");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should verify edit privilages of a promoted version", () => {
|
||||||
|
data.appName = `${fake.companyName} App`;
|
||||||
|
cy.apiCreateApp(data.appName);
|
||||||
|
cy.openApp();
|
||||||
|
cy.waitForAppLoad();
|
||||||
|
cy.dragAndDropWidget("Text", 550, 650);
|
||||||
|
appPromote("development", "production");
|
||||||
|
|
||||||
|
createNewVersion(
|
||||||
|
(currentVersion = "v1"),
|
||||||
|
(newVersion = ["v2"]),
|
||||||
|
(versionFrom = "v1")
|
||||||
|
);
|
||||||
|
appPromote("development", "release");
|
||||||
|
|
||||||
|
createNewVersion(
|
||||||
|
(currentVersion = "v2"),
|
||||||
|
(newVersion = ["v3"]),
|
||||||
|
(versionFrom = "v2")
|
||||||
|
);
|
||||||
|
appPromote("development", "staging");
|
||||||
|
|
||||||
|
selectVersion((currentVersion = "v3"), (newVersion = ["v1"]));
|
||||||
|
cy.get(commonSelectors.warningText).eq(0).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"App cannot be edited after promotion. Please create a new version from Development to make any changes."
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.forceClickOnCanvas();
|
||||||
|
cy.get(".datasource-picker").should("have.class", "disabled");
|
||||||
|
cy.get(commonEeSelectors.AddQueryButton).should("be.disabled");
|
||||||
|
cy.get(".components-container").should("have.class", "disabled");
|
||||||
|
|
||||||
|
cy.wait(1000);
|
||||||
|
selectEnv("development");
|
||||||
|
cy.get(commonSelectors.warningText).eq(0).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"App cannot be edited after promotion. Please create a new version from Development to make any changes."
|
||||||
|
);
|
||||||
|
cy.get(".datasource-picker").should("have.class", "disabled");
|
||||||
|
cy.get(commonEeSelectors.AddQueryButton).should("be.disabled");
|
||||||
|
cy.get(".components-container").should("have.class", "disabled");
|
||||||
|
|
||||||
|
selectVersion((currentVersion = "v1"), (newVersion = ["v2"]));
|
||||||
|
cy.get(commonSelectors.warningText).eq(0).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"This version of the app is released. Please create a new version in development to make any changes."
|
||||||
|
);
|
||||||
|
cy.get(".datasource-picker").should("have.class", "disabled");
|
||||||
|
cy.get(commonEeSelectors.AddQueryButton).should("be.disabled");
|
||||||
|
cy.get(".components-container").should("have.class", "disabled");
|
||||||
|
|
||||||
|
cy.wait(1000);
|
||||||
|
selectEnv("staging");
|
||||||
|
cy.get(commonSelectors.warningText).eq(0).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"This version of the app is released. Please create a new version in development to make any changes."
|
||||||
|
);
|
||||||
|
cy.get(".datasource-picker").should("have.class", "disabled");
|
||||||
|
cy.get(commonEeSelectors.AddQueryButton).should("be.disabled");
|
||||||
|
cy.get(".components-container").should("have.class", "disabled");
|
||||||
|
|
||||||
|
cy.wait(1000);
|
||||||
|
selectEnv("production");
|
||||||
|
cy.get(commonSelectors.warningText).eq(0).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"This version of the app is released. Please create a new version in development to make any changes."
|
||||||
|
);
|
||||||
|
cy.get(".datasource-picker").should("have.class", "disabled");
|
||||||
|
cy.get(commonEeSelectors.AddQueryButton).should("be.disabled");
|
||||||
|
cy.get(".components-container").should("have.class", "disabled");
|
||||||
|
cy.get(commonSelectors.releaseButton).should("be.disabled");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should verify last exisiting version", () => {
|
||||||
|
data.appName = `${fake.companyName} App`;
|
||||||
|
cy.apiCreateApp(data.appName);
|
||||||
|
cy.openApp();
|
||||||
|
cy.waitForAppLoad();
|
||||||
|
cy.dragAndDropWidget("Text", 550, 650);
|
||||||
|
|
||||||
|
appPromote("development", "staging");
|
||||||
|
createNewVersion(
|
||||||
|
(currentVersion = "v1"),
|
||||||
|
(newVersion = ["v2"]),
|
||||||
|
(versionFrom = "v1")
|
||||||
|
);
|
||||||
|
|
||||||
|
selectVersion((currentVersion = "v2"), (newVersion = ["v1"]));
|
||||||
|
|
||||||
|
cy.wait(1000);
|
||||||
|
selectEnv("staging");
|
||||||
|
|
||||||
|
cy.get(appVersionSelectors.currentVersionField(newVersion[0]))
|
||||||
|
.should("be.visible")
|
||||||
|
.and("have.text", "v1");
|
||||||
|
|
||||||
|
appPromote("staging", "production");
|
||||||
|
|
||||||
|
cy.wait(3000)
|
||||||
|
deleteVersionAndVerify(
|
||||||
|
(currentVersion = "v1"),
|
||||||
|
deleteVersionText.deleteToastMessage((currentVersion = "v1"))
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.wait(2000);
|
||||||
|
cy.get('[data-cy="list-current-env-name"]').click();
|
||||||
|
verifyTooltip(
|
||||||
|
'[data-cy="env-name-dropdown"]:eq(1)',
|
||||||
|
"There are no versions in this environment"
|
||||||
|
);
|
||||||
|
verifyTooltip(
|
||||||
|
'[data-cy="env-name-dropdown"]:eq(2)',
|
||||||
|
"There are no versions in this environment"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("Should verify version deletion", () => {
|
||||||
|
data.appName = `${fake.companyName} App`;
|
||||||
|
cy.apiCreateApp(data.appName);
|
||||||
|
cy.openApp();
|
||||||
|
cy.waitForAppLoad();
|
||||||
|
cy.dragAndDropWidget("Text", 550, 650);
|
||||||
|
|
||||||
|
appPromote("development", "staging");
|
||||||
|
createNewVersion(
|
||||||
|
(currentVersion = "v1"),
|
||||||
|
(newVersion = ["v2"]),
|
||||||
|
(versionFrom = "v1")
|
||||||
|
);
|
||||||
|
appPromote("development", "staging");
|
||||||
|
|
||||||
|
createNewVersion(
|
||||||
|
(currentVersion = "v2"),
|
||||||
|
(newVersion = ["v3"]),
|
||||||
|
(versionFrom = "v2")
|
||||||
|
);
|
||||||
|
appPromote("development", "production");
|
||||||
|
|
||||||
|
selectEnv("staging");
|
||||||
|
selectVersion((currentVersion = "v3"), (newVersion = ["v2"]));
|
||||||
|
deleteVersionAndVerify(
|
||||||
|
(currentVersion = "v2"),
|
||||||
|
deleteVersionText.deleteToastMessage((currentVersion = "v2"))
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get('[data-cy="v3-current-version-text"]')
|
||||||
|
.should("be.visible")
|
||||||
|
.and("have.text", "v3");
|
||||||
|
|
||||||
|
cy.get('[data-cy="list-current-env-name"]').should(
|
||||||
|
"have.text",
|
||||||
|
"Staging"
|
||||||
|
);
|
||||||
|
})
|
||||||
|
|
||||||
|
it("Verify the multi env components UI", () => {
|
||||||
|
data.appName = `${fake.companyName} App`;
|
||||||
|
cy.apiCreateApp(data.appName);
|
||||||
|
cy.openApp();
|
||||||
|
cy.waitForAppLoad();
|
||||||
|
cy.dragAndDropWidget("Text", 550, 650);
|
||||||
|
cy.get(multiEnvSelector.envContainer).should("be.visible");
|
||||||
|
cy.get(multiEnvSelector.currentEnvName)
|
||||||
|
.verifyVisibleElement("have.text", "Development")
|
||||||
|
.click();
|
||||||
|
cy.get(multiEnvSelector.envArrow).should("be.visible");
|
||||||
|
cy.get(multiEnvSelector.selectedEnvName).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
" Development"
|
||||||
|
);
|
||||||
|
cy.get(multiEnvSelector.envNameList)
|
||||||
|
.eq(0)
|
||||||
|
.verifyVisibleElement("have.text", "Development");
|
||||||
|
cy.get(multiEnvSelector.envNameList)
|
||||||
|
.eq(1)
|
||||||
|
.verifyVisibleElement("have.text", "Staging");
|
||||||
|
cy.get(multiEnvSelector.envNameList)
|
||||||
|
.eq(2)
|
||||||
|
.verifyVisibleElement("have.text", "Production");
|
||||||
|
|
||||||
|
verifyTooltip(
|
||||||
|
'[data-cy="env-name-dropdown"]:eq(1)',
|
||||||
|
"There are no versions in this environment"
|
||||||
|
);
|
||||||
|
verifyTooltip(
|
||||||
|
'[data-cy="env-name-dropdown"]:eq(2)',
|
||||||
|
"There are no versions in this environment"
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get(multiEnvSelector.appVersionLabel).should("be.visible");
|
||||||
|
cy.get('[data-cy="v1-current-version-text"]')
|
||||||
|
.verifyVisibleElement("have.text", "v1")
|
||||||
|
.click();
|
||||||
|
cy.get(multiEnvSelector.currentVersion).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"v1"
|
||||||
|
);
|
||||||
|
cy.get(".col-10 > .app-version-name").verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"v1"
|
||||||
|
);
|
||||||
|
cy.get(multiEnvSelector.createNewVersionButton).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"Create new version"
|
||||||
|
);
|
||||||
|
|
||||||
|
verifyPromoteModalUI("v1", "Development", "Staging");
|
||||||
|
cy.get('[data-cy="env-change-info-text"]').verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"You won’t be able to edit this version after promotion. Are you sure you want to continue?"
|
||||||
|
);
|
||||||
|
cy.get(commonSelectors.closeButton).click();
|
||||||
|
cy.get(multiEnvSelector.currentEnvName).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"Development"
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get(commonEeSelectors.promoteButton).click();
|
||||||
|
cy.get(commonSelectors.cancelButton).click();
|
||||||
|
cy.get(multiEnvSelector.currentEnvName).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"Development"
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get(commonEeSelectors.promoteButton).click();
|
||||||
|
cy.get(commonEeSelectors.promoteButton).eq(1).click();
|
||||||
|
|
||||||
|
cy.waitForAppLoad();
|
||||||
|
cy.wait(3000);
|
||||||
|
|
||||||
|
cy.get(commonSelectors.warningText).eq(0).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"App cannot be edited after promotion. Please create a new version from Development to make any changes."
|
||||||
|
);
|
||||||
|
cy.get(multiEnvSelector.envContainer).should("be.visible");
|
||||||
|
cy.get(multiEnvSelector.currentEnvName)
|
||||||
|
.verifyVisibleElement("have.text", "Staging")
|
||||||
|
.click();
|
||||||
|
cy.get(multiEnvSelector.envArrow).should("be.visible");
|
||||||
|
cy.get(multiEnvSelector.currentEnvName).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"Staging"
|
||||||
|
);
|
||||||
|
cy.get(multiEnvSelector.envNameList)
|
||||||
|
.eq(0)
|
||||||
|
.verifyVisibleElement("have.text", "Development");
|
||||||
|
cy.get(multiEnvSelector.envNameList)
|
||||||
|
.eq(1)
|
||||||
|
.verifyVisibleElement("have.text", "Staging");
|
||||||
|
cy.get(multiEnvSelector.envNameList)
|
||||||
|
.eq(2)
|
||||||
|
.verifyVisibleElement("have.text", "Production");
|
||||||
|
cy.wait(2000)
|
||||||
|
verifyTooltip(
|
||||||
|
'[data-cy="env-name-dropdown"]:eq(2)',
|
||||||
|
"There are no versions in this environment"
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get(multiEnvSelector.appVersionLabel).should("be.visible");
|
||||||
|
cy.get('[data-cy="v1-current-version-text"]')
|
||||||
|
.verifyVisibleElement("have.text", "v1")
|
||||||
|
.click();
|
||||||
|
cy.get(multiEnvSelector.currentVersion).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"v1"
|
||||||
|
);
|
||||||
|
cy.get(".col-10 > .app-version-name").verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"v1"
|
||||||
|
);
|
||||||
|
cy.get(multiEnvSelector.createNewVersionButton).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"Create new version"
|
||||||
|
);
|
||||||
|
|
||||||
|
verifyTooltip(
|
||||||
|
multiEnvSelector.createNewVersionButton,
|
||||||
|
"New versions can only be created in development"
|
||||||
|
);
|
||||||
|
cy.forceClickOnCanvas();
|
||||||
|
cy.get(".datasource-picker").should("have.class", "disabled");
|
||||||
|
cy.get(commonEeSelectors.AddQueryButton).should("be.disabled");
|
||||||
|
cy.get(".components-container").should("have.class", "disabled");
|
||||||
|
|
||||||
|
verifyPromoteModalUI("v1", "Staging", "Production");
|
||||||
|
cy.get(commonSelectors.closeButton).click();
|
||||||
|
cy.get(multiEnvSelector.currentEnvName).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"Staging"
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get(commonEeSelectors.promoteButton).click();
|
||||||
|
cy.get(commonSelectors.cancelButton).click();
|
||||||
|
cy.get(multiEnvSelector.currentEnvName).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"Staging"
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get(commonEeSelectors.promoteButton).click();
|
||||||
|
cy.get(commonEeSelectors.promoteButton).eq(1).click();
|
||||||
|
cy.waitForAppLoad();
|
||||||
|
cy.wait(3000);
|
||||||
|
|
||||||
|
cy.get(commonSelectors.warningText).eq(0).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"App cannot be edited after promotion. Please create a new version from Development to make any changes."
|
||||||
|
);
|
||||||
|
cy.get(multiEnvSelector.envContainer).should("be.visible");
|
||||||
|
cy.get(multiEnvSelector.currentEnvName)
|
||||||
|
.verifyVisibleElement("have.text", "Production")
|
||||||
|
.click();
|
||||||
|
cy.get(multiEnvSelector.envArrow).should("be.visible");
|
||||||
|
cy.get(multiEnvSelector.currentEnvName).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"Production"
|
||||||
|
);
|
||||||
|
cy.get(multiEnvSelector.envNameList)
|
||||||
|
.eq(0)
|
||||||
|
.verifyVisibleElement("have.text", "Development");
|
||||||
|
cy.get(multiEnvSelector.envNameList)
|
||||||
|
.eq(1)
|
||||||
|
.verifyVisibleElement("have.text", "Staging");
|
||||||
|
cy.get(multiEnvSelector.envNameList)
|
||||||
|
.eq(2)
|
||||||
|
.verifyVisibleElement("have.text", "Production");
|
||||||
|
|
||||||
|
cy.get(multiEnvSelector.appVersionLabel).should("be.visible");
|
||||||
|
cy.get('[data-cy="v1-current-version-text"]')
|
||||||
|
.verifyVisibleElement("have.text", "v1")
|
||||||
|
.click();
|
||||||
|
cy.get(multiEnvSelector.currentVersion).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"v1"
|
||||||
|
);
|
||||||
|
cy.get(".col-10 > .app-version-name").verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"v1"
|
||||||
|
);
|
||||||
|
cy.get(multiEnvSelector.createNewVersionButton).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"Create new version"
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get(commonSelectors.releaseButton)
|
||||||
|
.verifyVisibleElement("have.text", "Release")
|
||||||
|
.click();
|
||||||
|
cy.get('[data-cy="modal-title"]').verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"Release Version"
|
||||||
|
);
|
||||||
|
cy.get(commonSelectors.closeButton).should("be.visible");
|
||||||
|
cy.get('[data-cy="confirm-dialogue-box-text"]').verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"Are you sure you want to release this version?"
|
||||||
|
);
|
||||||
|
cy.get(commonSelectors.cancelButton).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"Cancel"
|
||||||
|
);
|
||||||
|
cy.get(commonSelectors.yesButton).verifyVisibleElement("have.text", "Yes");
|
||||||
|
|
||||||
|
cy.get(commonSelectors.closeButton).click();
|
||||||
|
cy.get(multiEnvSelector.currentEnvName).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"Production"
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get(commonSelectors.releaseButton).click();
|
||||||
|
cy.get(commonSelectors.cancelButton).click();
|
||||||
|
cy.get(multiEnvSelector.currentEnvName).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"Production"
|
||||||
|
);
|
||||||
|
|
||||||
|
cy.get(commonSelectors.releaseButton).click();
|
||||||
|
cy.get(commonSelectors.yesButton).click();
|
||||||
|
cy.verifyToastMessage(commonSelectors.toastMessage, "Version v1 released");
|
||||||
|
cy.wait(500);
|
||||||
|
cy.get(commonSelectors.warningText).eq(0).verifyVisibleElement(
|
||||||
|
"have.text",
|
||||||
|
"This version of the app is released. Please create a new version in development to make any changes."
|
||||||
|
);
|
||||||
|
cy.get('[data-cy="v1-current-version-text"]').click();
|
||||||
|
verifyTooltip(
|
||||||
|
multiEnvSelector.createNewVersionButton,
|
||||||
|
"New versions can only be created in development"
|
||||||
|
);
|
||||||
|
cy.get(".datasource-picker").should("have.class", "disabled");
|
||||||
|
cy.get(commonEeSelectors.AddQueryButton).should("be.disabled");
|
||||||
|
cy.get(".components-container").should("have.class", "disabled");
|
||||||
|
cy.get(commonSelectors.releaseButton).should("be.disabled");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -1,112 +1,132 @@
|
||||||
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||||
|
import { appPromote } from "Support/utils/platform/multiEnv";
|
||||||
|
|
||||||
const slugValidations = [
|
const slugValidations = [
|
||||||
{ input: "", error: "App slug can't be empty" },
|
{ input: "", error: "App slug can't be empty" },
|
||||||
{ input: "_2#", error: "Special characters are not accepted." },
|
{ input: "_2#", error: "Special characters are not accepted." },
|
||||||
{ input: "t ", error: "Cannot contain spaces" },
|
{ input: "t ", error: "Cannot contain spaces" },
|
||||||
{ input: "T", error: "Only lowercase letters are accepted." },
|
{ input: "T", error: "Only lowercase letters are accepted." },
|
||||||
];
|
];
|
||||||
|
|
||||||
export const verifySlugValidations = (inputSelector) => {
|
export const verifySlugValidations = (inputSelector) => {
|
||||||
slugValidations.forEach(({ input, error }) => {
|
slugValidations.forEach(({ input, error }) => {
|
||||||
cy.get(inputSelector).clear();
|
cy.get(inputSelector).clear();
|
||||||
if (input) cy.clearAndType(inputSelector, input);
|
if (input) cy.clearAndType(inputSelector, input);
|
||||||
cy.wait(500);
|
cy.wait(500);
|
||||||
cy.get(commonWidgetSelector.appSlugErrorLabel).verifyVisibleElement(
|
cy.get(commonWidgetSelector.appSlugErrorLabel).verifyVisibleElement(
|
||||||
"have.text",
|
"have.text",
|
||||||
error
|
error
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const verifySuccessfulSlugUpdate = (workspaceId, slug) => {
|
export const verifySuccessfulSlugUpdate = (workspaceId, slug) => {
|
||||||
const host = resolveHost();
|
const host = resolveHost();
|
||||||
cy.get('[data-cy="app-slug-accepted-label"]').verifyVisibleElement(
|
cy.get('[data-cy="app-slug-accepted-label"]').verifyVisibleElement(
|
||||||
"have.text",
|
"have.text",
|
||||||
"Slug accepted!"
|
"Slug accepted!"
|
||||||
);
|
);
|
||||||
|
|
||||||
cy.wait(500);
|
cy.wait(500);
|
||||||
// cy.get(commonWidgetSelector.appLinkSucessLabel).should('be.visible');
|
// cy.get(commonWidgetSelector.appLinkSucessLabel).should('be.visible');
|
||||||
cy.get(commonWidgetSelector.appLinkSucessLabel).should(
|
cy.get(commonWidgetSelector.appLinkSucessLabel).should(
|
||||||
"have.text",
|
"have.text",
|
||||||
"Link updated successfully!"
|
"Link updated successfully!"
|
||||||
);
|
);
|
||||||
cy.get(commonWidgetSelector.appLinkField).verifyVisibleElement(
|
cy.get(commonWidgetSelector.appLinkField).verifyVisibleElement(
|
||||||
"have.text",
|
"have.text",
|
||||||
`${host}/${workspaceId}/apps/${slug}`
|
`${host}/${workspaceId}/apps/${slug}`
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const verifyURLs = (workspaceId, slug, page) => {
|
export const verifyURLs = (workspaceId, slug, page) => {
|
||||||
const baseUrl = Cypress.config("baseUrl");
|
const baseUrl = Cypress.config("baseUrl");
|
||||||
|
|
||||||
cy.url().should(
|
cy.url().should(
|
||||||
"eq",
|
"eq",
|
||||||
page
|
page
|
||||||
? `${baseUrl}/${workspaceId}/apps/${slug}/home`
|
? `${baseUrl}/${workspaceId}/apps/${slug}/home`
|
||||||
: `${baseUrl}/${workspaceId}/apps/${slug}`
|
: `${baseUrl}/${workspaceId}/apps/${slug}`
|
||||||
);
|
);
|
||||||
|
|
||||||
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
cy.openInCurrentTab(commonWidgetSelector.previewButton);
|
||||||
|
cy.ifEnv("Community", () => {
|
||||||
cy.url().should("eq", `${baseUrl}/applications/${slug}/home?version=v1`);
|
cy.url().should("eq", `${baseUrl}/applications/${slug}/home?version=v1`);
|
||||||
|
});
|
||||||
|
cy.ifEnv("Enterprise", () => {
|
||||||
|
cy.url().should(
|
||||||
|
"eq",
|
||||||
|
`${baseUrl}/applications/${slug}/home?env=production&version=v1`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
cy.visit("/my-workspace");
|
cy.visit("/my-workspace");
|
||||||
cy.visitSlug({
|
cy.visitSlug({
|
||||||
actualUrl: `${baseUrl}/applications/${slug}`,
|
actualUrl: `${baseUrl}/applications/${slug}`,
|
||||||
});
|
});
|
||||||
cy.url().should("eq", `${baseUrl}/applications/${slug}`);
|
cy.url().should("eq", `${baseUrl}/applications/${slug}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setUpSlug = (slug) => {
|
export const setUpSlug = (slug) => {
|
||||||
cy.get(commonWidgetSelector.shareAppButton).click();
|
cy.get(commonWidgetSelector.shareAppButton).click();
|
||||||
cy.clearAndType(commonWidgetSelector.appNameSlugInput, slug);
|
cy.clearAndType(commonWidgetSelector.appNameSlugInput, slug);
|
||||||
cy.get('[data-cy="app-slug-accepted-label"]')
|
cy.get('[data-cy="app-slug-accepted-label"]')
|
||||||
.should("be.visible")
|
.should("be.visible")
|
||||||
.and("have.text", "Slug accepted!");
|
.and("have.text", "Slug accepted!");
|
||||||
cy.get(commonWidgetSelector.modalCloseButton).click();
|
cy.get(commonWidgetSelector.modalCloseButton).click();
|
||||||
};
|
};
|
||||||
|
|
||||||
export const setupAppWithSlug = (appName, slug) => {
|
export const setupAppWithSlug = (appName, slug) => {
|
||||||
cy.apiCreateApp(appName);
|
cy.apiCreateApp(appName);
|
||||||
cy.apiAddComponentToApp(appName, "text1");
|
cy.apiAddComponentToApp(appName, "text1");
|
||||||
cy.apiReleaseApp(appName);
|
|
||||||
cy.apiAddAppSlug(appName, slug);
|
cy.ifEnv("Enterprise", () => {
|
||||||
|
cy.openApp(
|
||||||
|
"",
|
||||||
|
Cypress.env("workspaceId"),
|
||||||
|
Cypress.env("appId"),
|
||||||
|
commonWidgetSelector.draggableWidget("text1")
|
||||||
|
);
|
||||||
|
appPromote("development", "production");
|
||||||
|
});
|
||||||
|
|
||||||
|
cy.apiReleaseApp(appName);
|
||||||
|
cy.apiAddAppSlug(appName, slug);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const verifyRestrictedAccess = () => {
|
export const verifyRestrictedAccess = () => {
|
||||||
cy.get('[data-cy="modal-header"]').should("have.text", "Restricted access");
|
cy.get('[data-cy="modal-header"]').should("have.text", "Restricted access");
|
||||||
cy.get('[data-cy="modal-description"]')
|
cy.get('[data-cy="modal-description"]')
|
||||||
.invoke("text")
|
.invoke("text")
|
||||||
.then((text) => {
|
.then((text) => {
|
||||||
const normalizedText = text.replace(/’/g, "'");
|
const normalizedText = text.replace(/’/g, "'");
|
||||||
expect(normalizedText).to.equal(
|
expect(normalizedText).to.equal(
|
||||||
"You don't have access to this app. Kindly contact admin to know more."
|
"You don't have access to this app. Kindly contact admin to know more."
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
cy.get('[data-cy="back-to-home-button"]').verifyVisibleElement(
|
cy.get('[data-cy="back-to-home-button"]').verifyVisibleElement(
|
||||||
"have.text",
|
"have.text",
|
||||||
"Back to home page"
|
"Back to home page"
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const onboardUserFromAppLink = (
|
export const onboardUserFromAppLink = (
|
||||||
email,
|
email,
|
||||||
slug,
|
slug,
|
||||||
workspaceName = "My workspace",
|
workspaceName = "My workspace",
|
||||||
isNonExistingUser = true
|
isNonExistingUser = true
|
||||||
) => {
|
) => {
|
||||||
const dbConfig = Cypress.env("app_db");
|
const dbConfig = Cypress.env("app_db");
|
||||||
|
|
||||||
const query = isNonExistingUser
|
const query = isNonExistingUser
|
||||||
? `
|
? `
|
||||||
SELECT u.invitation_token, o.id AS workspace_id, ou.invitation_token AS organization_token
|
SELECT u.invitation_token, o.id AS workspace_id, ou.invitation_token AS organization_token
|
||||||
FROM users u
|
FROM users u
|
||||||
JOIN organization_users ou ON u.id = ou.user_id
|
JOIN organization_users ou ON u.id = ou.user_id
|
||||||
JOIN organizations o ON ou.organization_id = o.id
|
JOIN organizations o ON ou.organization_id = o.id
|
||||||
WHERE u.email = '${email}' AND o.name = '${workspaceName}';
|
WHERE u.email = '${email}' AND o.name = '${workspaceName}';
|
||||||
`
|
`
|
||||||
: `
|
: `
|
||||||
SELECT ou.invitation_token, o.id AS workspace_id
|
SELECT ou.invitation_token, o.id AS workspace_id
|
||||||
FROM users u
|
FROM users u
|
||||||
JOIN organization_users ou ON u.id = ou.user_id
|
JOIN organization_users ou ON u.id = ou.user_id
|
||||||
|
|
@ -114,33 +134,33 @@ export const onboardUserFromAppLink = (
|
||||||
WHERE u.email = '${email}' AND o.name = '${workspaceName}';
|
WHERE u.email = '${email}' AND o.name = '${workspaceName}';
|
||||||
`;
|
`;
|
||||||
|
|
||||||
cy.task("dbConnection", { dbconfig: dbConfig, sql: query }).then((resp) => {
|
cy.task("dbConnection", { dbconfig: dbConfig, sql: query }).then((resp) => {
|
||||||
if (!resp.rows || resp.rows.length === 0) {
|
if (!resp.rows || resp.rows.length === 0) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`No records found for email: ${email} and workspace: ${workspaceName}`
|
`No records found for email: ${email} and workspace: ${workspaceName}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { invitation_token, workspace_id, organization_token } = resp.rows[0];
|
const { invitation_token, workspace_id, organization_token } = resp.rows[0];
|
||||||
const token = isNonExistingUser ? organization_token : invitation_token;
|
const token = isNonExistingUser ? organization_token : invitation_token;
|
||||||
const url = isNonExistingUser
|
const url = isNonExistingUser
|
||||||
? `${Cypress.config("baseUrl")}/invitations/${invitation_token}/workspaces/${organization_token}?oid=${workspace_id}&redirectTo=%2Fapplications%2F${slug}`
|
? `${Cypress.config("baseUrl")}/invitations/${invitation_token}/workspaces/${organization_token}?oid=${workspace_id}&redirectTo=%2Fapplications%2F${slug}`
|
||||||
: `${Cypress.config("baseUrl")}/organization-invitations/${token}?oid=${workspace_id}&redirectTo=%2Fapplications%2F${slug}`;
|
: `${Cypress.config("baseUrl")}/organization-invitations/${token}?oid=${workspace_id}&redirectTo=%2Fapplications%2F${slug}`;
|
||||||
|
|
||||||
cy.visit(url);
|
cy.visit(url);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const resolveHost = () => {
|
export const resolveHost = () => {
|
||||||
const baseUrl = Cypress.config("baseUrl");
|
const baseUrl = Cypress.config("baseUrl");
|
||||||
|
|
||||||
const urlMapping = {
|
const urlMapping = {
|
||||||
"http://localhost:8082": "http://localhost:8082",
|
"http://localhost:8082": "http://localhost:8082",
|
||||||
"http://localhost:3000": "http://localhost:3000",
|
"http://localhost:3000": "http://localhost:3000",
|
||||||
"http://localhost:3000/apps": "http://localhost:3000/apps",
|
"http://localhost:3000/apps": "http://localhost:3000/apps",
|
||||||
"http://localhost:4001": "http://localhost:3000",
|
"http://localhost:4001": "http://localhost:3000",
|
||||||
"http://localhost:4001/apps": "http://localhost:3000/apps",
|
"http://localhost:4001/apps": "http://localhost:3000/apps",
|
||||||
};
|
};
|
||||||
|
|
||||||
return urlMapping[baseUrl];
|
return urlMapping[baseUrl];
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import moment from "moment";
|
||||||
import { dashboardSelector } from "Selectors/dashboard";
|
import { dashboardSelector } from "Selectors/dashboard";
|
||||||
import { groupsSelector } from "Selectors/manageGroups";
|
import { groupsSelector } from "Selectors/manageGroups";
|
||||||
import { groupsText } from "Texts/manageGroups";
|
import { groupsText } from "Texts/manageGroups";
|
||||||
|
import { appPromote } from "Support/utils/platform/multiEnv";
|
||||||
|
|
||||||
export const navigateToProfile = () => {
|
export const navigateToProfile = () => {
|
||||||
cy.get(commonSelectors.settingsIcon).click();
|
cy.get(commonSelectors.settingsIcon).click();
|
||||||
|
|
@ -48,7 +49,7 @@ export const randomDateOrTime = (format = "DD/MM/YYYY") => {
|
||||||
let startDate = new Date(2018, 0, 1);
|
let startDate = new Date(2018, 0, 1);
|
||||||
startDate = new Date(
|
startDate = new Date(
|
||||||
startDate.getTime() +
|
startDate.getTime() +
|
||||||
Math.random() * (endDate.getTime() - startDate.getTime())
|
Math.random() * (endDate.getTime() - startDate.getTime())
|
||||||
);
|
);
|
||||||
return moment(startDate).format(format);
|
return moment(startDate).format(format);
|
||||||
};
|
};
|
||||||
|
|
@ -104,7 +105,7 @@ export const viewAppCardOptions = (appName) => {
|
||||||
cy.get(commonSelectors.appCard(appName))
|
cy.get(commonSelectors.appCard(appName))
|
||||||
.realHover()
|
.realHover()
|
||||||
.find(commonSelectors.appCardOptionsButton)
|
.find(commonSelectors.appCardOptionsButton)
|
||||||
.realHover()
|
.realHover();
|
||||||
cy.contains("div", appName)
|
cy.contains("div", appName)
|
||||||
.parent()
|
.parent()
|
||||||
.within(() => {
|
.within(() => {
|
||||||
|
|
@ -230,6 +231,9 @@ export const navigateToworkspaceConstants = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const releaseApp = () => {
|
export const releaseApp = () => {
|
||||||
|
cy.ifEnv("Enterprise", () => {
|
||||||
|
appPromote("development", "production");
|
||||||
|
});
|
||||||
cy.get(commonSelectors.releaseButton).click();
|
cy.get(commonSelectors.releaseButton).click();
|
||||||
cy.get(commonSelectors.yesButton).click();
|
cy.get(commonSelectors.yesButton).click();
|
||||||
cy.verifyToastMessage(commonSelectors.toastMessage, "Version v1 released");
|
cy.verifyToastMessage(commonSelectors.toastMessage, "Version v1 released");
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ export const userSignUp = (fullName, email, workspaceName = "test") => {
|
||||||
cy.visit(invitationLink);
|
cy.visit(invitationLink);
|
||||||
cy.wait(2500);
|
cy.wait(2500);
|
||||||
});
|
});
|
||||||
if (Cypress.env("environment") !== "Community") {
|
if (Cypress.env("environment") == "Cloud") {
|
||||||
cy.clearAndType(
|
cy.clearAndType(
|
||||||
'[data-cy="onboarding-workspace-name-input"]',
|
'[data-cy="onboarding-workspace-name-input"]',
|
||||||
workspaceName
|
workspaceName
|
||||||
|
|
|
||||||
120
cypress-tests/cypress/support/utils/platform/multiEnv.js
Normal file
120
cypress-tests/cypress/support/utils/platform/multiEnv.js
Normal file
|
|
@ -0,0 +1,120 @@
|
||||||
|
import { multiEnvSelector, commonEeSelectors } from "Selectors/eeCommon";
|
||||||
|
import { commonSelectors, commonWidgetSelector } from "Selectors/common";
|
||||||
|
import { appVersionSelectors } from "Selectors/exportImport";
|
||||||
|
import { appVersionText } from "Texts/exportImport";
|
||||||
|
|
||||||
|
export const promoteApp = () => {
|
||||||
|
cy.get(commonEeSelectors.promoteButton).click();
|
||||||
|
cy.get(commonEeSelectors.promoteButton).eq(1).click();
|
||||||
|
cy.waitForAppLoad();
|
||||||
|
cy.wait(3000);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const releaseApp = () => {
|
||||||
|
cy.get(commonSelectors.releaseButton).click();
|
||||||
|
cy.get(commonSelectors.yesButton).click();
|
||||||
|
cy.verifyToastMessage(commonSelectors.toastMessage, "Version v1 released");
|
||||||
|
cy.wait(500);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const launchApp = () => {
|
||||||
|
cy.url().then((url) => {
|
||||||
|
const parts = url.split("/");
|
||||||
|
const value = parts[parts.length - 1];
|
||||||
|
cy.log(`Extracted value: ${value}`);
|
||||||
|
cy.visit(`/applications/${value}`);
|
||||||
|
cy.wait(3000);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const appPromote = (fromEnv, toEnv) => {
|
||||||
|
const commonActions = () => {
|
||||||
|
cy.get(commonEeSelectors.promoteButton).click();
|
||||||
|
cy.get(commonEeSelectors.promoteButton).eq(1).click();
|
||||||
|
cy.waitForAppLoad();
|
||||||
|
cy.wait(2000);
|
||||||
|
};
|
||||||
|
|
||||||
|
const transitions = {
|
||||||
|
development: {
|
||||||
|
staging: commonActions,
|
||||||
|
production: () => {
|
||||||
|
commonActions();
|
||||||
|
appPromote("staging", "production");
|
||||||
|
},
|
||||||
|
release: () => {
|
||||||
|
commonActions();
|
||||||
|
commonActions();
|
||||||
|
cy.get(commonSelectors.releaseButton).click();
|
||||||
|
cy.get(commonSelectors.yesButton).click();
|
||||||
|
cy.wait(500);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
staging: {
|
||||||
|
production: commonActions,
|
||||||
|
release: () => {
|
||||||
|
commonActions();
|
||||||
|
cy.get(commonSelectors.releaseButton).click();
|
||||||
|
cy.get(commonSelectors.yesButton).click();
|
||||||
|
cy.wait(500);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const transition = transitions[fromEnv]?.[toEnv];
|
||||||
|
|
||||||
|
transition();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const createNewVersion = (value, newVersion = [], version) => {
|
||||||
|
cy.get('[data-cy="list-current-env-name"]').click();
|
||||||
|
cy.get(multiEnvSelector.envNameList).eq(0).click();
|
||||||
|
cy.get(appVersionSelectors.currentVersionField(value)).click();
|
||||||
|
cy.get(appVersionSelectors.createNewVersionButton).click();
|
||||||
|
cy.get(appVersionSelectors.createVersionInputField).click();
|
||||||
|
cy.contains(`[id*="react-select-"]`, version).click();
|
||||||
|
cy.get(appVersionSelectors.versionNameInputField).click().type(newVersion[0]);
|
||||||
|
cy.get(appVersionSelectors.createNewVersionButton).click();
|
||||||
|
cy.waitForAppLoad();
|
||||||
|
cy.verifyToastMessage(
|
||||||
|
commonSelectors.toastMessage,
|
||||||
|
appVersionText.createdToastMessage
|
||||||
|
);
|
||||||
|
cy.get(appVersionSelectors.currentVersionField(newVersion[0])).should(
|
||||||
|
"be.visible"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const selectVersion = (value, newVersion = []) => {
|
||||||
|
cy.get(appVersionSelectors.currentVersionField(value)).click();
|
||||||
|
cy.get(".react-select__menu-list .app-version-name")
|
||||||
|
.contains(newVersion[0])
|
||||||
|
.click();
|
||||||
|
cy.waitForAppLoad();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const selectEnv = (envName) => {
|
||||||
|
const envIndex = {
|
||||||
|
development: 0,
|
||||||
|
staging: 1,
|
||||||
|
production: 2,
|
||||||
|
}[envName];
|
||||||
|
|
||||||
|
const isValidEnvName = (envName) => {
|
||||||
|
return (
|
||||||
|
envName === "development" ||
|
||||||
|
envName === "staging" ||
|
||||||
|
envName === "production"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isValidEnvName(envName)) {
|
||||||
|
cy.get('[data-cy="list-current-env-name"]').click();
|
||||||
|
cy.wait(500)
|
||||||
|
const envSelector = `${multiEnvSelector.envNameList}:eq(${envIndex})`;
|
||||||
|
cy.get(envSelector).click();
|
||||||
|
cy.waitForAppLoad();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -9,6 +9,7 @@ import {
|
||||||
} from "Selectors/version";
|
} from "Selectors/version";
|
||||||
import { deleteVersionText, releasedVersionText } from "Texts/version";
|
import { deleteVersionText, releasedVersionText } from "Texts/version";
|
||||||
import { verifyComponent } from "Support/utils/basicComponents";
|
import { verifyComponent } from "Support/utils/basicComponents";
|
||||||
|
import { appPromote } from "./platform/multiEnv";
|
||||||
|
|
||||||
export const navigateToCreateNewVersionModal = (value) => {
|
export const navigateToCreateNewVersionModal = (value) => {
|
||||||
cy.get(appVersionSelectors.appVersionLabel).click();
|
cy.get(appVersionSelectors.appVersionLabel).click();
|
||||||
|
|
@ -121,6 +122,9 @@ export const verifyDuplicateVersion = (newVersion = [], version) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const releasedVersionAndVerify = (currentVersion) => {
|
export const releasedVersionAndVerify = (currentVersion) => {
|
||||||
|
cy.ifEnv("Enterprise", () => {
|
||||||
|
appPromote("development", "production");
|
||||||
|
});
|
||||||
cy.contains("Release").click();
|
cy.contains("Release").click();
|
||||||
|
|
||||||
cy.get(confirmVersionModalSelectors.yesButton).click();
|
cy.get(confirmVersionModalSelectors.yesButton).click();
|
||||||
|
|
|
||||||
|
|
@ -3,15 +3,14 @@ FROM node:22.15.1 AS builder
|
||||||
# Fix for JS heap limit allocation issue
|
# Fix for JS heap limit allocation issue
|
||||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||||
|
|
||||||
RUN npm i -g npm@10.9.2
|
RUN npm i -g npm@10.9.2 && npm cache clean --force
|
||||||
RUN mkdir -p /app
|
|
||||||
RUN npm cache clean --force
|
|
||||||
|
|
||||||
|
RUN mkdir -p /app
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Set GitHub token and branch as build arguments
|
# Set GitHub token and branch as build arguments
|
||||||
ARG CUSTOM_GITHUB_TOKEN
|
ARG CUSTOM_GITHUB_TOKEN
|
||||||
ARG BRANCH_NAME=main
|
ARG BRANCH_NAME
|
||||||
|
|
||||||
# Clone and checkout the frontend repository
|
# Clone and checkout the frontend repository
|
||||||
RUN git config --global url."https://x-access-token:${CUSTOM_GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/"
|
RUN git config --global url."https://x-access-token:${CUSTOM_GITHUB_TOKEN}@github.com/".insteadOf "https://github.com/"
|
||||||
|
|
@ -21,7 +20,7 @@ RUN git config --global http.postBuffer 524288000
|
||||||
RUN git clone https://github.com/ToolJet/ToolJet.git .
|
RUN git clone https://github.com/ToolJet/ToolJet.git .
|
||||||
|
|
||||||
# The branch name needs to be changed the branch with modularisation in CE repo
|
# The branch name needs to be changed the branch with modularisation in CE repo
|
||||||
RUN git checkout main
|
RUN git checkout ${BRANCH_NAME}
|
||||||
|
|
||||||
RUN git submodule update --init --recursive
|
RUN git submodule update --init --recursive
|
||||||
|
|
||||||
|
|
@ -33,85 +32,61 @@ COPY ./package.json ./package.json
|
||||||
|
|
||||||
# Build plugins
|
# Build plugins
|
||||||
COPY ./plugins/package.json ./plugins/package-lock.json ./plugins/
|
COPY ./plugins/package.json ./plugins/package-lock.json ./plugins/
|
||||||
RUN npm --prefix plugins install
|
RUN npm --prefix plugins ci --omit=dev
|
||||||
COPY ./plugins/ ./plugins/
|
COPY ./plugins/ ./plugins/
|
||||||
RUN NODE_ENV=production npm --prefix plugins run build
|
RUN NODE_ENV=production npm --prefix plugins run build
|
||||||
RUN npm --prefix plugins prune --production
|
RUN npm --prefix plugins prune --omit=dev
|
||||||
|
|
||||||
ENV TOOLJET_EDITION=ee
|
|
||||||
|
|
||||||
# Build frontend
|
# Build frontend
|
||||||
COPY ./frontend/package.json ./frontend/package-lock.json ./frontend/
|
COPY ./frontend/package.json ./frontend/package-lock.json ./frontend/
|
||||||
RUN npm --prefix frontend install
|
RUN npm --prefix frontend install
|
||||||
COPY ./frontend/ ./frontend/
|
COPY ./frontend/ ./frontend/
|
||||||
RUN npm --prefix frontend run build --production
|
RUN npm --prefix frontend run build --production && npm --prefix frontend prune --production
|
||||||
RUN npm --prefix frontend prune --production
|
|
||||||
|
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
ENV TOOLJET_EDITION=ee
|
|
||||||
|
|
||||||
# Build server
|
# Build server
|
||||||
COPY ./server/package.json ./server/package-lock.json ./server/
|
COPY ./server/package.json ./server/package-lock.json ./server/
|
||||||
RUN npm --prefix server install
|
RUN npm --prefix server ci --omit=dev
|
||||||
COPY ./server/ ./server/
|
COPY ./server/ ./server/
|
||||||
RUN npm install -g @nestjs/cli
|
RUN npm install -g @nestjs/cli
|
||||||
RUN npm install -g copyfiles
|
RUN npm install -g copyfiles
|
||||||
RUN npm --prefix server run build
|
RUN npm --prefix server run build
|
||||||
|
RUN npm prune --production --prefix server
|
||||||
|
|
||||||
FROM debian:12
|
# Install dependencies for PostgREST, curl, unzip, etc.
|
||||||
|
|
||||||
RUN apt-get update -yq \
|
|
||||||
&& apt-get install curl wget gnupg zip -yq \
|
|
||||||
&& apt-get install -yq build-essential \
|
|
||||||
&& apt -y install redis \
|
|
||||||
&& apt-get clean -y
|
|
||||||
|
|
||||||
# Install required dependencies for downloading and extracting files
|
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
curl tar xz-utils postgresql postgresql-contrib postgresql-client && \
|
curl ca-certificates unzip tar \
|
||||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Install PostgREST from official Docker image
|
ENV POSTGREST_VERSION=v12.2.0
|
||||||
COPY --from=postgrest/postgrest:v12.2.0 /bin/postgrest /bin
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y supervisor
|
RUN curl -Lo postgrest.tar.xz https://github.com/PostgREST/postgrest/releases/download/${POSTGREST_VERSION}/postgrest-v12.2.0-linux-static-x64.tar.xz && \
|
||||||
|
tar -xf postgrest.tar.xz && \
|
||||||
|
mv postgrest /postgrest && \
|
||||||
|
rm postgrest.tar.xz && \
|
||||||
|
chmod +x /postgrest
|
||||||
|
|
||||||
# Create supervisord configuration file
|
FROM debian:12-slim
|
||||||
RUN echo "[supervisord]\n" \
|
|
||||||
"nodaemon=true\n" \
|
|
||||||
"\n" \
|
|
||||||
"[program:postgrest]\n" \
|
|
||||||
"command=/bin/postgrest\n" \
|
|
||||||
"autostart=true\n" \
|
|
||||||
"autorestart=true\n" \
|
|
||||||
"stdout_logfile=/dev/stdout\n" \
|
|
||||||
"stderr_logfile=/dev/stderr\n" \
|
|
||||||
"stdout_logfile_maxbytes=0\n" \
|
|
||||||
"stderr_logfile_maxbytes=0\n" \
|
|
||||||
"\n" \
|
|
||||||
"[program:neo4j]\n" \
|
|
||||||
"command=neo4j console\n" \
|
|
||||||
"autostart=true\n" \
|
|
||||||
"autorestart=unexpected\n" \
|
|
||||||
"startsecs=30\n" \
|
|
||||||
"startretries=999\n" \
|
|
||||||
"priority=90\n" \
|
|
||||||
"exitcodes=0,1,2\n" \
|
|
||||||
"stopsignal=SIGTERM\n" \
|
|
||||||
"stopasgroup=true\n" \
|
|
||||||
"killasgroup=true\n" \
|
|
||||||
"redirect_stderr=true\n" \
|
|
||||||
"stdout_logfile=/var/log/neo4j/neo4j.log\n" \
|
|
||||||
"stdout_logfile_backups=10\n" \
|
|
||||||
"stderr_capture_maxbytes=20MB\n" \
|
|
||||||
"\n" | sed 's/ //' > /etc/supervisor/conf.d/supervisord.conf
|
|
||||||
|
|
||||||
# Create a wrapper for PostgREST to prefix its logs
|
RUN apt-get update && \
|
||||||
RUN mv /bin/postgrest /bin/postgrest-original && \
|
apt-get install -y --no-install-recommends \
|
||||||
echo '#!/bin/bash\n\
|
curl \
|
||||||
exec /bin/postgrest-original "$@" 2>&1 | sed "s/^/[PostgREST] /"\n\
|
wget \
|
||||||
' > /bin/postgrest && \
|
gnupg \
|
||||||
chmod +x /bin/postgrest
|
unzip \
|
||||||
|
ca-certificates \
|
||||||
|
xz-utils \
|
||||||
|
tar \
|
||||||
|
zip \
|
||||||
|
postgresql-client \
|
||||||
|
redis \
|
||||||
|
libaio1 \
|
||||||
|
git \
|
||||||
|
freetds-dev \
|
||||||
|
&& apt-get upgrade -y -o Dpkg::Options::="--force-confold" \
|
||||||
|
&& apt-get autoremove -y \
|
||||||
|
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
|
||||||
RUN curl -O https://nodejs.org/dist/v22.15.1/node-v22.15.1-linux-x64.tar.xz \
|
RUN curl -O https://nodejs.org/dist/v22.15.1/node-v22.15.1-linux-x64.tar.xz \
|
||||||
|
|
@ -125,53 +100,18 @@ ENV PATH=/usr/local/lib/nodejs/bin:$PATH
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
ENV TOOLJET_EDITION=ee
|
ENV TOOLJET_EDITION=ee
|
||||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||||
RUN apt-get update && \
|
|
||||||
apt-get install -y postgresql-client freetds-dev libaio1 wget && \
|
|
||||||
apt-get -o Dpkg::Options::="--force-confold" upgrade -q -y --force-yes && \
|
|
||||||
apt-get -y autoremove && \
|
|
||||||
apt-get -y autoclean
|
|
||||||
|
|
||||||
# Install Neo4j
|
# Install Neo4j + APOC
|
||||||
RUN wget -O - https://debian.neo4j.com/neotechnology.gpg.key | apt-key add - && \
|
RUN wget -O - https://debian.neo4j.com/neotechnology.gpg.key | apt-key add - && \
|
||||||
echo "deb https://debian.neo4j.com stable 5" > /etc/apt/sources.list.d/neo4j.list && \
|
echo "deb https://debian.neo4j.com stable 5" > /etc/apt/sources.list.d/neo4j.list && \
|
||||||
apt-get update && \
|
apt-get update && apt-get install -y neo4j=1:5.26.6 && apt-mark hold neo4j && \
|
||||||
apt-get install -y neo4j=1:5.26.6 && \
|
mkdir -p /var/lib/neo4j/plugins && \
|
||||||
apt-mark hold neo4j && \
|
|
||||||
apt-get clean && rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Set the necessary Neo4j environment variables
|
|
||||||
ENV NEO4J_HOME=/opt/neo4j
|
|
||||||
ENV NEO4J_CONF=/etc/neo4j
|
|
||||||
ENV NEO4J_DATA=/var/lib/neo4j/data
|
|
||||||
ENV NEO4J_LOG=/var/log/neo4j
|
|
||||||
ENV NEO4J_PLUGIN=/var/lib/neo4j/plugins
|
|
||||||
ENV NEO4J_IMPORT=/var/lib/neo4j/import
|
|
||||||
|
|
||||||
# Create the necessary directories for Neo4j
|
|
||||||
RUN mkdir -p /data/db /data/logs /data/plugins
|
|
||||||
RUN mkdir -p /opt/neo4j/plugins
|
|
||||||
|
|
||||||
# Configure APOC plugin for Neo4j
|
|
||||||
ENV NEO4J_dbms_active_plugins=apoc
|
|
||||||
|
|
||||||
# Download and install APOC plugin for Neo4j 5.x (BEFORE creating user)
|
|
||||||
RUN mkdir -p /var/lib/neo4j/plugins && \
|
|
||||||
wget -P /var/lib/neo4j/plugins https://github.com/neo4j/apoc/releases/download/5.26.6/apoc-5.26.6-core.jar && \
|
wget -P /var/lib/neo4j/plugins https://github.com/neo4j/apoc/releases/download/5.26.6/apoc-5.26.6-core.jar && \
|
||||||
# Try to download extended version
|
echo "dbms.security.procedures.unrestricted=apoc.*" >> /etc/neo4j/neo4j.conf && \
|
||||||
(wget -P /var/lib/neo4j/plugins https://github.com/neo4j/apoc/releases/download/5.26.6/apoc-5.26.6-extended.jar || \
|
|
||||||
wget -P /var/lib/neo4j/plugins https://neo4j-contrib.github.io/neo4j-apoc-procedures/5.26.6/apoc-5.26.6-extended.jar || \
|
|
||||||
echo "Extended JAR not available, continuing with core only")
|
|
||||||
|
|
||||||
# Configure Neo4j with APOC
|
|
||||||
RUN echo "dbms.security.procedures.unrestricted=apoc.*" >> /etc/neo4j/neo4j.conf && \
|
|
||||||
echo "dbms.security.procedures.allowlist=apoc.*,algo.*,gds.*" >> /etc/neo4j/neo4j.conf && \
|
echo "dbms.security.procedures.allowlist=apoc.*,algo.*,gds.*" >> /etc/neo4j/neo4j.conf && \
|
||||||
echo "dbms.directories.plugins=/var/lib/neo4j/plugins" >> /etc/neo4j/neo4j.conf
|
echo "dbms.directories.plugins=/var/lib/neo4j/plugins" >> /etc/neo4j/neo4j.conf && \
|
||||||
|
echo "dbms.security.auth_enabled=true" >> /etc/neo4j/neo4j.conf && \
|
||||||
# Configure Neo4j to use authentication
|
apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
RUN if [ -f "/etc/neo4j/neo4j.conf" ]; then \
|
|
||||||
sed -i '/dbms.security.auth_enabled/d' /etc/neo4j/neo4j.conf && \
|
|
||||||
echo "dbms.security.auth_enabled=true" >> /etc/neo4j/neo4j.conf; \
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Install Instantclient Basic Light Oracle and Dependencies
|
# Install Instantclient Basic Light Oracle and Dependencies
|
||||||
WORKDIR /opt/oracle
|
WORKDIR /opt/oracle
|
||||||
|
|
@ -186,40 +126,39 @@ RUN wget https://tooljet-plugins-production.s3.us-east-2.amazonaws.com/marketpla
|
||||||
# Set the Instant Client library paths
|
# Set the Instant Client library paths
|
||||||
ENV LD_LIBRARY_PATH="/opt/oracle/instantclient_11_2:/opt/oracle/instantclient_21_10:${LD_LIBRARY_PATH}"
|
ENV LD_LIBRARY_PATH="/opt/oracle/instantclient_11_2:/opt/oracle/instantclient_21_10:${LD_LIBRARY_PATH}"
|
||||||
|
|
||||||
|
RUN rm -f *.zip *.key && apt-get clean && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
WORKDIR /
|
WORKDIR /
|
||||||
|
|
||||||
RUN mkdir -p /app
|
RUN mkdir -p /app
|
||||||
# copy npm scripts
|
|
||||||
COPY --from=builder /app/package.json ./app/package.json
|
|
||||||
# copy plugins dependencies
|
|
||||||
COPY --from=builder /app/plugins/dist ./app/plugins/dist
|
|
||||||
COPY --from=builder /app/plugins/client.js ./app/plugins/client.js
|
|
||||||
COPY --from=builder /app/plugins/node_modules ./app/plugins/node_modules
|
|
||||||
COPY --from=builder /app/plugins/packages/common ./app/plugins/packages/common
|
|
||||||
COPY --from=builder /app/plugins/package.json ./app/plugins/package.json
|
|
||||||
# copy frontend build
|
|
||||||
COPY --from=builder /app/frontend/build ./app/frontend/build
|
|
||||||
# copy server build
|
|
||||||
COPY --from=builder /app/server/package.json ./app/server/package.json
|
|
||||||
COPY --from=builder /app/server/.version ./app/server/.version
|
|
||||||
COPY --from=builder /app/server/ee/keys ./app/server/ee/keys
|
|
||||||
COPY --from=builder /app/server/node_modules ./app/server/node_modules
|
|
||||||
COPY --from=builder /app/server/templates ./app/server/templates
|
|
||||||
COPY --from=builder /app/server/scripts ./app/server/scripts
|
|
||||||
COPY --from=builder /app/server/dist ./app/server/dist
|
|
||||||
COPY --from=builder /app/server/src/assets ./app/server/src/assets
|
|
||||||
|
|
||||||
COPY ./docker/ee/ee-entrypoint.sh ./app/server/ee-entrypoint.sh
|
RUN useradd --create-home --home-dir /home/appuser appuser
|
||||||
|
|
||||||
# Define non-sudo user
|
# Use the PostgREST binary from the builder stage
|
||||||
RUN useradd --create-home --home-dir /home/appuser appuser \
|
COPY --from=builder --chown=appuser:0 /postgrest /usr/local/bin/postgrest
|
||||||
&& chown -R appuser:0 /app \
|
|
||||||
&& chown -R appuser:0 /home \
|
RUN mv /usr/local/bin/postgrest /usr/local/bin/postgrest-original && \
|
||||||
&& chmod u+x /app \
|
echo '#!/bin/bash\nexec /usr/local/bin/postgrest-original "$@" 2>&1 | sed "s/^/[PostgREST] /"' > /usr/local/bin/postgrest && \
|
||||||
&& chmod u+x /home \
|
chmod +x /usr/local/bin/postgrest
|
||||||
&& chmod -R g=u /app \
|
|
||||||
&& chmod -R g=u /home
|
|
||||||
|
# Copy application with ownership set directly to avoid chown -R
|
||||||
|
COPY --from=builder --chown=appuser:0 /app/package.json ./app/package.json
|
||||||
|
COPY --from=builder --chown=appuser:0 /app/plugins/dist ./app/plugins/dist
|
||||||
|
COPY --from=builder --chown=appuser:0 /app/plugins/client.js ./app/plugins/client.js
|
||||||
|
COPY --from=builder --chown=appuser:0 /app/plugins/node_modules ./app/plugins/node_modules
|
||||||
|
COPY --from=builder --chown=appuser:0 /app/plugins/packages/common ./app/plugins/packages/common
|
||||||
|
COPY --from=builder --chown=appuser:0 /app/plugins/package.json ./app/plugins/package.json
|
||||||
|
COPY --from=builder --chown=appuser:0 /app/frontend/build ./app/frontend/build
|
||||||
|
COPY --from=builder --chown=appuser:0 /app/server/package.json ./app/server/package.json
|
||||||
|
COPY --from=builder --chown=appuser:0 /app/server/.version ./app/server/.version
|
||||||
|
COPY --from=builder --chown=appuser:0 /app/server/ee/keys ./app/server/ee/keys
|
||||||
|
COPY --from=builder --chown=appuser:0 /app/server/node_modules ./app/server/node_modules
|
||||||
|
COPY --from=builder --chown=appuser:0 /app/server/templates ./app/server/templates
|
||||||
|
COPY --from=builder --chown=appuser:0 /app/server/scripts ./app/server/scripts
|
||||||
|
COPY --from=builder --chown=appuser:0 /app/server/dist ./app/server/dist
|
||||||
|
COPY --from=builder --chown=appuser:0 /app/server/src/assets ./app/server/src/assets
|
||||||
|
COPY ./docker/ee/ee-entrypoint.sh ./app/server/ee-entrypoint.sh
|
||||||
|
|
||||||
RUN mkdir -p /var/lib/neo4j/data/databases /var/lib/neo4j/data/transactions /var/log/neo4j /opt/neo4j/run && \
|
RUN mkdir -p /var/lib/neo4j/data/databases /var/lib/neo4j/data/transactions /var/log/neo4j /opt/neo4j/run && \
|
||||||
chown -R appuser:0 /var/lib/neo4j /var/log/neo4j /etc/neo4j /opt/neo4j/run && \
|
chown -R appuser:0 /var/lib/neo4j /var/log/neo4j /etc/neo4j /opt/neo4j/run && \
|
||||||
|
|
@ -258,31 +197,11 @@ RUN mkdir -p /var/lib/redis /var/log/redis /etc/redis \
|
||||||
&& chmod g+s /var/lib/redis /var/log/redis /etc/redis \
|
&& chmod g+s /var/lib/redis /var/log/redis /etc/redis \
|
||||||
&& chmod -R g=u /var/lib/redis /var/log/redis /etc/redis
|
&& chmod -R g=u /var/lib/redis /var/log/redis /etc/redis
|
||||||
|
|
||||||
# Set permissions for PostgREST binary
|
|
||||||
RUN chown appuser:0 /bin/postgrest && chmod u+x /bin/postgrest && chmod g=u /bin/postgrest
|
|
||||||
|
|
||||||
RUN touch /tmp/postgrest.conf \
|
|
||||||
&& chown appuser:0 /tmp/postgrest.conf \
|
|
||||||
&& chmod 640 /tmp/postgrest.conf
|
|
||||||
|
|
||||||
# Create PostgREST data, log, and configuration directories
|
|
||||||
RUN mkdir -p /var/lib/postgrest /var/log/postgrest /etc/postgrest \
|
|
||||||
&& chown -R appuser:0 /var/lib/postgrest /var/log/postgrest /etc/postgrest \
|
|
||||||
&& chmod g+s /var/lib/postgrest /var/log/postgrest /etc/postgrest \
|
|
||||||
&& chmod -R g=u /var/lib/postgrest /var/log/postgrest /etc/postgrest
|
|
||||||
|
|
||||||
ENV HOME=/home/appuser
|
ENV HOME=/home/appuser
|
||||||
|
|
||||||
# Installing git for simple git commands
|
|
||||||
RUN apt-get update && apt-get install -y git && apt-get clean
|
|
||||||
|
|
||||||
# Switch back to appuser
|
# Switch back to appuser
|
||||||
USER appuser
|
USER appuser
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
# Dependencies for scripts outside nestjs
|
|
||||||
RUN npm install dotenv@10.0.0 joi@17.4.1
|
|
||||||
|
|
||||||
RUN npm cache clean --force
|
RUN npm install --prefix server --no-save dotenv@10.0.0 joi@17.4.1 && npm cache clean --force
|
||||||
|
|
||||||
ENTRYPOINT ["./server/ee-entrypoint.sh"]
|
ENTRYPOINT ["./server/ee-entrypoint.sh"]
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,47 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
|
# Install grpcurl if not already installed
|
||||||
|
if ! command -v grpcurl &> /dev/null; then
|
||||||
|
echo "grpcurl not found, installing..."
|
||||||
|
apt update && apt install -y curl \
|
||||||
|
&& curl -sSL https://github.com/fullstorydev/grpcurl/releases/download/v1.8.0/grpcurl_1.8.0_linux_x86_64.tar.gz | tar -xzv -C /usr/local/bin grpcurl
|
||||||
|
fi
|
||||||
|
|
||||||
# Start Redis
|
# Start Redis
|
||||||
# service redis-server start
|
service redis-server start
|
||||||
# redis-server /etc/redis/redis.conf
|
|
||||||
|
|
||||||
# Start Postgres
|
# Start Postgres
|
||||||
service postgresql start
|
service postgresql start
|
||||||
|
|
||||||
|
# Start Temporal Server (SQLite configuration)
|
||||||
|
echo "Starting Temporal Server..."
|
||||||
|
/usr/bin/temporal-server -r / -c /etc/temporal/ -e temporal-server start &
|
||||||
|
|
||||||
# Export the PORT variable to be used by the application
|
# Export the PORT variable to be used by the application
|
||||||
export PORT=${PORT:-80}
|
export PORT=${PORT:-80}
|
||||||
|
|
||||||
# Start Supervisor
|
# Start Supervisor
|
||||||
exec supervisord -c /etc/supervisor/conf.d/supervisord.conf
|
exec supervisord -c /etc/supervisor/conf.d/supervisord.conf &
|
||||||
|
|
||||||
|
# Wait for Temporal Server to be ready
|
||||||
|
echo "Waiting for Temporal Server to be ready..."
|
||||||
|
sleep 10
|
||||||
|
|
||||||
|
# Check if namespace already exists
|
||||||
|
echo "Checking if Temporal namespace exists..."
|
||||||
|
if grpcurl -plaintext localhost:7233 temporal.api.workflowservice.v1.WorkflowService/ListNamespaces | grep -q '"name": "default"'; then
|
||||||
|
echo "Namespace 'default' already exists."
|
||||||
|
else
|
||||||
|
# Register the namespace if it doesn't exist
|
||||||
|
echo "Registering Temporal namespace..."
|
||||||
|
grpcurl -plaintext -d '{
|
||||||
|
"namespace": "default",
|
||||||
|
"description": "Default namespace",
|
||||||
|
"workflowExecutionRetentionPeriod": "259200s"
|
||||||
|
}' localhost:7233 temporal.api.workflowservice.v1.WorkflowService/RegisterNamespace
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run the worker process (last step)
|
||||||
|
echo "Starting worker process..."
|
||||||
|
npm run worker:prod
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,20 @@
|
||||||
FROM tooljet/tooljet:ee-lts-latest
|
FROM tooljet/tooljet:ee-lts-latest
|
||||||
|
|
||||||
# Copy PostgREST executable
|
# Copy postgrest executable
|
||||||
COPY --from=postgrest/postgrest:v12.2.0 /bin/postgrest /bin
|
COPY --from=postgrest/postgrest:v12.2.0 /bin/postgrest /bin
|
||||||
|
|
||||||
# Install PostgreSQL
|
# Install Postgres
|
||||||
USER root
|
USER root
|
||||||
RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
|
RUN wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | apt-key add -
|
||||||
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list
|
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ bullseye-pgdg main" | tee /etc/apt/sources.list.d/pgdg.list
|
||||||
|
RUN echo "deb http://deb.debian.org/debian"
|
||||||
RUN apt update && apt -y install postgresql-13 postgresql-client-13 supervisor
|
RUN apt update && apt -y install postgresql-13 postgresql-client-13 supervisor
|
||||||
|
|
||||||
USER postgres
|
USER postgres
|
||||||
RUN service postgresql start && \
|
RUN service postgresql start && \
|
||||||
psql -c "create role tooljet with login superuser password 'postgres';"
|
psql -c "create role tooljet with login superuser password 'postgres';"
|
||||||
USER root
|
USER root
|
||||||
|
|
||||||
# Install Redis
|
|
||||||
RUN apt update && apt -y install redis
|
RUN apt update && apt -y install redis
|
||||||
|
|
||||||
# Create appuser home & ensure permission for supervisord and services
|
# Create appuser home & ensure permission for supervisord and services
|
||||||
|
|
@ -22,6 +22,28 @@ RUN mkdir -p /var/log/supervisor /var/run/postgresql /var/lib/postgresql /var/li
|
||||||
chown -R appuser:appuser /etc/supervisor /var/log/supervisor /var/lib/redis && \
|
chown -R appuser:appuser /etc/supervisor /var/log/supervisor /var/lib/redis && \
|
||||||
chown -R postgres:postgres /var/run/postgresql /var/lib/postgresql
|
chown -R postgres:postgres /var/run/postgresql /var/lib/postgresql
|
||||||
|
|
||||||
|
# Install Temporal Server Binaries
|
||||||
|
RUN curl -OL https://github.com/temporalio/temporal/releases/download/v1.24.2/temporal_1.24.2_linux_amd64.tar.gz && \
|
||||||
|
tar -xzf temporal_1.24.2_linux_amd64.tar.gz && \
|
||||||
|
mv temporal-server /usr/bin/temporal-server && \
|
||||||
|
chmod +x /usr/bin/temporal-server && \
|
||||||
|
rm temporal_1.24.2_linux_amd64.tar.gz
|
||||||
|
|
||||||
|
# Install Temporal UI Server Binaries
|
||||||
|
RUN curl -OL https://github.com/temporalio/ui-server/releases/download/v2.28.0/ui-server_2.28.0_linux_amd64.tar.gz && \
|
||||||
|
tar -xzf ui-server_2.28.0_linux_amd64.tar.gz && \
|
||||||
|
mv ui-server /usr/bin/temporal-ui-server && \
|
||||||
|
chmod +x /usr/bin/temporal-ui-server && \
|
||||||
|
rm ui-server_2.28.0_linux_amd64.tar.gz
|
||||||
|
|
||||||
|
# Copy Temporal configuration files
|
||||||
|
COPY ./docker/ee/temporal-server.yaml /etc/temporal/temporal-server.yaml
|
||||||
|
COPY ./docker/ee/temporal-ui-server.yaml /etc/temporal/temporal-ui-server.yaml
|
||||||
|
|
||||||
|
# Install grpcurl
|
||||||
|
RUN apt update && apt install -y curl \
|
||||||
|
&& curl -sSL https://github.com/fullstorydev/grpcurl/releases/download/v1.8.0/grpcurl_1.8.0_linux_x86_64.tar.gz | tar -xzv -C /usr/local/bin grpcurl
|
||||||
|
|
||||||
# Configure Supervisor to manage PostgREST, ToolJet, and Redis
|
# Configure Supervisor to manage PostgREST, ToolJet, and Redis
|
||||||
RUN echo "[supervisord] \n" \
|
RUN echo "[supervisord] \n" \
|
||||||
"nodaemon=true \n" \
|
"nodaemon=true \n" \
|
||||||
|
|
@ -52,8 +74,10 @@ RUN echo "[supervisord] \n" \
|
||||||
"stdout_logfile=/dev/stdout \n" \
|
"stdout_logfile=/dev/stdout \n" \
|
||||||
"stdout_logfile_maxbytes=0 \n" | sed 's/ //' > /etc/supervisor/conf.d/supervisord.conf
|
"stdout_logfile_maxbytes=0 \n" | sed 's/ //' > /etc/supervisor/conf.d/supervisord.conf
|
||||||
|
|
||||||
|
|
||||||
# ENV defaults
|
# ENV defaults
|
||||||
ENV TOOLJET_HOST=http://localhost \
|
ENV TOOLJET_HOST=http://localhost \
|
||||||
|
TOOLJET_SERVER_URL=http://localhost \
|
||||||
PORT=80 \
|
PORT=80 \
|
||||||
NODE_ENV=production \
|
NODE_ENV=production \
|
||||||
LOCKBOX_MASTER_KEY=replace_with_lockbox_master_key \
|
LOCKBOX_MASTER_KEY=replace_with_lockbox_master_key \
|
||||||
|
|
@ -78,7 +102,14 @@ ENV TOOLJET_HOST=http://localhost \
|
||||||
REDIS_PORT=6379 \
|
REDIS_PORT=6379 \
|
||||||
REDIS_USER=default \
|
REDIS_USER=default \
|
||||||
REDIS_PASS= \
|
REDIS_PASS= \
|
||||||
TERM=xterm
|
ENABLE_MARKETPLACE_FEATURE=true \
|
||||||
|
TERM=xterm \
|
||||||
|
ENABLE_WORKFLOW_SCHEDULING=true \
|
||||||
|
TEMPORAL_SERVER_ADDRESS=localhost:7233 \
|
||||||
|
TEMPORAL_TASK_QUEUE_NAME_FOR_WORKFLOWS=tooljet-workflows \
|
||||||
|
TOOLJET_WORKFLOWS_TEMPORAL_NAMESPACE=default \
|
||||||
|
TEMPORAL_ADDRESS=localhost:7233 \
|
||||||
|
TEMPORAL_CORS_ORIGINS=http://localhost:8080
|
||||||
|
|
||||||
# Set the entrypoint
|
# Set the entrypoint
|
||||||
COPY ./docker/ee/ee-try-entrypoint-lts.sh /ee-try-entrypoint-lts.sh
|
COPY ./docker/ee/ee-try-entrypoint-lts.sh /ee-try-entrypoint-lts.sh
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue