Merge branch 'release' into fix/dropdown-overlap-modal
This commit is contained in:
commit
b95ff2001e
|
|
@ -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 = (
|
||||
<ReactJson src={props.evaluatedValue} {...reactJsonProps} />
|
||||
|
|
|
|||
|
|
@ -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) {
|
|||
updateWidgetMetaProperty,
|
||||
updateWidgetProperty,
|
||||
} = 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 (
|
||||
<EditorContext.Provider
|
||||
value={{
|
||||
executeAction,
|
||||
updateWidget,
|
||||
updateWidgetProperty,
|
||||
updateWidgetMetaProperty,
|
||||
disableDrag,
|
||||
resetChildrenMetaProperty,
|
||||
deleteWidgetProperty,
|
||||
batchUpdateWidgetProperty,
|
||||
}}
|
||||
>
|
||||
<EditorContext.Provider value={contextValue}>
|
||||
{children}
|
||||
</EditorContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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<HTMLDivElement>(null);
|
||||
// Fetch information from the context
|
||||
const { updateWidget } = useContext(EditorContext);
|
||||
|
|
@ -109,15 +110,18 @@ export const ResizableComponent = memo((props: ResizableComponentProps) => {
|
|||
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 +
|
||||
|
|
|
|||
|
|
@ -26,7 +26,11 @@ import AppErrorBoundary from "./AppErrorBoundry";
|
|||
import GlobalStyles from "globalStyles";
|
||||
appInitializer();
|
||||
|
||||
import useRemoveSignUpCompleteParam from "utils/hooks/useRemoveSignUpCompleteParam";
|
||||
|
||||
function App() {
|
||||
useRemoveSignUpCompleteParam();
|
||||
|
||||
return (
|
||||
<Sentry.ErrorBoundary fallback={"An error has occured"}>
|
||||
<Provider store={store}>
|
||||
|
|
|
|||
21
app/client/src/utils/hooks/useRemoveSignUpCompleteParam.ts
Normal file
21
app/client/src/utils/hooks/useRemoveSignUpCompleteParam.ts
Normal file
|
|
@ -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;
|
||||
|
|
@ -10,6 +10,7 @@ public enum AnalyticsEvents {
|
|||
EXECUTE_ACTION("execute_ACTION_TRIGGERED"),
|
||||
UPDATE_LAYOUT,
|
||||
PUBLISH_APPLICATION("publish_APPLICATION"),
|
||||
FORK
|
||||
;
|
||||
|
||||
private final String eventName;
|
||||
|
|
|
|||
|
|
@ -48,6 +48,9 @@ import static com.appsmith.server.acl.AclPermission.ORGANIZATION_READ_APPLICATIO
|
|||
@Service
|
||||
public class DatasourceServiceImpl extends BaseService<DatasourceRepository, Datasource, String> 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<DatasourceRepository, Dat
|
|||
});
|
||||
}
|
||||
|
||||
private boolean endpointContainsLocalhost(Endpoint endpoint) {
|
||||
if (endpoint == null || StringUtils.isEmpty(endpoint.getHost())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String host = endpoint.getHost().toLowerCase();
|
||||
if (host.contains(LOCALHOST_STRING) || host.contains(LOCALHOST_IP)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Mono<Datasource> populateHintMessages(Datasource datasource) {
|
||||
|
||||
if(datasource == null) {
|
||||
|
|
@ -157,7 +173,7 @@ public class DatasourceServiceImpl extends BaseService<DatasourceRepository, Dat
|
|||
.getDatasourceConfiguration()
|
||||
.getEndpoints()
|
||||
.stream()
|
||||
.anyMatch(endpoint -> endpoint.getHost().contains("localhost"));
|
||||
.anyMatch(endpoint -> endpointContainsLocalhost(endpoint));
|
||||
}
|
||||
|
||||
if(usingLocalhostUrl) {
|
||||
|
|
|
|||
|
|
@ -756,12 +756,14 @@ public class NewActionServiceImpl extends BaseService<NewActionRepository, NewAc
|
|||
.flatMap(application -> 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<String, Object> data = new HashMap<>();
|
||||
|
|
@ -769,6 +771,7 @@ public class NewActionServiceImpl extends BaseService<NewActionRepository, NewAc
|
|||
data.putAll(Map.of(
|
||||
"username", user.getUsername(),
|
||||
"type", pluginType,
|
||||
"pluginName", plugin.getName(),
|
||||
"name", actionDTO.getName(),
|
||||
"datasource", Map.of(
|
||||
"name", datasource.getName()
|
||||
|
|
@ -777,11 +780,11 @@ public class NewActionServiceImpl extends BaseService<NewActionRepository, NewAc
|
|||
"appId", action.getApplicationId(),
|
||||
"appMode", TRUE.equals(viewMode) ? "view" : "edit",
|
||||
"appName", application.getName(),
|
||||
"isExampleApp", application.isAppIsExample(),
|
||||
"request", request
|
||||
"isExampleApp", application.isAppIsExample()
|
||||
));
|
||||
|
||||
data.putAll(Map.of(
|
||||
"request", request,
|
||||
"pageId", ObjectUtils.defaultIfNull(actionDTO.getPageId(), ""),
|
||||
"pageName", pageName,
|
||||
"isSuccessfulExecution", ObjectUtils.defaultIfNull(actionExecutionResult.getIsExecutionSuccess(), false),
|
||||
|
|
|
|||
|
|
@ -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<Application> forkApplicationToOrganization(String applicationId, String organizationId) {
|
||||
final Mono<Application> sourceApplicationMono = applicationService.findById(applicationId, AclPermission.READ_APPLICATIONS)
|
||||
|
|
@ -34,7 +40,9 @@ public class ApplicationForkingService {
|
|||
final Mono<Organization> 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<User> 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<Application> sendForkApplicationAnalyticsEvent(String applicationId, String orgId, Application application) {
|
||||
return applicationService.findById(applicationId, AclPermission.READ_APPLICATIONS)
|
||||
.flatMap(sourceApplication -> {
|
||||
|
||||
final Map<String, Object> 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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Plugin> 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<Datasource> datasourceMono = pluginMono.map(plugin -> {
|
||||
datasource.setPluginId(plugin.getId());
|
||||
return datasource;
|
||||
}).flatMap(datasourceService::create);
|
||||
|
||||
StepVerifier
|
||||
.create(datasourceMono)
|
||||
.assertNext(createdDatasource -> {
|
||||
assertThat(createdDatasource.getMessages()).isEmpty();
|
||||
})
|
||||
.verifyComplete();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,6 +28,16 @@ You can find the archives of the calls below with a brief summary of each sessio
|
|||
|
||||
## Archives
|
||||
|
||||
<strong>Appsmith Live Demo #3, 29th May 2021: Stitching APIs together to automate workflows</strong>
|
||||
|
||||
<a href = "https://youtu.be/_8qLbyWwXeA">Video Link</a>
|
||||
|
||||
#### 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.
|
||||
|
||||
------------------
|
||||
|
||||
<strong>Office Hours May 13th, 2021: Product roadmap discussion</strong>
|
||||
|
||||
<a href = "https://youtu.be/1sw84i_M4_Y">Video Link</a>
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user