Skip to content

Commit

Permalink
feat: Execute script for selected rows (#2508)
Browse files Browse the repository at this point in the history
  • Loading branch information
patelmilanun authored Dec 16, 2023
1 parent ca2138b commit 5d9901e
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 0 deletions.
60 changes: 60 additions & 0 deletions src/dashboard/Data/Browser/Browser.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import EmptyState from 'components/EmptyState/EmptyState.react';
import ExportDialog from 'dashboard/Data/Browser/ExportDialog.react';
import AttachRowsDialog from 'dashboard/Data/Browser/AttachRowsDialog.react';
import AttachSelectedRowsDialog from 'dashboard/Data/Browser/AttachSelectedRowsDialog.react';
import ExecuteScriptRowsDialog from 'dashboard/Data/Browser/ExecuteScriptRowsDialog.react';
import CloneSelectedRowsDialog from 'dashboard/Data/Browser/CloneSelectedRowsDialog.react';
import EditRowDialog from 'dashboard/Data/Browser/EditRowDialog.react';
import ExportSelectedRowsDialog from 'dashboard/Data/Browser/ExportSelectedRowsDialog.react';
Expand Down Expand Up @@ -95,6 +96,8 @@ class Browser extends DashboardView {

useMasterKey: true,
currentUser: Parse.User.current(),

processedScripts: 0,
};

this.prefetchData = this.prefetchData.bind(this);
Expand All @@ -114,6 +117,9 @@ class Browser extends DashboardView {
this.cancelAttachRows = this.cancelAttachRows.bind(this);
this.confirmAttachRows = this.confirmAttachRows.bind(this);
this.showAttachSelectedRowsDialog = this.showAttachSelectedRowsDialog.bind(this);
this.showExecuteScriptRowsDialog = this.showExecuteScriptRowsDialog.bind(this);
this.confirmExecuteScriptRows = this.confirmExecuteScriptRows.bind(this);
this.cancelExecuteScriptRowsDialog = this.cancelExecuteScriptRowsDialog.bind(this);
this.confirmAttachSelectedRows = this.confirmAttachSelectedRows.bind(this);
this.cancelAttachSelectedRows = this.cancelAttachSelectedRows.bind(this);
this.showCloneSelectedRowsDialog = this.showCloneSelectedRowsDialog.bind(this);
Expand Down Expand Up @@ -1326,6 +1332,18 @@ class Browser extends DashboardView {
});
}

showExecuteScriptRowsDialog() {
this.setState({
showExecuteScriptRowsDialog: true,
});
}

cancelExecuteScriptRowsDialog() {
this.setState({
showExecuteScriptRowsDialog: false,
});
}

async confirmAttachSelectedRows(
className,
targetObjectId,
Expand All @@ -1346,6 +1364,37 @@ class Browser extends DashboardView {
});
}

async confirmExecuteScriptRows(script) {
try {
const objects = [];
Object.keys(this.state.selection).forEach(key =>
objects.push(Parse.Object.extend(this.props.params.className).createWithoutData(key))
);
for (const object of objects) {
const response = await Parse.Cloud.run(
script.cloudCodeFunction,
{ object: object.toPointer() },
{ useMasterKey: true }
);
this.setState(prevState => ({
processedScripts: prevState.processedScripts + 1,
}));
this.showNote(
response ||
`Ran script "${script.title}" on "${this.props.className}" object "${object.id}".`
);
}
this.refresh();
} catch (e) {
this.showNote(e.message, true);
console.log(`Could not run ${script.title}: ${e}`);
} finally{
this.setState(({
processedScripts: 0,
}));
}
}

showCloneSelectedRowsDialog() {
this.setState({
showCloneSelectedRowsDialog: true,
Expand Down Expand Up @@ -1790,6 +1839,7 @@ class Browser extends DashboardView {
onRefresh={this.refresh}
onAttachRows={this.showAttachRowsDialog}
onAttachSelectedRows={this.showAttachSelectedRowsDialog}
onExecuteScriptRows={this.showExecuteScriptRowsDialog}
onCloneSelectedRows={this.showCloneSelectedRowsDialog}
onEditSelectedRow={this.showEditRowDialog}
onEditPermissions={this.onDialogToggle}
Expand Down Expand Up @@ -1943,6 +1993,16 @@ class Browser extends DashboardView {
onConfirm={this.confirmAttachSelectedRows}
/>
);
} else if (this.state.showExecuteScriptRowsDialog) {
extras = (
<ExecuteScriptRowsDialog
currentClass={this.props.params.className}
selection={this.state.selection}
onCancel={this.cancelExecuteScriptRowsDialog}
onConfirm={this.confirmExecuteScriptRows}
processedScripts={this.state.processedScripts}
/>
);
} else if (this.state.showCloneSelectedRowsDialog) {
extras = (
<CloneSelectedRowsDialog
Expand Down
14 changes: 14 additions & 0 deletions src/dashboard/Data/Browser/BrowserToolbar.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ const BrowserToolbar = ({
onExport,
onRemoveColumn,
onDeleteRows,
onExecuteScriptRows,
onDropClass,
onChangeCLP,
onRefresh,
Expand Down Expand Up @@ -161,6 +162,7 @@ const BrowserToolbar = ({
text={selectionLength === 1 && !selection['*'] ? 'Delete this row' : 'Delete these rows'}
onClick={() => onDeleteRows(selection)}
/>
<Separator />
{enableColumnManipulation ? (
<MenuItem text="Delete a column" onClick={onRemoveColumn} />
) : (
Expand Down Expand Up @@ -378,6 +380,18 @@ const BrowserToolbar = ({
<noscript />
)}
{enableSecurityDialog ? <div className={styles.toolbarSeparator} /> : <noscript />}
<BrowserMenu
setCurrent={setCurrent}
title="Script"
icon="gear-solid"
>
<MenuItem
disabled={selectionLength === 0}
text={selectionLength === 1 && !selection['*'] ? 'Run script on selected row...' : `Run script on ${selectionLength} selected rows...`}
onClick={() => onExecuteScriptRows(selection)}
/>
</BrowserMenu>
<div className={styles.toolbarSeparator} />
{menu}
{editCloneRows && editCloneRows.length > 0 && <div className={styles.toolbarSeparator} />}
{editCloneRows && editCloneRows.length > 0 && (
Expand Down
78 changes: 78 additions & 0 deletions src/dashboard/Data/Browser/ExecuteScriptRowsDialog.react.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import React from 'react';
import FormModal from 'components/FormModal/FormModal.react';
import Field from 'components/Field/Field.react';
import Label from 'components/Label/Label.react';
import Dropdown from 'components/Dropdown/Dropdown.react';
import Option from 'components/Dropdown/Option.react';
import { CurrentApp } from 'context/currentApp';

export default class ExecuteScriptRowsDialog extends React.Component {
static contextType = CurrentApp;
constructor(props) {
super(props);

this.state = {
currentScript: null,
validScripts: [],
};

this.handleConfirm = this.handleConfirm.bind(this);
this.handleScriptChange = this.handleScriptChange.bind(this);
}

componentWillMount() {
const { selection, currentClass } = this.props;

const validScripts = (this.context.scripts || []).filter(script =>
script.classes?.includes(currentClass)
);

if (selection && validScripts.length > 0) {
this.setState({
currentScript: validScripts[0],
validScripts: validScripts,
});
}
}

handleConfirm() {
return this.props.onConfirm(this.state.currentScript);
}

handleScriptChange(scriptName) {
this.setState({
currentScript: this.state.validScripts.find(script => script.title === scriptName),
});
}

render() {
const { validScripts } = this.state;
const { selection, processedScripts } = this.props;
const selectionLength = Object.keys(selection).length;
return (
<FormModal
open
icon="gears"
iconSize={40}
title={selectionLength > 1 ? `Run script on ${selectionLength} selected rows` : 'Run script on selected row'}
submitText="Run"
inProgressText={`Executed ${processedScripts} of ${selectionLength} rows`}
onClose={this.props.onCancel}
onSubmit={this.handleConfirm}
>
<Field
label={<Label text="Script" />}
input={
<Dropdown value={this.state.currentScript?.title} onChange={this.handleScriptChange}>
{validScripts.map(script => (
<Option key={script.title} value={script.title}>
{script.title}
</Option>
))}
</Dropdown>
}
/>
</FormModal>
);
}
}

0 comments on commit 5d9901e

Please sign in to comment.