mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 03:20:46 +02:00
[V2] fix(automation): enhance parameter handling and default values across operations, fix error in ManyToOne tools (#5123)
# Description of Changes This pull request focuses on improving the handling of tool parameters in the frontend automation and tool operation system. The main changes ensure that default parameter values are consistently applied, missing or undefined values are handled gracefully, and custom tools like Automate and Merge correctly track input file consumption. These updates enhance reliability and reduce errors due to missing or improperly formatted parameters. **Parameter Handling and Defaults:** * Tool configuration and automation forms now merge user-provided parameters with tool-specific default parameters, guaranteeing all required fields are present during initialization and execution (`ToolConfigurationModal.tsx`, `useAutomationForm.ts`, `automationExecutor.ts`). * All tool operation hooks (e.g., Add Watermark, Merge, Sanitize, Split, Compress, Change Permissions, OCR) have been updated to safely handle missing or undefined parameter values by providing sensible defaults, preventing runtime errors and backend issues (`useAddWatermarkOperation.ts`, `useMergeOperation.ts`, `useSanitizeOperation.ts`, `useSplitOperation.ts`, `useCompressOperation.ts`, `useChangePermissionsOperation.ts`, `useOCROperation.ts`). **Tool Operation Configuration Enhancements:** * The `BaseToolOperationConfig` interface now supports a `consumesAllInputs` flag, allowing tools with complex input-output relationships (like Automate and Merge) to mark all input files as successfully processed when appropriate (`useToolOperation.ts`). * The Automate tool operation is configured to use this new flag, and the core hook logic is updated to respect it when determining success (`useAutomateOperation.ts`, `useToolOperation.ts`). **Registry and Configuration Consistency:** * Default parameters are now explicitly included in tool operation configs (e.g., Merge), ensuring consistency between registry definitions and runtime behavior (`useMergeOperation.ts`). **Dependency Updates:** * React hooks and callbacks now correctly include dependencies related to parameter defaults to avoid stale values in forms and modals (`useAutomationForm.ts`). These changes collectively improve the robustness and maintainability of the automation and tool configuration system. <!-- Please provide a summary of the changes, including: - What was changed - Why the change was made - Any challenges encountered Closes #(issue_number) --> --- ## Checklist ### General - [X] I have read the [Contribution Guidelines](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/CONTRIBUTING.md) - [ ] I have read the [Stirling-PDF Developer Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md) (if applicable) - [X] I have read the [How to add new languages to Stirling-PDF](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md) (if applicable) - [X] I have performed a self-review of my own code - [X] My changes generate no new warnings ### Documentation - [ ] I have updated relevant docs on [Stirling-PDF's doc repo](https://github.com/Stirling-Tools/Stirling-Tools.github.io/blob/main/docs/) (if functionality has heavily changed) - [ ] I have read the section [Add New Translation Tags](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/HowToAddNewLanguage.md#add-new-translation-tags) (for new translation tags only) ### Translations (if applicable) - [ ] I ran [`scripts/counter_translation.py`](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/docs/counter_translation.md) ### UI Changes (if applicable) - [ ] Screenshots or videos demonstrating the UI changes are attached (e.g., as comments or direct attachments in the PR) ### Testing (if applicable) - [X] I have tested my changes locally. Refer to the [Testing Guide](https://github.com/Stirling-Tools/Stirling-PDF/blob/main/devGuide/DeveloperGuide.md#6-testing) for more details. --------- Signed-off-by: Balázs Szücs <[email protected]> Co-authored-by: Reece Browne <[email protected]>
This commit is contained in:
co-authored by
Reece Browne
parent
8bb807471f
commit
bd75ad042a
@@ -18,7 +18,7 @@ export const buildAddWatermarkFormData = (parameters: AddWatermarkParameters, fi
|
||||
formData.append("watermarkImage", parameters.watermarkImage);
|
||||
}
|
||||
|
||||
// Required parameters with correct formatting
|
||||
// Required parameters with correct formatting (defaults merged in automationExecutor)
|
||||
formData.append("fontSize", parameters.fontSize.toString());
|
||||
formData.append("rotation", parameters.rotation.toString());
|
||||
formData.append("opacity", (parameters.opacity / 100).toString()); // Convert percentage to decimal
|
||||
@@ -26,9 +26,9 @@ export const buildAddWatermarkFormData = (parameters: AddWatermarkParameters, fi
|
||||
formData.append("heightSpacer", parameters.heightSpacer.toString());
|
||||
|
||||
// Backend-expected parameters from user input
|
||||
formData.append("alphabet", parameters.alphabet);
|
||||
formData.append("customColor", parameters.customColor);
|
||||
formData.append("convertPDFToImage", parameters.convertPDFToImage.toString());
|
||||
formData.append("alphabet", parameters.alphabet || "");
|
||||
formData.append("customColor", parameters.customColor || "");
|
||||
formData.append("convertPDFToImage", (parameters.convertPDFToImage ?? false).toString());
|
||||
|
||||
return formData;
|
||||
};
|
||||
|
||||
@@ -38,7 +38,7 @@ export function useAutomateOperation() {
|
||||
console.log(`✅ Automation completed, returning ${finalResults.length} files`);
|
||||
return {
|
||||
files: finalResults,
|
||||
consumedAllInputs: false,
|
||||
consumedAllInputs: true,
|
||||
};
|
||||
}, [toolRegistry]);
|
||||
|
||||
@@ -46,5 +46,6 @@ export function useAutomateOperation() {
|
||||
toolType: ToolType.custom,
|
||||
operationType: 'automate',
|
||||
customProcessor,
|
||||
consumesAllInputs: true,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ export function useAutomationForm({ mode, existingAutomation, toolRegistry }: Us
|
||||
operation: operation,
|
||||
name: getToolName(operation),
|
||||
configured: isConfigured,
|
||||
parameters: typeof op === 'object' ? op.parameters || {} : {}
|
||||
parameters: typeof op === 'object' ? { ...getToolDefaultParameters(operation), ...(op.parameters || {}) } : getToolDefaultParameters(operation)
|
||||
};
|
||||
});
|
||||
|
||||
@@ -68,7 +68,7 @@ export function useAutomationForm({ mode, existingAutomation, toolRegistry }: Us
|
||||
}));
|
||||
setSelectedTools(defaultTools);
|
||||
}
|
||||
}, [mode, existingAutomation, t, getToolName]);
|
||||
}, [mode, existingAutomation, t, getToolName, getToolDefaultParameters]);
|
||||
|
||||
const addTool = (operation: string) => {
|
||||
const toolEntry = toolRegistry[operation as ToolId];
|
||||
|
||||
@@ -3,11 +3,12 @@ import { ToolType, useToolOperation } from '@app/hooks/tools/shared/useToolOpera
|
||||
import { createStandardErrorHandler } from '@app/utils/toolErrorHandler';
|
||||
import { ChangePermissionsParameters, defaultParameters } from '@app/hooks/tools/changePermissions/useChangePermissionsParameters';
|
||||
|
||||
export const getFormData = ((parameters: ChangePermissionsParameters) =>
|
||||
Object.entries(parameters).map(([key, value]) =>
|
||||
[key, value.toString()]
|
||||
) as string[][]
|
||||
);
|
||||
export const getFormData = ((parameters: ChangePermissionsParameters) => {
|
||||
if (!parameters) return [];
|
||||
return Object.entries(parameters).map(([key, value]) =>
|
||||
[key, (value ?? false).toString()]
|
||||
) as string[][];
|
||||
});
|
||||
|
||||
// Static function that can be used by both the hook and automation executor
|
||||
export const buildChangePermissionsFormData = (parameters: ChangePermissionsParameters, file: File): FormData => {
|
||||
|
||||
@@ -18,7 +18,7 @@ export const buildCompressFormData = (parameters: CompressParameters, file: File
|
||||
}
|
||||
}
|
||||
|
||||
formData.append("grayscale", parameters.grayscale.toString());
|
||||
formData.append("grayscale", (parameters.grayscale ?? false).toString());
|
||||
formData.append("lineArt", parameters.lineArt.toString());
|
||||
formData.append("linearize", parameters.linearize.toString());
|
||||
if (parameters.lineArt) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useToolOperation, ToolOperationConfig, ToolType } from '@app/hooks/tools/shared/useToolOperation';
|
||||
import { createStandardErrorHandler } from '@app/utils/toolErrorHandler';
|
||||
import { MergeParameters } from '@app/hooks/tools/merge/useMergeParameters';
|
||||
import { MergeParameters, defaultParameters } from '@app/hooks/tools/merge/useMergeParameters';
|
||||
|
||||
const buildFormData = (parameters: MergeParameters, files: File[]): FormData => {
|
||||
const formData = new FormData();
|
||||
@@ -13,8 +13,8 @@ const buildFormData = (parameters: MergeParameters, files: File[]): FormData =>
|
||||
const clientIds: string[] = files.map((f: any) => String((f as any).fileId || f.name));
|
||||
formData.append('clientFileIds', JSON.stringify(clientIds));
|
||||
formData.append("sortType", "orderProvided"); // Always use orderProvided since UI handles sorting
|
||||
formData.append("removeCertSign", parameters.removeDigitalSignature.toString());
|
||||
formData.append("generateToc", parameters.generateTableOfContents.toString());
|
||||
formData.append("removeCertSign", (parameters.removeDigitalSignature ?? false).toString());
|
||||
formData.append("generateToc", (parameters.generateTableOfContents ?? false).toString());
|
||||
|
||||
return formData;
|
||||
};
|
||||
@@ -26,6 +26,7 @@ export const mergeOperationConfig: ToolOperationConfig<MergeParameters> = {
|
||||
operationType: 'merge',
|
||||
endpoint: '/api/v1/general/merge-pdfs',
|
||||
filePrefix: 'merged_',
|
||||
defaultParameters,
|
||||
};
|
||||
|
||||
export const useMergeOperation = () => {
|
||||
|
||||
@@ -44,11 +44,13 @@ export const buildOCRFormData = (parameters: OCRParameters, file: File): FormDat
|
||||
parameters.languages.forEach((lang) => formData.append('languages', lang));
|
||||
formData.append('ocrType', parameters.ocrType);
|
||||
formData.append('ocrRenderType', parameters.ocrRenderType);
|
||||
formData.append('sidecar', parameters.additionalOptions.includes('sidecar').toString());
|
||||
formData.append('deskew', parameters.additionalOptions.includes('deskew').toString());
|
||||
formData.append('clean', parameters.additionalOptions.includes('clean').toString());
|
||||
formData.append('cleanFinal', parameters.additionalOptions.includes('cleanFinal').toString());
|
||||
formData.append('removeImagesAfter', parameters.additionalOptions.includes('removeImagesAfter').toString());
|
||||
|
||||
const options = parameters.additionalOptions || [];
|
||||
formData.append('sidecar', options.includes('sidecar').toString());
|
||||
formData.append('deskew', options.includes('deskew').toString());
|
||||
formData.append('clean', options.includes('clean').toString());
|
||||
formData.append('cleanFinal', options.includes('cleanFinal').toString());
|
||||
formData.append('removeImagesAfter', options.includes('removeImagesAfter').toString());
|
||||
return formData;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ export const buildSanitizeFormData = (parameters: SanitizeParameters, file: File
|
||||
formData.append('fileInput', file);
|
||||
|
||||
// Add parameters
|
||||
formData.append('removeJavaScript', parameters.removeJavaScript.toString());
|
||||
formData.append('removeEmbeddedFiles', parameters.removeEmbeddedFiles.toString());
|
||||
formData.append('removeXMPMetadata', parameters.removeXMPMetadata.toString());
|
||||
formData.append('removeMetadata', parameters.removeMetadata.toString());
|
||||
formData.append('removeLinks', parameters.removeLinks.toString());
|
||||
formData.append('removeFonts', parameters.removeFonts.toString());
|
||||
formData.append('removeJavaScript', (parameters.removeJavaScript ?? false).toString());
|
||||
formData.append('removeEmbeddedFiles', (parameters.removeEmbeddedFiles ?? false).toString());
|
||||
formData.append('removeXMPMetadata', (parameters.removeXMPMetadata ?? false).toString());
|
||||
formData.append('removeMetadata', (parameters.removeMetadata ?? false).toString());
|
||||
formData.append('removeLinks', (parameters.removeLinks ?? false).toString());
|
||||
formData.append('removeFonts', (parameters.removeFonts ?? false).toString());
|
||||
|
||||
return formData;
|
||||
};
|
||||
|
||||
@@ -71,6 +71,13 @@ interface BaseToolOperationConfig<TParams> {
|
||||
|
||||
/** Default parameter values for automation */
|
||||
defaultParameters?: TParams;
|
||||
|
||||
/**
|
||||
* For custom tools: if true, success implies all input files were successfully processed.
|
||||
* Use this for tools like Automate or Merge where Many-to-One relationships exist
|
||||
* and exact input-output mapping is difficult.
|
||||
*/
|
||||
consumesAllInputs?: boolean;
|
||||
}
|
||||
|
||||
export interface SingleFileToolOperationConfig<TParams> extends BaseToolOperationConfig<TParams> {
|
||||
|
||||
@@ -19,7 +19,7 @@ export const buildSplitFormData = (parameters: SplitParameters, file: File): For
|
||||
case SPLIT_METHODS.BY_SECTIONS:
|
||||
formData.append("horizontalDivisions", parameters.hDiv);
|
||||
formData.append("verticalDivisions", parameters.vDiv);
|
||||
formData.append("merge", parameters.merge.toString());
|
||||
formData.append("merge", (parameters.merge ?? false).toString());
|
||||
formData.append("splitMode", parameters.splitMode || 'SPLIT_ALL');
|
||||
if (parameters.splitMode === 'CUSTOM' && parameters.customPages) {
|
||||
formData.append("pageNumbers", parameters.customPages);
|
||||
@@ -39,11 +39,11 @@ export const buildSplitFormData = (parameters: SplitParameters, file: File): For
|
||||
break;
|
||||
case SPLIT_METHODS.BY_CHAPTERS:
|
||||
formData.append("bookmarkLevel", parameters.bookmarkLevel);
|
||||
formData.append("includeMetadata", parameters.includeMetadata.toString());
|
||||
formData.append("allowDuplicates", parameters.allowDuplicates.toString());
|
||||
formData.append("includeMetadata", (parameters.includeMetadata ?? false).toString());
|
||||
formData.append("allowDuplicates", (parameters.allowDuplicates ?? false).toString());
|
||||
break;
|
||||
case SPLIT_METHODS.BY_PAGE_DIVIDER:
|
||||
formData.append("duplexMode", parameters.duplexMode.toString());
|
||||
formData.append("duplexMode", (parameters.duplexMode ?? false).toString());
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown split method: ${parameters.method}`);
|
||||
|
||||
Reference in New Issue
Block a user