Merge branch 'release'

This commit is contained in:
Shrikant Sharat Kandula 2021-02-10 14:35:02 +05:30
commit 95d1717f57
43 changed files with 1030 additions and 328 deletions

19
.deepsource.toml Normal file
View File

@ -0,0 +1,19 @@
version = 1
exclude_patterns = [
"static/**",
"deploy/**",
"contributions/**"
]
[[analyzers]]
name = "javascript"
enabled = true
[analyzers.meta]
plugins = ["react"]
environment = [
"nodejs",
"browser"
]
dialect = "typescript"

View File

@ -48,10 +48,10 @@ jobs:
- name: Figure out the PR number - name: Figure out the PR number
run: echo ${{ github.event.pull_request.number }} run: echo ${{ github.event.pull_request.number }}
- name: Use Node.js 10.16.3 - name: Use Node.js 14.15.4
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: "10.16.3" node-version: "14.15.4"
# Retrieve npm dependencies from cache. After a successful run, these dependencies are cached again # Retrieve npm dependencies from cache. After a successful run, these dependencies are cached again
- name: Cache npm dependencies - name: Cache npm dependencies
@ -151,10 +151,10 @@ jobs:
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch' if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Use Node.js 10.16.3 - name: Use Node.js 14.15.4
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: "10.16.3" node-version: "14.15.4"
# Retrieve npm dependencies from cache. After a successful run, these dependencies are cached again # Retrieve npm dependencies from cache. After a successful run, these dependencies are cached again
- name: Cache npm dependencies - name: Cache npm dependencies

View File

@ -1 +1 @@
lts/dubnium lts/fermium

View File

@ -3,8 +3,8 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"engines": { "engines": {
"node": "^10.16.3", "node": "^14.15.4",
"npm": "^6.9.0" "npm": "^6.14.10"
}, },
"cracoConfig": "craco.dev.config.js", "cracoConfig": "craco.dev.config.js",
"dependencies": { "dependencies": {
@ -35,14 +35,14 @@
"@types/react-table": "^7.0.13", "@types/react-table": "^7.0.13",
"@types/styled-components": "^5.1.3", "@types/styled-components": "^5.1.3",
"@types/tinycolor2": "^1.4.2", "@types/tinycolor2": "^1.4.2",
"@uppy/core": "^1.8.2", "@uppy/core": "^1.16.0",
"@uppy/dashboard": "^1.6.2", "@uppy/dashboard": "^1.16.0",
"@uppy/file-input": "^1.3.1", "@uppy/file-input": "^1.4.22",
"@uppy/google-drive": "^1.3.2", "@uppy/google-drive": "^1.5.22",
"@uppy/onedrive": "^0.1.1", "@uppy/onedrive": "^1.1.22",
"@uppy/react": "^1.4.5", "@uppy/react": "^1.11.2",
"@uppy/url": "^1.3.2", "@uppy/url": "^1.5.16",
"@uppy/webcam": "^1.3.1", "@uppy/webcam": "^1.8.4",
"@welldone-software/why-did-you-render": "^4.2.5", "@welldone-software/why-did-you-render": "^4.2.5",
"algoliasearch": "^4.2.0", "algoliasearch": "^4.2.0",
"axios": "^0.21.1", "axios": "^0.21.1",

View File

@ -381,7 +381,7 @@ const TextLoadingState = ({ text }: { text?: string }) => (
); );
const IconLoadingState = ({ size, icon }: { size?: Size; icon?: IconName }) => ( const IconLoadingState = ({ size, icon }: { size?: Size; icon?: IconName }) => (
<Icon name={icon} size={IconSizeProp(size)} invisible={true} /> <Icon name={icon} size={IconSizeProp(size)} invisible />
); );
const getIconContent = (props: ButtonProps) => const getIconContent = (props: ButtonProps) =>

View File

@ -263,7 +263,7 @@ export const EditableText = (props: EditableTextProps) => {
onChange={onInputchange} onChange={onInputchange}
onConfirm={onConfirm} onConfirm={onConfirm}
value={value} value={value}
selectAllOnFocus={true} selectAllOnFocus
placeholder={props.placeholder || defaultValue} placeholder={props.placeholder || defaultValue}
className={props.className} className={props.className}
onCancel={onConfirm} onCancel={onConfirm}

View File

@ -35,7 +35,7 @@ const PopoverVideo = (props: VideoComponentProps) => {
minimal minimal
usePortal usePortal
enforceFocus={false} enforceFocus={false}
lazy={true} lazy
modifiers={{ modifiers={{
flip: { flip: {
behavior: ["right", "left", "bottom", "top"], behavior: ["right", "left", "bottom", "top"],

View File

@ -55,7 +55,7 @@ const PageNumberInput = (props: {
min={1} min={1}
max={props.pageCount || 1} max={props.pageCount || 1}
buttonPosition="none" buttonPosition="none"
clampValueOnBlur={true} clampValueOnBlur
onBlur={(e: any) => { onBlur={(e: any) => {
const oldPageNo = Number(props.pageNo || 0); const oldPageNo = Number(props.pageNo || 0);
const value = e.target.value; const value = e.target.value;

View File

@ -114,7 +114,7 @@ class HelpModal extends React.Component<Props> {
<> <>
{isHelpModalOpen && ( {isHelpModalOpen && (
<ModalComponent <ModalComponent
canOutsideClickClose={true} canOutsideClickClose
canEscapeKeyClose canEscapeKeyClose
scrollContents scrollContents
height={MODAL_HEIGHT} height={MODAL_HEIGHT}

View File

@ -13,7 +13,9 @@ export type ComparisonOperations =
| "EQUALS" | "EQUALS"
| "NOT_EQUALS" | "NOT_EQUALS"
| "LESSER" | "LESSER"
| "GREATER"; | "GREATER"
| "IN"
| "NOT_IN";
export type HiddenType = export type HiddenType =
| boolean | boolean

View File

@ -54,6 +54,22 @@ describe("isHidden test", () => {
comparison: "NOT_EQUALS", comparison: "NOT_EQUALS",
}, },
}, },
{
values: { name: "Name", config: { type: "Different BODY" } },
hidden: {
path: "config.type",
value: ["EMAIL", "BODY"],
comparison: "IN",
},
},
{
values: { name: "Name", config: { type: "BODY" } },
hidden: {
path: "config.type",
value: ["EMAIL", "BODY"],
comparison: "NOT_IN",
},
},
{ {
values: undefined, values: undefined,
hidden: false, hidden: false,

View File

@ -15,6 +15,10 @@ export const isHidden = (values: any, hiddenConfig?: HiddenType) => {
return valueAtPath > value; return valueAtPath > value;
case "LESSER": case "LESSER":
return valueAtPath < value; return valueAtPath < value;
case "IN":
return Array.isArray(value) && value.includes(valueAtPath);
case "NOT_IN":
return Array.isArray(value) && !value.includes(valueAtPath);
default: default:
return true; return true;
} }

View File

@ -8,12 +8,12 @@ export const FIELD_REQUIRED_ERROR = "This field is required";
export const VALID_FUNCTION_NAME_ERROR = export const VALID_FUNCTION_NAME_ERROR =
"Must be a valid variable name (camelCase)"; "Must be a valid variable name (camelCase)";
export const UNIQUE_NAME_ERROR = "Name must be unique"; export const UNIQUE_NAME_ERROR = "Name must be unique";
export const NAME_SPACE_ERROR = "Name must not have spaces"; export const NAME_SPACE_ERROR = "Name cannot have spaces";
export const FORM_VALIDATION_EMPTY_EMAIL = "Please enter an email"; export const FORM_VALIDATION_EMPTY_EMAIL = "Please enter an email";
export const FORM_VALIDATION_INVALID_EMAIL = export const FORM_VALIDATION_INVALID_EMAIL =
"Please provide a valid email address"; "Please provide a valid email address";
export const ENTER_VIDEO_URL = "Please provide a valid url"; export const ENTER_VIDEO_URL = "Please provide a valid URL";
export const FORM_VALIDATION_EMPTY_PASSWORD = "Please enter the password"; export const FORM_VALIDATION_EMPTY_PASSWORD = "Please enter the password";
export const FORM_VALIDATION_PASSWORD_RULE = export const FORM_VALIDATION_PASSWORD_RULE =
@ -27,11 +27,11 @@ export const LOGIN_PAGE_PASSWORD_INPUT_LABEL = "Password";
export const LOGIN_PAGE_EMAIL_INPUT_PLACEHOLDER = "Email"; export const LOGIN_PAGE_EMAIL_INPUT_PLACEHOLDER = "Email";
export const LOGIN_PAGE_PASSWORD_INPUT_PLACEHOLDER = "Password"; export const LOGIN_PAGE_PASSWORD_INPUT_PLACEHOLDER = "Password";
export const LOGIN_PAGE_INVALID_CREDS_ERROR = export const LOGIN_PAGE_INVALID_CREDS_ERROR =
"It looks like you may have entered incorrect/invalid credentials. Please try again or reset password using the button below."; "You may have entered incorrect/invalid credentials. Please try again or reset your password.";
export const LOGIN_PAGE_INVALID_CREDS_FORGOT_PASSWORD_LINK = "Reset Password"; export const LOGIN_PAGE_INVALID_CREDS_FORGOT_PASSWORD_LINK = "Reset Password";
export const NEW_TO_APPSMITH = "New to Appsmith?"; export const NEW_TO_APPSMITH = "New to Appsmith?";
export const LOGIN_PAGE_LOGIN_BUTTON_TEXT = "sign in"; export const LOGIN_PAGE_LOGIN_BUTTON_TEXT = "Sign in";
export const LOGIN_PAGE_FORGOT_PASSWORD_TEXT = "Forgot Password"; export const LOGIN_PAGE_FORGOT_PASSWORD_TEXT = "Forgot Password";
export const LOGIN_PAGE_REMEMBER_ME_LABEL = "Remember"; export const LOGIN_PAGE_REMEMBER_ME_LABEL = "Remember";
export const LOGIN_PAGE_SIGN_UP_LINK_TEXT = "Sign up"; export const LOGIN_PAGE_SIGN_UP_LINK_TEXT = "Sign up";
@ -44,16 +44,16 @@ export const SIGNUP_PAGE_NAME_INPUT_LABEL = "Name";
export const SIGNUP_PAGE_PASSWORD_INPUT_LABEL = "Password"; export const SIGNUP_PAGE_PASSWORD_INPUT_LABEL = "Password";
export const SIGNUP_PAGE_PASSWORD_INPUT_PLACEHOLDER = "Password"; export const SIGNUP_PAGE_PASSWORD_INPUT_PLACEHOLDER = "Password";
export const SIGNUP_PAGE_LOGIN_LINK_TEXT = "Sign In"; export const SIGNUP_PAGE_LOGIN_LINK_TEXT = "Sign In";
export const SIGNUP_PAGE_NAME_INPUT_SUBTEXT = "How should we call you?"; export const SIGNUP_PAGE_NAME_INPUT_SUBTEXT = "What should we call you?";
export const SIGNUP_PAGE_SUBMIT_BUTTON_TEXT = "Sign Up"; export const SIGNUP_PAGE_SUBMIT_BUTTON_TEXT = "Sign Up";
export const ALREADY_HAVE_AN_ACCOUNT = "Already have an account?"; export const ALREADY_HAVE_AN_ACCOUNT = "You may already have an account.";
export const SIGNUP_PAGE_SUCCESS = "Awesome! You have successfully registered."; export const SIGNUP_PAGE_SUCCESS = "Awesome! You have successfully registered.";
export const SIGNUP_PAGE_SUCCESS_LOGIN_BUTTON_TEXT = "Login"; export const SIGNUP_PAGE_SUCCESS_LOGIN_BUTTON_TEXT = "Login";
export const RESET_PASSWORD_PAGE_PASSWORD_INPUT_LABEL = "New Password"; export const RESET_PASSWORD_PAGE_PASSWORD_INPUT_LABEL = "New Password";
export const RESET_PASSWORD_PAGE_PASSWORD_INPUT_PLACEHOLDER = "New Password"; export const RESET_PASSWORD_PAGE_PASSWORD_INPUT_PLACEHOLDER = "New Password";
export const RESET_PASSWORD_LOGIN_LINK_TEXT = "Back to Sign In"; export const RESET_PASSWORD_LOGIN_LINK_TEXT = "Back to sign in page";
export const RESET_PASSWORD_PAGE_TITLE = "Reset Password"; export const RESET_PASSWORD_PAGE_TITLE = "Reset Password";
export const RESET_PASSWORD_SUBMIT_BUTTON_TEXT = "Reset"; export const RESET_PASSWORD_SUBMIT_BUTTON_TEXT = "Reset";
export const RESET_PASSWORD_PAGE_SUBTITLE = export const RESET_PASSWORD_PAGE_SUBTITLE =
@ -63,9 +63,9 @@ export const RESET_PASSWORD_RESET_SUCCESS = "Your password has been reset"; //"Y
export const RESET_PASSWORD_RESET_SUCCESS_LOGIN_LINK = "Login"; export const RESET_PASSWORD_RESET_SUCCESS_LOGIN_LINK = "Login";
export const RESET_PASSWORD_EXPIRED_TOKEN = export const RESET_PASSWORD_EXPIRED_TOKEN =
"The password reset link has expired. Please try generating a new link"; "This password reset link has expired. Please try generating a new link";
export const RESET_PASSWORD_INVALID_TOKEN = export const RESET_PASSWORD_INVALID_TOKEN =
"The password reset link is invalid. Please try generating a new link"; "This password reset link is invalid. Please try generating a new link";
export const RESET_PASSWORD_FORGOT_PASSWORD_LINK = "Forgot Password"; export const RESET_PASSWORD_FORGOT_PASSWORD_LINK = "Forgot Password";
export const FORGOT_PASSWORD_PAGE_EMAIL_INPUT_LABEL = "Email"; export const FORGOT_PASSWORD_PAGE_EMAIL_INPUT_LABEL = "Email";
@ -81,7 +81,7 @@ export const PRIVACY_POLICY_LINK = "Privacy Policy";
export const TERMS_AND_CONDITIONS_LINK = "Terms and Conditions"; export const TERMS_AND_CONDITIONS_LINK = "Terms and Conditions";
export const ERROR_500 = export const ERROR_500 =
"We apologize, Something went wrong. We're working to fix things."; "Whoops, something went wrong. This is unexpected, and we'll look into this.";
export const ERROR_0 = export const ERROR_0 =
"We could not connect to our servers. Please check your network connection"; "We could not connect to our servers. Please check your network connection";
export const ERROR_401 = export const ERROR_401 =
@ -159,7 +159,7 @@ export const LIGHTNING_MENU_API_CREATE_NEW = "Create new API";
export const LIGHTNING_MENU_OPTION_TEXT = "Plain Text"; export const LIGHTNING_MENU_OPTION_TEXT = "Plain Text";
export const LIGHTNING_MENU_OPTION_JS = "Write JS"; export const LIGHTNING_MENU_OPTION_JS = "Write JS";
export const LIGHTNING_MENU_OPTION_HTML = "Write HTML"; export const LIGHTNING_MENU_OPTION_HTML = "Write HTML";
export const CHECK_REQUEST_BODY = "Check Request body to debug?"; export const CHECK_REQUEST_BODY = "Please check request body to debug?";
export const DONT_SHOW_THIS_AGAIN = "Don't show this again"; export const DONT_SHOW_THIS_AGAIN = "Don't show this again";
export const SHOW_REQUEST = "Show Request"; export const SHOW_REQUEST = "Show Request";
@ -168,7 +168,7 @@ export const TABLE_FILTER_COLUMN_TYPE_CALLOUT =
export const WIDGET_SIDEBAR_TITLE = "Widgets"; export const WIDGET_SIDEBAR_TITLE = "Widgets";
export const WIDGET_SIDEBAR_CAPTION = export const WIDGET_SIDEBAR_CAPTION =
"To add a widget, please drag and drop a widget on the canvas to the right"; "To add a widget, please click + and add widget to the canvas.";
export const GOOGLE_RECAPTCHA_KEY_ERROR = export const GOOGLE_RECAPTCHA_KEY_ERROR =
"Google Re-Captcha Token Generation failed! Please check the Re-captcha Site Key."; "Google Re-Captcha Token Generation failed! Please check the Re-captcha Site Key.";
export const GOOGLE_RECAPTCHA_DOMAIN_ERROR = export const GOOGLE_RECAPTCHA_DOMAIN_ERROR =

View File

@ -122,6 +122,8 @@ export const PageTabsContainer = (props: AppViewerHeaderProps) => {
onMouseDown={() => startScrolling(true)} onMouseDown={() => startScrolling(true)}
onMouseUp={stopScrolling} onMouseUp={stopScrolling}
onMouseLeave={stopScrolling} onMouseLeave={stopScrolling}
onTouchStart={() => startScrolling(true)}
onTouchEnd={stopScrolling}
visible={shouldShowLeftArrow} visible={shouldShowLeftArrow}
> >
<Icon name="chevron-left" /> <Icon name="chevron-left" />
@ -137,6 +139,8 @@ export const PageTabsContainer = (props: AppViewerHeaderProps) => {
onMouseDown={() => startScrolling(false)} onMouseDown={() => startScrolling(false)}
onMouseUp={stopScrolling} onMouseUp={stopScrolling}
onMouseLeave={stopScrolling} onMouseLeave={stopScrolling}
onTouchStart={() => startScrolling(false)}
onTouchEnd={stopScrolling}
visible={shouldShowRightArrow} visible={shouldShowRightArrow}
> >
<Icon name="chevron-right" /> <Icon name="chevron-right" />

View File

@ -1346,10 +1346,10 @@ function* addTableWidgetFromQuerySaga(action: ReduxAction<string>) {
rightColumn: columns, rightColumn: columns,
columns, columns,
rows, rows,
parentId: "0", parentId: MAIN_CONTAINER_WIDGET_ID,
widgetName, widgetName,
renderMode: RenderModes.CANVAS, renderMode: RenderModes.CANVAS,
parentRowSpace: 1, parentRowSpace: GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
parentColumnSpace: 1, parentColumnSpace: 1,
isLoading: false, isLoading: false,
props: { props: {
@ -1362,7 +1362,11 @@ function* addTableWidgetFromQuerySaga(action: ReduxAction<string>) {
topRow, topRow,
rightColumn, rightColumn,
bottomRow, bottomRow,
} = yield calculateNewWidgetPosition(newWidget, "0", widgets); } = yield calculateNewWidgetPosition(
newWidget,
MAIN_CONTAINER_WIDGET_ID,
widgets,
);
newWidget = { newWidget = {
...newWidget, ...newWidget,

View File

@ -27,13 +27,11 @@ class FilePickerWidget extends BaseWidget<
FilePickerWidgetProps, FilePickerWidgetProps,
FilePickerWidgetState FilePickerWidgetState
> { > {
uppy: any;
constructor(props: FilePickerWidgetProps) { constructor(props: FilePickerWidgetProps) {
super(props); super(props);
this.state = { this.state = {
version: 0,
isLoading: false, isLoading: false,
uppy: this.initializeUppy(),
}; };
} }
@ -63,24 +61,70 @@ class FilePickerWidget extends BaseWidget<
}; };
} }
refreshUppy = (props: FilePickerWidgetProps) => { static getTriggerPropertyMap(): TriggerPropertiesMap {
this.uppy = Uppy({ return {
onFilesSelected: true,
};
}
/**
* if uppy is not initialized before, initialize it
* else setState of uppy instance
*/
initializeUppy = () => {
const uppyState = {
id: this.props.widgetId, id: this.props.widgetId,
autoProceed: false, autoProceed: false,
allowMultipleUploads: true, allowMultipleUploads: true,
debug: false, debug: false,
restrictions: {
maxFileSize: this.props.maxFileSize
? this.props.maxFileSize * 1024 * 1024
: null,
maxNumberOfFiles: this.props.maxNumFiles,
minNumberOfFiles: null,
allowedFileTypes:
this.props.allowedFileTypes &&
(this.props.allowedFileTypes.includes("*") ||
_.isEmpty(this.props.allowedFileTypes))
? null
: this.props.allowedFileTypes,
},
};
return Uppy(uppyState);
};
/**
* set states on the uppy instance with new values
*/
reinitializeUppy = (props: FilePickerWidgetProps) => {
const uppyState = {
id: props.widgetId,
autoProceed: false,
allowMultipleUploads: true,
debug: false,
restrictions: { restrictions: {
maxFileSize: props.maxFileSize ? props.maxFileSize * 1024 * 1024 : null, maxFileSize: props.maxFileSize ? props.maxFileSize * 1024 * 1024 : null,
maxNumberOfFiles: props.maxNumFiles, maxNumberOfFiles: props.maxNumFiles,
minNumberOfFiles: null, minNumberOfFiles: null,
allowedFileTypes: allowedFileTypes:
props.allowedFileTypes && props.allowedFileTypes &&
(props.allowedFileTypes.includes("*") || (this.props.allowedFileTypes.includes("*") ||
_.isEmpty(props.allowedFileTypes)) _.isEmpty(props.allowedFileTypes))
? null ? null
: props.allowedFileTypes, : props.allowedFileTypes,
}, },
}) };
this.state.uppy.setOptions(uppyState);
};
/**
* add all uppy events listeners needed
*/
initializeUppyEventListeners = () => {
this.state.uppy
.use(Dashboard, { .use(Dashboard, {
target: "body", target: "body",
metaFields: [], metaFields: [],
@ -101,7 +145,11 @@ class FilePickerWidget extends BaseWidget<
disablePageScrollWhenModalOpen: true, disablePageScrollWhenModalOpen: true,
proudlyDisplayPoweredByUppy: false, proudlyDisplayPoweredByUppy: false,
onRequestCloseModal: () => { onRequestCloseModal: () => {
this.uppy.getPlugin("Dashboard").closeModal(); const plugin = this.state.uppy.getPlugin("Dashboard");
if (plugin) {
plugin.closeModal();
}
}, },
locale: {}, locale: {},
}) })
@ -117,7 +165,8 @@ class FilePickerWidget extends BaseWidget<
facingMode: "user", facingMode: "user",
locale: {}, locale: {},
}); });
this.uppy.on("file-removed", (file: any) => {
this.state.uppy.on("file-removed", (file: any) => {
const updatedFiles = this.props.files const updatedFiles = this.props.files
? this.props.files.filter((dslFile) => { ? this.props.files.filter((dslFile) => {
return file.id !== dslFile.id; return file.id !== dslFile.id;
@ -125,10 +174,13 @@ class FilePickerWidget extends BaseWidget<
: []; : [];
this.props.updateWidgetMetaProperty("files", updatedFiles); this.props.updateWidgetMetaProperty("files", updatedFiles);
}); });
this.uppy.on("file-added", (file: any) => {
const dslFiles = this.props.files ? [...this.props.files] : [];
const reader = new FileReader();
this.state.uppy.on("files-added", (files: any[]) => {
const dslFiles = this.props.files ? [...this.props.files] : [];
const fileReaderPromises = files.map((file) => {
const reader = new FileReader();
return new Promise((resolve) => {
reader.readAsDataURL(file.data); reader.readAsDataURL(file.data);
reader.onloadend = () => { reader.onloadend = () => {
const base64data = reader.result; const base64data = reader.result;
@ -148,24 +200,24 @@ class FilePickerWidget extends BaseWidget<
text: text, text: text,
name: file.meta ? file.meta.name : undefined, name: file.meta ? file.meta.name : undefined,
}; };
dslFiles.push(newFile);
this.props.updateWidgetMetaProperty("files", dslFiles); resolve(newFile);
}; };
}; };
}; };
}); });
this.uppy.on("upload", () => { });
Promise.all(fileReaderPromises).then((files) => {
this.props.updateWidgetMetaProperty("files", dslFiles.concat(files));
});
});
this.state.uppy.on("upload", () => {
this.onFilesSelected(); this.onFilesSelected();
}); });
this.setState({ version: this.state.version + 1 });
}; };
static getTriggerPropertyMap(): TriggerPropertiesMap {
return {
onFilesSelected: true,
};
}
/** /**
* this function is called when user selects the files and it do two things: * this function is called when user selects the files and it do two things:
* 1. calls the action if any * 1. calls the action if any
@ -208,29 +260,30 @@ class FilePickerWidget extends BaseWidget<
prevProps.files.length > 0 && prevProps.files.length > 0 &&
this.props.files === undefined this.props.files === undefined
) { ) {
this.uppy.reset(); this.state.uppy.reset();
} else if ( } else if (
!shallowequal(prevProps.allowedFileTypes, this.props.allowedFileTypes) || !shallowequal(prevProps.allowedFileTypes, this.props.allowedFileTypes) ||
prevProps.maxNumFiles !== this.props.maxNumFiles || prevProps.maxNumFiles !== this.props.maxNumFiles ||
prevProps.maxFileSize !== this.props.maxFileSize prevProps.maxFileSize !== this.props.maxFileSize
) { ) {
this.refreshUppy(this.props); this.reinitializeUppy(this.props);
} }
} }
componentDidMount() { componentDidMount() {
super.componentDidMount(); super.componentDidMount();
this.refreshUppy(this.props);
this.initializeUppyEventListeners();
} }
componentWillUnmount() { componentWillUnmount() {
this.uppy.close(); this.state.uppy.close();
} }
getPageView() { getPageView() {
return ( return (
<FilePickerComponent <FilePickerComponent
uppy={this.uppy} uppy={this.state.uppy}
widgetId={this.props.widgetId} widgetId={this.props.widgetId}
key={this.props.widgetId} key={this.props.widgetId}
label={this.props.label} label={this.props.label}
@ -247,8 +300,8 @@ class FilePickerWidget extends BaseWidget<
} }
export interface FilePickerWidgetState extends WidgetState { export interface FilePickerWidgetState extends WidgetState {
version: number;
isLoading: boolean; isLoading: boolean;
uppy: any;
} }
export interface FilePickerWidgetProps extends WidgetProps, WithMeta { export interface FilePickerWidgetProps extends WidgetProps, WithMeta {

View File

@ -154,10 +154,11 @@ class InputWidget extends BaseWidget<InputWidgetProps, WidgetState> {
| React.KeyboardEvent<HTMLTextAreaElement> | React.KeyboardEvent<HTMLTextAreaElement>
| React.KeyboardEvent<HTMLInputElement>, | React.KeyboardEvent<HTMLInputElement>,
) => { ) => {
const { isValid, onSubmit } = this.props;
const isEnterKey = e.key === "Enter" || e.keyCode === 13; const isEnterKey = e.key === "Enter" || e.keyCode === 13;
if (isEnterKey && this.props.onSubmit) { if (isEnterKey && onSubmit && isValid) {
super.executeAction({ super.executeAction({
dynamicString: this.props.onSubmit, dynamicString: onSubmit,
event: { event: {
type: EventType.ON_SUBMIT, type: EventType.ON_SUBMIT,
callback: this.onSubmitSuccess, callback: this.onSubmitSuccess,

View File

@ -4501,39 +4501,40 @@
"@typescript-eslint/types" "4.6.0" "@typescript-eslint/types" "4.6.0"
eslint-visitor-keys "^2.0.0" eslint-visitor-keys "^2.0.0"
"@uppy/companion-client@^1.4.1", "@uppy/companion-client@^1.5.4": "@uppy/companion-client@^1.8.1":
version "1.5.4" version "1.8.1"
resolved "https://registry.yarnpkg.com/@uppy/companion-client/-/companion-client-1.5.4.tgz#11a9d2ee91a13789ca083f88a3ac7378b3fb20ed" resolved "https://registry.yarnpkg.com/@uppy/companion-client/-/companion-client-1.8.1.tgz#e8796ac5d2219663da1ef46367efd9cbcf629b17"
integrity sha512-AmrKamjHraqm/eV00ps8brw5ajI9NqjzBotTtPH9QaC+D3wIQ/EoWZNTGx4OlGYRahEoVr1K6hhlyUoHmEQc4w== integrity sha512-me07SraLbf+Q7z5i6IVTrKc6tiIe84ztqrifx8ftL/XKfOf2niYrS04PU4sppOCtkHiSPG/jP1TDFYd9HVL3aA==
dependencies: dependencies:
"@uppy/utils" "^3.2.3" "@uppy/utils" "^3.4.0"
namespace-emitter "^2.0.1" namespace-emitter "^2.0.1"
qs-stringify "^1.1.0"
"@uppy/core@^1.8.2": "@uppy/core@^1.16.0":
version "1.13.2" version "1.16.0"
resolved "https://registry.yarnpkg.com/@uppy/core/-/core-1.13.2.tgz#78c03019ca78ebaa2920c1fa4888ee76531d2e59" resolved "https://registry.yarnpkg.com/@uppy/core/-/core-1.16.0.tgz#a5058e4adb1c7e3d7b52d260eef48fe7283294d7"
integrity sha512-h0fbwlntm51lJ5i9k16csAsGNuWSzAZe69gLX/5DOISWqziRdF3g2EtSG17p/p/tK/MwX1dzyNU8JI6krDYIwA== integrity sha512-qzd/G23KVTLOVmfmszPj8qaTuKyt6ZfLNpp5UnjrZkdvs9b+tY/uKEjUI3Dw8ScSa7roaizQU064z3eik8G84Q==
dependencies: dependencies:
"@transloadit/prettier-bytes" "0.0.7" "@transloadit/prettier-bytes" "0.0.7"
"@uppy/store-default" "^1.2.4" "@uppy/store-default" "^1.2.5"
"@uppy/utils" "^3.2.3" "@uppy/utils" "^3.4.0"
cuid "^2.1.1" cuid "^2.1.1"
lodash.throttle "^4.1.1" lodash.throttle "^4.1.1"
mime-match "^1.0.2" mime-match "^1.0.2"
namespace-emitter "^2.0.1" namespace-emitter "^2.0.1"
preact "8.2.9" preact "8.2.9"
"@uppy/dashboard@^1.12.8", "@uppy/dashboard@^1.6.2": "@uppy/dashboard@^1.16.0":
version "1.12.8" version "1.16.0"
resolved "https://registry.yarnpkg.com/@uppy/dashboard/-/dashboard-1.12.8.tgz#1ec26020753fb58ac6182f5adad7d018e6157097" resolved "https://registry.yarnpkg.com/@uppy/dashboard/-/dashboard-1.16.0.tgz#5489dedae26cf4980894de3dd5147a5f62073343"
integrity sha512-ryz2x2i/jwVJCDGcdiQqD6Az92WPY6ft/r5iu2TTxOIzUFdou3T1rKF/6Dbh3qVCbzzFviPMLdTVfaSsEoQdNw== integrity sha512-Kc4OyIvR7b9rVmuJkDG1hWgCYNcE+x2u5a2tB73CAa6BzXFRUTizd3sx3OAVZELWFBJ6GR0gERcjjeXZItXBSw==
dependencies: dependencies:
"@transloadit/prettier-bytes" "0.0.7" "@transloadit/prettier-bytes" "0.0.7"
"@uppy/informer" "^1.5.11" "@uppy/informer" "^1.6.0"
"@uppy/provider-views" "^1.7.7" "@uppy/provider-views" "^1.11.0"
"@uppy/status-bar" "^1.7.6" "@uppy/status-bar" "^1.9.0"
"@uppy/thumbnail-generator" "^1.6.7" "@uppy/thumbnail-generator" "^1.7.5"
"@uppy/utils" "^3.2.3" "@uppy/utils" "^3.4.0"
classnames "^2.2.6" classnames "^2.2.6"
cuid "^2.1.1" cuid "^2.1.1"
is-shallow-equal "^1.0.1" is-shallow-equal "^1.0.1"
@ -4543,134 +4544,128 @@
preact "8.2.9" preact "8.2.9"
resize-observer-polyfill "^1.5.0" resize-observer-polyfill "^1.5.0"
"@uppy/drag-drop@^1.4.19": "@uppy/drag-drop@^1.4.24":
version "1.4.19" version "1.4.24"
resolved "https://registry.yarnpkg.com/@uppy/drag-drop/-/drag-drop-1.4.19.tgz#b8e2748c6384cddaf42b43d55c801288d59f5e60" resolved "https://registry.yarnpkg.com/@uppy/drag-drop/-/drag-drop-1.4.24.tgz#02ee9df001a1074f45ddaeb93fc84f7f7c05d82f"
integrity sha512-gY1zDOcAqw1tNNCIhjjJm63IujZyNpAUA6yMVWuSri9Sisy7D2GjJUuoRhUUBZlg37AJthFSku7NVERMBxqSTw== integrity sha512-uiAGu2GlLH/AE3B6H1ooyDGuRmVPcycpVXupzavlXMMTd4NDnove9DK24svvzTYYm83WgRIF3UDqkDn12wYn1w==
dependencies: dependencies:
"@uppy/utils" "^3.2.3" "@uppy/utils" "^3.4.0"
preact "8.2.9" preact "8.2.9"
"@uppy/file-input@^1.3.1": "@uppy/file-input@^1.4.22":
version "1.4.17" version "1.4.22"
resolved "https://registry.yarnpkg.com/@uppy/file-input/-/file-input-1.4.17.tgz#60cd804b57be35dcfb4a6edfd1a7b6c15f1095ed" resolved "https://registry.yarnpkg.com/@uppy/file-input/-/file-input-1.4.22.tgz#e8642542ceedce71a11494f08d41a5144605f814"
integrity sha512-Z29AYWLwd2pOCEeEFdrhkEvzKOCYRm60H7j5heZ6HIovU0bdz7znpY5s9pVg6R4JDwtUux05+zMexFKKMxuVmA== integrity sha512-GvH3B3fQqJdeKUxRIK1RMAd3BizlCigXjDXO7ndEfXlc/iK7YQi7P8UUZadfdgOK7bne2JPzKydvkSP1EbVAwQ==
dependencies: dependencies:
"@uppy/utils" "^3.2.3" "@uppy/utils" "^3.4.0"
preact "8.2.9" preact "8.2.9"
"@uppy/google-drive@^1.3.2": "@uppy/google-drive@^1.5.22":
version "1.5.16" version "1.5.22"
resolved "https://registry.yarnpkg.com/@uppy/google-drive/-/google-drive-1.5.16.tgz#12f37a0c8320980edda901b335d1fd5bb8a84ecb" resolved "https://registry.yarnpkg.com/@uppy/google-drive/-/google-drive-1.5.22.tgz#d185e8da86ba2a42d1198544401a60c9092e4025"
integrity sha512-es6rKyY3iUnpX6i5LNt/okzz5GIQKdnE8GS/GzbIlywsenRFw9hpRo5oqIDFqkKCQ4t0PKOoB5kb1d18VfJxuw== integrity sha512-yzlT4J3FDh2By1ogjmFWRFxuVybLCVuZYg55vTUKnIrQWCbd8clena2YZuuJofJOAYjd4HlCaaRKZDFKekS89w==
dependencies: dependencies:
"@uppy/companion-client" "^1.5.4" "@uppy/companion-client" "^1.8.1"
"@uppy/provider-views" "^1.7.7" "@uppy/provider-views" "^1.11.0"
"@uppy/utils" "^3.2.3" "@uppy/utils" "^3.4.0"
preact "8.2.9" preact "8.2.9"
"@uppy/informer@^1.5.11": "@uppy/informer@^1.6.0":
version "1.5.11" version "1.6.0"
resolved "https://registry.yarnpkg.com/@uppy/informer/-/informer-1.5.11.tgz#ebd5b8960838e47ec2088a2ce0e6b914b664a283" resolved "https://registry.yarnpkg.com/@uppy/informer/-/informer-1.6.0.tgz#65e90c1f258c8fc54c71dcbf4a8d0c485400fc8e"
integrity sha512-sZIWKvV3UO5Epk6V2e5girlzgsqukDAKnLkZh6nOqjSGRkMudGUrEZwuSOB496lBVqo38BTavI/zf+eLvrdBlA== integrity sha512-599eQol5XlsVk+rIRctMKGbUHldyeDOHxzSHgvzsTOSBLL3vj6bN+OblBX1kF4DlHgEHhanLrjo+UfjvpNvy+A==
dependencies: dependencies:
"@uppy/utils" "^3.2.3" "@uppy/utils" "^3.4.0"
preact "8.2.9" preact "8.2.9"
"@uppy/onedrive@^0.1.1": "@uppy/onedrive@^1.1.22":
version "0.1.4" version "1.1.22"
resolved "https://registry.yarnpkg.com/@uppy/onedrive/-/onedrive-0.1.4.tgz#18e4459019070ec7f2430629cdeb16fddeb343c4" resolved "https://registry.yarnpkg.com/@uppy/onedrive/-/onedrive-1.1.22.tgz#16e983f299cd1afc12c5c8b7e6551417ebff20f8"
integrity sha512-3qOhqjh7kv7I7S7t8qSFYNQf3Jr6g+DrNgsf/jcht/mp7zm1F+UlJR/nmxZC5BEtUbA91GYPXwcowxdeX+oWvQ== integrity sha512-nCqj+l6+YsYASt/bHIzuvSDuFzweESD/gzBi+/1XE6CaQytzqqDbEfxhxkTcaoyj6iu+GGsKHxTJ8h8YSPEr4g==
dependencies: dependencies:
"@uppy/companion-client" "^1.4.1" "@uppy/companion-client" "^1.8.1"
"@uppy/provider-views" "^1.5.2" "@uppy/provider-views" "^1.11.0"
"@uppy/utils" "^2.1.2" "@uppy/utils" "^3.4.0"
preact "8.2.9" preact "8.2.9"
"@uppy/progress-bar@^1.3.19": "@uppy/progress-bar@^1.3.24":
version "1.3.19" version "1.3.24"
resolved "https://registry.yarnpkg.com/@uppy/progress-bar/-/progress-bar-1.3.19.tgz#6abf87a2c05f1d91c2d1fb2955ce0d5ca2199408" resolved "https://registry.yarnpkg.com/@uppy/progress-bar/-/progress-bar-1.3.24.tgz#a4fa3ca88931b6333a1583ee9a2670f591206dca"
integrity sha512-o0sCpN7J/CcW17IbOoQ/0kbUGHoVPgwfHgyrO77I0J5aMn+qFqEs094xvVnU7gp92NMBsllvT1Vyr8ZNtPq+Dg== integrity sha512-FLfGGaswnr+Glsk+1WIxd5tT0DfxxZJiLhjmBHVZZGHy2qpBpXbPTeFxcPdlOUPokYKeBGqMKyAYz/LBVpjXAQ==
dependencies: dependencies:
"@uppy/utils" "^3.2.3" "@uppy/utils" "^3.4.0"
preact "8.2.9" preact "8.2.9"
"@uppy/provider-views@^1.5.2", "@uppy/provider-views@^1.7.7": "@uppy/provider-views@^1.11.0":
version "1.7.7" version "1.11.0"
resolved "https://registry.yarnpkg.com/@uppy/provider-views/-/provider-views-1.7.7.tgz#fe85617d86b72ce376f9efa0c369ab7b2eb3af2e" resolved "https://registry.yarnpkg.com/@uppy/provider-views/-/provider-views-1.11.0.tgz#efd6db4d77a539ad092f4b178a3480701bbc9f7e"
integrity sha512-PLIAeUJKNOU+Yfxr4iX6oFoHS15SZZhnDj+n2JWyg3l6iNOG7gvLq85OMARFOUA0DlP5srRq4NlR/nlMWo+0xw== integrity sha512-aFGGzByDAR3w8QgMRiuE2XEkDsr51bN6kPiE2p6zplwKZ5FGPVx+j5/+pUNa0Qsb8w0dYJBqlhCCnFcRBDkgjw==
dependencies: dependencies:
"@uppy/utils" "^3.2.3" "@uppy/utils" "^3.4.0"
classnames "^2.2.6" classnames "^2.2.6"
preact "8.2.9" preact "8.2.9"
"@uppy/react@^1.4.5": "@uppy/react@^1.11.2":
version "1.10.8" version "1.11.2"
resolved "https://registry.yarnpkg.com/@uppy/react/-/react-1.10.8.tgz#260bc37693c8a1aa6f719cada22b67414913466d" resolved "https://registry.yarnpkg.com/@uppy/react/-/react-1.11.2.tgz#9d1a1a4c0710e456654cdb4dc17735501036a54a"
integrity sha512-FO6PThrYZaEGZd7G3YooHHKsxhZqF6/Euy+jI8CLhROf78Kg0Gr3dURhwaH902NgPrA5Yfa6xRSC1d4etwMktg== integrity sha512-Pz4ghEn+9I8XiAkqJGKMLXbVvcnvTAID/o7aqXq7yMQGi61sy3SUgeagX0yaCuKOZ6l4IXItPsZnBMe/Km3MJQ==
dependencies: dependencies:
"@uppy/dashboard" "^1.12.8" "@uppy/dashboard" "^1.16.0"
"@uppy/drag-drop" "^1.4.19" "@uppy/drag-drop" "^1.4.24"
"@uppy/progress-bar" "^1.3.19" "@uppy/file-input" "^1.4.22"
"@uppy/status-bar" "^1.7.6" "@uppy/progress-bar" "^1.3.24"
"@uppy/utils" "^3.2.3" "@uppy/status-bar" "^1.9.0"
"@uppy/utils" "^3.4.0"
prop-types "^15.6.1" prop-types "^15.6.1"
"@uppy/status-bar@^1.7.6": "@uppy/status-bar@^1.9.0":
version "1.7.6" version "1.9.0"
resolved "https://registry.yarnpkg.com/@uppy/status-bar/-/status-bar-1.7.6.tgz#f25b50466398b1952c8b60b16d2c491ea342992f" resolved "https://registry.yarnpkg.com/@uppy/status-bar/-/status-bar-1.9.0.tgz#1db2859fd10b1dc7eb6045dbeb56bb3b4cbcb518"
integrity sha512-nlZztGjDNEmWZQfWfV/DajeNjtdhqeGO1mRQVpmjWvrCABFaTvWN9jb1t7VEZjuaegSujZot00RP7uU4au/Q+w== integrity sha512-5Hmx3iRRDfP04xmopDXAzEYz7GM4SNAXs2ayRPYSQZ5OC2Bvqqb2IYVLj09PHCRsH695VBGz9+3kIEvn/OFqbQ==
dependencies: dependencies:
"@transloadit/prettier-bytes" "0.0.7" "@transloadit/prettier-bytes" "0.0.7"
"@uppy/utils" "^3.2.3" "@uppy/utils" "^3.4.0"
classnames "^2.2.6" classnames "^2.2.6"
lodash.throttle "^4.1.1" lodash.throttle "^4.1.1"
preact "8.2.9" preact "8.2.9"
"@uppy/store-default@^1.2.4": "@uppy/store-default@^1.2.5":
version "1.2.4" version "1.2.5"
resolved "https://registry.yarnpkg.com/@uppy/store-default/-/store-default-1.2.4.tgz#480f50cbf23f36158557a42b7699da0cf629d368" resolved "https://registry.yarnpkg.com/@uppy/store-default/-/store-default-1.2.5.tgz#52936331d6ac4911197d256d13974d5bae93b3a6"
integrity sha512-d4YTq6SeH792+vG9OXGCfmCIoo4RteLJKU2mZCzMYNjCZ1yNfU/FVw1r7heTc7f2wNls56ABor2xSybk/X+wFA== integrity sha512-jnf0U8cfb8Bhgt6yh86YRJO9EEnCyG9BgXZ8dPWWLybgC9Expw3Ah/s3T21tcdChgv4zzdhSACd0JKxCQowyYg==
"@uppy/thumbnail-generator@^1.6.7": "@uppy/thumbnail-generator@^1.7.5":
version "1.6.7" version "1.7.5"
resolved "https://registry.yarnpkg.com/@uppy/thumbnail-generator/-/thumbnail-generator-1.6.7.tgz#65375106dae2cfd1f4cddb073e9d8ff278ca94e5" resolved "https://registry.yarnpkg.com/@uppy/thumbnail-generator/-/thumbnail-generator-1.7.5.tgz#ae2fa68c684463b41fa7b4a878c4e29974680c6f"
integrity sha512-8FRO042ulfK3qRDE1tEETxw/iDIrLzlHxqhrHG45kxZ2QfqbByg5bJAgvlwg7Zyd4202rkq75J8KeFowS/Ntjg== integrity sha512-YUbFLpkfOudbdJMQPqWmVmq/qHh40+0ZHkJHrStpZmTq/OSjHLxyXWSZ1RDgJnAcsh3lUhR4yuembNVmtSrtjg==
dependencies: dependencies:
"@uppy/utils" "^3.2.3" "@uppy/utils" "^3.4.0"
exifr "^5.0.2" exifr "^6.0.0"
math-log2 "^1.0.1" math-log2 "^1.0.1"
"@uppy/url@^1.3.2": "@uppy/url@^1.5.16":
version "1.5.11" version "1.5.16"
resolved "https://registry.yarnpkg.com/@uppy/url/-/url-1.5.11.tgz#a664b6a386c913ce33af51c8f497c3e036a0389b" resolved "https://registry.yarnpkg.com/@uppy/url/-/url-1.5.16.tgz#636830aa01b13a3726abfe87c2e66006412ba867"
integrity sha512-EYAK/9m++O1HApKCty3Vd6T5qcdCMC3GL0IfN1kO0cE6J7fmhL7nEnud5eWqq6752ydNmhvR9lFYrPv/8AHljQ== integrity sha512-IEsfhVwTUoyvgZcr/uHEwxBMX8SDQQBXFkd3eW0T+jSNVzyP7jQo7bqVyUEjrALOp9xH8X21aXTPvnWc4k1iww==
dependencies: dependencies:
"@uppy/companion-client" "^1.5.4" "@uppy/companion-client" "^1.8.1"
"@uppy/utils" "^3.2.3" "@uppy/utils" "^3.4.0"
preact "8.2.9" preact "8.2.9"
"@uppy/utils@^2.1.2": "@uppy/utils@^3.4.0":
version "2.4.4" version "3.4.0"
resolved "https://registry.yarnpkg.com/@uppy/utils/-/utils-2.4.4.tgz#29520cdfaf926af58828a9c141cb7b0fa43f8bc4" resolved "https://registry.yarnpkg.com/@uppy/utils/-/utils-3.4.0.tgz#4afcf8c26dec13ee54676bd1f7958fc1063b68df"
integrity sha512-7A0uwK5Rf8XcKqlpNUZ5L5LmkHT5c0/UWjDJGwmzeCxp2lECgzsMC+4vgA6kT4sFzPFbLtUtxHi7ecFwow3NQQ== integrity sha512-XcT4UgUm1NhWAeQjMrr5LCE7Uookg12FTwQw/QwXBuGABoVilfk56i6h8ecf1bVX0D74zdpSnUiB1h+8a7ollw==
dependencies:
lodash.throttle "^4.1.1"
"@uppy/utils@^3.2.3":
version "3.2.3"
resolved "https://registry.yarnpkg.com/@uppy/utils/-/utils-3.2.3.tgz#91cb4a133bd610b861fe3069be1f0e85036dcc20"
integrity sha512-ue14v4yKK6bYBPGjt31wvLnYMThKCVOsy3R7y++eFuPsxfidIGmOGiv+Qmv0895kMJtt7gk0MddXjbCdBX4Ksg==
dependencies: dependencies:
abortcontroller-polyfill "^1.4.0" abortcontroller-polyfill "^1.4.0"
lodash.throttle "^4.1.1" lodash.throttle "^4.1.1"
"@uppy/webcam@^1.3.1": "@uppy/webcam@^1.8.4":
version "1.7.0" version "1.8.4"
resolved "https://registry.yarnpkg.com/@uppy/webcam/-/webcam-1.7.0.tgz#e770733b0679454ed1aa78075d55f49a95b1cc6c" resolved "https://registry.yarnpkg.com/@uppy/webcam/-/webcam-1.8.4.tgz#7a4f7f9dc4def955e701560a59c22fdb08e5efe4"
integrity sha512-j5OAAAHWAPDLThdnHS/a6K9O28FQbsi7ZOX7FJzA+u14iOBEmAbuWy99ziETITNdE6vIjiRrak1ixeDSdPFRQQ== integrity sha512-WhJrOgH0N2V5QhQZc10N1kIunxOF3/SQ/KTssTEudn7tkVNhkTLRldAlmcA4vZ0HB3Zxg9HaJPy4twBvUgFUbw==
dependencies: dependencies:
"@uppy/utils" "^3.2.3" "@uppy/utils" "^3.4.0"
preact "8.2.9" preact "8.2.9"
"@vue/compiler-core@3.0.0": "@vue/compiler-core@3.0.0":
@ -4912,9 +4907,9 @@ abbrev@1:
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
abortcontroller-polyfill@^1.4.0: abortcontroller-polyfill@^1.4.0:
version "1.5.0" version "1.7.1"
resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.5.0.tgz#2c562f530869abbcf88d949a2b60d1d402e87a7c" resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.1.tgz#27084bac87d78a7224c8ee78135d05df430c2d2f"
integrity sha512-O6Xk757Jb4o0LMzMOMdWvxpHWrQzruYBaUruFaIOfAQRnWFxfdXYobw12jrVHGtoXk6WiiyYzc0QWN9aL62HQA== integrity sha512-yml9NiDEH4M4p0G4AcPkg8AAa4mF3nfYF28VQxaokpO67j9H7gWgmsVWJ/f1Rn+PzsnDYvzJzWIQzCqDKRvWlA==
accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7:
version "1.3.7" version "1.3.7"
@ -8905,10 +8900,10 @@ executable@^4.1.1:
dependencies: dependencies:
pify "^2.2.0" pify "^2.2.0"
exifr@^5.0.2: exifr@^6.0.0:
version "5.0.6" version "6.0.0"
resolved "https://registry.yarnpkg.com/exifr/-/exifr-5.0.6.tgz#7c8e5aad1083fa4ec172103dca24215203001209" resolved "https://registry.yarnpkg.com/exifr/-/exifr-6.0.0.tgz#e82af10e158852a1c7e19aea45bceb4cdd486727"
integrity sha512-iDB4IhKoKVF+uDDrHRlyNxWqGaTxYluVWqvBWVG54HkQZe8qkFYl9eQrjEP3d8Q4UMBZ9rWu3Pa+mfC+o4CZuw== integrity sha512-a8n3SVIyuI5NP5VJCb/rJHsqXnofgYL1ZXcJdKBXOmCNIrj+pSExaBFHcbdEF5xp5GQrK4kpOabLJ+wBfUGYuA==
exit-hook@^1.0.0: exit-hook@^1.0.0:
version "1.1.1" version "1.1.1"
@ -15374,6 +15369,11 @@ q@^1.1.2:
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
qs-stringify@^1.1.0:
version "1.2.1"
resolved "https://registry.yarnpkg.com/qs-stringify/-/qs-stringify-1.2.1.tgz#9b39ef6b816bd83309628fc9dad435fc0eccc28b"
integrity sha512-2N5xGLGZUxpgAYq1fD1LmBSCbxQVsXYt5JU0nU3FuPWO8PlCnKNFQwXkZgyB6mrTdg7IbexX4wxIR403dJw9pw==
qs@6.7.0: qs@6.7.0:
version "6.7.0" version "6.7.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc"

View File

@ -21,6 +21,8 @@ public enum AppsmithPluginError {
AppsmithErrorAction.DEFAULT), AppsmithErrorAction.DEFAULT),
PLUGIN_JSON_PARSE_ERROR(500, 5006, "Plugin failed to parse JSON \"{0}\" with error: {1}", PLUGIN_JSON_PARSE_ERROR(500, 5006, "Plugin failed to parse JSON \"{0}\" with error: {1}",
AppsmithErrorAction.DEFAULT), AppsmithErrorAction.DEFAULT),
PLUGIN_DATASOURCE_TEST_GENERIC_ERROR(500, 5007, "Plugin failed to test with the given configuration. Please reach out to Appsmith customer support to report this",
AppsmithErrorAction.LOG_EXTERNALLY),
; ;
private final Integer httpErrorCode; private final Integer httpErrorCode;

View File

@ -8,6 +8,7 @@ import java.util.List;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor
public class DatasourceStructure { public class DatasourceStructure {
List<Table> tables; List<Table> tables;

View File

@ -1,11 +1,14 @@
package com.appsmith.external.models; package com.appsmith.external.models;
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.ToString; import lombok.ToString;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import java.util.Arrays;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors;
@Getter @Getter
@Setter @Setter
@ -21,6 +24,15 @@ public class DatasourceTestResult {
* @param invalids String messages that explain why the test failed. * @param invalids String messages that explain why the test failed.
*/ */
public DatasourceTestResult(String... invalids) { public DatasourceTestResult(String... invalids) {
if (invalids == null) {
invalids = new String[]{AppsmithPluginError.PLUGIN_DATASOURCE_TEST_GENERIC_ERROR.getMessage()};
} else {
invalids = Arrays
.stream(invalids)
.map(x -> x == null ? AppsmithPluginError.PLUGIN_DATASOURCE_TEST_GENERIC_ERROR.getMessage() : x)
.toArray(String[]::new);
}
this.invalids = Set.of(invalids); this.invalids = Set.of(invalids);
} }

View File

@ -0,0 +1,21 @@
package com.appsmith.external.models;
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
import org.junit.Test;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
public class DatasourceTestResultTest {
@Test
public void testNewDatasourceTestResult_NullInvalidArray() {
DatasourceTestResult nullInvalidsResult = new DatasourceTestResult((String) null);
assertNotNull(nullInvalidsResult);
assertTrue(nullInvalidsResult.getInvalids().contains(AppsmithPluginError.PLUGIN_DATASOURCE_TEST_GENERIC_ERROR.getMessage()));
nullInvalidsResult = new DatasourceTestResult(new String[]{null});
assertNotNull(nullInvalidsResult);
assertTrue(nullInvalidsResult.getInvalids().contains(AppsmithPluginError.PLUGIN_DATASOURCE_TEST_GENERIC_ERROR.getMessage()));
}
}

View File

@ -1,13 +1,14 @@
package com.external.plugins; package com.external.plugins;
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
import com.appsmith.external.models.ActionConfiguration; import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.ActionExecutionResult; import com.appsmith.external.models.ActionExecutionResult;
import com.appsmith.external.models.DBAuth; import com.appsmith.external.models.DBAuth;
import com.appsmith.external.models.DatasourceConfiguration; import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.DatasourceStructure;
import com.appsmith.external.models.DatasourceTestResult; import com.appsmith.external.models.DatasourceTestResult;
import com.appsmith.external.models.Endpoint; import com.appsmith.external.models.Endpoint;
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginError;
import com.appsmith.external.exceptions.pluginExceptions.AppsmithPluginException;
import com.appsmith.external.plugins.BasePlugin; import com.appsmith.external.plugins.BasePlugin;
import com.appsmith.external.plugins.PluginExecutor; import com.appsmith.external.plugins.PluginExecutor;
import lombok.NonNull; import lombok.NonNull;
@ -29,6 +30,7 @@ import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient; import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.DynamoDbClientBuilder; import software.amazon.awssdk.services.dynamodb.DynamoDbClientBuilder;
import software.amazon.awssdk.services.dynamodb.model.DynamoDbResponse; import software.amazon.awssdk.services.dynamodb.model.DynamoDbResponse;
import software.amazon.awssdk.services.dynamodb.model.ListTablesResponse;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@ -38,6 +40,7 @@ import java.net.URI;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -73,15 +76,15 @@ public class DynamoPlugin extends BasePlugin {
DatasourceConfiguration datasourceConfiguration, DatasourceConfiguration datasourceConfiguration,
ActionConfiguration actionConfiguration) { ActionConfiguration actionConfiguration) {
return (Mono<ActionExecutionResult>) Mono.fromCallable(() -> { return Mono.fromCallable(() -> {
ActionExecutionResult result = new ActionExecutionResult(); ActionExecutionResult result = new ActionExecutionResult();
final String action = actionConfiguration.getPath(); final String action = actionConfiguration.getPath();
if (StringUtils.isEmpty(action)) { if (StringUtils.isEmpty(action)) {
return Mono.error(new AppsmithPluginException( throw new AppsmithPluginException(
AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR,
"Missing action name (like `ListTables`, `GetItem` etc.)." "Missing action name (like `ListTables`, `GetItem` etc.)."
)); );
} }
final String body = actionConfiguration.getBody(); final String body = actionConfiguration.getBody();
@ -93,17 +96,17 @@ public class DynamoPlugin extends BasePlugin {
} catch (IOException e) { } catch (IOException e) {
final String message = "Error parsing the JSON body: " + e.getMessage(); final String message = "Error parsing the JSON body: " + e.getMessage();
log.warn(message, e); log.warn(message, e);
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, message)); throw new AppsmithPluginException(AppsmithPluginError.PLUGIN_EXECUTE_ARGUMENT_ERROR, message);
} }
final Class<?> requestClass; final Class<?> requestClass;
try { try {
requestClass = Class.forName("software.amazon.awssdk.services.dynamodb.model." + action + "Request"); requestClass = Class.forName("software.amazon.awssdk.services.dynamodb.model." + action + "Request");
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
return Mono.error(new AppsmithPluginException( throw new AppsmithPluginException(
AppsmithPluginError.PLUGIN_ERROR, AppsmithPluginError.PLUGIN_ERROR,
"Unknown action: `" + action + "`. Note that action names are case-sensitive." "Unknown action: `" + action + "`. Note that action names are case-sensitive."
)); );
} }
try { try {
@ -117,21 +120,20 @@ public class DynamoPlugin extends BasePlugin {
} catch (AppsmithPluginException | InvocationTargetException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException e) { } catch (AppsmithPluginException | InvocationTargetException | IllegalAccessException | NoSuchMethodException | ClassNotFoundException e) {
final String message = "Error executing the DynamoDB Action: " + (e.getCause() == null ? e : e.getCause()).getMessage(); final String message = "Error executing the DynamoDB Action: " + (e.getCause() == null ? e : e.getCause()).getMessage();
log.warn(message, e); log.warn(message, e);
return Mono.error(new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, message)); throw new AppsmithPluginException(AppsmithPluginError.PLUGIN_ERROR, message);
} }
result.setIsExecutionSuccess(true); result.setIsExecutionSuccess(true);
System.out.println(Thread.currentThread().getName() + ": In the DynamoPlugin, got action execution result"); System.out.println(Thread.currentThread().getName() + ": In the DynamoPlugin, got action execution result");
return Mono.just(result); return result;
}) })
.flatMap(obj -> obj)
.subscribeOn(scheduler); .subscribeOn(scheduler);
} }
@Override @Override
public Mono<DynamoDbClient> datasourceCreate(DatasourceConfiguration datasourceConfiguration) { public Mono<DynamoDbClient> datasourceCreate(DatasourceConfiguration datasourceConfiguration) {
return (Mono<DynamoDbClient>) Mono.fromCallable(() -> { return Mono.fromCallable(() -> {
final DynamoDbClientBuilder builder = DynamoDbClient.builder(); final DynamoDbClientBuilder builder = DynamoDbClient.builder();
if (!CollectionUtils.isEmpty(datasourceConfiguration.getEndpoints())) { if (!CollectionUtils.isEmpty(datasourceConfiguration.getEndpoints())) {
@ -141,10 +143,10 @@ public class DynamoPlugin extends BasePlugin {
final DBAuth authentication = (DBAuth) datasourceConfiguration.getAuthentication(); final DBAuth authentication = (DBAuth) datasourceConfiguration.getAuthentication();
if (authentication == null || StringUtils.isEmpty(authentication.getDatabaseName())) { if (authentication == null || StringUtils.isEmpty(authentication.getDatabaseName())) {
return Mono.error(new AppsmithPluginException( throw new AppsmithPluginException(
AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR, AppsmithPluginError.PLUGIN_DATASOURCE_ARGUMENT_ERROR,
"Missing region in datasource." "Missing region in datasource."
)); );
} }
builder.region(Region.of(authentication.getDatabaseName())); builder.region(Region.of(authentication.getDatabaseName()));
@ -153,9 +155,8 @@ public class DynamoPlugin extends BasePlugin {
AwsBasicCredentials.create(authentication.getUsername(), authentication.getPassword()) AwsBasicCredentials.create(authentication.getUsername(), authentication.getPassword())
)); ));
return Mono.justOrEmpty(builder.build()); return builder.build();
}) })
.flatMap(obj -> obj)
.subscribeOn(scheduler); .subscribeOn(scheduler);
} }
@ -204,6 +205,28 @@ public class DynamoPlugin extends BasePlugin {
) )
.subscribeOn(scheduler); .subscribeOn(scheduler);
} }
@Override
public Mono<DatasourceStructure> getStructure(DynamoDbClient ddb, DatasourceConfiguration datasourceConfiguration) {
return Mono.fromCallable(() -> {
final ListTablesResponse listTablesResponse = ddb.listTables();
List<DatasourceStructure.Table> tables = new ArrayList<>();
for (final String tableName : listTablesResponse.tableNames()) {
tables.add(new DatasourceStructure.Table(
DatasourceStructure.TableType.TABLE,
tableName,
Collections.emptyList(),
Collections.emptyList(),
Collections.emptyList()
));
}
return new DatasourceStructure(tables);
}).subscribeOn(scheduler);
}
} }
private static String toLowerCamelCase(String action) { private static String toLowerCamelCase(String action) {
@ -259,7 +282,8 @@ public class DynamoPlugin extends BasePlugin {
|| value instanceof Integer || value instanceof Integer
|| value instanceof Float || value instanceof Float
|| value instanceof Double) { || value instanceof Double) {
// These data types have a setter method that takes a the value as is. Nothing fancy here. // This will *never* be successful. DynamoDB takes in numeric values as strings, which means the
// control should never flow here for numeric types.
builderType.getMethod(setterName, value.getClass()).invoke(builder, value); builderType.getMethod(setterName, value.getClass()).invoke(builder, value);
} else if (value instanceof Map) { } else if (value instanceof Map) {
@ -337,44 +361,41 @@ public class DynamoPlugin extends BasePlugin {
} }
} }
private static Map<String, Object> sdkToPlain(SdkPojo response) { private static Object sdkToPlain(Object valueObj) {
if (valueObj instanceof SdkPojo) {
final SdkPojo response = (SdkPojo) valueObj;
final Map<String, Object> plain = new HashMap<>(); final Map<String, Object> plain = new HashMap<>();
for (final SdkField<?> field : response.sdkFields()) { for (final SdkField<?> field : response.sdkFields()) {
Object value = field.getValueOrDefault(response); Object value = field.getValueOrDefault(response);
plain.put(field.memberName(), sdkToPlain(value));
if (value instanceof SdkPojo) {
value = sdkToPlain((SdkPojo) value);
} else if (value instanceof Map) {
final Map<String, Object> valueAsMap = (Map) value;
final Map<String, Object> plainMap = new HashMap<>();
for (final Map.Entry<String, Object> entry : valueAsMap.entrySet()) {
final var key = entry.getKey();
Object innerValue = entry.getValue();
if (innerValue instanceof SdkPojo) {
innerValue = sdkToPlain((SdkPojo) innerValue);
}
plainMap.put(key, innerValue);
}
value = plainMap;
} else if (value instanceof List) {
final List<Object> valueAsList = (List) value;
final List<Object> plainList = new ArrayList<>();
for (Object item : valueAsList) {
if (item instanceof SdkPojo) {
item = sdkToPlain((SdkPojo) item);
}
plainList.add(item);
}
value = plainList;
}
plain.put(field.memberName(), value);
} }
return plain; return plain;
} else if (valueObj instanceof Map) {
final Map<?, ?> valueAsMap = (Map<?, ?>) valueObj;
final Map<Object, Object> plainMap = new HashMap<>();
for (final Map.Entry<?, ?> entry : valueAsMap.entrySet()) {
plainMap.put(entry.getKey(), sdkToPlain(entry.getValue()));
}
return plainMap;
} else if (valueObj instanceof Collection) {
final List<?> valueAsList = (List<?>) valueObj;
final List<Object> plainList = new ArrayList<>();
for (Object item : valueAsList) {
plainList.add(sdkToPlain(item));
}
return plainList;
}
return valueObj;
} }
private static boolean isUpperCase(String s) { private static boolean isUpperCase(String s) {

View File

@ -4,6 +4,7 @@ import com.appsmith.external.models.ActionConfiguration;
import com.appsmith.external.models.ActionExecutionResult; import com.appsmith.external.models.ActionExecutionResult;
import com.appsmith.external.models.DBAuth; import com.appsmith.external.models.DBAuth;
import com.appsmith.external.models.DatasourceConfiguration; import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.DatasourceStructure;
import com.appsmith.external.models.Endpoint; import com.appsmith.external.models.Endpoint;
import lombok.extern.log4j.Log4j; import lombok.extern.log4j.Log4j;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@ -29,6 +30,7 @@ import java.net.URI;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -91,8 +93,6 @@ public class DynamoPluginTest {
)) ))
.build()); .build());
System.out.println(ddb.listTables());
Endpoint endpoint = new Endpoint(); Endpoint endpoint = new Endpoint();
endpoint.setHost(host); endpoint.setHost(host);
endpoint.setPort(port.longValue()); endpoint.setPort(port.longValue());
@ -221,4 +221,41 @@ public class DynamoPluginTest {
.verifyComplete(); .verifyComplete();
} }
@Test
public void testScan() {
final String body = "{\n" +
" \"TableName\": \"cities\"\n" +
"}\n";
StepVerifier.create(execute("Scan", body))
.assertNext(result -> {
assertNotNull(result);
assertTrue(result.getIsExecutionSuccess());
assertNotNull(result.getBody());
final List<Object> items = (List<Object>) ((Map<String, Object>) result.getBody()).get("Items");
assertEquals(2, items.size());
})
.verifyComplete();
}
@Test
public void testStructure() {
final Mono<DatasourceStructure> structureMono = pluginExecutor
.datasourceCreate(dsConfig)
.flatMap(conn -> pluginExecutor.getStructure(conn, dsConfig));
StepVerifier.create(structureMono)
.assertNext(structure -> {
assertNotNull(structure);
assertNotNull(structure.getTables());
assertEquals(
List.of("cities"),
structure.getTables().stream()
.map(DatasourceStructure.Table::getName)
.collect(Collectors.toList())
);
})
.verifyComplete();
}
} }

View File

@ -188,7 +188,12 @@
{ {
"label": "Body", "label": "Body",
"configProperty": "actionConfiguration.body", "configProperty": "actionConfiguration.body",
"controlType": "QUERY_DYNAMIC_TEXT" "controlType": "QUERY_DYNAMIC_TEXT",
"hidden": {
"path": "actionConfiguration.pluginSpecifiedTemplates[0].value",
"comparison": "IN",
"value": ["GET_DOCUMENT", "GET_COLLECTION", "DELETE_DOCUMENT"]
}
} }
] ]
} }

View File

@ -347,13 +347,15 @@ public class MySqlPlugin extends BasePlugin {
@Override @Override
public Mono<DatasourceTestResult> testDatasource(DatasourceConfiguration datasourceConfiguration) { public Mono<DatasourceTestResult> testDatasource(DatasourceConfiguration datasourceConfiguration) {
return datasourceCreate(datasourceConfiguration) return datasourceCreate(datasourceConfiguration)
.flatMap(connection -> { .flatMap(connection -> Mono.from(connection.close()))
return Mono.from(connection.close());
})
.then(Mono.just(new DatasourceTestResult())) .then(Mono.just(new DatasourceTestResult()))
.onErrorResume(error -> { .onErrorResume(error -> {
log.error("Error when testing MySQL datasource.", error); // We always expect to have an error object, but the error object may not be well formed
return Mono.just(new DatasourceTestResult(error.getMessage())); final String errorMessage = error.getMessage() == null
? AppsmithPluginError.PLUGIN_DATASOURCE_TEST_GENERIC_ERROR.getMessage()
: error.getMessage();
System.out.println("Error when testing MySQL datasource. " + errorMessage);
return Mono.just(new DatasourceTestResult(errorMessage));
}) })
.subscribeOn(scheduler); .subscribeOn(scheduler);

View File

@ -2,11 +2,11 @@ package com.appsmith.server.controllers;
import com.appsmith.external.models.ActionExecutionResult; import com.appsmith.external.models.ActionExecutionResult;
import com.appsmith.server.constants.Url; import com.appsmith.server.constants.Url;
import com.appsmith.server.domains.Layout;
import com.appsmith.server.dtos.ActionDTO; import com.appsmith.server.dtos.ActionDTO;
import com.appsmith.server.dtos.ActionMoveDTO; import com.appsmith.server.dtos.ActionMoveDTO;
import com.appsmith.server.dtos.ActionViewDTO; import com.appsmith.server.dtos.ActionViewDTO;
import com.appsmith.server.dtos.ExecuteActionDTO; import com.appsmith.server.dtos.ExecuteActionDTO;
import com.appsmith.server.dtos.LayoutDTO;
import com.appsmith.server.dtos.RefactorNameDTO; import com.appsmith.server.dtos.RefactorNameDTO;
import com.appsmith.server.dtos.ResponseDTO; import com.appsmith.server.dtos.ResponseDTO;
import com.appsmith.server.services.ActionCollectionService; import com.appsmith.server.services.ActionCollectionService;
@ -82,7 +82,7 @@ public class ActionController {
} }
@PutMapping("/refactor") @PutMapping("/refactor")
public Mono<ResponseDTO<Layout>> refactorActionName(@RequestBody RefactorNameDTO refactorNameDTO) { public Mono<ResponseDTO<LayoutDTO>> refactorActionName(@RequestBody RefactorNameDTO refactorNameDTO) {
return layoutActionService.refactorActionName(refactorNameDTO) return layoutActionService.refactorActionName(refactorNameDTO)
.map(created -> new ResponseDTO<>(HttpStatus.OK.value(), created, null)); .map(created -> new ResponseDTO<>(HttpStatus.OK.value(), created, null));
} }

View File

@ -2,6 +2,7 @@ package com.appsmith.server.controllers;
import com.appsmith.server.constants.Url; import com.appsmith.server.constants.Url;
import com.appsmith.server.domains.Layout; import com.appsmith.server.domains.Layout;
import com.appsmith.server.dtos.LayoutDTO;
import com.appsmith.server.dtos.RefactorNameDTO; import com.appsmith.server.dtos.RefactorNameDTO;
import com.appsmith.server.dtos.ResponseDTO; import com.appsmith.server.dtos.ResponseDTO;
import com.appsmith.server.services.LayoutActionService; import com.appsmith.server.services.LayoutActionService;
@ -46,7 +47,7 @@ public class LayoutController {
} }
@PutMapping("/{layoutId}/pages/{pageId}") @PutMapping("/{layoutId}/pages/{pageId}")
public Mono<ResponseDTO<Layout>> updateLayout(@PathVariable String pageId, @PathVariable String layoutId, @RequestBody Layout layout) { public Mono<ResponseDTO<LayoutDTO>> updateLayout(@PathVariable String pageId, @PathVariable String layoutId, @RequestBody Layout layout) {
return layoutActionService.updateLayout(pageId, layoutId, layout) return layoutActionService.updateLayout(pageId, layoutId, layout)
.map(created -> new ResponseDTO<>(HttpStatus.OK.value(), created, null)); .map(created -> new ResponseDTO<>(HttpStatus.OK.value(), created, null));
} }
@ -58,7 +59,7 @@ public class LayoutController {
} }
@PutMapping("/refactor") @PutMapping("/refactor")
public Mono<ResponseDTO<Layout>> refactorWidgetName(@RequestBody RefactorNameDTO refactorNameDTO) { public Mono<ResponseDTO<LayoutDTO>> refactorWidgetName(@RequestBody RefactorNameDTO refactorNameDTO) {
return layoutActionService.refactorWidgetName(refactorNameDTO) return layoutActionService.refactorWidgetName(refactorNameDTO)
.map(created -> new ResponseDTO<>(HttpStatus.OK.value(), created, null)); .map(created -> new ResponseDTO<>(HttpStatus.OK.value(), created, null));
} }

View File

@ -0,0 +1,16 @@
package com.appsmith.server.dtos;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
/**
* This class would be used to send any action updates that have happened as part of update layout. The client should
* consume this structure to update the actions in its local storage (instead of fetching all the page actions afresh).
*/
public class LayoutActionUpdateDTO {
String id;
String name;
Boolean executeOnLoad;
}

View File

@ -0,0 +1,31 @@
package com.appsmith.server.dtos;
import com.appsmith.server.domains.ScreenType;
import lombok.Getter;
import lombok.Setter;
import net.minidev.json.JSONObject;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Getter
@Setter
public class LayoutDTO {
private String id;
ScreenType screen;
JSONObject dsl;
List<HashSet<DslActionDTO>> layoutOnLoadActions;
// All the actions which have been updated as part of updateLayout function call
List<LayoutActionUpdateDTO> actionUpdates;
// All the toast messages that the developer user should be displayed to inform about the consequences of update layout.
List<String> messages;
public Set<String> userPermissions = new HashSet<>();
}

View File

@ -251,14 +251,14 @@ public class MustacheHelper {
} else if (value instanceof List) { } else if (value instanceof List) {
for (Object childValue : (List) value) { for (Object childValue : (List) value) {
if (isDomainModel(childValue.getClass())) { if (childValue != null && isDomainModel(childValue.getClass())) {
renderFieldValues(childValue, context); renderFieldValues(childValue, context);
} }
} }
} else if (value instanceof Map) { } else if (value instanceof Map) {
for (Object childValue : ((Map) value).values()) { for (Object childValue : ((Map) value).values()) {
if (isDomainModel(childValue.getClass())) { if (childValue != null && isDomainModel(childValue.getClass())) {
renderFieldValues(childValue, context); renderFieldValues(childValue, context);
} }
} }

View File

@ -1578,4 +1578,43 @@ public class DatabaseChangelog {
public void clearUserDataCollection(MongoTemplate mongoTemplate) { public void clearUserDataCollection(MongoTemplate mongoTemplate) {
mongoTemplate.dropCollection(UserData.class); mongoTemplate.dropCollection(UserData.class);
} }
@ChangeSet(order = "050", id = "update-database-documentation-links-v1-2-1", author = "")
public void updateDatabaseDocumentationLinks_v1_2_1(MongoTemplate mongoTemplate) {
for (Plugin plugin : mongoTemplate.findAll(Plugin.class)) {
switch (plugin.getPackageName()) {
case "postgres-plugin":
plugin.setDocumentationLink("https://docs.appsmith.com/v/v1.2.1/datasource-reference/querying-postgres");
break;
case "mongo-plugin":
plugin.setDocumentationLink("https://docs.appsmith.com/v/v1.2.1/datasource-reference/querying-mongodb");
break;
case "elasticsearch-plugin":
plugin.setDocumentationLink("https://docs.appsmith.com/v/v1.2.1/datasource-reference/querying-elasticsearch");
break;
case "dynamo-plugin":
plugin.setDocumentationLink("https://docs.appsmith.com/v/v1.2.1/datasource-reference/querying-dynamodb");
break;
case "redis-plugin":
plugin.setDocumentationLink("https://docs.appsmith.com/v/v1.2.1/datasource-reference/querying-redis");
break;
case "mssql-plugin":
plugin.setDocumentationLink("https://docs.appsmith.com/v/v1.2.1/datasource-reference/querying-mssql");
break;
case "firestore-plugin":
plugin.setDocumentationLink("https://docs.appsmith.com/v/v1.2.1/datasource-reference/querying-firestore");
break;
case "redshift-plugin":
plugin.setDocumentationLink("https://docs.appsmith.com/v/v1.2.1/datasource-reference/querying-redshift");
break;
case "mysql-plugin":
plugin.setDocumentationLink("https://docs.appsmith.com/v/v1.2.1/datasource-reference/querying-mysql");
break;
default:
continue;
}
mongoTemplate.save(plugin);
}
}
} }

View File

@ -37,8 +37,10 @@ public class CustomNewActionRepositoryImpl extends BaseAppsmithRepositoryImpl<Ne
public Mono<NewAction> findByUnpublishedNameAndPageId(String name, String pageId, AclPermission aclPermission) { public Mono<NewAction> findByUnpublishedNameAndPageId(String name, String pageId, AclPermission aclPermission) {
Criteria nameCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.name)).is(name); Criteria nameCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.name)).is(name);
Criteria pageCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.pageId)).is(pageId); Criteria pageCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.pageId)).is(pageId);
// In case an action has been deleted in edit mode, but still exists in deployed mode, NewAction object would exist. To handle this, only fetch non-deleted actions
Criteria deletedCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.deletedAt)).is(null);
return queryOne(List.of(nameCriteria, pageCriteria), aclPermission); return queryOne(List.of(nameCriteria, pageCriteria, deletedCriteria), aclPermission);
} }
@Override @Override
@ -77,17 +79,26 @@ public class CustomNewActionRepositoryImpl extends BaseAppsmithRepositoryImpl<Ne
@Override @Override
public Flux<NewAction> findByPageIdAndViewMode(String pageId, Boolean viewMode, AclPermission aclPermission) { public Flux<NewAction> findByPageIdAndViewMode(String pageId, Boolean viewMode, AclPermission aclPermission) {
Criteria pageCriteria;
List<Criteria> criteria = new ArrayList<>();
Criteria pageCriterion;
// Fetch published actions // Fetch published actions
if (Boolean.TRUE.equals(viewMode)) { if (Boolean.TRUE.equals(viewMode)) {
pageCriteria = where(fieldName(QNewAction.newAction.publishedAction) + "." + fieldName(QNewAction.newAction.publishedAction.pageId)).is(pageId); pageCriterion = where(fieldName(QNewAction.newAction.publishedAction) + "." + fieldName(QNewAction.newAction.publishedAction.pageId)).is(pageId);
criteria.add(pageCriterion);
} }
// Fetch unpublished actions // Fetch unpublished actions
else { else {
pageCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.pageId)).is(pageId); pageCriterion = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.pageId)).is(pageId);
criteria.add(pageCriterion);
// In case an action has been deleted in edit mode, but still exists in deployed mode, NewAction object would exist. To handle this, only fetch non-deleted actions
Criteria deletedCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.deletedAt)).is(null);
criteria.add(deletedCriteria);
} }
return queryAll(List.of(pageCriteria), aclPermission); return queryAll(criteria, aclPermission);
} }
@Override @Override
@ -163,6 +174,10 @@ public class CustomNewActionRepositoryImpl extends BaseAppsmithRepositoryImpl<Ne
Criteria pageCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.pageId)).in(pageIds); Criteria pageCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.pageId)).in(pageIds);
criteriaList.add(pageCriteria); criteriaList.add(pageCriteria);
} }
// In case an action has been deleted in edit mode, but still exists in deployed mode, NewAction object would exist. To handle this, only fetch non-deleted actions
Criteria deletedCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.deletedAt)).is(null);
criteriaList.add(deletedCriteria);
} }
return queryAll(criteriaList, aclPermission, sort); return queryAll(criteriaList, aclPermission, sort);
@ -183,12 +198,17 @@ public class CustomNewActionRepositoryImpl extends BaseAppsmithRepositoryImpl<Ne
Criteria executeOnLoadCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.executeOnLoad)).is(Boolean.TRUE); Criteria executeOnLoadCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.executeOnLoad)).is(Boolean.TRUE);
criteriaList.add(executeOnLoadCriteria); criteriaList.add(executeOnLoadCriteria);
// In case an action has been deleted in edit mode, but still exists in deployed mode, NewAction object would exist. To handle this, only fetch non-deleted actions
Criteria deletedCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.deletedAt)).is(null);
criteriaList.add(deletedCriteria);
return queryAll(criteriaList, permission); return queryAll(criteriaList, permission);
} }
@Override @Override
public Flux<NewAction> findUnpublishedActionsByNameInAndPageId(Set<String> names, String pageId, AclPermission permission) { public Flux<NewAction> findUnpublishedActionsByNameInAndPageId(Set<String> names, String pageId, AclPermission permission) {
List<Criteria> criteriaList = new ArrayList<>(); List<Criteria> criteriaList = new ArrayList<>();
if (names != null) { if (names != null) {
Criteria namesCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.name)).in(names); Criteria namesCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.name)).in(names);
criteriaList.add(namesCriteria); criteriaList.add(namesCriteria);
@ -196,6 +216,10 @@ public class CustomNewActionRepositoryImpl extends BaseAppsmithRepositoryImpl<Ne
Criteria pageCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.pageId)).is(pageId); Criteria pageCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.pageId)).is(pageId);
criteriaList.add(pageCriteria); criteriaList.add(pageCriteria);
// In case an action has been deleted in edit mode, but still exists in deployed mode, NewAction object would exist. To handle this, only fetch non-deleted actions
Criteria deletedCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.deletedAt)).is(null);
criteriaList.add(deletedCriteria);
return queryAll(criteriaList, permission); return queryAll(criteriaList, permission);
} }
@ -212,6 +236,10 @@ public class CustomNewActionRepositoryImpl extends BaseAppsmithRepositoryImpl<Ne
Criteria pageCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.pageId)).is(pageId); Criteria pageCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.pageId)).is(pageId);
criteriaList.add(pageCriteria); criteriaList.add(pageCriteria);
// In case an action has been deleted in edit mode, but still exists in deployed mode, NewAction object would exist. To handle this, only fetch non-deleted actions
Criteria deletedCriteria = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.deletedAt)).is(null);
criteriaList.add(deletedCriteria);
return queryAll(criteriaList, permission); return queryAll(criteriaList, permission);
} }
@ -228,9 +256,18 @@ public class CustomNewActionRepositoryImpl extends BaseAppsmithRepositoryImpl<Ne
Boolean viewMode, Boolean viewMode,
AclPermission aclPermission) { AclPermission aclPermission) {
Criteria applicationCriteria = where(fieldName(QNewAction.newAction.applicationId)).is(applicationId); List<Criteria> criteria = new ArrayList<>();
return queryAll(List.of(applicationCriteria), aclPermission); Criteria applicationCriterion = where(fieldName(QNewAction.newAction.applicationId)).is(applicationId);
criteria.add(applicationCriterion);
if (Boolean.FALSE.equals(viewMode)) {
// In case an action has been deleted in edit mode, but still exists in deployed mode, NewAction object would exist. To handle this, only fetch non-deleted actions
Criteria deletedCriterion = where(fieldName(QNewAction.newAction.unpublishedAction) + "." + fieldName(QNewAction.newAction.unpublishedAction.deletedAt)).is(null);
criteria.add(deletedCriterion);
}
return queryAll(criteria, aclPermission);
} }
@Override @Override

View File

@ -12,6 +12,7 @@ import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.springframework.data.mongodb.core.query.Criteria.where; import static org.springframework.data.mongodb.core.query.Criteria.where;
@ -33,37 +34,65 @@ public class CustomNewPageRepositoryImpl extends BaseAppsmithRepositoryImpl<NewP
@Override @Override
public Mono<NewPage> findByIdAndLayoutsIdAndViewMode(String id, String layoutId, AclPermission aclPermission, Boolean viewMode) { public Mono<NewPage> findByIdAndLayoutsIdAndViewMode(String id, String layoutId, AclPermission aclPermission, Boolean viewMode) {
Criteria idCriterion = getIdCriteria(id);
String layoutsIdKey; String layoutsIdKey;
String layoutsKey; String layoutsKey;
List<Criteria> criteria = new ArrayList<>();
Criteria idCriterion = getIdCriteria(id);
criteria.add(idCriterion);
if (Boolean.TRUE.equals(viewMode)) { if (Boolean.TRUE.equals(viewMode)) {
layoutsKey = fieldName(QNewPage.newPage.publishedPage) + "." + fieldName(QNewPage.newPage.publishedPage.layouts); layoutsKey = fieldName(QNewPage.newPage.publishedPage) + "." + fieldName(QNewPage.newPage.publishedPage.layouts);
} else { } else {
layoutsKey = fieldName(QNewPage.newPage.unpublishedPage) + "." + fieldName(QNewPage.newPage.unpublishedPage.layouts); layoutsKey = fieldName(QNewPage.newPage.unpublishedPage) + "." + fieldName(QNewPage.newPage.unpublishedPage.layouts);
// In case a page has been deleted in edit mode, but still exists in deployed mode, NewPage object would exist. To handle this, only fetch non-deleted pages
Criteria deletedCriterion = where (fieldName(QNewPage.newPage.unpublishedPage) + "." + fieldName(QNewPage.newPage.unpublishedPage.deletedAt)).is(null);
criteria.add(deletedCriterion);
} }
layoutsIdKey = layoutsKey + "." + fieldName(QLayout.layout.id); layoutsIdKey = layoutsKey + "." + fieldName(QLayout.layout.id);
Criteria layoutCriterion = where(layoutsIdKey).is(layoutId); Criteria layoutCriterion = where(layoutsIdKey).is(layoutId);
criteria.add(layoutCriterion);
List<Criteria> criteria = List.of(idCriterion, layoutCriterion);
return queryOne(criteria, aclPermission); return queryOne(criteria, aclPermission);
} }
@Override @Override
public Mono<NewPage> findByNameAndViewMode(String name, AclPermission aclPermission, Boolean viewMode) { public Mono<NewPage> findByNameAndViewMode(String name, AclPermission aclPermission, Boolean viewMode) {
Criteria nameCriterion = getNameCriterion(name, viewMode);
return queryOne(List.of(nameCriterion), aclPermission); List<Criteria> criteria = new ArrayList<>();
Criteria nameCriterion = getNameCriterion(name, viewMode);
criteria.add(nameCriterion);
if (Boolean.FALSE.equals(viewMode)) {
// In case a page has been deleted in edit mode, but still exists in deployed mode, NewPage object would exist. To handle this, only fetch non-deleted pages
Criteria deletedCriterion = where (fieldName(QNewPage.newPage.unpublishedPage) + "." + fieldName(QNewPage.newPage.unpublishedPage.deletedAt)).is(null);
criteria.add(deletedCriterion);
}
return queryOne(criteria, aclPermission);
} }
@Override @Override
public Mono<NewPage> findByNameAndApplicationIdAndViewMode(String name, String applicationId, AclPermission aclPermission, Boolean viewMode) { public Mono<NewPage> findByNameAndApplicationIdAndViewMode(String name, String applicationId, AclPermission aclPermission, Boolean viewMode) {
List<Criteria> criteria = new ArrayList<>();
Criteria nameCriterion = getNameCriterion(name, viewMode); Criteria nameCriterion = getNameCriterion(name, viewMode);
criteria.add(nameCriterion);
Criteria applicationIdCriterion = where(fieldName(QNewPage.newPage.applicationId)).is(applicationId); Criteria applicationIdCriterion = where(fieldName(QNewPage.newPage.applicationId)).is(applicationId);
criteria.add(applicationIdCriterion);
return queryOne(List.of(nameCriterion, applicationIdCriterion), aclPermission); if (Boolean.FALSE.equals(viewMode)) {
// In case a page has been deleted in edit mode, but still exists in deployed mode, NewPage object would exist. To handle this, only fetch non-deleted pages
Criteria deletedCriteria = where (fieldName(QNewPage.newPage.unpublishedPage) + "." + fieldName(QNewPage.newPage.unpublishedPage.deletedAt)).is(null);
criteria.add(deletedCriteria);
}
return queryOne(criteria, aclPermission);
} }
@Override @Override

View File

@ -4,16 +4,17 @@ import com.appsmith.server.domains.Layout;
import com.appsmith.server.dtos.ActionDTO; import com.appsmith.server.dtos.ActionDTO;
import com.appsmith.server.dtos.ActionMoveDTO; import com.appsmith.server.dtos.ActionMoveDTO;
import com.appsmith.server.dtos.RefactorNameDTO; import com.appsmith.server.dtos.RefactorNameDTO;
import com.appsmith.server.dtos.LayoutDTO;
import reactor.core.publisher.Mono; import reactor.core.publisher.Mono;
public interface LayoutActionService { public interface LayoutActionService {
Mono<Layout> updateLayout(String pageId, String layoutId, Layout layout); Mono<LayoutDTO> updateLayout(String pageId, String layoutId, Layout layout);
Mono<ActionDTO> moveAction(ActionMoveDTO actionMoveDTO); Mono<ActionDTO> moveAction(ActionMoveDTO actionMoveDTO);
Mono<Layout> refactorWidgetName(RefactorNameDTO refactorNameDTO); Mono<LayoutDTO> refactorWidgetName(RefactorNameDTO refactorNameDTO);
Mono<Layout> refactorActionName(RefactorNameDTO refactorNameDTO); Mono<LayoutDTO> refactorActionName(RefactorNameDTO refactorNameDTO);
Mono<ActionDTO> updateAction(String id, ActionDTO action); Mono<ActionDTO> updateAction(String id, ActionDTO action);

View File

@ -7,8 +7,10 @@ import com.appsmith.server.domains.Layout;
import com.appsmith.server.dtos.ActionDTO; import com.appsmith.server.dtos.ActionDTO;
import com.appsmith.server.dtos.ActionMoveDTO; import com.appsmith.server.dtos.ActionMoveDTO;
import com.appsmith.server.dtos.DslActionDTO; import com.appsmith.server.dtos.DslActionDTO;
import com.appsmith.server.dtos.LayoutActionUpdateDTO;
import com.appsmith.server.dtos.PageDTO; import com.appsmith.server.dtos.PageDTO;
import com.appsmith.server.dtos.RefactorNameDTO; import com.appsmith.server.dtos.RefactorNameDTO;
import com.appsmith.server.dtos.LayoutDTO;
import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.helpers.MustacheHelper; import com.appsmith.server.helpers.MustacheHelper;
@ -121,7 +123,7 @@ public class LayoutActionServiceImpl implements LayoutActionService {
} }
@Override @Override
public Mono<Layout> refactorWidgetName(RefactorNameDTO refactorNameDTO) { public Mono<LayoutDTO> refactorWidgetName(RefactorNameDTO refactorNameDTO) {
String pageId = refactorNameDTO.getPageId(); String pageId = refactorNameDTO.getPageId();
String layoutId = refactorNameDTO.getLayoutId(); String layoutId = refactorNameDTO.getLayoutId();
String oldName = refactorNameDTO.getOldName(); String oldName = refactorNameDTO.getOldName();
@ -136,7 +138,7 @@ public class LayoutActionServiceImpl implements LayoutActionService {
} }
@Override @Override
public Mono<Layout> refactorActionName(RefactorNameDTO refactorNameDTO) { public Mono<LayoutDTO> refactorActionName(RefactorNameDTO refactorNameDTO) {
String pageId = refactorNameDTO.getPageId(); String pageId = refactorNameDTO.getPageId();
String layoutId = refactorNameDTO.getLayoutId(); String layoutId = refactorNameDTO.getLayoutId();
String oldName = refactorNameDTO.getOldName(); String oldName = refactorNameDTO.getOldName();
@ -167,7 +169,7 @@ public class LayoutActionServiceImpl implements LayoutActionService {
* @param newName * @param newName
* @return * @return
*/ */
private Mono<Layout> refactorName(String pageId, String layoutId, String oldName, String newName) { private Mono<LayoutDTO> refactorName(String pageId, String layoutId, String oldName, String newName) {
String regexPattern = preWord + oldName + postWord; String regexPattern = preWord + oldName + postWord;
Pattern oldNamePattern = Pattern.compile(regexPattern); Pattern oldNamePattern = Pattern.compile(regexPattern);
@ -466,11 +468,11 @@ public class LayoutActionServiceImpl implements LayoutActionService {
} }
@Override @Override
public Mono<Layout> updateLayout(String pageId, String layoutId, Layout layout) { public Mono<LayoutDTO> updateLayout(String pageId, String layoutId, Layout layout) {
JSONObject dsl = layout.getDsl(); JSONObject dsl = layout.getDsl();
if (dsl == null) { if (dsl == null) {
// There is no DSL here. No need to process anything. Return as is. // There is no DSL here. No need to process anything. Return as is.
return Mono.just(layout); return Mono.just(generateResponseDTO(layout));
} }
Set<String> widgetNames = new HashSet<>(); Set<String> widgetNames = new HashSet<>();
@ -496,6 +498,8 @@ public class LayoutActionServiceImpl implements LayoutActionService {
Set<ActionDependencyEdge> edges = new HashSet<>(); Set<ActionDependencyEdge> edges = new HashSet<>();
Set<String> actionsUsedInDSL = new HashSet<>(); Set<String> actionsUsedInDSL = new HashSet<>();
List<ActionDTO> flatmapPageLoadActions = new ArrayList<>(); List<ActionDTO> flatmapPageLoadActions = new ArrayList<>();
List<LayoutActionUpdateDTO> actionUpdates = new ArrayList<>();
List<String> messages = new ArrayList<>();
Mono<List<HashSet<DslActionDTO>>> allOnLoadActionsMono = pageLoadActionsUtil Mono<List<HashSet<DslActionDTO>>> allOnLoadActionsMono = pageLoadActionsUtil
.findAllOnLoadActions(dynamicBindingNames, actionNames, pageId, edges, actionsUsedInDSL, flatmapPageLoadActions); .findAllOnLoadActions(dynamicBindingNames, actionNames, pageId, edges, actionsUsedInDSL, flatmapPageLoadActions);
@ -504,7 +508,9 @@ public class LayoutActionServiceImpl implements LayoutActionService {
return allOnLoadActionsMono return allOnLoadActionsMono
.flatMap(allOnLoadActions -> { .flatMap(allOnLoadActions -> {
// Update these actions to be executed on load, unless the user has touched the executeOnLoad setting for this // Update these actions to be executed on load, unless the user has touched the executeOnLoad setting for this
return newActionService.setOnLoad((flatmapPageLoadActions)).thenReturn(allOnLoadActions); return newActionService
.updateActionsExecuteOnLoad(flatmapPageLoadActions, pageId, actionUpdates, messages)
.thenReturn(allOnLoadActions);
}) })
.zipWith(newPageService.findByIdAndLayoutsId(pageId, layoutId, MANAGE_PAGES, false) .zipWith(newPageService.findByIdAndLayoutsId(pageId, layoutId, MANAGE_PAGES, false)
.switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.ACL_NO_RESOURCE_FOUND, .switchIfEmpty(Mono.error(new AppsmithException(AppsmithError.ACL_NO_RESOURCE_FOUND,
@ -542,7 +548,26 @@ public class LayoutActionServiceImpl implements LayoutActionService {
} }
} }
return Mono.empty(); return Mono.empty();
})
.map(savedLayout -> {
LayoutDTO layoutDTO = generateResponseDTO(savedLayout);
layoutDTO.setActionUpdates(actionUpdates);
layoutDTO.setMessages(messages);
return layoutDTO;
}); });
} }
private LayoutDTO generateResponseDTO(Layout layout) {
LayoutDTO layoutDTO = new LayoutDTO();
layoutDTO.setId(layout.getId());
layoutDTO.setDsl(layout.getDsl());
layoutDTO.setScreen(layout.getScreen());
layoutDTO.setLayoutOnLoadActions(layout.getLayoutOnLoadActions());
layoutDTO.setUserPermissions(layout.getUserPermissions());
return layoutDTO;
}
} }

View File

@ -6,6 +6,7 @@ import com.appsmith.server.domains.NewAction;
import com.appsmith.server.dtos.ActionDTO; import com.appsmith.server.dtos.ActionDTO;
import com.appsmith.server.dtos.ActionViewDTO; import com.appsmith.server.dtos.ActionViewDTO;
import com.appsmith.server.dtos.ExecuteActionDTO; import com.appsmith.server.dtos.ExecuteActionDTO;
import com.appsmith.server.dtos.LayoutActionUpdateDTO;
import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
@ -57,5 +58,5 @@ public interface NewActionService extends CrudService<NewAction, String> {
Flux<NewAction> findByPageId(String pageId); Flux<NewAction> findByPageId(String pageId);
Mono<Boolean> setOnLoad(List<ActionDTO> actions); Mono<Boolean> updateActionsExecuteOnLoad(List<ActionDTO> actions, String pageId, List<LayoutActionUpdateDTO> actionUpdates, List<String> messages);
} }

View File

@ -30,6 +30,7 @@ import com.appsmith.server.domains.User;
import com.appsmith.server.dtos.ActionDTO; import com.appsmith.server.dtos.ActionDTO;
import com.appsmith.server.dtos.ActionViewDTO; import com.appsmith.server.dtos.ActionViewDTO;
import com.appsmith.server.dtos.ExecuteActionDTO; import com.appsmith.server.dtos.ExecuteActionDTO;
import com.appsmith.server.dtos.LayoutActionUpdateDTO;
import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.exceptions.AppsmithException;
import com.appsmith.server.helpers.MustacheHelper; import com.appsmith.server.helpers.MustacheHelper;
@ -42,6 +43,7 @@ import org.springframework.data.mongodb.core.ReactiveMongoTemplate;
import org.springframework.data.mongodb.core.convert.MongoConverter; import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedCaseInsensitiveMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import reactor.core.publisher.Flux; import reactor.core.publisher.Flux;
@ -57,6 +59,7 @@ import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
@ -916,25 +919,147 @@ public class NewActionServiceImpl extends BaseService<NewActionRepository, NewAc
return repository.findByPageId(pageId); return repository.findByPageId(pageId);
} }
/**
* !!!WARNING!!! This function edits the parameters actionUpdates and messages which are eventually returned back to
* the caller with the updates values.
* @param onLoadActions : All the actions which have been found to be on page load
* @param pageId
* @param actionUpdates : Empty array list which would be set in this function with all the page actions whose
* execute on load setting has changed (whether flipped from true to false, or vice versa)
* @param messages : Empty array list which would be set in this function with all the messages that should be
* displayed to the developer user communicating the action executeOnLoad changes.
* @return
*/
@Override @Override
public Mono<Boolean> setOnLoad(List<ActionDTO> actions) { public Mono<Boolean> updateActionsExecuteOnLoad(List<ActionDTO> onLoadActions,
if (actions == null) { String pageId,
List<LayoutActionUpdateDTO> actionUpdates,
List<String> messages) {
List<ActionDTO> toUpdateActions = new ArrayList<>();
MultiValueMap<String, String> params = CollectionUtils.toMultiValueMap(new LinkedCaseInsensitiveMap<>(8, Locale.ENGLISH));
params.add(FieldName.PAGE_ID, pageId);
// Fetch all the actions which exist in this page.
Flux<ActionDTO> pageActionsFlux = this.getUnpublishedActions(params).cache();
// Before we update the actions, fetch all the actions which are currently set to execute on load.
Mono<List<ActionDTO>> existingOnPageLoadActionsMono = pageActionsFlux
.flatMap(action -> {
if (TRUE.equals(action.getExecuteOnLoad())) {
return Mono.just(action);
}
return Mono.empty();
})
.collectList();
return existingOnPageLoadActionsMono
.zipWith(pageActionsFlux.collectList())
.flatMap( tuple -> {
List<ActionDTO> existingOnPageLoadActions = tuple.getT1();
List<ActionDTO> pageActions = tuple.getT2();
// There are no actions in this page. No need to proceed further since no actions would get updated
if (pageActions.isEmpty()) {
return Mono.just(FALSE); return Mono.just(FALSE);
} }
List<ActionDTO> toUpdateActions = new ArrayList<>(); // No actions require an update if no actions have been found as page load actions as well as
for (ActionDTO action : actions) { // existing on load page actions are empty
if (existingOnPageLoadActions.isEmpty() && (onLoadActions == null || onLoadActions.isEmpty())) {
return Mono.just(FALSE);
}
// Extract names of existing pageload actions and new page load actions for quick lookup.
Set<String> existingOnPageLoadActionNames = existingOnPageLoadActions
.stream()
.map(action -> action.getName())
.collect(Collectors.toSet());
Set<String> newOnLoadActionNames = onLoadActions
.stream()
.map(action -> action.getName())
.collect(Collectors.toSet());
// Calculate the actions which would need to be updated from execute on load TRUE to FALSE.
Set<String> turnedOffActionNames = new HashSet<>();
turnedOffActionNames.addAll(existingOnPageLoadActionNames);
turnedOffActionNames.removeAll(newOnLoadActionNames);
// Calculate the actions which would need to be updated from execute on load FALSE to TRUE
Set<String> turnedOnActionNames = new HashSet<>();
turnedOnActionNames.addAll(newOnLoadActionNames);
turnedOnActionNames.removeAll(existingOnPageLoadActionNames);
for (ActionDTO action : pageActions) {
String actionName = action.getName();
// If a user has ever set execute on load, this field can not be changed automatically. It has to be // If a user has ever set execute on load, this field can not be changed automatically. It has to be
// explicitly changed by the user again. Add the action to update only if this condition is false. // explicitly changed by the user again. Add the action to update only if this condition is false.
if (FALSE.equals(action.getUserSetOnLoad())) { if (FALSE.equals(action.getUserSetOnLoad())) {
// If this action is no longer an onload action, turn the execute on load to false
if (turnedOffActionNames.contains(actionName)) {
action.setExecuteOnLoad(FALSE);
toUpdateActions.add(action);
}
// If this action is newly found to be on load, turn execute on load to true
if (turnedOnActionNames.contains(actionName)) {
action.setExecuteOnLoad(TRUE); action.setExecuteOnLoad(TRUE);
toUpdateActions.add(action); toUpdateActions.add(action);
} }
} else {
// Remove the action name from either of the lists (if present) because this action should
// not be updated
turnedOnActionNames.remove(actionName);
turnedOffActionNames.remove(actionName);
}
} }
// Add newly turned on page actions to report back to the caller
actionUpdates.addAll(
addActionUpdatesForActionNames(pageActions, turnedOnActionNames)
);
// Add newly turned off page actions to report back to the caller
actionUpdates.addAll(
addActionUpdatesForActionNames(pageActions, turnedOffActionNames)
);
// Now add messages that would eventually be displayed to the developer user informing them
// about the action setting change.
if (!turnedOffActionNames.isEmpty()) {
messages.add(turnedOffActionNames.toString() + " will no longer be executed on page load");
}
if (!turnedOnActionNames.isEmpty()) {
messages.add(turnedOnActionNames.toString() + " will be executed automatically on page load");
}
// Finally update the actions which require an update
return Flux.fromIterable(toUpdateActions) return Flux.fromIterable(toUpdateActions)
.flatMap(actionDTO -> updateUnpublishedAction(actionDTO.getId(), actionDTO)) .flatMap(actionDTO -> updateUnpublishedAction(actionDTO.getId(), actionDTO))
.then(Mono.just(TRUE)); .then(Mono.just(TRUE));
});
}
private List<LayoutActionUpdateDTO> addActionUpdatesForActionNames(List<ActionDTO> pageActions,
Set<String> actionNames) {
return pageActions
.stream()
.filter(pageAction -> actionNames.contains(pageAction.getName()))
.map(pageAction -> {
LayoutActionUpdateDTO layoutActionUpdateDTO = new LayoutActionUpdateDTO();
layoutActionUpdateDTO.setId(pageAction.getId());
layoutActionUpdateDTO.setName(pageAction.getName());
layoutActionUpdateDTO.setExecuteOnLoad(pageAction.getExecuteOnLoad());
return layoutActionUpdateDTO;
})
.collect(Collectors.toList());
} }
@Override @Override

View File

@ -5,15 +5,11 @@ import com.appsmith.external.models.Connection;
import com.appsmith.external.models.DatasourceConfiguration; import com.appsmith.external.models.DatasourceConfiguration;
import com.appsmith.external.models.Endpoint; import com.appsmith.external.models.Endpoint;
import com.appsmith.external.models.Property; import com.appsmith.external.models.Property;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.assertj.core.api.IterableAssert; import org.assertj.core.api.IterableAssert;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -29,13 +25,8 @@ import static org.assertj.core.api.Assertions.assertThat;
// Disabling this so we may use `Arrays.asList` with single argument, which is easier to refactor, just for tests. // Disabling this so we may use `Arrays.asList` with single argument, which is easier to refactor, just for tests.
"ArraysAsListWithZeroOrOneArgument" "ArraysAsListWithZeroOrOneArgument"
) )
@RunWith(SpringRunner.class)
@SpringBootTest
public class MustacheHelperTest { public class MustacheHelperTest {
@Autowired
private ObjectMapper objectMapper;
private void checkTokens(String template, List<String> expected) { private void checkTokens(String template, List<String> expected) {
assertThat(tokenize(template)).isEqualTo(expected); assertThat(tokenize(template)).isEqualTo(expected);
} }
@ -406,7 +397,14 @@ public class MustacheHelperTest {
new Property("param2", "{{ queryParam2 }}") new Property("param2", "{{ queryParam2 }}")
)); ));
final Map<String, String> context = Map.of( configuration.setPluginSpecifiedTemplates(Arrays.asList(
null,
new Property("prop1", "{{ pluginSpecifiedProp1 }}"),
null,
new Property("prop2", "{{ pluginSpecifiedProp2 }}")
));
final Map<String, String> context = new HashMap<>(Map.of(
"body", "rendered body", "body", "rendered body",
"path", "rendered path", "path", "rendered path",
"next", "rendered next", "next", "rendered next",
@ -416,7 +414,12 @@ public class MustacheHelperTest {
"bodyParam2", "rendered bodyParam2", "bodyParam2", "rendered bodyParam2",
"queryParam1", "rendered queryParam1", "queryParam1", "rendered queryParam1",
"queryParam2", "rendered queryParam2" "queryParam2", "rendered queryParam2"
); ));
context.putAll(Map.of(
"pluginSpecifiedProp1", "rendered pluginSpecifiedProp1",
"pluginSpecifiedProp2", "rendered pluginSpecifiedProp2"
));
assertKeys(configuration).hasSameElementsAs(context.keySet()); assertKeys(configuration).hasSameElementsAs(context.keySet());
@ -440,6 +443,13 @@ public class MustacheHelperTest {
new Property("param1", "rendered queryParam1"), new Property("param1", "rendered queryParam1"),
new Property("param2", "rendered queryParam2") new Property("param2", "rendered queryParam2")
); );
assertThat(configuration.getPluginSpecifiedTemplates()).containsExactly(
null,
new Property("prop1", "rendered pluginSpecifiedProp1"),
null,
new Property("prop2", "rendered pluginSpecifiedProp2")
);
} }
@Test @Test

View File

@ -11,6 +11,8 @@ import com.appsmith.server.domains.Plugin;
import com.appsmith.server.domains.User; import com.appsmith.server.domains.User;
import com.appsmith.server.dtos.ActionDTO; import com.appsmith.server.dtos.ActionDTO;
import com.appsmith.server.dtos.DslActionDTO; import com.appsmith.server.dtos.DslActionDTO;
import com.appsmith.server.dtos.LayoutActionUpdateDTO;
import com.appsmith.server.dtos.LayoutDTO;
import com.appsmith.server.dtos.PageDTO; import com.appsmith.server.dtos.PageDTO;
import com.appsmith.server.dtos.RefactorNameDTO; import com.appsmith.server.dtos.RefactorNameDTO;
import com.appsmith.server.helpers.MockPluginExecutor; import com.appsmith.server.helpers.MockPluginExecutor;
@ -37,6 +39,7 @@ import reactor.test.StepVerifier;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -216,7 +219,7 @@ public class LayoutActionServiceTest {
ActionDTO createdAction = newActionService.createAction(action).block(); ActionDTO createdAction = newActionService.createAction(action).block();
Layout firstLayout = layoutActionService.updateLayout(testPage.getId(), layout.getId(), layout).block(); LayoutDTO firstLayout = layoutActionService.updateLayout(testPage.getId(), layout.getId(), layout).block();
RefactorNameDTO refactorNameDTO = new RefactorNameDTO(); RefactorNameDTO refactorNameDTO = new RefactorNameDTO();
@ -225,7 +228,7 @@ public class LayoutActionServiceTest {
refactorNameDTO.setOldName("beforeNameChange"); refactorNameDTO.setOldName("beforeNameChange");
refactorNameDTO.setNewName("PostNameChange"); refactorNameDTO.setNewName("PostNameChange");
Layout postNameChangeLayout = layoutActionService.refactorActionName(refactorNameDTO).block(); LayoutDTO postNameChangeLayout = layoutActionService.refactorActionName(refactorNameDTO).block();
Mono<NewAction> postNameChangeActionMono = newActionService.findById(createdAction.getId(), READ_ACTIONS); Mono<NewAction> postNameChangeActionMono = newActionService.findById(createdAction.getId(), READ_ACTIONS);
@ -238,11 +241,152 @@ public class LayoutActionServiceTest {
DslActionDTO actionDTO = postNameChangeLayout.getLayoutOnLoadActions().get(0).iterator().next(); DslActionDTO actionDTO = postNameChangeLayout.getLayoutOnLoadActions().get(0).iterator().next();
assertThat(actionDTO.getName()).isEqualTo("PostNameChange"); assertThat(actionDTO.getName()).isEqualTo("PostNameChange");
// JSONObject newDsl = new JSONObject(Map.of("widgetName", "firstWidget", "mustacheProp", "{{ PostNameChange.data }}"));
dsl.put("testField", "{{ PostNameChange.data }}"); dsl.put("testField", "{{ PostNameChange.data }}");
assertThat(postNameChangeLayout.getDsl()).isEqualTo(dsl); assertThat(postNameChangeLayout.getDsl()).isEqualTo(dsl);
}) })
.verifyComplete(); .verifyComplete();
} }
@Test
@WithUserDetails(value = "api_user")
public void refactorActionNameToDeletedName() {
Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new MockPluginExecutor()));
ActionDTO action = new ActionDTO();
action.setName("Query1");
action.setPageId(testPage.getId());
ActionConfiguration actionConfiguration = new ActionConfiguration();
actionConfiguration.setHttpMethod(HttpMethod.GET);
action.setActionConfiguration(actionConfiguration);
action.setDatasource(datasource);
Layout layout = testPage.getLayouts().get(0);
ActionDTO firstAction = newActionService.createAction(action).block();
LayoutDTO firstLayout = layoutActionService.updateLayout(testPage.getId(), layout.getId(), layout).block();
applicationPageService.publish(testPage.getApplicationId()).block();
newActionService.deleteUnpublishedAction(firstAction.getId()).block();
// Create another action with the same name as the erstwhile deleted action
action.setId(null);
ActionDTO secondAction = newActionService.createAction(action).block();
RefactorNameDTO refactorNameDTO = new RefactorNameDTO();
refactorNameDTO.setPageId(testPage.getId());
refactorNameDTO.setLayoutId(firstLayout.getId());
refactorNameDTO.setOldName("Query1");
refactorNameDTO.setNewName("NewActionName");
layoutActionService.refactorActionName(refactorNameDTO).block();
Mono<NewAction> postNameChangeActionMono = newActionService.findById(secondAction.getId(), READ_ACTIONS);
StepVerifier
.create(postNameChangeActionMono)
.assertNext(updatedAction -> {
assertThat(updatedAction.getUnpublishedAction().getName()).isEqualTo("NewActionName");
})
.verifyComplete();
}
@Test
@WithUserDetails(value = "api_user")
public void actionExecuteOnLoadChangeOnUpdateLayout() {
Mockito.when(pluginExecutorHelper.getPluginExecutor(Mockito.any())).thenReturn(Mono.just(new MockPluginExecutor()));
ActionDTO action1 = new ActionDTO();
action1.setName("firstAction");
action1.setPageId(testPage.getId());
ActionConfiguration actionConfiguration1 = new ActionConfiguration();
actionConfiguration1.setHttpMethod(HttpMethod.GET);
action1.setActionConfiguration(actionConfiguration1);
action1.setDatasource(datasource);
ActionDTO action2 = new ActionDTO();
action2.setName("secondAction");
action2.setPageId(testPage.getId());
ActionConfiguration actionConfiguration2 = new ActionConfiguration();
actionConfiguration2.setHttpMethod(HttpMethod.GET);
action2.setActionConfiguration(actionConfiguration2);
action2.setDatasource(datasource);
JSONObject dsl = new JSONObject();
dsl.put("widgetName", "firstWidget");
JSONArray temp = new JSONArray();
temp.addAll(List.of(new JSONObject(Map.of("key", "testField"))));
dsl.put("dynamicBindingPathList", temp);
dsl.put("testField", "{{ firstAction.data }}");
Layout layout = testPage.getLayouts().get(0);
layout.setDsl(dsl);
ActionDTO createdAction1 = newActionService.createAction(action1).block();
ActionDTO createdAction2 = newActionService.createAction(action2).block();
Mono<LayoutDTO> updateLayoutMono = layoutActionService.updateLayout(testPage.getId(), layout.getId(), layout);
StepVerifier.create(updateLayoutMono)
.assertNext(updatedLayout -> {
log.debug("{}", updatedLayout.getMessages());
DslActionDTO actionDTO = updatedLayout.getLayoutOnLoadActions().get(0).iterator().next();
assertThat(actionDTO.getName()).isEqualTo("firstAction");
List<LayoutActionUpdateDTO> actionUpdates = updatedLayout.getActionUpdates();
assertThat(actionUpdates.size()).isEqualTo(1);
assertThat(actionUpdates.get(0).getName()).isEqualTo("firstAction");
assertThat(actionUpdates.get(0).getExecuteOnLoad()).isTrue();
})
.verifyComplete();
StepVerifier.create(newActionService.findById(createdAction1.getId()))
.assertNext(newAction -> assertThat(newAction.getUnpublishedAction().getExecuteOnLoad()).isTrue());
StepVerifier.create(newActionService.findById(createdAction2.getId()))
.assertNext(newAction -> assertThat(newAction.getUnpublishedAction().getExecuteOnLoad()).isFalse());
dsl = new JSONObject();
dsl.put("widgetName", "firstWidget");
temp = new JSONArray();
temp.addAll(List.of(new JSONObject(Map.of("key", "testField"))));
dsl.put("dynamicBindingPathList", temp);
dsl.put("testField", "{{ secondAction.data }}");
layout.setDsl(dsl);
updateLayoutMono = layoutActionService.updateLayout(testPage.getId(), layout.getId(), layout);
StepVerifier.create(updateLayoutMono)
.assertNext(updatedLayout -> {
log.debug("{}", updatedLayout.getMessages());
DslActionDTO actionDTO = updatedLayout.getLayoutOnLoadActions().get(0).iterator().next();
assertThat(actionDTO.getName()).isEqualTo("secondAction");
List<LayoutActionUpdateDTO> actionUpdates = updatedLayout.getActionUpdates();
assertThat(actionUpdates.size()).isEqualTo(2);
Optional<LayoutActionUpdateDTO> firstActionUpdateOptional = actionUpdates.stream().filter(actionUpdate -> actionUpdate.getName().equals("firstAction")).findFirst();
LayoutActionUpdateDTO firstActionUpdate = firstActionUpdateOptional.get();
assertThat(firstActionUpdate).isNotNull();
assertThat(firstActionUpdate.getExecuteOnLoad()).isFalse();
Optional<LayoutActionUpdateDTO> secondActionUpdateOptional = actionUpdates.stream().filter(actionUpdate -> actionUpdate.getName().equals("secondAction")).findFirst();
LayoutActionUpdateDTO secondActionUpdate = secondActionUpdateOptional.get();
assertThat(secondActionUpdate).isNotNull();
assertThat(secondActionUpdate.getExecuteOnLoad()).isTrue();
})
.verifyComplete();
StepVerifier.create(newActionService.findById(createdAction1.getId()))
.assertNext(newAction -> assertThat(newAction.getUnpublishedAction().getExecuteOnLoad()).isFalse());
StepVerifier.create(newActionService.findById(createdAction2.getId()))
.assertNext(newAction -> assertThat(newAction.getUnpublishedAction().getExecuteOnLoad()).isTrue());
}
} }

View File

@ -7,13 +7,12 @@ import com.appsmith.server.constants.FieldName;
import com.appsmith.server.domains.Application; import com.appsmith.server.domains.Application;
import com.appsmith.server.domains.Datasource; import com.appsmith.server.domains.Datasource;
import com.appsmith.server.domains.Layout; import com.appsmith.server.domains.Layout;
import com.appsmith.server.domains.NewAction;
import com.appsmith.server.domains.NewPage;
import com.appsmith.server.domains.Plugin; import com.appsmith.server.domains.Plugin;
import com.appsmith.server.domains.PluginType; import com.appsmith.server.domains.PluginType;
import com.appsmith.server.domains.User; import com.appsmith.server.domains.User;
import com.appsmith.server.dtos.ActionDTO; import com.appsmith.server.dtos.ActionDTO;
import com.appsmith.server.dtos.DslActionDTO; import com.appsmith.server.dtos.DslActionDTO;
import com.appsmith.server.dtos.LayoutDTO;
import com.appsmith.server.dtos.PageDTO; import com.appsmith.server.dtos.PageDTO;
import com.appsmith.server.exceptions.AppsmithError; import com.appsmith.server.exceptions.AppsmithError;
import com.appsmith.server.exceptions.AppsmithException; import com.appsmith.server.exceptions.AppsmithException;
@ -205,7 +204,7 @@ public class LayoutServiceTest {
Layout startLayout = layoutService.createLayout(page.getId(), testLayout).block(); Layout startLayout = layoutService.createLayout(page.getId(), testLayout).block();
Mono<Layout> updatedLayoutMono = layoutActionService.updateLayout("random-impossible-id-page", startLayout.getId(), updateLayout); Mono<LayoutDTO> updatedLayoutMono = layoutActionService.updateLayout("random-impossible-id-page", startLayout.getId(), updateLayout);
StepVerifier StepVerifier
.create(updatedLayoutMono) .create(updatedLayoutMono)
@ -238,7 +237,7 @@ public class LayoutServiceTest {
Mono<Layout> startLayoutMono = pageMono.flatMap(page -> layoutService.createLayout(page.getId(), testLayout)); Mono<Layout> startLayoutMono = pageMono.flatMap(page -> layoutService.createLayout(page.getId(), testLayout));
Mono<Layout> updatedLayoutMono = Mono.zip(pageMono, startLayoutMono) Mono<LayoutDTO> updatedLayoutMono = Mono.zip(pageMono, startLayoutMono)
.flatMap(tuple -> { .flatMap(tuple -> {
PageDTO page = tuple.getT1(); PageDTO page = tuple.getT1();
Layout startLayout = tuple.getT2(); Layout startLayout = tuple.getT2();
@ -276,7 +275,7 @@ public class LayoutServiceTest {
Mono<PageDTO> pageMono = createPage(app, testPage).cache(); Mono<PageDTO> pageMono = createPage(app, testPage).cache();
Mono<Layout> testMono = pageMono Mono<LayoutDTO> testMono = pageMono
.flatMap(page1 -> { .flatMap(page1 -> {
List<Mono<ActionDTO>> monos = new ArrayList<>(); List<Mono<ActionDTO>> monos = new ArrayList<>();
@ -464,7 +463,7 @@ public class LayoutServiceTest {
Mono<PageDTO> pageMono = createPage(app, testPage).cache(); Mono<PageDTO> pageMono = createPage(app, testPage).cache();
Mono<Layout> testMono = pageMono Mono<LayoutDTO> testMono = pageMono
.flatMap(page1 -> { .flatMap(page1 -> {
List<Mono<ActionDTO>> monos = new ArrayList<>(); List<Mono<ActionDTO>> monos = new ArrayList<>();

View File

@ -266,6 +266,46 @@ public class PageServiceTest {
.verifyComplete(); .verifyComplete();
} }
@Test
@WithUserDetails(value = "api_user")
public void reuseDeletedPageName() {
PageDTO testPage = new PageDTO();
testPage.setName("reuseDeletedPageName");
setupTestApplication();
testPage.setApplicationId(application.getId());
// Create Page
PageDTO firstPage = applicationPageService.createPage(testPage).block();
// Publish the application
applicationPageService.publish(application.getId());
//Delete Page in edit mode
applicationPageService.deleteUnpublishedPage(firstPage.getId()).block();
testPage.setId(null);
testPage.setName("New Page Name");
// Create Second Page
PageDTO secondPage = applicationPageService.createPage(testPage).block();
//Update the name of the new page
PageDTO newPage = new PageDTO();
newPage.setId(secondPage.getId());
newPage.setName("reuseDeletedPageName");
Mono<PageDTO> updatePageNameMono = newPageService.updatePage(secondPage.getId(), newPage);
StepVerifier
.create(updatePageNameMono)
.assertNext(page -> {
assertThat(page).isNotNull();
assertThat(page.getId()).isNotNull();
assertThat("reuseDeletedPageName".equals(page.getName()));
})
.verifyComplete();
}
@After @After
public void purgeAllPages() { public void purgeAllPages() {

View File

@ -100,5 +100,5 @@ example:
![381611580157_ pic_hd](https://user-images.githubusercontent.com/4025839/105710505-2ead5300-5f52-11eb-9549-531e459e86ea.jpg) ![381611580157_ pic_hd](https://user-images.githubusercontent.com/4025839/105710505-2ead5300-5f52-11eb-9549-531e459e86ea.jpg)
## Need Assistance ## Need Assistance
- If you are unable to resolve any issue while doing the setup, please initiate a Github discussion or send an email to support@appsmith.com. We'll be happy to help you. - If you are unable to resolve any issue while doing the setup, please feel free to ask questions on our [Discord channel](https://discord.com/invite/rBTTVJp) or initiate a [Github discussion](https://github.com/appsmithorg/appsmith/discussions) or send an email to `support@appsmith.com`. We'll be happy to help you.
- In case you notice any discrepancy, please raise an issue on Github and/or send an email to support@appsmith.com. - In case you notice any discrepancy, please raise an issue on Github and/or send an email to support@appsmith.com.