Skip to content

Commit 8feac9b

Browse files
authored
feat: Add optional restriction of script execution to certain object fields and values (#2488)
1 parent ed3312f commit 8feac9b

File tree

5 files changed

+64
-6
lines changed

5 files changed

+64
-6
lines changed

README.md

+25
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,31 @@ You can specify scripts to execute Cloud Functions with the `scripts` option:
398398
]
399399
```
400400

401+
You can also specify custom fields with the `scrips` option:
402+
403+
```json
404+
"apps": [
405+
{
406+
"scripts": [
407+
{
408+
"title": "Delete account",
409+
"classes": [
410+
{
411+
"name": "_User",
412+
"fields": [
413+
{ "name": "createdAt", "validator": "value => value > new Date(\"2025\")" }
414+
]
415+
}
416+
],
417+
"cloudCodeFunction": "deleteAccount"
418+
}
419+
]
420+
}
421+
]
422+
423+
```
424+
425+
401426
Next, define the Cloud Function in Parse Server that will be called. The object that has been selected in the data browser will be made available as a request parameter:
402427

403428
```js

src/components/BrowserCell/BrowserCell.react.js

+31-6
Original file line numberDiff line numberDiff line change
@@ -336,23 +336,48 @@ export default class BrowserCell extends Component {
336336
});
337337
}
338338

339-
const { className, objectId } = this.props;
340-
const validScripts = (this.props.scripts || []).filter(script =>
341-
script.classes?.includes(this.props.className)
342-
);
339+
const { className, objectId, field, scripts = [], rowValue } = this.props;
340+
let validator = null;
341+
const validScripts = (scripts || []).filter(script => {
342+
if (script.classes?.includes(className)) {
343+
return true;
344+
}
345+
for (const script of script?.classes || []) {
346+
if (script?.name !== className) {
347+
continue;
348+
}
349+
const fields = script?.fields || [];
350+
if (script?.fields.includes(field) || script?.fields.includes('*')) {
351+
return true;
352+
}
353+
for (const currentField of fields) {
354+
if (Object.prototype.toString.call(currentField) === '[object Object]') {
355+
if (currentField.name === field) {
356+
if (typeof currentField.validator === 'string') {
357+
validator = eval(currentField.validator);
358+
} else {
359+
validator = currentField.validator;
360+
}
361+
return true;
362+
}
363+
}
364+
}
365+
}
366+
});
343367
if (validScripts.length) {
344368
onEditSelectedRow &&
345369
contextMenuOptions.push({
346370
text: 'Scripts',
347371
items: validScripts.map(script => {
348372
return {
349373
text: script.title,
374+
disabled: validator?.(rowValue, field) === false,
350375
callback: () => {
351376
this.selectedScript = { ...script, className, objectId };
352377
if (script.showConfirmationDialog) {
353378
this.toggleConfirmationDialog();
354379
} else {
355-
this.executeSript(script);
380+
this.executeScript(script);
356381
}
357382
},
358383
};
@@ -363,7 +388,7 @@ export default class BrowserCell extends Component {
363388
return contextMenuOptions;
364389
}
365390

366-
async executeSript(script) {
391+
async executeScript(script) {
367392
try {
368393
const object = Parse.Object.extend(this.props.className).createWithoutData(
369394
this.props.objectId

src/components/BrowserRow/BrowserRow.react.js

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export default class BrowserRow extends Component {
3131
order,
3232
readOnlyFields,
3333
row,
34+
rowValue,
3435
rowWidth,
3536
selection,
3637
selectRow,
@@ -122,6 +123,7 @@ export default class BrowserRow extends Component {
122123
className={className}
123124
field={name}
124125
row={row}
126+
rowValue={rowValue}
125127
col={j}
126128
type={type}
127129
readonly={isUnique || readOnlyFields.indexOf(name) > -1}

src/components/ContextMenu/ContextMenu.react.js

+4
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,11 @@ const MenuSection = ({ level, items, path, setPath, hide }) => {
7171
<li
7272
key={`menu-section-${level}-${index}`}
7373
className={styles.option}
74+
style={item.disabled ? { opacity: 0.5, cursor: 'not-allowed' } : {}}
7475
onClick={() => {
76+
if (item.disabled === true) {
77+
return;
78+
}
7579
item.callback && item.callback();
7680
hide();
7781
}}

src/dashboard/Data/Browser/BrowserTable.react.js

+2
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ export default class BrowserTable extends React.Component {
155155
order={this.props.order}
156156
readOnlyFields={READ_ONLY}
157157
row={index}
158+
rowValue={this.props.data[index]}
158159
rowWidth={rowWidth}
159160
selection={this.props.selection}
160161
selectRow={this.props.selectRow}
@@ -310,6 +311,7 @@ export default class BrowserTable extends React.Component {
310311
order={this.props.order}
311312
readOnlyFields={READ_ONLY}
312313
row={i}
314+
rowValue={this.props.data[i]}
313315
rowWidth={rowWidth}
314316
selection={this.props.selection}
315317
selectRow={this.props.selectRow}

0 commit comments

Comments
 (0)