Merge branch 'release' of https://github.com/appsmithorg/appsmith into release

This commit is contained in:
Automated Github Action 2020-08-10 10:05:02 +00:00
commit 940c4b96fd
30 changed files with 506 additions and 119 deletions

View File

@ -0,0 +1,9 @@
<svg width="20" height="19" viewBox="0 0 20 19" fill="none" xmlns="http://www.w3.org/2000/svg">
<g>
<path d="M8.14648 2.72705L9.60103 4.54523H20.0001V2.72705H8.14648Z" fill="#2E3D49"/>
<path d="M19.9996 6.36377H6.36328V8.18195H19.9996V6.36377Z" fill="#2E3D49"/>
<path d="M19.9996 10H6.36328V11.8182H19.9996V10Z" fill="#2E3D49"/>
<path d="M8.14648 15.4544H20.0001V13.6362H9.60103L8.14648 15.4544Z" fill="#2E3D49"/>
<path d="M4.54545 4.54545H7.27273L3.63636 0L0 4.54545H2.72727V13.6364H0L3.63636 18.1818L7.27273 13.6364H4.54545V4.54545Z" fill="#2E3D49"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 565 B

View File

@ -7,6 +7,7 @@ import {
getMenuOptions,
getAllTableColumnKeys,
} from "components/designSystems/appsmith/TableUtilities";
import { CompactMode } from "components/designSystems/appsmith/TableCompactMode";
export enum ColumnTypes {
CURRENCY = "currency",
@ -95,6 +96,8 @@ interface ReactTableComponentProps {
handleReorderColumn: Function;
searchTableData: (searchKey: any) => void;
columns: ReactTableColumnProps[];
compactMode?: CompactMode;
updateCompactMode: (compactMode: CompactMode) => void;
}
const ReactTableComponent = (props: ReactTableComponentProps) => {
@ -322,6 +325,8 @@ const ReactTableComponent = (props: ReactTableComponentProps) => {
props.disableDrag(false);
}}
searchTableData={debounce(props.searchTableData, 500)}
compactMode={props.compactMode}
updateCompactMode={props.updateCompactMode}
/>
);
};

View File

@ -15,12 +15,29 @@ import { TableHeaderCell, renderEmptyRows } from "./TableUtilities";
import TableHeader from "./TableHeader";
import { Classes } from "@blueprintjs/core";
import { ColumnAction } from "components/propertyControls/ColumnActionSelectorControl";
import {
CompactMode,
CompactModeTypes,
} from "components/designSystems/appsmith/TableCompactMode";
export enum TABLE_SIZES {
COLUMN_HEADER_HEIGHT = 52,
TABLE_HEADER_HEIGHT = 61,
ROW_HEIGHT = 52,
}
export type TableSizes = {
COLUMN_HEADER_HEIGHT: number;
TABLE_HEADER_HEIGHT: number;
ROW_HEIGHT: number;
};
export const TABLE_SIZES: { [key: string]: TableSizes } = {
[CompactModeTypes.DEFAULT]: {
COLUMN_HEADER_HEIGHT: 52,
TABLE_HEADER_HEIGHT: 61,
ROW_HEIGHT: 52,
},
[CompactModeTypes.SHORT]: {
COLUMN_HEADER_HEIGHT: 52,
TABLE_HEADER_HEIGHT: 61,
ROW_HEIGHT: 40,
},
};
interface TableProps {
width: number;
@ -54,24 +71,26 @@ interface TableProps {
enableDrag: () => void;
searchTableData: (searchKey: any) => void;
columnActions?: ColumnAction[];
compactMode?: CompactMode;
updateCompactMode: (compactMode: CompactMode) => void;
}
const defaultColumn = {
minWidth: 30,
width: 150,
maxWidth: 400,
};
export const Table = (props: TableProps) => {
const defaultColumn = React.useMemo(
() => ({
minWidth: 30,
width: 150,
maxWidth: 400,
}),
[],
);
const pageCount = Math.ceil(props.data.length / props.pageSize);
const currentPageIndex = props.pageNo < pageCount ? props.pageNo : 0;
const data = React.useMemo(() => props.data, [JSON.stringify(props.data)]);
const columns = React.useMemo(() => props.columns, [
JSON.stringify(props.columns),
JSON.stringify(props.columnActions),
]);
const columnMemoKey = JSON.stringify({
columns: props.columns,
columnActions: props.columnActions,
compactMode: props.compactMode,
});
const columns = React.useMemo(() => props.columns, [columnMemoKey]);
const {
getTableProps,
getTableBodyProps,
@ -104,11 +123,19 @@ export const Table = (props: TableProps) => {
}
const subPage = page.slice(startIndex, endIndex);
const selectedRowIndex = props.selectedRowIndex;
const tableSizes = TABLE_SIZES[props.compactMode || CompactModeTypes.DEFAULT];
/* Subtracting 9px to handling widget padding */
const tableRowHeight =
(props.height -
(tableSizes.COLUMN_HEADER_HEIGHT + tableSizes.TABLE_HEADER_HEIGHT + 9)) /
props.pageSize;
return (
<TableWrapper
width={props.width}
height={props.height}
tableSizes={tableSizes}
id={`table${props.widgetId}`}
tableRowHeight={tableRowHeight}
>
<TableHeader
width={props.width}
@ -131,6 +158,8 @@ export const Table = (props: TableProps) => {
hiddenColumns={props.hiddenColumns}
updateHiddenColumns={props.updateHiddenColumns}
displayColumnActions={props.displayColumnActions}
compactMode={props.compactMode}
updateCompactMode={props.updateCompactMode}
/>
<div className={props.isLoading ? Classes.SKELETON : "tableWrap"}>
<div {...getTableProps()} className="table">

View File

@ -0,0 +1,135 @@
import React from "react";
import {
Popover,
Classes,
PopoverInteractionKind,
Position,
Icon,
Tooltip,
} from "@blueprintjs/core";
import { IconWrapper } from "constants/IconConstants";
import styled from "styled-components";
import { Colors } from "constants/Colors";
import { ReactComponent as CompactIcon } from "assets/icons/control/compact.svg";
import { TableIconWrapper } from "components/designSystems/appsmith/TableStyledWrappers";
const DropDownWrapper = styled.div`
display: flex;
flex-direction: column;
background: white;
z-index: 1;
border-radius: 4px;
border: 1px solid ${Colors.ATHENS_GRAY};
padding: 8px;
`;
const OptionWrapper = styled.div<{ selected?: boolean }>`
display: flex;
width: calc(100% - 20px);
justify-content: space-between;
align-items: center;
height: 32px;
box-sizing: border-box;
padding: 8px;
color: ${Colors.OXFORD_BLUE};
opacity: ${props => (props.selected ? 1 : 0.7)};
min-width: 200px;
cursor: pointer;
margin-bottom: 4px;
background: ${props => (props.selected ? Colors.POLAR : Colors.WHITE)};
border-left: ${props => (props.selected ? "4px solid #29CCA3" : "none")};
border-radius: 4px;
.option-title {
font-weight: 500;
font-size: 14px;
line-height: 24px;
}
&:hover {
background: ${Colors.POLAR};
}
`;
export enum CompactModeTypes {
SHORT = "SHORT",
DEFAULT = "DEFAULT",
}
export type CompactMode = keyof typeof CompactModeTypes;
type CompactModeItem = {
title: string;
value: CompactMode;
};
const CompactModes: CompactModeItem[] = [
{
title: "Short",
value: CompactModeTypes.SHORT,
},
{
title: "Default",
value: CompactModeTypes.DEFAULT,
},
];
interface TableCompactModeProps {
compactMode?: CompactMode;
updateCompactMode: (mode: CompactMode) => void;
}
const TableCompactMode = (props: TableCompactModeProps) => {
const [selected, selectMenu] = React.useState(false);
return (
<Popover
minimal
enforceFocus={false}
interactionKind={PopoverInteractionKind.CLICK}
position={Position.BOTTOM}
onClose={() => {
selectMenu(false);
}}
>
<TableIconWrapper
selected={selected}
onClick={e => {
selectMenu(!selected);
}}
>
<Tooltip
autoFocus={false}
hoverOpenDelay={1000}
content="Row Height"
position="top"
>
<IconWrapper
width={20}
height={20}
color={selected ? Colors.OXFORD_BLUE : Colors.CADET_BLUE}
>
<CompactIcon />
</IconWrapper>
</Tooltip>
</TableIconWrapper>
<DropDownWrapper>
{CompactModes.map((item: CompactModeItem, index: number) => {
return (
<OptionWrapper
selected={
props.compactMode ? props.compactMode === item.value : false
}
key={index}
onClick={() => {
props.updateCompactMode(item.value);
}}
className={Classes.POPOVER_DISMISS}
>
{item.title}
</OptionWrapper>
);
})}
</DropDownWrapper>
</Popover>
);
};
export default TableCompactMode;

View File

@ -12,6 +12,9 @@ import SearchComponent from "components/designSystems/appsmith/SearchComponent";
import TableColumnsVisibility from "components/designSystems/appsmith/TableColumnsVisibility";
import { ReactTableColumnProps } from "components/designSystems/appsmith/ReactTableComponent";
import TableDataDownload from "components/designSystems/appsmith/TableDataDownload";
import TableCompactMode, {
CompactMode,
} from "components/designSystems/appsmith/TableCompactMode";
import { Colors } from "constants/Colors";
const PageNumberInputWrapper = styled(NumericInput)`
@ -72,6 +75,8 @@ interface TableHeaderProps {
searchTableData: (searchKey: any) => void;
serverSidePaginationEnabled: boolean;
displayColumnActions: boolean;
compactMode?: CompactMode;
updateCompactMode: (compactMode: CompactMode) => void;
width: number;
}
@ -99,6 +104,10 @@ const TableHeader = (props: TableHeaderProps) => {
updateHiddenColumns={props.updateHiddenColumns}
/>
)}
<TableCompactMode
compactMode={props.compactMode}
updateCompactMode={props.updateCompactMode}
/>
</CommonFunctionsMenuWrapper>
{props.serverSidePaginationEnabled && (
<PaginationWrapper>

View File

@ -1,8 +1,13 @@
import styled from "styled-components";
import { Colors } from "constants/Colors";
import { TABLE_SIZES } from "components/designSystems/appsmith/Table";
import { TableSizes } from "components/designSystems/appsmith/Table";
export const TableWrapper = styled.div<{ width: number; height: number }>`
export const TableWrapper = styled.div<{
width: number;
height: number;
tableSizes: TableSizes;
tableRowHeight?: number;
}>`
width: 100%;
height: 100%;
background: white;
@ -24,7 +29,8 @@ export const TableWrapper = styled.div<{ width: number; height: number }>`
position: relative;
overflow-y: auto;
/* Subtracting 9px to handling widget padding */
height: ${props => props.height - TABLE_SIZES.TABLE_HEADER_HEIGHT - 9}px;
height: ${props =>
props.height - props.tableSizes.TABLE_HEADER_HEIGHT - 9}px;
.thead,
.tbody {
overflow: hidden;
@ -77,8 +83,14 @@ export const TableWrapper = styled.div<{ width: number; height: number }>`
background: ${Colors.ATHENS_GRAY_DARKER};
}
.td {
height: 52px;
line-height: 52px;
height: ${props =>
props.tableRowHeight
? props.tableRowHeight
: props.tableSizes.ROW_HEIGHT}px;
line-height: ${props =>
props.tableRowHeight
? props.tableRowHeight
: props.tableSizes.ROW_HEIGHT}px;
padding: 0 10px;
}
}
@ -263,7 +275,7 @@ export const TableHeaderWrapper = styled.div<{
width: number;
}>`
display: flex;
align-items: center;
align-items: flex-end;
width: 100%;
border-bottom: 1px solid ${Colors.GEYSER_LIGHT};
min-width: ${props =>
@ -276,7 +288,7 @@ export const TableHeaderWrapper = styled.div<{
export const CommonFunctionsMenuWrapper = styled.div`
display: flex;
align-items: center;
height: 100%;
height: 60px;
`;
export const RowWrapper = styled.div`

View File

@ -347,7 +347,9 @@ export const renderCell = (
) => {
switch (columnType) {
case ColumnTypes.IMAGE:
if (!isString(value)) {
if (!value) {
return <CellWrapper isHidden={isHidden}></CellWrapper>;
} else if (!isString(value)) {
return (
<CellWrapper isHidden={isHidden}>
<div>Invalid Image </div>
@ -384,7 +386,9 @@ export const renderCell = (
);
case ColumnTypes.VIDEO:
const youtubeRegex = /^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=|\?v=)([^#\&\?]*).*/;
if (isString(value) && youtubeRegex.test(value)) {
if (!value) {
return <CellWrapper isHidden={isHidden}></CellWrapper>;
} else if (isString(value) && youtubeRegex.test(value)) {
return (
<CellWrapper isHidden={isHidden} className="video-cell">
<VideoComponent url={value} />

View File

@ -5,6 +5,8 @@ import {
} from "components/designSystems/appsmith/TableStyledWrappers";
import { useTable, useFlexLayout } from "react-table";
import styled from "styled-components";
import { CompactModeTypes } from "components/designSystems/appsmith/TableCompactMode";
import { TABLE_SIZES } from "components/designSystems/appsmith/Table";
interface TableProps {
data: Record<string, any>[];
@ -59,7 +61,11 @@ const Table = (props: TableProps) => {
);
return (
<StyledTableWrapped width={200} height={200}>
<StyledTableWrapped
width={200}
height={200}
tableSizes={TABLE_SIZES[CompactModeTypes.DEFAULT]}
>
<div className="tableWrap">
<div {...getTableProps()} className="table">
{headerGroups.map((headerGroup: any, index: number) => (

View File

@ -78,7 +78,8 @@ const WidgetsEditor = () => {
if (!isFetchingPage && window.location.hash.length > 0) {
const widgetIdFromURLHash = window.location.hash.substr(1);
flashElementById(widgetIdFromURLHash);
selectWidget(widgetIdFromURLHash);
if (document.getElementById(widgetIdFromURLHash))
selectWidget(widgetIdFromURLHash);
}
}, [isFetchingPage, selectWidget]);

View File

@ -2,6 +2,8 @@ import React, { useEffect } from "react";
import { connect } from "react-redux";
import { Icon } from "@blueprintjs/core";
import { TableWrapper } from "components/designSystems/appsmith/TableStyledWrappers";
import { CompactModeTypes } from "components/designSystems/appsmith/TableCompactMode";
import { TABLE_SIZES } from "components/designSystems/appsmith/Table";
import { AppState } from "reducers";
import {
getAllUsers,
@ -265,7 +267,11 @@ export const OrgSettings = (props: PageProps) => {
{props.isFetchAllUsers && props.isFetchAllRoles ? (
<Spinner size={30} />
) : (
<StyledTableWrapped width={200} height={200}>
<StyledTableWrapped
width={200}
height={200}
tableSizes={TABLE_SIZES[CompactModeTypes.DEFAULT]}
>
<div className="tableWrap">
<div {...getTableProps()} className="table">
{headerGroups.map((headerGroup: any, index: number) => (

View File

@ -20,6 +20,10 @@ import {
BASE_WIDGET_VALIDATION,
} from "utils/ValidationFactory";
import { ColumnAction } from "components/propertyControls/ColumnActionSelectorControl";
import {
CompactMode,
CompactModeTypes,
} from "components/designSystems/appsmith/TableCompactMode";
import { TriggerPropertiesMap } from "utils/WidgetFactory";
import Skeleton from "components/utils/Skeleton";
import moment from "moment";
@ -245,8 +249,10 @@ class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
}
if (isValidDate) {
tableRow[accessor] = moment(value).format(format);
} else {
} else if (value) {
tableRow[accessor] = "Invalid Value";
} else {
tableRow[accessor] = "";
}
break;
case ColumnTypes.TIME:
@ -259,8 +265,10 @@ class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
}
if (isValidTime) {
tableRow[accessor] = moment(value).format("HH:mm");
} else {
} else if (value) {
tableRow[accessor] = "Invalid Value";
} else {
tableRow[accessor] = "";
}
break;
default:
@ -289,12 +297,22 @@ class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
super.updateWidgetMetaProperty("pageNo", pageNo);
}
const { componentWidth, componentHeight } = this.getComponentDimensions();
const pageSize = Math.floor(
const tableSizes =
TABLE_SIZES[this.props.compactMode || CompactModeTypes.DEFAULT];
let pageSize = Math.floor(
(componentHeight -
TABLE_SIZES.TABLE_HEADER_HEIGHT -
TABLE_SIZES.COLUMN_HEADER_HEIGHT) /
TABLE_SIZES.ROW_HEIGHT,
tableSizes.TABLE_HEADER_HEIGHT -
tableSizes.COLUMN_HEADER_HEIGHT) /
tableSizes.ROW_HEIGHT,
);
if (
componentHeight -
(tableSizes.TABLE_HEADER_HEIGHT +
tableSizes.COLUMN_HEADER_HEIGHT +
tableSizes.ROW_HEIGHT * pageSize) >
10
)
pageSize += 1;
if (pageSize !== this.props.pageSize) {
super.updateWidgetMetaProperty("pageSize", pageSize);
@ -354,6 +372,10 @@ class TableWidget extends BaseWidget<TableWidgetProps, WidgetState> {
this.disableDrag(disable);
}}
searchTableData={this.handleSearchTable}
compactMode={this.props.compactMode}
updateCompactMode={(compactMode: CompactMode) => {
super.updateWidgetMetaProperty("compactMode", compactMode);
}}
sortTableColumn={(column: string, asc: boolean) => {
this.resetSelectedRowIndex();
super.updateWidgetMetaProperty("sortedColumn", {
@ -467,6 +489,7 @@ export interface TableWidgetProps extends WidgetProps {
columnNameMap?: { [key: string]: string };
columnTypeMap?: { [key: string]: { type: string; format: string } };
columnSizeMap?: { [key: string]: number };
compactMode?: CompactMode;
sortedColumn?: {
column: string;
asc: boolean;

View File

@ -1,6 +1,7 @@
package com.appsmith.external.plugins;
import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.ActionExecutionResult;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.DatasourceTestResult;
import org.pf4j.ExtensionPoint;
@ -20,7 +21,7 @@ public interface PluginExecutor extends ExtensionPoint {
* @param actionConfiguration : These are the configurations which have been used to create an Action from a Datasource.
* @return ActionExecutionResult : This object is returned to the user which contains the result values from the execution.
*/
Mono<Object> execute(Object connection, DatasourceConfiguration datasourceConfiguration, ActionConfiguration actionConfiguration);
Mono<ActionExecutionResult> execute(Object connection, DatasourceConfiguration datasourceConfiguration, ActionConfiguration actionConfiguration);
/**
* This function is responsible for creating the connection to the data source and returning the connection variable

View File

@ -78,9 +78,9 @@ public class MongoPlugin extends BasePlugin {
* @return Result data from executing the action's query.
*/
@Override
public Mono<Object> execute(Object connection,
DatasourceConfiguration datasourceConfiguration,
ActionConfiguration actionConfiguration) {
public Mono<ActionExecutionResult> execute(Object connection,
DatasourceConfiguration datasourceConfiguration,
ActionConfiguration actionConfiguration) {
MongoClient mongoClient = (MongoClient) connection;
if (mongoClient == null) {

View File

@ -51,14 +51,10 @@ public class MySqlPlugin extends BasePlugin {
@Extension
public static class MySqlPluginExecutor implements PluginExecutor {
private Mono<Object> pluginErrorMono(Object... args) {
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, args));
}
@Override
public Mono<Object> execute(Object connection,
DatasourceConfiguration datasourceConfiguration,
ActionConfiguration actionConfiguration) {
public Mono<ActionExecutionResult> execute(Object connection,
DatasourceConfiguration datasourceConfiguration,
ActionConfiguration actionConfiguration) {
Connection conn = (Connection) connection;
@ -76,7 +72,7 @@ public class MySqlPlugin extends BasePlugin {
String query = actionConfiguration.getBody();
if (query == null) {
return pluginErrorMono("Missing required parameter: Query.");
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, "Missing required parameter: Query."));
}
List<Map<String, Object>> rowsList = new ArrayList<>(50);
@ -105,14 +101,14 @@ public class MySqlPlugin extends BasePlugin {
}
} catch (SQLException e) {
return pluginErrorMono(e.getMessage());
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e.getMessage()));
} finally {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
log.warn("Error closing MySql ResultSet", e);
log.warn("Error closing MySQL ResultSet", e);
}
}
@ -120,7 +116,7 @@ public class MySqlPlugin extends BasePlugin {
try {
statement.close();
} catch (SQLException e) {
log.warn("Error closing MySql Statement", e);
log.warn("Error closing MySQL Statement", e);
}
}
@ -138,7 +134,7 @@ public class MySqlPlugin extends BasePlugin {
try {
Class.forName(JDBC_DRIVER);
} catch (ClassNotFoundException e) {
return pluginErrorMono("Error loading MySql JDBC Driver class.");
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, "Error loading MySQL JDBC Driver class."));
}
String url;
@ -178,7 +174,7 @@ public class MySqlPlugin extends BasePlugin {
configurationConnection != null && READ_ONLY.equals(configurationConnection.getMode()));
return Mono.just(connection);
} catch (SQLException e) {
return pluginErrorMono("Error connecting to MySQL: " + e.getMessage(), e);
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, "Error connecting to MySQL: " + e.getMessage(), e));
}
}

View File

@ -63,9 +63,9 @@ public class PostgresPlugin extends BasePlugin {
public static class PostgresPluginExecutor implements PluginExecutor {
@Override
public Mono<Object> execute(Object connection,
DatasourceConfiguration datasourceConfiguration,
ActionConfiguration actionConfiguration) {
public Mono<ActionExecutionResult> execute(Object connection,
DatasourceConfiguration datasourceConfiguration,
ActionConfiguration actionConfiguration) {
Connection conn = (Connection) connection;
@ -83,7 +83,7 @@ public class PostgresPlugin extends BasePlugin {
String query = actionConfiguration.getBody();
if (query == null) {
return pluginErrorMono("Missing required parameter: Query.");
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, "Missing required parameter: Query."));
}
List<Map<String, Object>> rowsList = new ArrayList<>(50);
@ -149,7 +149,7 @@ public class PostgresPlugin extends BasePlugin {
}
} catch (SQLException e) {
return pluginErrorMono(e.getMessage());
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e.getMessage()));
} finally {
if (resultSet != null) {
@ -177,16 +177,12 @@ public class PostgresPlugin extends BasePlugin {
return Mono.just(result);
}
private Mono<Object> pluginErrorMono(Object... args) {
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, args));
}
@Override
public Mono<Object> datasourceCreate(DatasourceConfiguration datasourceConfiguration) {
try {
Class.forName(JDBC_DRIVER);
} catch (ClassNotFoundException e) {
return pluginErrorMono("Error loading Postgres JDBC Driver class.");
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, "Error loading Postgres JDBC Driver class."));
}
String url;
@ -233,7 +229,7 @@ public class PostgresPlugin extends BasePlugin {
return Mono.just(connection);
} catch (SQLException e) {
return pluginErrorMono("Error connecting to Postgres.", e);
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, "Error connecting to Postgres.", e));
}
}

View File

@ -24,6 +24,7 @@ import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.Exceptions;
import reactor.core.publisher.Mono;
import java.io.IOException;
@ -56,9 +57,9 @@ public class RapidApiPlugin extends BasePlugin {
private static final String RAPID_API_KEY_VALUE = System.getenv("APPSMITH_RAPID_API_KEY_VALUE");
@Override
public Mono<Object> execute(Object connection,
DatasourceConfiguration datasourceConfiguration,
ActionConfiguration actionConfiguration) {
public Mono<ActionExecutionResult> execute(Object connection,
DatasourceConfiguration datasourceConfiguration,
ActionConfiguration actionConfiguration) {
if (StringUtils.isEmpty(RAPID_API_KEY_VALUE)) {
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, "RapidAPI Key value not set."));
@ -100,10 +101,10 @@ public class RapidApiPlugin extends BasePlugin {
}
}
URI uri = null;
URI uri;
try {
uri = createFinalUriWithQueryParams(url, actionConfiguration.getQueryParameters());
System.out.println("Final URL is : " + uri.toString());
log.info("Final URL is : {}", uri);
} catch (URISyntaxException e) {
e.printStackTrace();
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e));
@ -114,7 +115,7 @@ public class RapidApiPlugin extends BasePlugin {
// First set the header to specify the content type
webClientBuilder.defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON.toString());
Map<String, String> keyValueMap = new HashMap<String, String>();
Map<String, String> keyValueMap = new HashMap<>();
List<Property> bodyFormData = actionConfiguration.getBodyFormData();
String jsonString = null;
@ -170,14 +171,14 @@ public class RapidApiPlugin extends BasePlugin {
headerInJsonString = objectMapper.writeValueAsString(headers);
} catch (JsonProcessingException e) {
e.printStackTrace();
return Mono.defer(() -> Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e)));
throw Exceptions.propagate(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e));
}
try {
// Set headers in the result now
result.setHeaders(objectMapper.readTree(headerInJsonString));
} catch (IOException e) {
e.printStackTrace();
return Mono.defer(() -> Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e)));
throw Exceptions.propagate(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e));
}
}
@ -194,7 +195,7 @@ public class RapidApiPlugin extends BasePlugin {
result.setBody(objectMapper.readTree(jsonBody));
} catch (IOException e) {
e.printStackTrace();
return Mono.defer(() -> Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e)));
throw Exceptions.propagate(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e));
}
} else if (MediaType.IMAGE_GIF.equals(contentType) ||
MediaType.IMAGE_JPEG.equals(contentType) ||
@ -207,9 +208,17 @@ public class RapidApiPlugin extends BasePlugin {
result.setBody(bodyString.trim());
}
}
return result;
})
.doOnError(e -> Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e)));
.onErrorMap(throwable -> {
final Throwable actualException = Exceptions.unwrap(throwable);
if (actualException instanceof AppsmithPluginException) {
return actualException;
} else {
return new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, actualException);
}
});
}
private Mono<ClientResponse> httpCall(WebClient webClient, HttpMethod httpMethod, URI uri, String requestBody, int iteration) {

View File

@ -31,6 +31,7 @@ import org.springframework.web.reactive.function.client.ClientResponse;
import org.springframework.web.reactive.function.client.ExchangeStrategies;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;
import reactor.core.Exceptions;
import reactor.core.publisher.Mono;
import java.io.IOException;
@ -65,15 +66,14 @@ public class RestApiPlugin extends BasePlugin {
public static class RestApiPluginExecutor implements PluginExecutor {
@Override
public Mono<Object> execute(Object connection,
DatasourceConfiguration datasourceConfiguration,
ActionConfiguration actionConfiguration) {
public Mono<ActionExecutionResult> execute(Object connection,
DatasourceConfiguration datasourceConfiguration,
ActionConfiguration actionConfiguration) {
ActionExecutionResult errorResult = new ActionExecutionResult();
errorResult.setStatusCode(AppsmithPluginError.PLUGIN_ERROR.getAppErrorCode().toString());
errorResult.setIsExecutionSuccess(false);
String path = (actionConfiguration.getPath() == null) ? "" : actionConfiguration.getPath();
String url = datasourceConfiguration.getUrl() + path;
String reqContentType = "";
@ -147,20 +147,19 @@ public class RestApiPlugin extends BasePlugin {
result.setStatusCode(statusCode.toString());
result.setIsExecutionSuccess(statusCode.is2xxSuccessful());
// Convert the headers into json tree to store in the results
String headerInJsonString;
try {
headerInJsonString = objectMapper.writeValueAsString(headers);
} catch (JsonProcessingException e) {
return Mono.defer(() -> Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e)));
throw Exceptions.propagate(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e));
}
// Set headers in the result now
try {
result.setHeaders(objectMapper.readTree(headerInJsonString));
} catch (IOException e) {
return Mono.defer(() -> Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e)));
throw Exceptions.propagate(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e));
}
if (body != null) {
@ -174,7 +173,7 @@ public class RestApiPlugin extends BasePlugin {
String jsonBody = new String(body);
result.setBody(objectMapper.readTree(jsonBody));
} catch (IOException e) {
return Mono.defer(() -> Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e)));
throw Exceptions.propagate(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, e));
}
} else if (MediaType.IMAGE_GIF.equals(contentType) ||
MediaType.IMAGE_JPEG.equals(contentType) ||
@ -191,7 +190,7 @@ public class RestApiPlugin extends BasePlugin {
return result;
})
.onErrorResume(e -> {
errorResult.setBody(AppsmithPluginError.PLUGIN_ERROR.getMessage(e));
errorResult.setBody(Exceptions.unwrap(e).getMessage());
errorResult.setRequest(actionExecutionRequest);
return Mono.just(errorResult);
});

View File

@ -36,11 +36,10 @@ public class RestApiPluginTest {
String requestBody = "{\"key\":\"value\"}";
actionConfig.setBody(requestBody);
Mono<Object> resultMono = pluginExecutor.execute(null, dsConfig, actionConfig);
Mono<ActionExecutionResult> resultMono = pluginExecutor.execute(null, dsConfig, actionConfig);
StepVerifier.create(resultMono)
.assertNext(r -> {
ActionExecutionResult result = (ActionExecutionResult) r;
.assertNext(result -> {
assertTrue(result.getIsExecutionSuccess());
assertNotNull(result.getBody());
JsonNode data = ((ObjectNode) result.getBody()).get("data");
@ -59,11 +58,10 @@ public class RestApiPluginTest {
actionConfig.setHeaders(List.of(new Property("content-type", "application/x-www-form-urlencoded")));
actionConfig.setHttpMethod(HttpMethod.POST);
actionConfig.setBodyFormData(List.of(new Property("key", "value"), new Property("key1", "value1")));
Mono<Object> resultMono = pluginExecutor.execute(null, dsConfig, actionConfig);
Mono<ActionExecutionResult> resultMono = pluginExecutor.execute(null, dsConfig, actionConfig);
StepVerifier.create(resultMono)
.assertNext(r -> {
ActionExecutionResult result = (ActionExecutionResult) r;
.assertNext(result -> {
assertTrue(result.getIsExecutionSuccess());
assertNotNull(result.getBody());
JsonNode data = ((ObjectNode) result.getBody()).get("form");

View File

@ -31,8 +31,11 @@ public class CustomFormLoginServiceImpl implements ReactiveUserDetailsService {
public Mono<UserDetails> findByUsername(String username) {
return repository.findByEmail(username)
.switchIfEmpty(Mono.error(new UsernameNotFoundException("Unable to find username: " + username)))
// This object cast is required to ensure that we send the right object type back to Spring framework.
// Doesn't work without this.
.map(user -> (UserDetails) user);
.onErrorMap(error -> {
log.error("Can't find user {}", username);
return error;
})
// This seemingly useless call to `.map` is required to Java's type checker to compile.
.map(user -> user);
}
}

View File

@ -25,6 +25,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.validation.Valid;
@ -50,7 +51,8 @@ public class ActionController extends BaseController<ActionService, Action, Stri
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<ResponseDTO<Action>> create(@Valid @RequestBody Action resource,
@RequestHeader(name = "Origin", required = false) String originHeader) {
@RequestHeader(name = "Origin", required = false) String originHeader,
ServerWebExchange exchange) {
log.debug("Going to create resource {}", resource.getClass().getName());
return actionCollectionService.createAction(resource)
.map(created -> new ResponseDTO<>(HttpStatus.CREATED.value(), created, null));

View File

@ -23,6 +23,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.validation.Valid;
@ -47,7 +48,8 @@ public class ApplicationController extends BaseController<ApplicationService, Ap
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<ResponseDTO<Application>> create(@Valid @RequestBody Application resource,
@RequestParam String orgId) {
@RequestParam String orgId,
ServerWebExchange exchange) {
if (orgId == null) {
return Mono.error(new AppsmithException(AppsmithError.INVALID_PARAMETER, "organization id"));
}

View File

@ -16,6 +16,7 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.validation.Valid;
@ -30,7 +31,8 @@ public abstract class BaseController<S extends CrudService, T extends BaseDomain
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<ResponseDTO<T>> create(@Valid @RequestBody T resource,
@RequestHeader(name = "Origin", required = false) String originHeader) {
@RequestHeader(name = "Origin", required = false) String originHeader,
ServerWebExchange exchange) {
log.debug("Going to create resource {}", resource.getClass().getName());
return service.create(resource)
.map(created -> new ResponseDTO<>(HttpStatus.CREATED.value(), created, null));

View File

@ -14,6 +14,7 @@ import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.validation.Valid;
@ -33,7 +34,8 @@ public class CollectionController extends BaseController<CollectionService, Coll
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<ResponseDTO<Collection>> create(@Valid @RequestBody Collection resource,
@RequestHeader(name = "Origin", required = false) String originHeader) {
@RequestHeader(name = "Origin", required = false) String originHeader,
ServerWebExchange exchange) {
log.debug("Going to create resource {}", resource.getClass().getName());
return actionCollectionService.createCollection(resource)
.map(created -> new ResponseDTO<>(HttpStatus.CREATED.value(), created, null));

View File

@ -18,6 +18,7 @@ import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.validation.Valid;
@ -37,7 +38,8 @@ public class PageController extends BaseController<PageService, Page, String> {
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Mono<ResponseDTO<Page>> create(@Valid @RequestBody Page resource,
@RequestHeader(name = "Origin", required = false) String originHeader) {
@RequestHeader(name = "Origin", required = false) String originHeader,
ServerWebExchange exchange) {
log.debug("Going to create resource {}", resource.getClass().getName());
return applicationPageService.createPage(resource)
.map(created -> new ResponseDTO<>(HttpStatus.CREATED.value(), created, null));

View File

@ -8,9 +8,11 @@ import com.appsmith.server.dtos.ResponseDTO;
import com.appsmith.server.services.SessionUserService;
import com.appsmith.server.services.UserOrganizationService;
import com.appsmith.server.services.UserService;
import com.appsmith.server.solutions.UserSignup;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
@ -21,6 +23,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import javax.validation.Valid;
@ -32,24 +35,34 @@ public class UserController extends BaseController<UserService, User, String> {
private final SessionUserService sessionUserService;
private final UserOrganizationService userOrganizationService;
private final UserSignup userSignup;
@Autowired
public UserController(UserService service,
SessionUserService sessionUserService,
UserOrganizationService userOrganizationService) {
UserOrganizationService userOrganizationService,
UserSignup userSignup) {
super(service);
this.sessionUserService = sessionUserService;
this.userOrganizationService = userOrganizationService;
this.userSignup = userSignup;
}
@PostMapping
@PostMapping(consumes = {MediaType.APPLICATION_JSON_VALUE})
@ResponseStatus(HttpStatus.CREATED)
public Mono<ResponseDTO<User>> create(@Valid @RequestBody User resource,
@RequestHeader(name = "Origin", required = false) String originHeader) {
return service.createUserAndSendEmail(resource, originHeader)
@RequestHeader(name = "Origin", required = false) String originHeader,
ServerWebExchange exchange) {
return userSignup.signupAndLogin(resource, exchange)
.map(created -> new ResponseDTO<>(HttpStatus.CREATED.value(), created, null));
}
@PostMapping(consumes = {MediaType.APPLICATION_FORM_URLENCODED_VALUE})
@ResponseStatus(HttpStatus.CREATED)
public Mono<Void> createFormEncoded(ServerWebExchange exchange) {
return userSignup.signupAndLoginFromFormData(exchange);
}
@PutMapping("/switchOrganization/{orgId}")
public Mono<ResponseDTO<User>> setCurrentOrganization(@PathVariable String orgId) {
return service.switchCurrentOrganization(orgId)

View File

@ -355,8 +355,17 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
Mono<PluginExecutor> pluginExecutorMono = pluginExecutorHelper.getPluginExecutor(pluginMono);
// 4. Execute the query
Mono<ActionExecutionResult> actionExecutionResultMono = actionMono
.flatMap(action -> datasourceMono.zipWith(pluginExecutorMono, (datasource, pluginExecutor) -> {
Mono<ActionExecutionResult> actionExecutionResultMono = Mono
.zip(
actionMono,
datasourceMono,
pluginExecutorMono
)
.flatMap(tuple -> {
final Action action = tuple.getT1();
final Datasource datasource = tuple.getT2();
final PluginExecutor pluginExecutor = tuple.getT3();
DatasourceConfiguration datasourceConfigurationTemp;
ActionConfiguration actionConfigurationTemp;
//Do variable substitution before invoking the plugin
@ -377,8 +386,8 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
(oldValue, newValue) -> oldValue)
);
datasourceConfigurationTemp = (DatasourceConfiguration) variableSubstitution(datasource.getDatasourceConfiguration(), replaceParamsMap);
actionConfigurationTemp = (ActionConfiguration) variableSubstitution(action.getActionConfiguration(), replaceParamsMap);
datasourceConfigurationTemp = variableSubstitution(datasource.getDatasourceConfiguration(), replaceParamsMap);
actionConfigurationTemp = variableSubstitution(action.getActionConfiguration(), replaceParamsMap);
} else {
datasourceConfigurationTemp = datasource.getDatasourceConfiguration();
actionConfigurationTemp = action.getActionConfiguration();
@ -413,7 +422,7 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
action.getPageId(), action.getId(), action.getName(), datasourceConfiguration,
actionConfiguration);
Mono<Object> executionMono = Mono.just(datasource)
Mono<ActionExecutionResult> executionMono = Mono.just(datasource)
.flatMap(datasourceContextService::getDatasourceContext)
// Now that we have the context (connection details), execute the action.
.flatMap(
@ -452,9 +461,7 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
}
return Mono.just(result);
});
}))
.flatMap(obj -> obj)
.map(obj -> (ActionExecutionResult) obj);
});
// Populate the actionExecution result by setting the cached response and saving it to the DB
return actionExecutionResultMono
@ -483,11 +490,8 @@ public class ActionServiceImpl extends BaseService<ActionRepository, Action, Str
log.debug("Action execution resulted in failure beyond the proxy with the result of {}", result);
return Mono.just(action);
});
return actionFromDbMono.zipWith(resultMono)
.map(tuple -> {
ActionExecutionResult executionResult = tuple.getT2();
return executionResult;
});
return actionFromDbMono.then(resultMono);
})
.onErrorResume(AppsmithException.class, error -> {
ActionExecutionResult result = new ActionExecutionResult();

View File

@ -3,6 +3,7 @@ package com.appsmith.server.services;
import com.appsmith.server.domains.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Mono;
@ -12,10 +13,9 @@ public class SessionUserServiceImpl implements SessionUserService {
@Override
public Mono<User> getCurrentUser() {
return ReactiveSecurityContextHolder.getContext()
.map(ctx -> ctx.getAuthentication())
.map(auth -> auth.getPrincipal())
.map(principal -> (User) principal);
.map(SecurityContext::getAuthentication)
.map(auth -> (User) auth.getPrincipal());
}
}

View File

@ -0,0 +1,118 @@
package com.appsmith.server.solutions;
import com.appsmith.server.authentication.handlers.AuthenticationSuccessHandler;
import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.LoginSource;
import com.appsmith.server.domains.User;
import com.appsmith.server.domains.UserState;
import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.services.UserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.client.utils.URIBuilder;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.web.server.DefaultServerRedirectStrategy;
import org.springframework.security.web.server.ServerRedirectStrategy;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilterChain;
import org.springframework.web.server.WebSession;
import reactor.core.publisher.Mono;
import java.net.URI;
import java.net.URISyntaxException;
import static org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository.DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME;
@Component
@RequiredArgsConstructor
@Slf4j
public class UserSignup {
private final UserService userService;
private final AuthenticationSuccessHandler authenticationSuccessHandler;
private static final ServerRedirectStrategy redirectStrategy = new DefaultServerRedirectStrategy();
private static final WebFilterChain EMPTY_WEB_FILTER_CHAIN = serverWebExchange -> Mono.empty();
/**
* This function does the sign-up flow of the given user object as a new user, and then logs that user. After the
* login is successful, the authentication success handlers will be called directly.
* This needed to be pulled out into a separate solution class since it was creating a circular autowiring error if
* placed inside UserService.
* @param user User object representing the new user to be signed-up and then logged-in.
* @param exchange ServerWebExchange object with details of the current web request.
* @return Mono of User, published the saved user object with a non-null value for its `getId()`.
*/
public Mono<User> signupAndLogin(User user, ServerWebExchange exchange) {
return Mono
.zip(
userService.createUserAndSendEmail(user, exchange.getRequest().getHeaders().getOrigin()),
exchange.getSession(),
ReactiveSecurityContextHolder.getContext()
)
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.INTERNAL_SERVER_ERROR)))
.flatMap(tuple -> {
final User savedUser = tuple.getT1();
final WebSession session = tuple.getT2();
final SecurityContext securityContext = tuple.getT3();
Authentication authentication = new UsernamePasswordAuthenticationToken(savedUser, null, savedUser.getAuthorities());
securityContext.setAuthentication(authentication);
session.getAttributes().put(DEFAULT_SPRING_SECURITY_CONTEXT_ATTR_NAME, securityContext);
final WebFilterExchange webFilterExchange = new WebFilterExchange(exchange, EMPTY_WEB_FILTER_CHAIN);
return authenticationSuccessHandler
.onAuthenticationSuccess(webFilterExchange, authentication)
.thenReturn(savedUser);
});
}
/**
* Creates a new user and logs them in, with the user details taken from the POST body, read as form-data.
* @param exchange The `ServerWebExchange` instance representing the request.
* @return Publisher of the created user object, with an `id` value.
*/
public Mono<Void> signupAndLoginFromFormData(ServerWebExchange exchange) {
return exchange.getFormData()
.map(formData -> {
final User user = new User();
user.setEmail(formData.getFirst(FieldName.EMAIL));
user.setPassword(formData.getFirst("password"));
if (formData.containsKey(FieldName.NAME)) {
user.setName(formData.getFirst(FieldName.NAME));
}
if (formData.containsKey("source")) {
user.setSource(LoginSource.valueOf(formData.getFirst("source")));
}
if (formData.containsKey("state")) {
user.setState(UserState.valueOf(formData.getFirst("state")));
}
if (formData.containsKey("isEnabled")) {
user.setIsEnabled(Boolean.valueOf(formData.getFirst("isEnabled")));
}
return user;
})
.flatMap(user -> signupAndLogin(user, exchange))
.then()
.onErrorResume(error -> {
final String referer = exchange.getRequest().getHeaders().getFirst("referer");
final URIBuilder redirectUriBuilder = new URIBuilder(URI.create(referer)).setParameter("error", error.getMessage());
URI redirectUri;
try {
redirectUri = redirectUriBuilder.build();
} catch (URISyntaxException e) {
log.error("Error building redirect URI with error for signup, {}.", e.getMessage(), error);
redirectUri = URI.create(referer);
}
return redirectStrategy.sendRedirect(exchange, redirectUri);
});
}
}

View File

@ -1,6 +1,7 @@
package com.appsmith.server.helpers;
import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.ActionExecutionResult;
import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.DatasourceTestResult;
import com.appsmith.external.plugins.PluginExecutor;
@ -12,7 +13,7 @@ import java.util.Set;
public class MockPluginExecutor implements PluginExecutor {
@Override
public Mono<Object> execute(Object connection, DatasourceConfiguration datasourceConfiguration, ActionConfiguration actionConfiguration) {
public Mono<ActionExecutionResult> execute(Object connection, DatasourceConfiguration datasourceConfiguration, ActionConfiguration actionConfiguration) {
System.out.println("In the execute");
return null;
}

View File

@ -35,7 +35,7 @@ public class PluginServiceTest {
@Test
public void checkPluginExecutor() {
Mono<Object> executeMono = pluginExecutor.execute(new Object(), new DatasourceConfiguration(), new ActionConfiguration());
Mono<ActionExecutionResult> executeMono = pluginExecutor.execute(new Object(), new DatasourceConfiguration(), new ActionConfiguration());
StepVerifier
.create(executeMono)