From 20d2eae1623e468067d09c594d1825c0bfae83ba Mon Sep 17 00:00:00 2001 From: Nidhi Date: Thu, 9 Sep 2021 22:40:29 +0530 Subject: [PATCH 01/60] fix: Make js editor get call in view mode accessible to anonymous users --- .../java/com/appsmith/server/configurations/SecurityConfig.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SecurityConfig.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SecurityConfig.java index c7bc2f10d0..74fba23701 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SecurityConfig.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/configurations/SecurityConfig.java @@ -37,6 +37,7 @@ import java.time.Duration; import java.util.Arrays; import java.util.HashSet; +import static com.appsmith.server.constants.Url.ACTION_COLLECTION_URL; import static com.appsmith.server.constants.Url.ACTION_URL; import static com.appsmith.server.constants.Url.APPLICATION_URL; import static com.appsmith.server.constants.Url.PAGE_URL; @@ -143,6 +144,7 @@ public class SecurityConfig { ServerWebExchangeMatchers.pathMatchers(HttpMethod.PUT, USER_URL + "/invite/confirm"), ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, USER_URL + "/me"), ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, ACTION_URL + "/**"), + ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, ACTION_COLLECTION_URL + "/view"), ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, PAGE_URL + "/**"), ServerWebExchangeMatchers.pathMatchers(HttpMethod.GET, APPLICATION_URL + "/**"), ServerWebExchangeMatchers.pathMatchers(HttpMethod.POST, ACTION_URL + "/execute") From c3acf9e416f0aa62ee87a5197daf58a1befb548c Mon Sep 17 00:00:00 2001 From: Arpit Mohan Date: Mon, 13 Sep 2021 14:37:30 +0530 Subject: [PATCH 02/60] fix: Fixing the previous page pagination for Firestore (#7374) The problem was that the pagination was designed & tested with only 2 pages. Firestore has a different function `limitToLast` which solves for previous page pagination commands. --- .../com/external/plugins/FirestorePlugin.java | 7 +- .../external/plugins/FirestorePluginTest.java | 154 ++++++++---------- 2 files changed, 77 insertions(+), 84 deletions(-) diff --git a/app/server/appsmith-plugins/firestorePlugin/src/main/java/com/external/plugins/FirestorePlugin.java b/app/server/appsmith-plugins/firestorePlugin/src/main/java/com/external/plugins/FirestorePlugin.java index 215bbaf429..c5cef77f99 100644 --- a/app/server/appsmith-plugins/firestorePlugin/src/main/java/com/external/plugins/FirestorePlugin.java +++ b/app/server/appsmith-plugins/firestorePlugin/src/main/java/com/external/plugins/FirestorePlugin.java @@ -656,7 +656,12 @@ public class FirestorePlugin extends BasePlugin { return Mono.just(query1); }) // Apply limit, always provided, since without it we can inadvertently end up processing too much data. - .map(query1 -> query1.limit(limit)) + .map(query1 -> { + if (PaginationField.PREV.equals(paginationField) && !CollectionUtils.isEmpty(endBefore)) { + return query1.limitToLast(limit); + } + return query1.limit(limit); + }) // Run the Firestore query to get a Future of the results. .map(Query::get) // Consume the future to get the actual results. diff --git a/app/server/appsmith-plugins/firestorePlugin/src/test/java/com/external/plugins/FirestorePluginTest.java b/app/server/appsmith-plugins/firestorePlugin/src/test/java/com/external/plugins/FirestorePluginTest.java index fd884b70c8..a25a210c88 100644 --- a/app/server/appsmith-plugins/firestorePlugin/src/test/java/com/external/plugins/FirestorePluginTest.java +++ b/app/server/appsmith-plugins/firestorePlugin/src/test/java/com/external/plugins/FirestorePluginTest.java @@ -31,7 +31,7 @@ import org.testcontainers.containers.FirestoreEmulatorContainer; import org.testcontainers.utility.DockerImageName; import reactor.core.publisher.Mono; import reactor.test.StepVerifier; -import reactor.util.function.Tuple3; +import reactor.util.function.Tuple4; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -445,92 +445,73 @@ public class FirestorePluginTest { .verifyComplete(); } + private ActionConfiguration constructActionConfiguration(Map first, Map last) { + final ObjectMapper objectMapper = new ObjectMapper(); + ActionConfiguration actionConfiguration = new ActionConfiguration(); + actionConfiguration.setPath("pagination"); + List propertyList = new ArrayList<>(); + propertyList.add(new Property("method", "GET_COLLECTION")); + propertyList.add(new Property("order", "[\"n\"]")); + propertyList.add(new Property("limit", "5")); + + if (first != null && last != null) { + try { + propertyList.add(new Property()); + propertyList.add(new Property()); + propertyList.add(new Property()); + propertyList.add(new Property("startAfter", objectMapper.writeValueAsString(last))); + propertyList.add(new Property("endBefore", objectMapper.writeValueAsString(first))); + } catch (JsonProcessingException e) { + e.printStackTrace(); + } + } + actionConfiguration.setPluginSpecifiedTemplates(propertyList); + return actionConfiguration; + } + + private Mono getNextOrPrevPage(ActionExecutionResult currentPage, PaginationField paginationField) { + List> results = (List) currentPage.getBody(); + final Map first = results.get(0); + final Map last = results.get(results.size() - 1); + + final ExecuteActionDTO execDetails = new ExecuteActionDTO(); + execDetails.setPaginationField(paginationField); + + final ActionConfiguration actionConfiguration1 = constructActionConfiguration(first, last); + return pluginExecutor.executeParameterized(firestoreConnection, execDetails, dsConfig, actionConfiguration1); + } + @Test public void testPagination() { - final ActionConfiguration actionConfiguration = new ActionConfiguration(); - actionConfiguration.setPath("pagination"); - actionConfiguration.setPluginSpecifiedTemplates(List.of( - new Property("method", "GET_COLLECTION"), - new Property("order", "[\"n\"]"), - new Property("limit", "5") - )); - - final ObjectMapper objectMapper = new ObjectMapper(); - - Mono> pagesMono = pluginExecutor + final ActionConfiguration actionConfiguration = constructActionConfiguration(null, null); + // Fetch data for page 1 + Mono page1Mono = pluginExecutor .executeParameterized(firestoreConnection, null, dsConfig, actionConfiguration) - .flatMap(result -> { - List> results = (List) result.getBody(); - final Map first = results.get(0); - final Map last = results.get(results.size() - 1); + .cache(); - final ExecuteActionDTO execDetails = new ExecuteActionDTO(); - execDetails.setPaginationField(PaginationField.NEXT); + // Fetch data for page 2 by clicking on the next button + Mono page2Mono = page1Mono + .flatMap(page1 -> getNextOrPrevPage(page1, PaginationField.NEXT)) + .cache(); - final ActionConfiguration actionConfiguration1 = new ActionConfiguration(); - actionConfiguration1.setPath(actionConfiguration.getPath()); - try { - actionConfiguration1.setPluginSpecifiedTemplates(List.of( - new Property("method", "GET_COLLECTION"), - new Property("order", "[\"n\"]"), - new Property("limit", "5"), - new Property(), - new Property(), - new Property(), - new Property("startAfter", objectMapper.writeValueAsString(last)), - new Property("endBefore", objectMapper.writeValueAsString(first)) - )); - } catch (JsonProcessingException e) { - e.printStackTrace(); - return Mono.error(e); - } + // Fetch data for page 3 by clicking on the next button + Mono page3Mono = page2Mono + .flatMap(page2 -> getNextOrPrevPage(page2, PaginationField.NEXT)) + .cache(); - return Mono.zip( - Mono.just(result), - pluginExecutor.executeParameterized(firestoreConnection, execDetails, dsConfig, actionConfiguration1) - ); - }) - .flatMap(twoPagesMono -> { - final ActionExecutionResult page1 = twoPagesMono.getT1(); - final ActionExecutionResult page2 = twoPagesMono.getT2(); + // Fetch data for page 2 by clicking on the previous button + Mono prevPage2Mono = page3Mono + .flatMap(page3 -> getNextOrPrevPage(page3, PaginationField.PREV)) + .cache(); - List> results = (List) page2.getBody(); - final Map first = results.get(0); - final Map last = results.get(results.size() - 1); - - final ExecuteActionDTO execDetails = new ExecuteActionDTO(); - execDetails.setPaginationField(PaginationField.PREV); - - final ActionConfiguration actionConfiguration1 = new ActionConfiguration(); - actionConfiguration1.setPath(actionConfiguration.getPath()); - try { - actionConfiguration1.setPluginSpecifiedTemplates(List.of( - new Property("method", "GET_COLLECTION"), - new Property("order", "[\"n\"]"), - new Property("limit", "5"), - new Property(), - new Property(), - new Property(), - new Property("startAfter", objectMapper.writeValueAsString(last)), - new Property("endBefore", objectMapper.writeValueAsString(first)) - )); - } catch (JsonProcessingException e) { - e.printStackTrace(); - return Mono.error(e); - } - - return Mono.zip( - Mono.just(page1), - Mono.just(page2), - pluginExecutor.executeParameterized(firestoreConnection, execDetails, dsConfig, actionConfiguration1) - ); - }); + var pagesMono = Mono.zip(page1Mono, page2Mono, page3Mono, prevPage2Mono); StepVerifier.create(pagesMono) .assertNext(resultPages -> { final ActionExecutionResult firstPageResult = resultPages.getT1(); final ActionExecutionResult secondPageResult = resultPages.getT2(); - final ActionExecutionResult firstPageResultAgain = resultPages.getT3(); + final ActionExecutionResult thirdPageResult = resultPages.getT3(); + final ActionExecutionResult secondPageResultAgain = resultPages.getT4(); assertTrue(firstPageResult.getIsExecutionSuccess()); @@ -548,13 +529,20 @@ public class FirestorePluginTest { secondResults.stream().map(m -> m.get("n").toString()).collect(Collectors.toList()).toString() ); - List> firstResultsAgain = (List) firstPageResultAgain.getBody(); + List> firstResultsAgain = (List) thirdPageResult.getBody(); assertEquals(5, firstResultsAgain.size()); assertEquals( - "[1, 2, 3, 4, 5]", + "[11, 12, 13, 14, 15]", firstResultsAgain.stream().map(m -> m.get("n").toString()).collect(Collectors.toList()).toString() ); + List> secondResultsAgain = (List) secondPageResultAgain.getBody(); + assertEquals(5, secondResultsAgain.size()); + assertEquals( + "[6, 7, 8, 9, 10]", + secondResultsAgain.stream().map(m -> m.get("n").toString()).collect(Collectors.toList()).toString() + ); + }) .verifyComplete(); } @@ -580,7 +568,7 @@ public class FirestorePluginTest { error.getMessage()); // Check that the error does not get logged externally. - assertFalse(AppsmithErrorAction.LOG_EXTERNALLY.equals(((AppsmithPluginException)error).getError().getErrorAction())); + assertFalse(AppsmithErrorAction.LOG_EXTERNALLY.equals(((AppsmithPluginException) error).getError().getErrorAction())); }) .verify(); } @@ -710,7 +698,7 @@ public class FirestorePluginTest { * - get all documents where category == test. * - this returns 2 documents. */ - ((List)whereProperty.getValue()).add(new HashMap() {{ + ((List) whereProperty.getValue()).add(new HashMap() {{ put("path", "{{Input1.text}}"); put("operator", "EQ"); put("value", "{{Input2.text}}"); @@ -720,7 +708,7 @@ public class FirestorePluginTest { * - get all documents where name == two. * - Of the two documents returned by above condition, this will narrow it down to one. */ - ((List)whereProperty.getValue()).add(new HashMap() {{ + ((List) whereProperty.getValue()).add(new HashMap() {{ put("path", "{{Input3.text}}"); put("operator", "EQ"); put("value", "{{Input4.text}}"); @@ -1048,7 +1036,7 @@ public class FirestorePluginTest { * - get all documents where category == test. * - this returns 2 documents. */ - ((List)whereProperty.getValue()).add(new HashMap() {{ + ((List) whereProperty.getValue()).add(new HashMap() {{ put("path", "{{Input2.text}}"); put("operator", "EQ"); put("value", "{{Input3.text}}"); @@ -1080,10 +1068,10 @@ public class FirestorePluginTest { // check if dynamic binding values have been substituted correctly assertEquals("initial", actionConfiguration.getPath()); assertEquals("category", - ((Map)((List)actionConfiguration.getPluginSpecifiedTemplates().get(3).getValue()).get(0)).get( + ((Map) ((List) actionConfiguration.getPluginSpecifiedTemplates().get(3).getValue()).get(0)).get( "path")); assertEquals("test", - ((Map)((List)actionConfiguration.getPluginSpecifiedTemplates().get(3).getValue()).get(0)).get( + ((Map) ((List) actionConfiguration.getPluginSpecifiedTemplates().get(3).getValue()).get(0)).get( "value")); } } From e5e0b8e29057dfb0c776817eb3cee054d8d3110f Mon Sep 17 00:00:00 2001 From: Hetu Nandu Date: Mon, 13 Sep 2021 15:55:22 +0530 Subject: [PATCH 03/60] Fix action error callback crash (#7377) --- .../ActionExecution/PromiseActionSaga.ts | 5 ++-- app/client/src/sagas/PostEvaluationSagas.ts | 8 ++++++ app/client/src/utils/DynamicBindingUtils.ts | 1 + app/client/src/workers/evaluation.worker.ts | 27 +++++++++++++++---- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/app/client/src/sagas/ActionExecution/PromiseActionSaga.ts b/app/client/src/sagas/ActionExecution/PromiseActionSaga.ts index 3358b6a5dd..8a2dd18226 100644 --- a/app/client/src/sagas/ActionExecution/PromiseActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/PromiseActionSaga.ts @@ -68,9 +68,8 @@ export default function* executePromiseSaga( responseData = e.responseData; } // if the catch callback is not an anonymous function, passing arguments will cause errors in execution - const catchArguments = ACTION_ANONYMOUS_FUNC_REGEX.test(trigger.catch) - ? responseData - : undefined; + const matches = [...trigger.catch.matchAll(ACTION_ANONYMOUS_FUNC_REGEX)]; + const catchArguments = matches.length ? responseData : undefined; yield call(executeAppAction, { dynamicString: trigger.catch, diff --git a/app/client/src/sagas/PostEvaluationSagas.ts b/app/client/src/sagas/PostEvaluationSagas.ts index bf13a3de67..9d2fb49dbf 100644 --- a/app/client/src/sagas/PostEvaluationSagas.ts +++ b/app/client/src/sagas/PostEvaluationSagas.ts @@ -267,6 +267,14 @@ export function* evalErrorHandler( }); break; } + case EvalErrorTypes.CLONE_ERROR: { + Sentry.captureException(new Error(error.message), { + extra: { + request: error.context, + }, + }); + break; + } default: { Sentry.captureException(error); log.debug(error); diff --git a/app/client/src/utils/DynamicBindingUtils.ts b/app/client/src/utils/DynamicBindingUtils.ts index e7925c7e6d..6f01f417b8 100644 --- a/app/client/src/utils/DynamicBindingUtils.ts +++ b/app/client/src/utils/DynamicBindingUtils.ts @@ -124,6 +124,7 @@ export enum EvalErrorTypes { BAD_UNEVAL_TREE_ERROR = "BAD_UNEVAL_TREE_ERROR", EVAL_TRIGGER_ERROR = "EVAL_TRIGGER_ERROR", PARSE_JS_ERROR = "PARSE_JS_ERROR", + CLONE_ERROR = "CLONE_ERROR", } export type EvalError = { diff --git a/app/client/src/workers/evaluation.worker.ts b/app/client/src/workers/evaluation.worker.ts index 6a24236e35..c15d152778 100644 --- a/app/client/src/workers/evaluation.worker.ts +++ b/app/client/src/workers/evaluation.worker.ts @@ -34,11 +34,28 @@ function messageEventListener( const { method, requestData, requestId } = e.data; const responseData = fn(method, requestData); const endTime = performance.now(); - ctx.postMessage({ - requestId, - responseData, - timeTaken: (endTime - startTime).toFixed(2), - }); + try { + ctx.postMessage({ + requestId, + responseData, + timeTaken: (endTime - startTime).toFixed(2), + }); + } catch (e) { + console.error(e); + ctx.postMessage({ + requestId, + responseData: { + errors: [ + { + type: EvalErrorTypes.CLONE_ERROR, + message: e, + context: requestData, + }, + ], + }, + timeTaken: (endTime - startTime).toFixed(2), + }); + } }; } From 28c68c88b110566f044659c351534a6e810c503c Mon Sep 17 00:00:00 2001 From: ashit-rath <88306433+ashit-rath@users.noreply.github.com> Date: Mon, 13 Sep 2021 18:05:05 +0530 Subject: [PATCH 04/60] fix: allow multiple draggable popper to co-exist (#7323) * fix: allow multiple draggable popper to co-exist * added custom data-cy for popper drag handler to fix failing cypress tests --- .../Draggable_PropertyPane_Widgets_spec.js | 18 +++++++++--------- .../Draggable_PropertyPane_spec.js | 18 +++++++++--------- app/client/src/pages/Editor/Popper.tsx | 9 ++++++++- .../src/pages/Editor/PropertyPane/index.tsx | 1 + app/client/src/pages/Editor/utils.ts | 8 ++++++++ 5 files changed, 35 insertions(+), 19 deletions(-) diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/Draggable_PropertyPane_Widgets_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/Draggable_PropertyPane_Widgets_spec.js index 006fcf5f17..58a61d16b1 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/Draggable_PropertyPane_Widgets_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/Draggable_PropertyPane_Widgets_spec.js @@ -26,14 +26,14 @@ describe("Widget property pane draggable", function() { it("Property pane initial position same untill it is dragged", function() { for (const testWidget of widgetList) { cy.openPropertyPane(testWidget); - cy.get("#popper-draghandler").then((oldPorpPane) => { + cy.get("[data-cy=t--property-pane-drag-handle]").then((oldPorpPane) => { const oldPropPanePosition = oldPorpPane[0].getBoundingClientRect(); cy.get(commonlocators.collapsesection) .first() .click(); cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.openPropertyPane(testWidget); - cy.get("#popper-draghandler").then((newPropPane) => { + cy.get("[data-cy=t--property-pane-drag-handle]").then((newPropPane) => { const newPropPanePosition = newPropPane[0].getBoundingClientRect(); cy.get(commonlocators.editPropCrossButton).click({ force: true }); expect(oldPropPanePosition.top).to.be.equal(newPropPanePosition.top); @@ -47,15 +47,15 @@ describe("Widget property pane draggable", function() { it("Property pane position should stay same after dragging down", () => { for (const testWidget of widgetList) { cy.openPropertyPane(testWidget); - cy.get("#popper-draghandler") + cy.get("[data-cy=t--property-pane-drag-handle]") .trigger("mousedown", { which: 1 }) .trigger("mousemove", { clientX: 400, clientY: 500 }) .trigger("mouseup", { force: true }); - cy.get("#popper-draghandler").then((oldPorpPane) => { + cy.get("[data-cy=t--property-pane-drag-handle]").then((oldPorpPane) => { const oldPropPanePosition = oldPorpPane[0].getBoundingClientRect(); cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.openPropertyPane("containerwidget"); - cy.get("#popper-draghandler").then((newPropPane) => { + cy.get("[data-cy=t--property-pane-drag-handle]").then((newPropPane) => { const newPropPanePosition = newPropPane[0].getBoundingClientRect(); cy.get(commonlocators.editPropCrossButton).click({ force: true }); expect(oldPropPanePosition.top).to.be.equal(newPropPanePosition.top); @@ -70,20 +70,20 @@ describe("Widget property pane draggable", function() { it("Property pane should come back into view if forced to drop out of view", () => { for (const testWidget of widgetList) { cy.openPropertyPane(testWidget); - cy.get("#popper-draghandler") + cy.get("[data-cy=t--property-pane-drag-handle]") .trigger("mousedown", { which: 1 }) .trigger("mousemove", { clientX: -10, clientY: -20 }) .trigger("mouseup", { force: true }); - cy.get("#popper-draghandler").then((porpPane) => { + cy.get("[data-cy=t--property-pane-drag-handle]").then((porpPane) => { const propPanePosition = porpPane[0].getBoundingClientRect(); expect(propPanePosition.top).to.be.greaterThan(0); expect(propPanePosition.left).to.be.gte(0); }); - cy.get("#popper-draghandler") + cy.get("[data-cy=t--property-pane-drag-handle]") .trigger("mousedown", { which: 1 }) .trigger("mousemove", { clientX: 1600, clientY: 800 }) .trigger("mouseup", { force: true }); - cy.get("#popper-draghandler").then((porpPane) => { + cy.get("[data-cy=t--property-pane-drag-handle]").then((porpPane) => { const propPanePosition = porpPane[0].getBoundingClientRect(); cy.get(commonlocators.editPropCrossButton).click({ force: true }); expect(propPanePosition.top).to.be.lessThan( diff --git a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/Draggable_PropertyPane_spec.js b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/Draggable_PropertyPane_spec.js index fb7863e4de..315d92a0ba 100644 --- a/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/Draggable_PropertyPane_spec.js +++ b/app/client/cypress/integration/Smoke_TestSuite/ClientSideTests/PropertyPane/Draggable_PropertyPane_spec.js @@ -8,14 +8,14 @@ describe("Table Widget property pane feature validation", function() { it("Property pane initial position same untill it is dragged", function() { cy.openPropertyPane("tablewidget"); - cy.get("#popper-draghandler").then((oldPorpPane) => { + cy.get("[data-cy=t--property-pane-drag-handle]").then((oldPorpPane) => { const oldPropPanePosition = oldPorpPane[0].getBoundingClientRect(); cy.get(commonlocators.collapsesection) .first() .click(); cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.openPropertyPane("tablewidget"); - cy.get("#popper-draghandler").then((newPropPane) => { + cy.get("[data-cy=t--property-pane-drag-handle]").then((newPropPane) => { const newPropPanePosition = newPropPane[0].getBoundingClientRect(); cy.get(commonlocators.editPropCrossButton).click({ force: true }); expect(oldPropPanePosition.top).to.be.equal(newPropPanePosition.top); @@ -26,15 +26,15 @@ describe("Table Widget property pane feature validation", function() { it("Property pane position should stay same after dragging down", () => { cy.openPropertyPane("tablewidget"); - cy.get("#popper-draghandler") + cy.get("[data-cy=t--property-pane-drag-handle]") .trigger("mousedown", { which: 1 }) .trigger("mousemove", { clientX: 400, clientY: 500 }) .trigger("mouseup", { force: true }); - cy.get("#popper-draghandler").then((oldPorpPane) => { + cy.get("[data-cy=t--property-pane-drag-handle]").then((oldPorpPane) => { const oldPropPanePosition = oldPorpPane[0].getBoundingClientRect(); cy.get(commonlocators.editPropCrossButton).click({ force: true }); cy.openPropertyPane("containerwidget"); - cy.get("#popper-draghandler").then((newPropPane) => { + cy.get("[data-cy=t--property-pane-drag-handle]").then((newPropPane) => { const newPropPanePosition = newPropPane[0].getBoundingClientRect(); cy.get(commonlocators.editPropCrossButton).click({ force: true }); expect(oldPropPanePosition.top).to.be.equal(newPropPanePosition.top); @@ -45,20 +45,20 @@ describe("Table Widget property pane feature validation", function() { it("Property pane should come back into view if forced to drop out of view", () => { cy.openPropertyPane("tablewidget"); - cy.get("#popper-draghandler") + cy.get("[data-cy=t--property-pane-drag-handle]") .trigger("mousedown", { which: 1 }) .trigger("mousemove", { clientX: -10, clientY: -20 }) .trigger("mouseup", { force: true }); - cy.get("#popper-draghandler").then((porpPane) => { + cy.get("[data-cy=t--property-pane-drag-handle]").then((porpPane) => { const propPanePosition = porpPane[0].getBoundingClientRect(); expect(propPanePosition.top).to.be.greaterThan(0); expect(propPanePosition.left).to.be.gte(0); }); - cy.get("#popper-draghandler") + cy.get("[data-cy=t--property-pane-drag-handle]") .trigger("mousedown", { which: 1 }) .trigger("mousemove", { clientX: 1600, clientY: 800 }) .trigger("mouseup", { force: true }); - cy.get("#popper-draghandler").then((porpPane) => { + cy.get("[data-cy=t--property-pane-drag-handle]").then((porpPane) => { const propPanePosition = porpPane[0].getBoundingClientRect(); cy.get(commonlocators.editPropCrossButton).click({ force: true }); expect(propPanePosition.top).to.be.lessThan( diff --git a/app/client/src/pages/Editor/Popper.tsx b/app/client/src/pages/Editor/Popper.tsx index 1eac72ccff..2ee60c43ed 100644 --- a/app/client/src/pages/Editor/Popper.tsx +++ b/app/client/src/pages/Editor/Popper.tsx @@ -6,6 +6,7 @@ import { AppState } from "reducers"; import { getThemeDetails, ThemeMode } from "selectors/themeSelectors"; import styled, { ThemeProvider } from "styled-components"; import { noop } from "utils/AppsmithUtils"; +import { generateReactKey } from "utils/generators"; // import { PopperDragHandle } from "./PropertyPane/PropertyPaneConnections"; import { draggableElement } from "./utils"; @@ -26,6 +27,7 @@ export type PopperProps = { modifiers?: Partial; isDraggable?: boolean; disablePopperEvents?: boolean; + cypressSelectorDragHandle?: string; position?: { top: number; left: number; @@ -82,6 +84,9 @@ export function PopperDragHandle() { /* eslint-disable react/display-name */ export default (props: PopperProps) => { const contentRef = useRef(null); + const popperIdRef = useRef(generateReactKey()); + const popperId = popperIdRef.current; + const { isDraggable = false, disablePopperEvents = false, @@ -90,6 +95,7 @@ export default (props: PopperProps) => { onPositionChange = noop, themeMode = props.themeMode || ThemeMode.LIGHT, renderDragBlockPositions, + cypressSelectorDragHandle, } = props; // Meomoizing to avoid rerender of draggable icon. // What is the cost of memoizing? @@ -149,7 +155,7 @@ export default (props: PopperProps) => { if (isDraggable) { disablePopperEvents && _popper.disableEventListeners(); draggableElement( - "popper", + `${popperId}-popper`, _popper.popper, onPositionChange, position, @@ -162,6 +168,7 @@ export default (props: PopperProps) => { ), + cypressSelectorDragHandle, ); } diff --git a/app/client/src/pages/Editor/PropertyPane/index.tsx b/app/client/src/pages/Editor/PropertyPane/index.tsx index 5279c4b65a..1abe151dac 100644 --- a/app/client/src/pages/Editor/PropertyPane/index.tsx +++ b/app/client/src/pages/Editor/PropertyPane/index.tsx @@ -231,6 +231,7 @@ class PropertyPane extends Component { )[0]; return ( JSX.Element, + cypressSelectorDragHandle?: string, ) => { let newXPos = 0, newYPos = 0, @@ -131,6 +132,7 @@ export const draggableElement = ( element, dragHandle, renderDragBlockPositions, + cypressSelectorDragHandle, ); } if (initPostion) { @@ -154,6 +156,7 @@ const createDragHandler = ( zIndex?: string; position?: string; }, + cypressSelectorDragHandle?: string, ) => { const oldDragHandler = document.getElementById(`${id}-draghandler`); const dragElement = document.createElement("div"); @@ -162,6 +165,11 @@ const createDragHandler = ( dragElement.style.left = renderDragBlockPositions?.left ?? "135px"; dragElement.style.top = renderDragBlockPositions?.top ?? "0px"; dragElement.style.zIndex = renderDragBlockPositions?.zIndex ?? "3"; + + if (cypressSelectorDragHandle) { + dragElement.setAttribute("data-cy", cypressSelectorDragHandle); + } + oldDragHandler ? el.replaceChild(dragElement, oldDragHandler) : el.appendChild(dragElement); From 64330ae59c24ba78bd4300ac45abb6716262c46e Mon Sep 17 00:00:00 2001 From: Hetu Nandu Date: Tue, 14 Sep 2021 13:56:54 +0530 Subject: [PATCH 05/60] always assume onSuccess, onError, .then, .catch to be callbacks (#7402) --- app/client/src/sagas/ActionExecution/PromiseActionSaga.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/app/client/src/sagas/ActionExecution/PromiseActionSaga.ts b/app/client/src/sagas/ActionExecution/PromiseActionSaga.ts index 8a2dd18226..d9372f1c31 100644 --- a/app/client/src/sagas/ActionExecution/PromiseActionSaga.ts +++ b/app/client/src/sagas/ActionExecution/PromiseActionSaga.ts @@ -8,7 +8,6 @@ import { EventType } from "constants/AppsmithActionConstants/ActionConstants"; import log from "loglevel"; import { Toaster } from "components/ads/Toast"; import { Variant } from "components/ads/common"; -import { ACTION_ANONYMOUS_FUNC_REGEX } from "components/editorComponents/ActionCreator/Fields"; export class TriggerFailureError extends Error { error?: Error; @@ -38,7 +37,7 @@ export default function* executePromiseSaga( ); if (trigger.then) { if (trigger.then.length) { - let responseData: unknown[] = []; + let responseData: unknown[] = [{}]; if (responses.length === 1) { responseData = responses[0]; } @@ -67,9 +66,7 @@ export default function* executePromiseSaga( if (e instanceof PluginTriggerFailureError) { responseData = e.responseData; } - // if the catch callback is not an anonymous function, passing arguments will cause errors in execution - const matches = [...trigger.catch.matchAll(ACTION_ANONYMOUS_FUNC_REGEX)]; - const catchArguments = matches.length ? responseData : undefined; + const catchArguments = responseData || [{}]; yield call(executeAppAction, { dynamicString: trigger.catch, From 6f1fe51afec0053f713942ce521b1c2cc344dd52 Mon Sep 17 00:00:00 2001 From: Trisha Anand Date: Sun, 19 Sep 2021 12:45:08 +0530 Subject: [PATCH 06/60] fix: Datasource structure command failure for Google Sheets (#7595) --- .../src/main/java/com/external/plugins/GoogleSheetsPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/plugins/GoogleSheetsPlugin.java b/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/plugins/GoogleSheetsPlugin.java index dad3831a1d..bc6247a10b 100644 --- a/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/plugins/GoogleSheetsPlugin.java +++ b/app/server/appsmith-plugins/googleSheetsPlugin/src/main/java/com/external/plugins/GoogleSheetsPlugin.java @@ -276,7 +276,7 @@ public class GoogleSheetsPlugin extends BasePlugin { DatasourceConfiguration datasourceConfiguration) { ActionConfiguration actionConfiguration = new ActionConfiguration(); actionConfiguration.setPluginSpecifiedTemplates(pluginSpecifiedTemplates); - return execute(null, datasourceConfiguration, actionConfiguration); + return executeCommon(null, datasourceConfiguration, actionConfiguration); } @Override From 984f352a451032050a15bbde9b7df8b97a746024 Mon Sep 17 00:00:00 2001 From: Trisha Anand Date: Wed, 22 Sep 2021 14:13:25 +0530 Subject: [PATCH 07/60] Adding migrations for dynamic binding path list for mongo actions post uqi migrations (#7718) --- .../server/migrations/DatabaseChangelog.java | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/DatabaseChangelog.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/DatabaseChangelog.java index 9b150fa286..f680f9f633 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/DatabaseChangelog.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/migrations/DatabaseChangelog.java @@ -97,6 +97,7 @@ import java.util.Optional; import java.util.Random; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -3186,4 +3187,57 @@ public class DatabaseChangelog { mongoTemplate.save(mongoAction); } } + + @ChangeSet(order = "088", id = "migrate-mongo-uqi-dynamicBindingPathList", author = "") + public void migrateMongoPluginDynamicBindingListUqi(MongoTemplate mongoTemplate) { + + Plugin mongoPlugin = mongoTemplate.findOne( + query(where("packageName").is("mongo-plugin")), + Plugin.class + ); + + // Now migrate all the existing actions dynamicBindingList to the new UQI structure. + List mongoActions = mongoTemplate.find( + query(new Criteria().andOperator( + where(fieldName(QNewAction.newAction.pluginId)).is(mongoPlugin.getId()) + )), + NewAction.class + ); + + for (NewAction mongoAction : mongoActions) { + if (mongoAction.getUnpublishedAction() == null || + mongoAction.getUnpublishedAction().getDynamicBindingPathList() == null || + mongoAction.getUnpublishedAction().getDynamicBindingPathList().isEmpty()) { + // No migrations required + continue; + } + + List dynamicBindingPathList = mongoAction.getUnpublishedAction().getDynamicBindingPathList(); + List newDynamicBindingPathList = new ArrayList<>(); + + for (Property path : dynamicBindingPathList) { + String pathKey = path.getKey(); + if (pathKey.contains("pluginSpecifiedTemplates")) { + + // Pattern looks for pluginSpecifiedTemplates[12 and extracts the 12 + Pattern pattern = Pattern.compile("(?<=pluginSpecifiedTemplates\\[)([0-9]+)"); + Matcher matcher = pattern.matcher(pathKey); + + while (matcher.find()) { + int index = Integer.parseInt(matcher.group()); + String partialPath = mongoMigrationMap.get(index); + Property dynamicBindingPath = new Property("formData." + partialPath, null); + newDynamicBindingPathList.add(dynamicBindingPath); + } + } else { + // this dynamic binding is for body. Add as is + newDynamicBindingPathList.add(path); + } + } + + mongoAction.getUnpublishedAction().setDynamicBindingPathList(newDynamicBindingPathList); + + mongoTemplate.save(mongoAction); + } + } } From 51be6a146ced0d84436eeaf8b0134437b66801e8 Mon Sep 17 00:00:00 2001 From: balajisoundar Date: Wed, 22 Sep 2021 23:14:55 +0530 Subject: [PATCH 08/60] chore: Fix signposting issue where application crashing out if there is nested binding (#7731) Fixes: #7730 (cherry picked from commit 0eaabe942374f6ad86a525717df136af33682429) --- .../Statusbar.test.tsx | 106 ++++++++++++++++-- .../FirstTimeUserOnboarding/testUtils.ts | 30 ++++- app/client/src/pages/Editor/utils.ts | 10 +- 3 files changed, 137 insertions(+), 9 deletions(-) diff --git a/app/client/src/pages/Editor/FirstTimeUserOnboarding/Statusbar.test.tsx b/app/client/src/pages/Editor/FirstTimeUserOnboarding/Statusbar.test.tsx index 10c57a07b2..41c04291f0 100644 --- a/app/client/src/pages/Editor/FirstTimeUserOnboarding/Statusbar.test.tsx +++ b/app/client/src/pages/Editor/FirstTimeUserOnboarding/Statusbar.test.tsx @@ -11,11 +11,7 @@ import { ONBOARDING_STATUS_STEPS_FIVETH, ONBOARDING_STATUS_STEPS_SIXTH, } from "constants/messages"; - -let useIsWidgetActionConnectionPresent = false; -jest.mock("pages/Editor/utils", () => ({ - useIsWidgetActionConnectionPresent: () => useIsWidgetActionConnectionPresent, -})); +import { useIsWidgetActionConnectionPresent } from "pages/Editor/utils"; let container: any = null; @@ -73,15 +69,111 @@ describe("Statusbar", () => { }); it("is showing fifth step", async () => { - useIsWidgetActionConnectionPresent = true; renderComponent(getStore(4)); const statusbarText = screen.queryAllByTestId("statusbar-text"); expect(statusbarText[0].innerHTML).toBe(ONBOARDING_STATUS_STEPS_FIVETH()); }); - it("is showing fifth step", async () => { + it("is showing sixth step", async () => { renderComponent(getStore(5)); const statusbarText = screen.queryAllByTestId("statusbar-text"); expect(statusbarText[0].innerHTML).toBe(ONBOARDING_STATUS_STEPS_SIXTH()); }); + + it("should test useIsWidgetActionConnectionPresent function", () => { + const store = getStore(4).getState() as any; + const useIsWidgetActionConnectionPresentHelper = () => { + return useIsWidgetActionConnectionPresent( + store.entities.canvasWidgets, + store.entities.actions, + store.evaluations.dependencies.inverseDependencyMap, + ); + }; + //Both property and trigger dependency present + expect(useIsWidgetActionConnectionPresentHelper()).toBe(true); + //only trigger dependency present + store.evaluations.dependencies.inverseDependencyMap = {}; + expect(useIsWidgetActionConnectionPresentHelper()).toBe(true); + //no dependency present + store.entities.canvasWidgets = {}; + store.entities.actions = []; + expect(useIsWidgetActionConnectionPresentHelper()).toBe(false); + //only trigger dependency present + store.entities.canvasWidgets = { + [Math.random()]: { + widgetName: "widget", + onClick: "{{Query.run()}}", + dynamicTriggerPathList: [ + { + key: "onClick", + }, + ], + text: "{{Query.data}}", + }, + }; + store.entities.actions = [ + { + config: { + id: Math.random(), + pageId: 1, + name: "Query", + }, + }, + ]; + expect(useIsWidgetActionConnectionPresentHelper()).toBe(true); + //no dependency present + store.entities.canvasWidgets = {}; + store.entities.actions = []; + expect(useIsWidgetActionConnectionPresentHelper()).toBe(false); + //only nested trigger dependency present + store.entities.canvasWidgets = { + [Math.random()]: { + widgetName: "widget", + column: { + onClick: "{{Query.run()}}", + }, + dynamicTriggerPathList: [ + { + key: "column.onClick", + }, + ], + text: "label", + }, + }; + store.entities.actions = [ + { + config: { + id: Math.random(), + pageId: 1, + name: "Query", + }, + }, + ]; + expect(useIsWidgetActionConnectionPresentHelper()).toBe(true); + //no dependency present + store.entities.canvasWidgets = {}; + store.entities.actions = []; + expect(useIsWidgetActionConnectionPresentHelper()).toBe(false); + //only property dependency present + store.entities.canvasWidgets = { + [Math.random()]: { + widgetName: "widget", + dynamicTriggerPathList: [], + text: "{{Query.data}}", + }, + }; + store.entities.actions = [ + { + config: { + id: Math.random(), + pageId: 1, + name: "Query", + }, + }, + ]; + store.evaluations.dependencies.inverseDependencyMap = { + "Query.data": ["Query", "widget.text"], + }; + expect(useIsWidgetActionConnectionPresentHelper()).toBe(true); + }); }); diff --git a/app/client/src/pages/Editor/FirstTimeUserOnboarding/testUtils.ts b/app/client/src/pages/Editor/FirstTimeUserOnboarding/testUtils.ts index 8611f0e547..8cef0f3567 100644 --- a/app/client/src/pages/Editor/FirstTimeUserOnboarding/testUtils.ts +++ b/app/client/src/pages/Editor/FirstTimeUserOnboarding/testUtils.ts @@ -66,12 +66,40 @@ export function getStore(step: number) { config: { id: Math.random(), pageId: PAGE_ID, + name: "Query", }, }, ]; - state.entities.canvasWidgets[Math.random()] = {}; + state.entities.canvasWidgets[Math.random()] = { + widgetName: "widget", + onClick: "", + dynamicTriggerPathList: [], + text: "", + }; break; case 4: + state.entities.actions = [ + { + config: { + id: Math.random(), + pageId: PAGE_ID, + name: "Query", + }, + }, + ]; + state.entities.canvasWidgets[Math.random()] = { + widgetName: "widget", + onClick: "{{Query.run()}}", + dynamicTriggerPathList: [ + { + key: "onClick", + }, + ], + text: "{{Query.data}}", + }; + state.evaluations.dependencies.inverseDependencyMap = { + "Query.data": ["widget.text"], + }; break; case 5: state.ui.applications.currentApplication.lastDeployedAt = Math.random(); diff --git a/app/client/src/pages/Editor/utils.ts b/app/client/src/pages/Editor/utils.ts index 6ca8024768..e48d67ee66 100644 --- a/app/client/src/pages/Editor/utils.ts +++ b/app/client/src/pages/Editor/utils.ts @@ -178,6 +178,13 @@ const createDragHandler = ( return dragElement; }; +// Function to access nested property in an object +const getNestedValue = (obj: Record, path = "") => { + return path.split(".").reduce((prev, cur) => { + return prev && prev[cur]; + }, obj); +}; + export const useIsWidgetActionConnectionPresent = ( widgets: any, actions: any, @@ -200,7 +207,8 @@ export const useIsWidgetActionConnectionPresent = ( widget.dynamicTriggerPathList && !!widget.dynamicTriggerPathList.find((path: { key: string }) => { return !!actionLables.find((label: string) => { - return widget[path.key].indexOf(`${label}.run`) > -1; + const snippet = getNestedValue(widget, path.key); + return snippet ? snippet.indexOf(`${label}.run`) > -1 : false; }); }) ); From e695cd70aac6f9462e516259f6e18870dc24585e Mon Sep 17 00:00:00 2001 From: Shrikant Sharat Kandula Date: Thu, 23 Sep 2021 16:36:50 +0530 Subject: [PATCH 09/60] ci: Build and push fat container in release workflow (#7753) The GitHub release workflow currently doesn't build the fat container image. This commit adds support for this. (cherry picked from commit 0554bfa28d454874e07a72094573a0f4ee0ab94f) --- .github/workflows/github-release.yml | 173 +++++++++++++++++++++++---- 1 file changed, 150 insertions(+), 23 deletions(-) diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index 7d2fa05b44..b191647acd 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -40,7 +40,7 @@ jobs: echo "::set-output name=is_beta::false" fi - build-client: + buildClient: needs: - prelude @@ -49,10 +49,13 @@ jobs: defaults: run: working-directory: app/client + shell: bash steps: # Checkout the code - uses: actions/checkout@v2 + with: + fetch-depth: 0 - name: Use Node.js 14.15.4 uses: actions/setup-node@v1 @@ -72,7 +75,6 @@ jobs: ${{ runner.OS }}-node- ${{ runner.OS }}- - # Install all the dependencies - name: Install dependencies run: yarn install @@ -86,39 +88,54 @@ jobs: run: | REACT_APP_VERSION_RELEASE_DATE="$(date -u '+%Y-%m-%dT%H:%M:%SZ')" \ yarn build + ls -l build - # Build Docker image and push to Docker Hub - - name: Push production image to Docker Hub with commit tag - run: | - echo ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin - docker build -t ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-editor:${{needs.prelude.outputs.tag}} . - docker push ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-editor:${{needs.prelude.outputs.tag}} + # Upload the build artifact so that it can be used by the test & deploy job in the workflow + - name: Upload react build bundle + uses: actions/upload-artifact@v2 + with: + name: client-build + path: app/client/build/ - # Only build & tag with latest if the tag doesn't contain beta - if [[ ! ${{needs.prelude.outputs.tag}} == *"beta"* ]]; then - docker build -t ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-editor:latest . - docker push ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-editor:latest - fi - - build-server: + buildServer: needs: - prelude - runs-on: ubuntu-latest - defaults: run: working-directory: app/server + runs-on: ubuntu-latest + # Only run this workflow for internally triggered events + if: | + github.event_name == 'workflow_dispatch' || + github.event_name == 'push' || + (github.event_name == 'pull_request_review' && github.event.review.state == 'approved') + + # Service containers to run with this job. Required for running tests + services: + # Label used to access the service container + redis: + # Docker Hub image for Redis + image: redis + ports: + # Opens tcp port 6379 on the host and service container + - 6379:6379 + mongo: + image: mongo + ports: + - 27017:27017 + steps: - name: Checkout the code uses: actions/checkout@v2 + with: + fetch-depth: 0 - # Setup Java - name: Set up JDK 1.11 uses: actions/setup-java@v1 with: - java-version: 1.11 + java-version: "11.0.10" # Retrieve maven dependencies from cache. After a successful run, these dependencies are cached again - name: Cache maven dependencies @@ -131,8 +148,8 @@ jobs: key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 - # Build the code - - name: Build without running any tests + - name: Build server without running any tests + working-directory: app/server run: | mvn --batch-mode versions:set \ -DnewVersion=${{ needs.prelude.outputs.tag }} \ @@ -140,8 +157,103 @@ jobs: -DprocessAllModules=true mvn --batch-mode package -DskipTests - # Build Docker image and push to Docker Hub - - name: Push image to Docker Hub + - name: Test and Build package + env: + APPSMITH_MONGODB_URI: "mongodb://localhost:27017/mobtools" + APPSMITH_REDIS_URL: "redis://127.0.0.1:6379" + APPSMITH_ENCRYPTION_PASSWORD: "password" + APPSMITH_ENCRYPTION_SALT: "salt" + APPSMITH_IS_SELF_HOSTED: false + working-directory: app/server + run: | + mvn --batch-mode versions:set \ + -DnewVersion=${{ needs.prelude.outputs.tag }} \ + -DgenerateBackupPoms=false \ + -DprocessAllModules=true + ./build.sh -DskipTests + ls -l dist + + - name: Upload server build bundle + uses: actions/upload-artifact@v2 + with: + name: server-build + path: app/server/dist/ + + buildRts: + needs: + - prelude + + defaults: + run: + working-directory: app/rts + + runs-on: ubuntu-latest + + steps: + # Checkout the code + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + + - name: Use Node.js 14.15.4 + uses: actions/setup-node@v1 + with: + node-version: "14.15.4" + + - name: Build + run: | + ./build.sh + ls -l dist + + # Upload the build artifact so that it can be used by the test & deploy job in the workflow + - name: Upload server build bundle + uses: actions/upload-artifact@v2 + with: + name: rts-build + path: app/rts/dist/ + + package: + needs: [buildClient, buildServer, buildRts] + + runs-on: ubuntu-latest + + steps: + - name: Checkout the merged commit from PR and base branch + uses: actions/checkout@v2 + + - name: Download the client build artifact + uses: actions/download-artifact@v2 + with: + name: client-build + path: app/client/build + + - name: Download the server build artifact + uses: actions/download-artifact@v2 + with: + name: server-build + path: app/server/dist + + - name: Download the rts build artifact + uses: actions/download-artifact@v2 + with: + name: rts-build + path: app/rts/dist + + - name: Build and push client image + working-directory: app/client + run: | + echo ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin + docker build -t ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-editor:${{needs.prelude.outputs.tag}} . + docker push ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-editor:${{needs.prelude.outputs.tag}} + + # Only build & tag with latest if the tag doesn't contain beta + if [[ ! ${{needs.prelude.outputs.tag}} == *"beta"* ]]; then + docker build -t ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-editor:latest . + docker push ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-editor:latest + fi + + - name: Build and push server image + working-directory: app/server run: | echo ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin docker build --build-arg APPSMITH_SEGMENT_CE_KEY=${{ secrets.APPSMITH_SEGMENT_CE_KEY }} -t ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-server:${{needs.prelude.outputs.tag}} . @@ -153,3 +265,18 @@ jobs: docker push ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-server:latest fi + - name: Build and push RTS image + working-directory: app/rts + run: | + docker build -t ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-rts:${{needs.prelude.outputs.tag}} . + echo ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin + docker push ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-rts:${{needs.prelude.outputs.tag}} + + - name: Build and push fat image + run: | + docker build \ + --build-arg APPSMITH_SEGMENT_CE_KEY=${{ secrets.APPSMITH_SEGMENT_CE_KEY }} \ + --tag ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-ce:${{needs.prelude.outputs.tag}} \ + . + echo ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin + docker push --all-tags ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-ce From e0e3f6cd7d9ef2792d506754c41f2cd5aef6055a Mon Sep 17 00:00:00 2001 From: Shrikant Sharat Kandula Date: Thu, 23 Sep 2021 16:48:45 +0530 Subject: [PATCH 10/60] ci: Remove unneeded checks and services (#7759) (cherry picked from commit 4f5a0c027a3cdbdc2a5b2da9f5823b21f8c5901f) --- .github/workflows/github-release.yml | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index b191647acd..95c982c065 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -106,25 +106,6 @@ jobs: working-directory: app/server runs-on: ubuntu-latest - # Only run this workflow for internally triggered events - if: | - github.event_name == 'workflow_dispatch' || - github.event_name == 'push' || - (github.event_name == 'pull_request_review' && github.event.review.state == 'approved') - - # Service containers to run with this job. Required for running tests - services: - # Label used to access the service container - redis: - # Docker Hub image for Redis - image: redis - ports: - # Opens tcp port 6379 on the host and service container - - 6379:6379 - mongo: - image: mongo - ports: - - 27017:27017 steps: - name: Checkout the code From fc97dcb12b80ed8402dde00b96418bb237c3576e Mon Sep 17 00:00:00 2001 From: Arpit Mohan Date: Thu, 23 Sep 2021 17:57:27 +0530 Subject: [PATCH 11/60] Removing duplicate step in github release workflow (#7764) --- .github/workflows/github-release.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index 95c982c065..71b58fe31a 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -129,22 +129,7 @@ jobs: key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 - - name: Build server without running any tests - working-directory: app/server - run: | - mvn --batch-mode versions:set \ - -DnewVersion=${{ needs.prelude.outputs.tag }} \ - -DgenerateBackupPoms=false \ - -DprocessAllModules=true - mvn --batch-mode package -DskipTests - - name: Test and Build package - env: - APPSMITH_MONGODB_URI: "mongodb://localhost:27017/mobtools" - APPSMITH_REDIS_URL: "redis://127.0.0.1:6379" - APPSMITH_ENCRYPTION_PASSWORD: "password" - APPSMITH_ENCRYPTION_SALT: "salt" - APPSMITH_IS_SELF_HOSTED: false working-directory: app/server run: | mvn --batch-mode versions:set \ From 198857678e408f45e06769df111a4a4cb1d5c83c Mon Sep 17 00:00:00 2001 From: Shrikant Sharat Kandula Date: Thu, 23 Sep 2021 19:07:39 +0530 Subject: [PATCH 12/60] ci: The package job needs prelude (#7768) --- .github/workflows/github-release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index 71b58fe31a..fb06da6cd5 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -179,7 +179,7 @@ jobs: path: app/rts/dist/ package: - needs: [buildClient, buildServer, buildRts] + needs: [prelude, buildClient, buildServer, buildRts] runs-on: ubuntu-latest From 8a4576c3478fde5642debc567bfbf3a0d4e98107 Mon Sep 17 00:00:00 2001 From: Shrikant Sharat Kandula Date: Thu, 23 Sep 2021 19:58:53 +0530 Subject: [PATCH 13/60] ci: Fix server jar paths in fat container image (#7771) --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 55cec30927..e7a2f5c1d3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -39,8 +39,8 @@ VOLUME [ "/appsmith-stacks" ] # ------------------------------------------------------------------------ # Add backend server - Application Layer -ARG JAR_FILE=./app/server/appsmith-server/target/server-*.jar -ARG PLUGIN_JARS=./app/server/appsmith-plugins/*/target/*.jar +ARG JAR_FILE=./app/server/dist/server-*.jar +ARG PLUGIN_JARS=./app/server/dist/plugins/*.jar ARG APPSMITH_SEGMENT_CE_KEY ENV APPSMITH_SEGMENT_CE_KEY=${APPSMITH_SEGMENT_CE_KEY} #Create the plugins directory From 41cbd851e83073b2019e18a87f08a09eb826c229 Mon Sep 17 00:00:00 2001 From: Shrikant Sharat Kandula Date: Thu, 23 Sep 2021 20:32:22 +0530 Subject: [PATCH 14/60] Include RTS' dependencies for Docker image (#7773) (cherry picked from commit 48e2a3d01a098194edf51ec888e0b373d06e612e) --- .github/workflows/github-release.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index fb06da6cd5..04e6727573 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -172,12 +172,18 @@ jobs: ls -l dist # Upload the build artifact so that it can be used by the test & deploy job in the workflow - - name: Upload server build bundle + - name: Upload RTS build bundle uses: actions/upload-artifact@v2 with: name: rts-build path: app/rts/dist/ + - name: Upload RTS dependencies bundle + uses: actions/upload-artifact@v2 + with: + name: rts-build-deps + path: app/rts/node_modules/ + package: needs: [prelude, buildClient, buildServer, buildRts] @@ -205,6 +211,12 @@ jobs: name: rts-build path: app/rts/dist + - name: Download the rts dependencies artifact + uses: actions/download-artifact@v2 + with: + name: rts-build-deps + path: app/rts/node_modules/ + - name: Build and push client image working-directory: app/client run: | From 5e496d76067eadbb9bb573ae0077cb08ec62a4c6 Mon Sep 17 00:00:00 2001 From: Shrikant Sharat Kandula Date: Thu, 23 Sep 2021 21:15:25 +0530 Subject: [PATCH 15/60] Push latest tag for RTS and fat (#7774) --- .github/workflows/github-release.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/.github/workflows/github-release.yml b/.github/workflows/github-release.yml index 04e6727573..f897638a84 100644 --- a/.github/workflows/github-release.yml +++ b/.github/workflows/github-release.yml @@ -250,11 +250,24 @@ jobs: echo ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin docker push ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-rts:${{needs.prelude.outputs.tag}} + # Only build & tag with latest if the tag doesn't contain beta + if [[ ! ${{needs.prelude.outputs.tag}} == *"beta"* ]]; then + docker build -t ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-rts:latest . + docker push ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-rts:latest + fi + - name: Build and push fat image run: | docker build \ --build-arg APPSMITH_SEGMENT_CE_KEY=${{ secrets.APPSMITH_SEGMENT_CE_KEY }} \ --tag ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-ce:${{needs.prelude.outputs.tag}} \ . + + # Only build & tag with latest if the tag doesn't contain beta + if [[ ! ${{needs.prelude.outputs.tag}} == *"beta"* ]]; then + docker build --tag ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-ce:latest . + docker push ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-ce:latest + fi + echo ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} | docker login -u ${{ secrets.DOCKER_HUB_USERNAME }} --password-stdin docker push --all-tags ${{ secrets.DOCKER_HUB_ORGANIZATION }}/appsmith-ce From af484dd97d6d995c8316e9ac8d9b439b28e9e0ee Mon Sep 17 00:00:00 2001 From: Aswath K Date: Tue, 28 Sep 2021 16:27:44 +0530 Subject: [PATCH 16/60] fix: make the row click event trigger when table button is clicked (#7878) (cherry picked from commit b4bab33d1e1b135dd4edbcd6455d71f86ff7e67e) --- .../widgets/TableWidget/component/TableUtilities.tsx | 6 +++++- app/client/src/widgets/TableWidget/widget/index.tsx | 11 +++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/app/client/src/widgets/TableWidget/component/TableUtilities.tsx b/app/client/src/widgets/TableWidget/component/TableUtilities.tsx index f309040256..ffebcd773b 100644 --- a/app/client/src/widgets/TableWidget/component/TableUtilities.tsx +++ b/app/client/src/widgets/TableWidget/component/TableUtilities.tsx @@ -322,7 +322,11 @@ function TableAction(props: { { + if (props.isSelected) { + e.stopPropagation(); + } + }} > {props.isCellVisible ? (