From c86390ebfe681d1b8ec922d0a499da90186c69e3 Mon Sep 17 00:00:00 2001 From: Satish Gandham Date: Fri, 7 May 2021 15:22:53 +0530 Subject: [PATCH 1/9] - Collapse all but the first two records in react-json-view --- .../editorComponents/CodeEditor/EvaluatedValuePopup.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/client/src/components/editorComponents/CodeEditor/EvaluatedValuePopup.tsx b/app/client/src/components/editorComponents/CodeEditor/EvaluatedValuePopup.tsx index c921c70fb8..39ca18f94f 100644 --- a/app/client/src/components/editorComponents/CodeEditor/EvaluatedValuePopup.tsx +++ b/app/client/src/components/editorComponents/CodeEditor/EvaluatedValuePopup.tsx @@ -216,6 +216,10 @@ export const CurrentValueViewer = memo( }, collapsed: 2, collapseStringsAfterLength: 20, + shouldCollapse: (field: any) => { + const index = field.name * 1; + return index >= 2 ? true : false; + }, }; content = ( From 8763f9518ba17aef421e3dcfb880b69904811141 Mon Sep 17 00:00:00 2001 From: Satish Gandham Date: Tue, 11 May 2021 11:55:15 +0530 Subject: [PATCH 2/9] - Memoize EditorContext value - Change mapDispatchToProps to object form --- .../EditorContextProvider.tsx | 102 ++++++++---------- 1 file changed, 47 insertions(+), 55 deletions(-) diff --git a/app/client/src/components/editorComponents/EditorContextProvider.tsx b/app/client/src/components/editorComponents/EditorContextProvider.tsx index b2dd17cc84..fb0a46f584 100644 --- a/app/client/src/components/editorComponents/EditorContextProvider.tsx +++ b/app/client/src/components/editorComponents/EditorContextProvider.tsx @@ -1,4 +1,4 @@ -import React, { Context, createContext, ReactNode } from "react"; +import React, { Context, createContext, ReactNode, useMemo } from "react"; import { connect } from "react-redux"; import { WidgetOperation } from "widgets/BaseWidget"; @@ -65,66 +65,58 @@ function EditorContextProvider(props: EditorContextProviderProps) { deleteWidgetProperty, batchUpdateWidgetProperty, } = props; + + // Memoize the context provider to prevent + // unnecessary renders + const contextValue = useMemo( + () => ({ + executeAction, + updateWidget, + updateWidgetProperty, + updateWidgetMetaProperty, + disableDrag, + resetChildrenMetaProperty, + deleteWidgetProperty, + batchUpdateWidgetProperty, + }), + [ + executeAction, + updateWidget, + updateWidgetProperty, + updateWidgetMetaProperty, + disableDrag, + resetChildrenMetaProperty, + deleteWidgetProperty, + batchUpdateWidgetProperty, + ], + ); return ( - + {children} ); } -const mapDispatchToProps = (dispatch: any) => { - return { - updateWidgetProperty: ( - widgetId: string, - propertyName: string, - propertyValue: any, - ) => - dispatch( - updateWidgetPropertyRequest( - widgetId, - propertyName, - propertyValue, - RenderModes.CANVAS, - ), - ), - executeAction: (actionPayload: ExecuteActionPayload) => - dispatch(executeAction(actionPayload)), - updateWidget: ( - operation: WidgetOperation, - widgetId: string, - payload: any, - ) => dispatch(updateWidget(operation, widgetId, payload)), - updateWidgetMetaProperty: ( - widgetId: string, - propertyName: string, - propertyValue: any, - ) => - dispatch(updateWidgetMetaProperty(widgetId, propertyName, propertyValue)), - resetChildrenMetaProperty: (widgetId: string) => - dispatch(resetChildrenMetaProperty(widgetId)), - disableDrag: (disable: boolean) => { - dispatch(disableDragAction(disable)); - }, - deleteWidgetProperty: (widgetId: string, propertyPaths: string[]) => - dispatch(deletePropertyAction(widgetId, propertyPaths)), - batchUpdateWidgetProperty: ( - widgetId: string, - updates: BatchPropertyUpdatePayload, - ) => { - dispatch(batchUpdatePropertyAction(widgetId, updates)); - }, - }; +const mapDispatchToProps = { + updateWidgetProperty: ( + widgetId: string, + propertyName: string, + propertyValue: any, + ) => + updateWidgetPropertyRequest( + widgetId, + propertyName, + propertyValue, + RenderModes.CANVAS, + ), + + executeAction, + updateWidget, + updateWidgetMetaProperty, + resetChildrenMetaProperty, + disableDrag: disableDragAction, + deleteWidgetProperty: deletePropertyAction, + batchUpdateWidgetProperty: batchUpdatePropertyAction, }; export default connect(null, mapDispatchToProps)(EditorContextProvider); From 812e893eeeb5f8ff260528d984e6664a9454bc69 Mon Sep 17 00:00:00 2001 From: Satish Gandham Date: Tue, 11 May 2021 12:03:07 +0530 Subject: [PATCH 3/9] - Add name to memoized resizableComponent --- .../src/components/editorComponents/ResizableComponent.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index 9c07e1fe6e..d2eb4dfcd9 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -43,8 +43,9 @@ export type ResizableComponentProps = WidgetProps & { paddingOffset: number; }; -/* eslint-disable react/display-name */ -export const ResizableComponent = memo((props: ResizableComponentProps) => { +export const ResizableComponent = memo(function ResizableComponent( + props: ResizableComponentProps, +) { const resizableRef = useRef(null); // Fetch information from the context const { updateWidget } = useContext(EditorContext); From 8bd947671dd77e166016e55bc6153e73fa73463b Mon Sep 17 00:00:00 2001 From: Satish Gandham Date: Tue, 11 May 2021 20:09:09 +0530 Subject: [PATCH 4/9] Move the expensive getBoundingReactangle call to isColliding function to avoid unnecessary calls. --- .../components/editorComponents/ResizableComponent.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/client/src/components/editorComponents/ResizableComponent.tsx b/app/client/src/components/editorComponents/ResizableComponent.tsx index d2eb4dfcd9..e49b884098 100644 --- a/app/client/src/components/editorComponents/ResizableComponent.tsx +++ b/app/client/src/components/editorComponents/ResizableComponent.tsx @@ -104,15 +104,18 @@ export const ResizableComponent = memo(function ResizableComponent( possibleBoundingElements.length > 0 ? possibleBoundingElements[0] : undefined; - const boundingElementClientRect = boundingElement - ? boundingElement.getBoundingClientRect() - : undefined; // onResize handler // Checks if the current resize position has any collisions // If yes, set isColliding flag to true. // If no, set isColliding flag to false. const isColliding = (newDimensions: UIElementSize, position: XYCoord) => { + // Moving the bounding element calculations inside + // to make this expensive operation only whne + const boundingElementClientRect = boundingElement + ? boundingElement.getBoundingClientRect() + : undefined; + const bottom = props.topRow + position.y / props.parentRowSpace + From 220c8d9d252af458432ab564bad824579d3d557e Mon Sep 17 00:00:00 2001 From: Rishabh Saxena Date: Fri, 21 May 2021 13:20:27 +0530 Subject: [PATCH 5/9] Remove isFromSignup param to prevent duplicate analytics hits (#4480) --- app/client/src/index.tsx | 4 ++++ .../hooks/useRemoveSignUpCompleteParam.ts | 21 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 app/client/src/utils/hooks/useRemoveSignUpCompleteParam.ts diff --git a/app/client/src/index.tsx b/app/client/src/index.tsx index adac756b97..09b9329784 100755 --- a/app/client/src/index.tsx +++ b/app/client/src/index.tsx @@ -26,7 +26,11 @@ import AppErrorBoundary from "./AppErrorBoundry"; import GlobalStyles from "globalStyles"; appInitializer(); +import useRemoveSignUpCompleteParam from "utils/hooks/useRemoveSignUpCompleteParam"; + function App() { + useRemoveSignUpCompleteParam(); + return ( diff --git a/app/client/src/utils/hooks/useRemoveSignUpCompleteParam.ts b/app/client/src/utils/hooks/useRemoveSignUpCompleteParam.ts new file mode 100644 index 0000000000..478bc5ffaf --- /dev/null +++ b/app/client/src/utils/hooks/useRemoveSignUpCompleteParam.ts @@ -0,0 +1,21 @@ +import { useEffect } from "react"; +import history from "utils/history"; + +const useRemoveSignUpCompleteParam = () => { + useEffect(() => { + if (window.location.href) { + const url = new URL(window.location.href); + const searchParams = url.searchParams; + if (searchParams.get("isFromSignup")) { + searchParams.delete("isFromSignup"); + history.replace({ + pathname: url.pathname, + search: url.search, + hash: url.hash, + }); + } + } + }, []); +}; + +export default useRemoveSignUpCompleteParam; From c156560313118b70b5d20e74c2d59b68e4254d43 Mon Sep 17 00:00:00 2001 From: Sumit Kumar Date: Fri, 21 May 2021 13:47:51 +0530 Subject: [PATCH 6/9] [Bug] fix NPE (#4613) * fix NPE * add ip check for localhost --- .../services/DatasourceServiceImpl.java | 18 +++++++++- .../services/DatasourceServiceTest.java | 36 +++++++++++++++++-- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceServiceImpl.java index bf41cf3c73..b6d0e02e4e 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/DatasourceServiceImpl.java @@ -48,6 +48,9 @@ import static com.appsmith.server.acl.AclPermission.ORGANIZATION_READ_APPLICATIO @Service public class DatasourceServiceImpl extends BaseService implements DatasourceService { + private static final String LOCALHOST_STRING = "localhost"; + private static final String LOCALHOST_IP = "127.0.0.1"; + private final OrganizationService organizationService; private final SessionUserService sessionUserService; private final PluginService pluginService; @@ -134,6 +137,19 @@ public class DatasourceServiceImpl extends BaseService populateHintMessages(Datasource datasource) { if(datasource == null) { @@ -157,7 +173,7 @@ public class DatasourceServiceImpl extends BaseService endpoint.getHost().contains("localhost")); + .anyMatch(endpoint -> endpointContainsLocalhost(endpoint)); } if(usingLocalhostUrl) { diff --git a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceServiceTest.java b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceServiceTest.java index 20a2f4c752..9befaaf572 100644 --- a/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceServiceTest.java +++ b/app/server/appsmith-server/src/test/java/com/appsmith/server/services/DatasourceServiceTest.java @@ -984,7 +984,7 @@ public class DatasourceServiceTest { @Test @WithUserDetails(value = "api_user") - public void testHintMessageOnLocalhostUrlOnUpdateEventOnNonApiDatasource() { + public void testHintMessageOnLocalhostIPAddressOnUpdateEventOnNonApiDatasource() { Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new MockPluginExecutor())); Datasource datasource = new Datasource(); @@ -1012,7 +1012,7 @@ public class DatasourceServiceTest { DatasourceConfiguration datasourceConfiguration1 = new DatasourceConfiguration(); Connection connection1 = new Connection(); datasourceConfiguration1.setConnection(connection1); - Endpoint endpoint = new Endpoint("http://localhost", 0L); + Endpoint endpoint = new Endpoint("http://127.0.0.1/xyz", 0L); datasourceConfiguration1.setEndpoints(new ArrayList<>()); datasourceConfiguration1.getEndpoints().add(endpoint); updates.setDatasourceConfiguration(datasourceConfiguration1); @@ -1034,4 +1034,36 @@ public class DatasourceServiceTest { }) .verifyComplete(); } + + @Test + @WithUserDetails(value = "api_user") + public void testHintMessageNPE() { + + Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new MockPluginExecutor())); + + Mono pluginMono = pluginService.findByName("Installed Plugin Name"); + Datasource datasource = new Datasource(); + datasource.setName("NPE check"); + datasource.setOrganizationId(orgId); + DatasourceConfiguration datasourceConfiguration = new DatasourceConfiguration(); + datasourceConfiguration.setEndpoints(new ArrayList<>()); + Endpoint nullEndpoint = null; + datasourceConfiguration.getEndpoints().add(nullEndpoint); + Endpoint nullHost = new Endpoint(null, 0L); + datasourceConfiguration.getEndpoints().add(nullHost); + + datasource.setDatasourceConfiguration(datasourceConfiguration); + + Mono datasourceMono = pluginMono.map(plugin -> { + datasource.setPluginId(plugin.getId()); + return datasource; + }).flatMap(datasourceService::create); + + StepVerifier + .create(datasourceMono) + .assertNext(createdDatasource -> { + assertThat(createdDatasource.getMessages()).isEmpty(); + }) + .verifyComplete(); + } } From 1845de739e6c4e82751a5035c585ebf4fcd37ab0 Mon Sep 17 00:00:00 2001 From: Abhijeet <41686026+abhvsn@users.noreply.github.com> Date: Fri, 21 May 2021 15:50:48 +0530 Subject: [PATCH 7/9] Added pluginName in execute_ACTION_TRIGGERED analytics event (#4610) --- .../appsmith/server/services/NewActionServiceImpl.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NewActionServiceImpl.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NewActionServiceImpl.java index 6712b5e93b..ab574c8322 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NewActionServiceImpl.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/services/NewActionServiceImpl.java @@ -756,12 +756,14 @@ public class NewActionServiceImpl extends BaseService Mono.zip( Mono.just(application), sessionUserService.getCurrentUser(), - newPageService.getNameByPageId(actionDTO.getPageId(), viewMode) + newPageService.getNameByPageId(actionDTO.getPageId(), viewMode), + pluginService.getById(action.getPluginId()) )) .map(tuple -> { final Application application = tuple.getT1(); final User user = tuple.getT2(); final String pageName = tuple.getT3(); + final Plugin plugin = tuple.getT4(); final PluginType pluginType = action.getPluginType(); final Map data = new HashMap<>(); @@ -769,6 +771,7 @@ public class NewActionServiceImpl extends BaseService Date: Fri, 21 May 2021 17:10:19 +0530 Subject: [PATCH 8/9] Fork application event added (#4620) --- .../server/constants/AnalyticsEvents.java | 1 + .../solutions/ApplicationForkingService.java | 33 +++++++++++++++++-- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/AnalyticsEvents.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/AnalyticsEvents.java index fdc8c2bc43..618149d21c 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/AnalyticsEvents.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/constants/AnalyticsEvents.java @@ -10,6 +10,7 @@ public enum AnalyticsEvents { EXECUTE_ACTION("execute_ACTION_TRIGGERED"), UPDATE_LAYOUT, PUBLISH_APPLICATION("publish_APPLICATION"), + FORK ; private final String eventName; diff --git a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ApplicationForkingService.java b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ApplicationForkingService.java index 54dff8682f..2aafd0e3c8 100644 --- a/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ApplicationForkingService.java +++ b/app/server/appsmith-server/src/main/java/com/appsmith/server/solutions/ApplicationForkingService.java @@ -1,24 +1,29 @@ package com.appsmith.server.solutions; import com.appsmith.server.acl.AclPermission; +import com.appsmith.server.constants.AnalyticsEvents; import com.appsmith.server.domains.Application; import com.appsmith.server.domains.Organization; import com.appsmith.server.domains.User; import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.helpers.PolicyUtils; +import com.appsmith.server.services.AnalyticsService; import com.appsmith.server.services.ApplicationService; import com.appsmith.server.services.OrganizationService; import com.appsmith.server.services.SessionUserService; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.Collections; +import java.util.Map; @Service @RequiredArgsConstructor +@Slf4j public class ApplicationForkingService { private final ApplicationService applicationService; @@ -26,6 +31,7 @@ public class ApplicationForkingService { private final ExamplesOrganizationCloner examplesOrganizationCloner; private final PolicyUtils policyUtils; private final SessionUserService sessionUserService; + private final AnalyticsService analyticsService; public Mono forkApplicationToOrganization(String applicationId, String organizationId) { final Mono sourceApplicationMono = applicationService.findById(applicationId, AclPermission.READ_APPLICATIONS) @@ -34,7 +40,9 @@ public class ApplicationForkingService { final Mono targetOrganizationMono = organizationService.findById(organizationId, AclPermission.ORGANIZATION_MANAGE_APPLICATIONS) .switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.ACL_NO_RESOURCE_FOUND, "organization", organizationId))); - return Mono.zip(sourceApplicationMono, targetOrganizationMono, sessionUserService.getCurrentUser()) + Mono userMono = sessionUserService.getCurrentUser(); + + return Mono.zip(sourceApplicationMono, targetOrganizationMono, userMono) .flatMap(tuple -> { final Application application = tuple.getT1(); final Organization targetOrganization = tuple.getT2(); @@ -58,8 +66,29 @@ public class ApplicationForkingService { }) .flatMap(applicationIds -> { final String newApplicationId = applicationIds.get(0); - return applicationService.getById(newApplicationId); + return applicationService.getById(newApplicationId) + .flatMap(application -> + sendForkApplicationAnalyticsEvent(applicationId, organizationId, application)); }); } + private Mono sendForkApplicationAnalyticsEvent(String applicationId, String orgId, Application application) { + return applicationService.findById(applicationId, AclPermission.READ_APPLICATIONS) + .flatMap(sourceApplication -> { + + final Map data = Map.of( + "forkedFromAppId", applicationId, + "forkedToOrgId", orgId, + "forkedFromAppName", sourceApplication.getName() + ); + + return analyticsService.sendObjectEvent(AnalyticsEvents.FORK, application, data); + }) + .onErrorResume(e -> { + log.warn("Error sending action execution data point", e); + return Mono.just(application); + }); + } + + } From 85660438b8a15719f6e36a678b1c8d05c9028cfe Mon Sep 17 00:00:00 2001 From: Confidence Okoghenun Date: Fri, 21 May 2021 14:07:50 +0100 Subject: [PATCH 9/9] chore: Adds update for live demo #3 --- office_hours.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/office_hours.md b/office_hours.md index 7c556b0749..4ee2e13ea7 100644 --- a/office_hours.md +++ b/office_hours.md @@ -28,6 +28,16 @@ You can find the archives of the calls below with a brief summary of each sessio ## Archives +Appsmith Live Demo #3, 29th May 2021: Stitching APIs together to automate workflows + +Video Link + +#### Summary + +Confidence shows the community how we internally use APIs from multiple services to build apps that simplifies our workflow within Appsmith. Top questions from members of our community was also discussed. + +------------------ + Office Hours May 13th, 2021: Product roadmap discussion Video Link