From 53d992e3126b90ac8c861d1e60a10d04f27c244a Mon Sep 17 00:00:00 2001 From: Rohit Date: Thu, 1 May 2025 18:06:58 +0530 Subject: [PATCH 1/3] feat: multiple capture list limit --- src/components/robot/RobotEdit.tsx | 121 ++++++++++++++++++++++------- 1 file changed, 94 insertions(+), 27 deletions(-) diff --git a/src/components/robot/RobotEdit.tsx b/src/components/robot/RobotEdit.tsx index 077c97ccd..3b110ba17 100644 --- a/src/components/robot/RobotEdit.tsx +++ b/src/components/robot/RobotEdit.tsx @@ -73,6 +73,13 @@ interface GroupedCredentials { others: string[]; } +interface ScrapeListLimit { + pairIndex: number; + actionIndex: number; + argIndex: number; + currentLimit: number; +} + export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettings }: RobotSettingsProps) => { const { t } = useTranslation(); const [credentials, setCredentials] = useState({}); @@ -85,6 +92,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin others: [] }); const [showPasswords, setShowPasswords] = useState({}); + const [scrapeListLimits, setScrapeListLimits] = useState([]); const isEmailPattern = (value: string): boolean => { return value.includes('@'); @@ -120,9 +128,36 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin const extractedCredentials = extractInitialCredentials(robot.recording.workflow); setCredentials(extractedCredentials); setCredentialGroups(groupCredentialsByType(extractedCredentials)); + + findScrapeListLimits(robot.recording.workflow); } }, [robot]); + const findScrapeListLimits = (workflow: WhereWhatPair[]) => { + const limits: ScrapeListLimit[] = []; + + workflow.forEach((pair, pairIndex) => { + if (!pair.what) return; + + pair.what.forEach((action, actionIndex) => { + if (action.action === 'scrapeList' && action.args && action.args.length > 0) { + // Check if first argument has a limit property + const arg = action.args[0]; + if (arg && typeof arg === 'object' && 'limit' in arg) { + limits.push({ + pairIndex, + actionIndex, + argIndex: 0, + currentLimit: arg.limit + }); + } + } + }); + }); + + setScrapeListLimits(limits); + }; + function extractInitialCredentials(workflow: any[]): Credentials { const credentials: Credentials = {}; @@ -285,20 +320,30 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin })); }; - const handleLimitChange = (newLimit: number) => { + const handleLimitChange = (pairIndex: number, actionIndex: number, argIndex: number, newLimit: number) => { setRobot((prev) => { if (!prev) return prev; const updatedWorkflow = [...prev.recording.workflow]; if ( - updatedWorkflow.length > 0 && - updatedWorkflow[0]?.what && - updatedWorkflow[0].what.length > 0 && - updatedWorkflow[0].what[0].args && - updatedWorkflow[0].what[0].args.length > 0 && - updatedWorkflow[0].what[0].args[0] + updatedWorkflow.length > pairIndex && + updatedWorkflow[pairIndex]?.what && + updatedWorkflow[pairIndex].what.length > actionIndex && + updatedWorkflow[pairIndex].what[actionIndex].args && + updatedWorkflow[pairIndex].what[actionIndex].args.length > argIndex ) { - updatedWorkflow[0].what[0].args[0].limit = newLimit; + updatedWorkflow[pairIndex].what[actionIndex].args[argIndex].limit = newLimit; + + setScrapeListLimits(prev => { + return prev.map(item => { + if (item.pairIndex === pairIndex && + item.actionIndex === actionIndex && + item.argIndex === argIndex) { + return { ...item, currentLimit: newLimit }; + } + return item; + }); + }); } return { ...prev, recording: { ...prev.recording, workflow: updatedWorkflow } }; @@ -358,9 +403,6 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin return ( <> - {/* - {headerText} - */} {selectors.map((selector, index) => { const isVisible = showPasswords[selector]; @@ -393,6 +435,40 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin ); }; + const renderScrapeListLimitFields = () => { + if (scrapeListLimits.length === 0) return null; + + return ( + <> + + {t('List Limits')} + + + {scrapeListLimits.map((limitInfo, index) => ( + { + const value = parseInt(e.target.value, 10); + if (value >= 1) { + handleLimitChange( + limitInfo.pairIndex, + limitInfo.actionIndex, + limitInfo.argIndex, + value + ); + } + }} + inputProps={{ min: 1 }} + style={{ marginBottom: '20px' }} + /> + ))} + + ); + }; + const handleSave = async () => { if (!robot) return; @@ -412,7 +488,12 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin const payload = { name: robot.recording_meta.name, - limit: robot.recording.workflow[0]?.what[0]?.args?.[0]?.limit, + limits: scrapeListLimits.map(limit => ({ + pairIndex: limit.pairIndex, + actionIndex: limit.actionIndex, + argIndex: limit.argIndex, + limit: limit.currentLimit + })), credentials: credentialsForPayload, targetUrl: targetUrl, }; @@ -468,21 +549,7 @@ export const RobotEditModal = ({ isOpen, handleStart, handleClose, initialSettin style={{ marginBottom: '20px' }} /> - {robot.recording.workflow?.[0]?.what?.[0]?.args?.[0]?.limit !== undefined && ( - { - const value = parseInt(e.target.value, 10); - if (value >= 1) { - handleLimitChange(value); - } - }} - inputProps={{ min: 1 }} - style={{ marginBottom: '20px' }} - /> - )} + {renderScrapeListLimitFields()} {(Object.keys(credentials).length > 0) && ( <> From 1535c05fa5bb1b6eabce6d6f67d3c3da109dfb76 Mon Sep 17 00:00:00 2001 From: Rohit Date: Thu, 1 May 2025 18:07:36 +0530 Subject: [PATCH 2/3] feat: edit update recording --- src/api/storage.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/api/storage.ts b/src/api/storage.ts index e3359563c..295f340c9 100644 --- a/src/api/storage.ts +++ b/src/api/storage.ts @@ -28,7 +28,12 @@ export const getStoredRecordings = async (): Promise => { } }; -export const updateRecording = async (id: string, data: { name?: string; limit?: number, credentials?: Credentials, targetUrl?: string }): Promise => { +export const updateRecording = async (id: string, data: { + name?: string; + limits?: Array<{pairIndex: number, actionIndex: number, argIndex: number, limit: number}>; + credentials?: Credentials; + targetUrl?: string +}): Promise => { try { const response = await axios.put(`${apiUrl}/storage/recordings/${id}`, data); if (response.status === 200) { From 24f3d50fd872c98fb479570b8b5959e1d47ed64e Mon Sep 17 00:00:00 2001 From: Rohit Date: Thu, 1 May 2025 18:09:27 +0530 Subject: [PATCH 3/3] feat: edit multiple list limits, change landing url --- server/src/routes/storage.ts | 84 +++++++++++++++--------------------- 1 file changed, 35 insertions(+), 49 deletions(-) diff --git a/server/src/routes/storage.ts b/server/src/routes/storage.ts index e7e3939c0..b91a0cfa5 100644 --- a/server/src/routes/storage.ts +++ b/server/src/routes/storage.ts @@ -254,11 +254,11 @@ function handleWorkflowActions(workflow: any[], credentials: Credentials) { router.put('/recordings/:id', requireSignIn, async (req: AuthenticatedRequest, res) => { try { const { id } = req.params; - const { name, limit, credentials, targetUrl } = req.body; + const { name, limits, credentials, targetUrl } = req.body; // Validate input - if (!name && limit === undefined && !targetUrl) { - return res.status(400).json({ error: 'Either "name", "limit" or "target_url" must be provided.' }); + if (!name && !limits && !credentials && !targetUrl) { + return res.status(400).json({ error: 'Either "name", "limits", "credentials" or "target_url" must be provided.' }); } // Fetch the robot by ID @@ -274,22 +274,26 @@ router.put('/recordings/:id', requireSignIn, async (req: AuthenticatedRequest, r } if (targetUrl) { - const updatedWorkflow = robot.recording.workflow.map((step) => { - if (step.where?.url && step.where.url !== "about:blank") { - step.where.url = targetUrl; - } - - step.what.forEach((action) => { + const updatedWorkflow = [...robot.recording.workflow]; + + for (let i = updatedWorkflow.length - 1; i >= 0; i--) { + const step = updatedWorkflow[i]; + for (let j = 0; j < step.what.length; j++) { + const action = step.what[j]; if (action.action === "goto" && action.args?.length) { - action.args[0] = targetUrl; - } - }); - - return step; - }); - robot.set('recording', { ...robot.recording, workflow: updatedWorkflow }); - robot.changed('recording', true); + action.args[0] = targetUrl; + if (step.where?.url && step.where.url !== "about:blank") { + step.where.url = targetUrl; + } + + robot.set('recording', { ...robot.recording, workflow: updatedWorkflow }); + robot.changed('recording', true); + i = -1; + break; + } + } + } } await robot.save(); @@ -300,38 +304,20 @@ router.put('/recordings/:id', requireSignIn, async (req: AuthenticatedRequest, r workflow = handleWorkflowActions(workflow, credentials); } - // Update the limit - if (limit !== undefined) { - // Ensure the workflow structure is valid before updating - if ( - workflow.length > 0 && - workflow[0]?.what?.[0] - ) { - // Create a new workflow object with the updated limit - workflow = workflow.map((step, index) => { - if (index === 0) { // Assuming you want to update the first step - return { - ...step, - what: step.what.map((action, actionIndex) => { - if (actionIndex === 0) { // Assuming the first action needs updating - return { - ...action, - args: (action.args ?? []).map((arg, argIndex) => { - if (argIndex === 0) { // Assuming the first argument needs updating - return { ...arg, limit }; - } - return arg; - }), - }; - } - return action; - }), - }; - } - return step; - }); - } else { - return res.status(400).json({ error: 'Invalid workflow structure for updating limit.' }); + if (limits && Array.isArray(limits) && limits.length > 0) { + for (const limitInfo of limits) { + const { pairIndex, actionIndex, argIndex, limit } = limitInfo; + + const pair = workflow[pairIndex]; + if (!pair || !pair.what) continue; + + const action = pair.what[actionIndex]; + if (!action || !action.args) continue; + + const arg = action.args[argIndex]; + if (!arg || typeof arg !== 'object') continue; + + (arg as { limit: number }).limit = limit; } }