Skip to content

Commit 5e404f8

Browse files
authored
Merge pull request #579 from getmaxun/multiple-limit
feat: support for editing multiple capture list limits
2 parents 323a610 + 24f3d50 commit 5e404f8

File tree

3 files changed

+135
-77
lines changed

3 files changed

+135
-77
lines changed

server/src/routes/storage.ts

Lines changed: 35 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,11 @@ function handleWorkflowActions(workflow: any[], credentials: Credentials) {
254254
router.put('/recordings/:id', requireSignIn, async (req: AuthenticatedRequest, res) => {
255255
try {
256256
const { id } = req.params;
257-
const { name, limit, credentials, targetUrl } = req.body;
257+
const { name, limits, credentials, targetUrl } = req.body;
258258

259259
// Validate input
260-
if (!name && limit === undefined && !targetUrl) {
261-
return res.status(400).json({ error: 'Either "name", "limit" or "target_url" must be provided.' });
260+
if (!name && !limits && !credentials && !targetUrl) {
261+
return res.status(400).json({ error: 'Either "name", "limits", "credentials" or "target_url" must be provided.' });
262262
}
263263

264264
// Fetch the robot by ID
@@ -274,22 +274,26 @@ router.put('/recordings/:id', requireSignIn, async (req: AuthenticatedRequest, r
274274
}
275275

276276
if (targetUrl) {
277-
const updatedWorkflow = robot.recording.workflow.map((step) => {
278-
if (step.where?.url && step.where.url !== "about:blank") {
279-
step.where.url = targetUrl;
280-
}
281-
282-
step.what.forEach((action) => {
277+
const updatedWorkflow = [...robot.recording.workflow];
278+
279+
for (let i = updatedWorkflow.length - 1; i >= 0; i--) {
280+
const step = updatedWorkflow[i];
281+
for (let j = 0; j < step.what.length; j++) {
282+
const action = step.what[j];
283283
if (action.action === "goto" && action.args?.length) {
284-
action.args[0] = targetUrl;
285-
}
286-
});
287-
288-
return step;
289-
});
290284

291-
robot.set('recording', { ...robot.recording, workflow: updatedWorkflow });
292-
robot.changed('recording', true);
285+
action.args[0] = targetUrl;
286+
if (step.where?.url && step.where.url !== "about:blank") {
287+
step.where.url = targetUrl;
288+
}
289+
290+
robot.set('recording', { ...robot.recording, workflow: updatedWorkflow });
291+
robot.changed('recording', true);
292+
i = -1;
293+
break;
294+
}
295+
}
296+
}
293297
}
294298

295299
await robot.save();
@@ -300,38 +304,20 @@ router.put('/recordings/:id', requireSignIn, async (req: AuthenticatedRequest, r
300304
workflow = handleWorkflowActions(workflow, credentials);
301305
}
302306

303-
// Update the limit
304-
if (limit !== undefined) {
305-
// Ensure the workflow structure is valid before updating
306-
if (
307-
workflow.length > 0 &&
308-
workflow[0]?.what?.[0]
309-
) {
310-
// Create a new workflow object with the updated limit
311-
workflow = workflow.map((step, index) => {
312-
if (index === 0) { // Assuming you want to update the first step
313-
return {
314-
...step,
315-
what: step.what.map((action, actionIndex) => {
316-
if (actionIndex === 0) { // Assuming the first action needs updating
317-
return {
318-
...action,
319-
args: (action.args ?? []).map((arg, argIndex) => {
320-
if (argIndex === 0) { // Assuming the first argument needs updating
321-
return { ...arg, limit };
322-
}
323-
return arg;
324-
}),
325-
};
326-
}
327-
return action;
328-
}),
329-
};
330-
}
331-
return step;
332-
});
333-
} else {
334-
return res.status(400).json({ error: 'Invalid workflow structure for updating limit.' });
307+
if (limits && Array.isArray(limits) && limits.length > 0) {
308+
for (const limitInfo of limits) {
309+
const { pairIndex, actionIndex, argIndex, limit } = limitInfo;
310+
311+
const pair = workflow[pairIndex];
312+
if (!pair || !pair.what) continue;
313+
314+
const action = pair.what[actionIndex];
315+
if (!action || !action.args) continue;
316+
317+
const arg = action.args[argIndex];
318+
if (!arg || typeof arg !== 'object') continue;
319+
320+
(arg as { limit: number }).limit = limit;
335321
}
336322
}
337323

src/api/storage.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ export const getStoredRecordings = async (): Promise<string[] | null> => {
2828
}
2929
};
3030

31-
export const updateRecording = async (id: string, data: { name?: string; limit?: number, credentials?: Credentials, targetUrl?: string }): Promise<boolean> => {
31+
export const updateRecording = async (id: string, data: {
32+
name?: string;
33+
limits?: Array<{pairIndex: number, actionIndex: number, argIndex: number, limit: number}>;
34+
credentials?: Credentials;
35+
targetUrl?: string
36+
}): Promise<boolean> => {
3237
try {
3338
const response = await axios.put(`${apiUrl}/storage/recordings/${id}`, data);
3439
if (response.status === 200) {

src/components/robot/RobotEdit.tsx

Lines changed: 94 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,13 @@ interface GroupedCredentials {
7373
others: string[];
7474
}
7575

76+
interface ScrapeListLimit {
77+
pairIndex: number;
78+
actionIndex: number;
79+
argIndex: number;
80+
currentLimit: number;
81+
}
82+
7683
export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettings }: RobotSettingsProps) => {
7784
const { t } = useTranslation();
7885
const [credentials, setCredentials] = useState<Credentials>({});
@@ -85,6 +92,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin
8592
others: []
8693
});
8794
const [showPasswords, setShowPasswords] = useState<CredentialVisibility>({});
95+
const [scrapeListLimits, setScrapeListLimits] = useState<ScrapeListLimit[]>([]);
8896

8997
const isEmailPattern = (value: string): boolean => {
9098
return value.includes('@');
@@ -120,9 +128,36 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin
120128
const extractedCredentials = extractInitialCredentials(robot.recording.workflow);
121129
setCredentials(extractedCredentials);
122130
setCredentialGroups(groupCredentialsByType(extractedCredentials));
131+
132+
findScrapeListLimits(robot.recording.workflow);
123133
}
124134
}, [robot]);
125135

136+
const findScrapeListLimits = (workflow: WhereWhatPair[]) => {
137+
const limits: ScrapeListLimit[] = [];
138+
139+
workflow.forEach((pair, pairIndex) => {
140+
if (!pair.what) return;
141+
142+
pair.what.forEach((action, actionIndex) => {
143+
if (action.action === 'scrapeList' && action.args && action.args.length > 0) {
144+
// Check if first argument has a limit property
145+
const arg = action.args[0];
146+
if (arg && typeof arg === 'object' && 'limit' in arg) {
147+
limits.push({
148+
pairIndex,
149+
actionIndex,
150+
argIndex: 0,
151+
currentLimit: arg.limit
152+
});
153+
}
154+
}
155+
});
156+
});
157+
158+
setScrapeListLimits(limits);
159+
};
160+
126161
function extractInitialCredentials(workflow: any[]): Credentials {
127162
const credentials: Credentials = {};
128163

@@ -285,20 +320,30 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin
285320
}));
286321
};
287322

288-
const handleLimitChange = (newLimit: number) => {
323+
const handleLimitChange = (pairIndex: number, actionIndex: number, argIndex: number, newLimit: number) => {
289324
setRobot((prev) => {
290325
if (!prev) return prev;
291326

292327
const updatedWorkflow = [...prev.recording.workflow];
293328
if (
294-
updatedWorkflow.length > 0 &&
295-
updatedWorkflow[0]?.what &&
296-
updatedWorkflow[0].what.length > 0 &&
297-
updatedWorkflow[0].what[0].args &&
298-
updatedWorkflow[0].what[0].args.length > 0 &&
299-
updatedWorkflow[0].what[0].args[0]
329+
updatedWorkflow.length > pairIndex &&
330+
updatedWorkflow[pairIndex]?.what &&
331+
updatedWorkflow[pairIndex].what.length > actionIndex &&
332+
updatedWorkflow[pairIndex].what[actionIndex].args &&
333+
updatedWorkflow[pairIndex].what[actionIndex].args.length > argIndex
300334
) {
301-
updatedWorkflow[0].what[0].args[0].limit = newLimit;
335+
updatedWorkflow[pairIndex].what[actionIndex].args[argIndex].limit = newLimit;
336+
337+
setScrapeListLimits(prev => {
338+
return prev.map(item => {
339+
if (item.pairIndex === pairIndex &&
340+
item.actionIndex === actionIndex &&
341+
item.argIndex === argIndex) {
342+
return { ...item, currentLimit: newLimit };
343+
}
344+
return item;
345+
});
346+
});
302347
}
303348

304349
return { ...prev, recording: { ...prev.recording, workflow: updatedWorkflow } };
@@ -358,9 +403,6 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin
358403

359404
return (
360405
<>
361-
{/* <Typography variant="h6" style={{ marginBottom: '20px' }}>
362-
{headerText}
363-
</Typography> */}
364406
{selectors.map((selector, index) => {
365407
const isVisible = showPasswords[selector];
366408

@@ -393,6 +435,40 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin
393435
);
394436
};
395437

438+
const renderScrapeListLimitFields = () => {
439+
if (scrapeListLimits.length === 0) return null;
440+
441+
return (
442+
<>
443+
<Typography variant="body1" style={{ marginBottom: '20px' }}>
444+
{t('List Limits')}
445+
</Typography>
446+
447+
{scrapeListLimits.map((limitInfo, index) => (
448+
<TextField
449+
key={`limit-${limitInfo.pairIndex}-${limitInfo.actionIndex}`}
450+
label={`${t('List Limit')} ${index + 1}`}
451+
type="number"
452+
value={limitInfo.currentLimit || ''}
453+
onChange={(e) => {
454+
const value = parseInt(e.target.value, 10);
455+
if (value >= 1) {
456+
handleLimitChange(
457+
limitInfo.pairIndex,
458+
limitInfo.actionIndex,
459+
limitInfo.argIndex,
460+
value
461+
);
462+
}
463+
}}
464+
inputProps={{ min: 1 }}
465+
style={{ marginBottom: '20px' }}
466+
/>
467+
))}
468+
</>
469+
);
470+
};
471+
396472
const handleSave = async () => {
397473
if (!robot) return;
398474

@@ -412,7 +488,12 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin
412488

413489
const payload = {
414490
name: robot.recording_meta.name,
415-
limit: robot.recording.workflow[0]?.what[0]?.args?.[0]?.limit,
491+
limits: scrapeListLimits.map(limit => ({
492+
pairIndex: limit.pairIndex,
493+
actionIndex: limit.actionIndex,
494+
argIndex: limit.argIndex,
495+
limit: limit.currentLimit
496+
})),
416497
credentials: credentialsForPayload,
417498
targetUrl: targetUrl,
418499
};
@@ -468,21 +549,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin
468549
style={{ marginBottom: '20px' }}
469550
/>
470551

471-
{robot.recording.workflow?.[0]?.what?.[0]?.args?.[0]?.limit !== undefined && (
472-
<TextField
473-
label={t('robot_edit.robot_limit')}
474-
type="number"
475-
value={robot.recording.workflow[0].what[0].args[0].limit || ''}
476-
onChange={(e) => {
477-
const value = parseInt(e.target.value, 10);
478-
if (value >= 1) {
479-
handleLimitChange(value);
480-
}
481-
}}
482-
inputProps={{ min: 1 }}
483-
style={{ marginBottom: '20px' }}
484-
/>
485-
)}
552+
{renderScrapeListLimitFields()}
486553

487554
{(Object.keys(credentials).length > 0) && (
488555
<>

0 commit comments

Comments
 (0)