mirror of
https://github.com/Stirling-Tools/Stirling-PDF.git
synced 2025-08-26 14:19:24 +00:00
Redesign ToolOperationConfig typing
This commit is contained in:
parent
6d7f76353e
commit
8423bda5fc
@ -20,13 +20,13 @@ vi.mock('../../../utils/toolErrorHandler', () => ({
|
||||
}));
|
||||
|
||||
// Import the mocked function
|
||||
import { ToolOperationConfig, ToolOperationHook, useToolOperation } from '../shared/useToolOperation';
|
||||
import { SingleFileToolOperationConfig, ToolOperationHook, useToolOperation } from '../shared/useToolOperation';
|
||||
|
||||
|
||||
describe('useAddPasswordOperation', () => {
|
||||
const mockUseToolOperation = vi.mocked(useToolOperation);
|
||||
|
||||
const getToolConfig = (): ToolOperationConfig<AddPasswordFullParameters> => mockUseToolOperation.mock.calls[0][0] as ToolOperationConfig<AddPasswordFullParameters>;
|
||||
const getToolConfig = () => mockUseToolOperation.mock.calls[0][0] as SingleFileToolOperationConfig<AddPasswordFullParameters>;
|
||||
|
||||
const mockToolOperationReturn: ToolOperationHook<unknown> = {
|
||||
files: [],
|
||||
@ -91,7 +91,7 @@ describe('useAddPasswordOperation', () => {
|
||||
};
|
||||
|
||||
const testFile = new File(['test content'], 'test.pdf', { type: 'application/pdf' });
|
||||
const formData = buildFormData(testParameters, testFile as any /* FIX ME */);
|
||||
const formData = buildFormData(testParameters, testFile);
|
||||
|
||||
// Verify the form data contains the file
|
||||
expect(formData.get('fileInput')).toBe(testFile);
|
||||
@ -112,7 +112,7 @@ describe('useAddPasswordOperation', () => {
|
||||
});
|
||||
|
||||
test.each([
|
||||
{ property: 'multiFileEndpoint' as const, expectedValue: false },
|
||||
{ property: 'toolType' as const, expectedValue: 'singleFile' },
|
||||
{ property: 'endpoint' as const, expectedValue: '/api/v1/security/add-password' },
|
||||
{ property: 'filePrefix' as const, expectedValue: 'translated-addPassword.filenamePrefix_' },
|
||||
{ property: 'operationType' as const, expectedValue: 'addPassword' }
|
||||
|
@ -26,11 +26,11 @@ const fullDefaultParameters: AddPasswordFullParameters = {
|
||||
|
||||
// Static configuration object
|
||||
export const addPasswordOperationConfig = {
|
||||
toolType: 'singleFile',
|
||||
buildFormData: buildAddPasswordFormData,
|
||||
operationType: 'addPassword',
|
||||
endpoint: '/api/v1/security/add-password',
|
||||
buildFormData: buildAddPasswordFormData,
|
||||
filePrefix: 'encrypted_', // Will be overridden in hook with translation
|
||||
multiFileEndpoint: false,
|
||||
defaultParameters: fullDefaultParameters,
|
||||
} as const;
|
||||
|
||||
|
@ -35,11 +35,11 @@ export const buildAddWatermarkFormData = (parameters: AddWatermarkParameters, fi
|
||||
|
||||
// Static configuration object
|
||||
export const addWatermarkOperationConfig = {
|
||||
toolType: 'singleFile',
|
||||
buildFormData: buildAddWatermarkFormData,
|
||||
operationType: 'watermark',
|
||||
endpoint: '/api/v1/security/add-watermark',
|
||||
buildFormData: buildAddWatermarkFormData,
|
||||
filePrefix: 'watermarked_', // Will be overridden in hook with translation
|
||||
multiFileEndpoint: false,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
|
||||
@ -51,4 +51,4 @@ export const useAddWatermarkOperation = () => {
|
||||
filePrefix: t('watermark.filenamePrefix', 'watermarked') + '_',
|
||||
getErrorMessage: createStandardErrorHandler(t('watermark.error.failed', 'An error occurred while adding watermark to the PDF.'))
|
||||
});
|
||||
};
|
||||
};
|
||||
|
@ -10,7 +10,7 @@ export function useAutomateOperation() {
|
||||
|
||||
const customProcessor = useCallback(async (params: AutomateParameters, files: File[]) => {
|
||||
console.log('🚀 Starting automation execution via customProcessor', { params, files });
|
||||
|
||||
|
||||
if (!params.automationConfig) {
|
||||
throw new Error('No automation configuration provided');
|
||||
}
|
||||
@ -40,10 +40,9 @@ export function useAutomateOperation() {
|
||||
}, [toolRegistry]);
|
||||
|
||||
return useToolOperation<AutomateParameters>({
|
||||
toolType: 'custom',
|
||||
operationType: 'automate',
|
||||
endpoint: '/api/v1/pipeline/handleData', // Not used with customProcessor
|
||||
buildFormData: () => new FormData(), // Not used with customProcessor
|
||||
customProcessor,
|
||||
filePrefix: AUTOMATION_CONSTANTS.FILE_PREFIX
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -20,12 +20,12 @@ vi.mock('../../../utils/toolErrorHandler', () => ({
|
||||
}));
|
||||
|
||||
// Import the mocked function
|
||||
import { ToolOperationConfig, ToolOperationHook, useToolOperation } from '../shared/useToolOperation';
|
||||
import { SingleFileToolOperationConfig, ToolOperationHook, useToolOperation } from '../shared/useToolOperation';
|
||||
|
||||
describe('useChangePermissionsOperation', () => {
|
||||
const mockUseToolOperation = vi.mocked(useToolOperation);
|
||||
|
||||
const getToolConfig = (): ToolOperationConfig<ChangePermissionsParameters> => mockUseToolOperation.mock.calls[0][0] as ToolOperationConfig<ChangePermissionsParameters>;
|
||||
const getToolConfig = () => mockUseToolOperation.mock.calls[0][0] as SingleFileToolOperationConfig<ChangePermissionsParameters>;
|
||||
|
||||
const mockToolOperationReturn: ToolOperationHook<unknown> = {
|
||||
files: [],
|
||||
@ -86,7 +86,7 @@ describe('useChangePermissionsOperation', () => {
|
||||
const buildFormData = callArgs.buildFormData;
|
||||
|
||||
const testFile = new File(['test content'], 'test.pdf', { type: 'application/pdf' });
|
||||
const formData = buildFormData(testParameters, testFile as any /* FIX ME */);
|
||||
const formData = buildFormData(testParameters, testFile);
|
||||
|
||||
// Verify the form data contains the file
|
||||
expect(formData.get('fileInput')).toBe(testFile);
|
||||
@ -106,7 +106,7 @@ describe('useChangePermissionsOperation', () => {
|
||||
});
|
||||
|
||||
test.each([
|
||||
{ property: 'multiFileEndpoint' as const, expectedValue: false },
|
||||
{ property: 'toolType' as const, expectedValue: 'singleFile' },
|
||||
{ property: 'endpoint' as const, expectedValue: '/api/v1/security/add-password' },
|
||||
{ property: 'filePrefix' as const, expectedValue: 'permissions_' },
|
||||
{ property: 'operationType' as const, expectedValue: 'change-permissions' }
|
||||
|
@ -24,11 +24,11 @@ export const buildChangePermissionsFormData = (parameters: ChangePermissionsPara
|
||||
|
||||
// Static configuration object
|
||||
export const changePermissionsOperationConfig = {
|
||||
toolType: 'singleFile',
|
||||
buildFormData: buildChangePermissionsFormData,
|
||||
operationType: 'change-permissions',
|
||||
endpoint: '/api/v1/security/add-password', // Change Permissions is a fake endpoint for the Add Password tool
|
||||
buildFormData: buildChangePermissionsFormData,
|
||||
filePrefix: 'permissions_',
|
||||
multiFileEndpoint: false,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
|
||||
@ -39,6 +39,6 @@ export const useChangePermissionsOperation = () => {
|
||||
...changePermissionsOperationConfig,
|
||||
getErrorMessage: createStandardErrorHandler(
|
||||
t('changePermissions.error.failed', 'An error occurred while changing PDF permissions.')
|
||||
)
|
||||
),
|
||||
});
|
||||
};
|
||||
|
@ -24,11 +24,11 @@ export const buildCompressFormData = (parameters: CompressParameters, file: File
|
||||
|
||||
// Static configuration object
|
||||
export const compressOperationConfig = {
|
||||
toolType: 'singleFile',
|
||||
buildFormData: buildCompressFormData,
|
||||
operationType: 'compress',
|
||||
endpoint: '/api/v1/misc/compress-pdf',
|
||||
buildFormData: buildCompressFormData,
|
||||
filePrefix: 'compressed_',
|
||||
multiFileEndpoint: false, // Individual API calls per file
|
||||
defaultParameters,
|
||||
} as const;
|
||||
|
||||
|
@ -129,11 +129,10 @@ export const convertProcessor = async (
|
||||
|
||||
// Static configuration object
|
||||
export const convertOperationConfig = {
|
||||
toolType: 'custom',
|
||||
customProcessor: convertProcessor, // Can't use callback version here
|
||||
operationType: 'convert',
|
||||
endpoint: '', // Not used with customProcessor but required
|
||||
buildFormData: buildConvertFormData, // Not used with customProcessor but required
|
||||
filePrefix: 'converted_',
|
||||
customProcessor: convertProcessor,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
|
||||
@ -158,6 +157,6 @@ export const useConvertOperation = () => {
|
||||
return error.message;
|
||||
}
|
||||
return t("convert.errorConversion", "An error occurred while converting the file.");
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
|
@ -52,7 +52,7 @@ export const buildOCRFormData = (parameters: OCRParameters, file: File): FormDat
|
||||
return formData;
|
||||
};
|
||||
|
||||
// Static response handler for OCR - can be used by automation executor
|
||||
// Static response handler for OCR - can be used by automation executor
|
||||
export const ocrResponseHandler = async (blob: Blob, originalFiles: File[], extractZipFiles: (blob: Blob) => Promise<File[]>): Promise<File[]> => {
|
||||
const headBuf = await blob.slice(0, 8).arrayBuffer();
|
||||
const head = new TextDecoder().decode(new Uint8Array(headBuf));
|
||||
@ -94,11 +94,11 @@ export const ocrResponseHandler = async (blob: Blob, originalFiles: File[], extr
|
||||
|
||||
// Static configuration object (without t function dependencies)
|
||||
export const ocrOperationConfig = {
|
||||
toolType: 'singleFile',
|
||||
buildFormData: buildOCRFormData,
|
||||
operationType: 'ocr',
|
||||
endpoint: '/api/v1/misc/ocr-pdf',
|
||||
buildFormData: buildOCRFormData,
|
||||
filePrefix: 'ocr_',
|
||||
multiFileEndpoint: false,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
|
||||
|
@ -12,11 +12,11 @@ export const buildRemoveCertificateSignFormData = (parameters: RemoveCertificate
|
||||
|
||||
// Static configuration object
|
||||
export const removeCertificateSignOperationConfig = {
|
||||
toolType: 'singleFile',
|
||||
buildFormData: buildRemoveCertificateSignFormData,
|
||||
operationType: 'remove-certificate-sign',
|
||||
endpoint: '/api/v1/security/remove-cert-sign',
|
||||
buildFormData: buildRemoveCertificateSignFormData,
|
||||
filePrefix: 'unsigned_', // Will be overridden in hook with translation
|
||||
multiFileEndpoint: false,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
|
||||
@ -28,4 +28,4 @@ export const useRemoveCertificateSignOperation = () => {
|
||||
filePrefix: t('removeCertSign.filenamePrefix', 'unsigned') + '_',
|
||||
getErrorMessage: createStandardErrorHandler(t('removeCertSign.error.failed', 'An error occurred while removing certificate signatures.'))
|
||||
});
|
||||
};
|
||||
};
|
||||
|
@ -20,12 +20,12 @@ vi.mock('../../../utils/toolErrorHandler', () => ({
|
||||
}));
|
||||
|
||||
// Import the mocked function
|
||||
import { ToolOperationConfig, ToolOperationHook, useToolOperation } from '../shared/useToolOperation';
|
||||
import { SingleFileToolOperationConfig, ToolOperationHook, useToolOperation } from '../shared/useToolOperation';
|
||||
|
||||
describe('useRemovePasswordOperation', () => {
|
||||
const mockUseToolOperation = vi.mocked(useToolOperation);
|
||||
|
||||
const getToolConfig = (): ToolOperationConfig<RemovePasswordParameters> => mockUseToolOperation.mock.calls[0][0] as ToolOperationConfig<RemovePasswordParameters>;
|
||||
const getToolConfig = () => mockUseToolOperation.mock.calls[0][0] as SingleFileToolOperationConfig<RemovePasswordParameters>;
|
||||
|
||||
const mockToolOperationReturn: ToolOperationHook<unknown> = {
|
||||
files: [],
|
||||
@ -91,7 +91,7 @@ describe('useRemovePasswordOperation', () => {
|
||||
});
|
||||
|
||||
test.each([
|
||||
{ property: 'multiFileEndpoint' as const, expectedValue: false },
|
||||
{ property: 'toolType' as const, expectedValue: 'singleFile' },
|
||||
{ property: 'endpoint' as const, expectedValue: '/api/v1/security/remove-password' },
|
||||
{ property: 'filePrefix' as const, expectedValue: 'translated-removePassword.filenamePrefix_' },
|
||||
{ property: 'operationType' as const, expectedValue: 'removePassword' }
|
||||
|
@ -13,11 +13,11 @@ export const buildRemovePasswordFormData = (parameters: RemovePasswordParameters
|
||||
|
||||
// Static configuration object
|
||||
export const removePasswordOperationConfig = {
|
||||
toolType: 'singleFile',
|
||||
buildFormData: buildRemovePasswordFormData,
|
||||
operationType: 'removePassword',
|
||||
endpoint: '/api/v1/security/remove-password',
|
||||
buildFormData: buildRemovePasswordFormData,
|
||||
filePrefix: 'decrypted_', // Will be overridden in hook with translation
|
||||
multiFileEndpoint: false,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
|
||||
|
@ -12,11 +12,11 @@ export const buildRepairFormData = (parameters: RepairParameters, file: File): F
|
||||
|
||||
// Static configuration object
|
||||
export const repairOperationConfig = {
|
||||
toolType: 'singleFile',
|
||||
buildFormData: buildRepairFormData,
|
||||
operationType: 'repair',
|
||||
endpoint: '/api/v1/misc/repair',
|
||||
buildFormData: buildRepairFormData,
|
||||
filePrefix: 'repaired_', // Will be overridden in hook with translation
|
||||
multiFileEndpoint: false,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
|
||||
@ -28,4 +28,4 @@ export const useRepairOperation = () => {
|
||||
filePrefix: t('repair.filenamePrefix', 'repaired') + '_',
|
||||
getErrorMessage: createStandardErrorHandler(t('repair.error.failed', 'An error occurred while repairing the PDF.'))
|
||||
});
|
||||
};
|
||||
};
|
||||
|
@ -21,9 +21,10 @@ export const buildSanitizeFormData = (parameters: SanitizeParameters, file: File
|
||||
|
||||
// Static configuration object
|
||||
export const sanitizeOperationConfig = {
|
||||
toolType: 'singleFile',
|
||||
buildFormData: buildSanitizeFormData,
|
||||
operationType: 'sanitize',
|
||||
endpoint: '/api/v1/security/sanitize-pdf',
|
||||
buildFormData: buildSanitizeFormData,
|
||||
filePrefix: 'sanitized_', // Will be overridden in hook with translation
|
||||
multiFileEndpoint: false,
|
||||
defaultParameters,
|
||||
|
@ -12,6 +12,8 @@ import { ResponseHandler } from '../../../utils/toolResponseProcessor';
|
||||
// Re-export for backwards compatibility
|
||||
export type { ProcessingProgress, ResponseHandler };
|
||||
|
||||
export type ToolConfigType = 'singleFile' | 'multiFile' | 'custom';
|
||||
|
||||
/**
|
||||
* Configuration for tool operations defining processing behavior and API integration.
|
||||
*
|
||||
@ -20,45 +22,16 @@ export type { ProcessingProgress, ResponseHandler };
|
||||
* 2. Multi-file tools: multiFileEndpoint: true, single API call with all files
|
||||
* 3. Complex tools: customProcessor handles all processing logic
|
||||
*/
|
||||
export interface ToolOperationConfig<TParams = void> {
|
||||
interface BaseToolOperationConfig<TParams> {
|
||||
/** Operation identifier for tracking and logging */
|
||||
operationType: string;
|
||||
|
||||
/**
|
||||
* API endpoint for the operation. Can be static string or function for dynamic routing.
|
||||
* Not used when customProcessor is provided.
|
||||
*/
|
||||
endpoint: string | ((params: TParams) => string);
|
||||
|
||||
/**
|
||||
* Builds FormData for API request. Signature determines processing approach:
|
||||
* - (params, file: File) => FormData: Single-file processing
|
||||
* - (params, files: File[]) => FormData: Multi-file processing
|
||||
* Not used when customProcessor is provided.
|
||||
*/
|
||||
buildFormData: ((params: TParams, file: File) => FormData) | ((params: TParams, files: File[]) => FormData); /* FIX ME */
|
||||
|
||||
/** Prefix added to processed filenames (e.g., 'compressed_', 'split_') */
|
||||
filePrefix: string;
|
||||
|
||||
/**
|
||||
* Whether this tool uses backends that accept MultipartFile[] arrays.
|
||||
* - true: Single API call with all files (backend uses MultipartFile[])
|
||||
* - false/undefined: Individual API calls per file (backend uses single MultipartFile)
|
||||
* Ignored when customProcessor is provided.
|
||||
*/
|
||||
multiFileEndpoint?: boolean;
|
||||
|
||||
/** How to handle API responses (e.g., ZIP extraction, single file response) */
|
||||
responseHandler?: ResponseHandler;
|
||||
|
||||
/**
|
||||
* Custom processing logic that completely bypasses standard file processing.
|
||||
* When provided, tool handles all API calls, response processing, and file creation.
|
||||
* Use for tools with complex routing logic or non-standard processing requirements.
|
||||
*/
|
||||
customProcessor?: (params: TParams, files: File[]) => Promise<File[]>;
|
||||
|
||||
/** Extract user-friendly error messages from API errors */
|
||||
getErrorMessage?: (error: any) => string;
|
||||
|
||||
@ -66,6 +39,49 @@ export interface ToolOperationConfig<TParams = void> {
|
||||
defaultParameters?: TParams;
|
||||
}
|
||||
|
||||
export interface SingleFileToolOperationConfig<TParams> extends BaseToolOperationConfig<TParams> {
|
||||
/** This tool processes one file at a time. */
|
||||
toolType: 'singleFile';
|
||||
|
||||
/** Builds FormData for API request. */
|
||||
buildFormData: ((params: TParams, file: File) => FormData);
|
||||
|
||||
/** API endpoint for the operation. Can be static string or function for dynamic routing. */
|
||||
endpoint: string | ((params: TParams) => string);
|
||||
|
||||
customProcessor?: undefined;
|
||||
}
|
||||
|
||||
export interface MultiFileToolOperationConfig<TParams> extends BaseToolOperationConfig<TParams> {
|
||||
/** This tool processes multiple files at once. */
|
||||
toolType: 'multiFile';
|
||||
|
||||
/** Builds FormData for API request. */
|
||||
buildFormData: ((params: TParams, files: File[]) => FormData);
|
||||
|
||||
/** API endpoint for the operation. Can be static string or function for dynamic routing. */
|
||||
endpoint: string | ((params: TParams) => string);
|
||||
|
||||
customProcessor?: undefined;
|
||||
}
|
||||
|
||||
export interface CustomToolOperationConfig<TParams> extends BaseToolOperationConfig<TParams> {
|
||||
/** This tool has custom behaviour. */
|
||||
toolType: 'custom';
|
||||
|
||||
buildFormData?: undefined;
|
||||
endpoint?: undefined;
|
||||
|
||||
/**
|
||||
* Custom processing logic that completely bypasses standard file processing.
|
||||
* This tool handles all API calls, response processing, and file creation.
|
||||
* Use for tools with complex routing logic or non-standard processing requirements.
|
||||
*/
|
||||
customProcessor: (params: TParams, files: File[]) => Promise<File[]>;
|
||||
}
|
||||
|
||||
export type ToolOperationConfig<TParams = void> = SingleFileToolOperationConfig<TParams> | MultiFileToolOperationConfig<TParams> | CustomToolOperationConfig<TParams>;
|
||||
|
||||
/**
|
||||
* Complete tool operation interface with execution capability
|
||||
*/
|
||||
@ -103,7 +119,7 @@ export { createStandardErrorHandler } from '../../../utils/toolErrorHandler';
|
||||
* @param config - Tool operation configuration
|
||||
* @returns Hook interface with state and execution methods
|
||||
*/
|
||||
export const useToolOperation = <TParams = void>(
|
||||
export const useToolOperation = <TParams>(
|
||||
config: ToolOperationConfig<TParams>
|
||||
): ToolOperationHook<TParams> => {
|
||||
const { t } = useTranslation();
|
||||
@ -143,15 +159,28 @@ export const useToolOperation = <TParams = void>(
|
||||
try {
|
||||
let processedFiles: File[];
|
||||
|
||||
if (config.customProcessor) {
|
||||
actions.setStatus('Processing files...');
|
||||
processedFiles = await config.customProcessor(params, validFiles);
|
||||
} else {
|
||||
// Use explicit multiFileEndpoint flag to determine processing approach
|
||||
if (config.multiFileEndpoint) {
|
||||
switch (config.toolType) {
|
||||
case 'singleFile':
|
||||
// Individual file processing - separate API call per file
|
||||
const apiCallsConfig: ApiCallsConfig<TParams> = {
|
||||
endpoint: config.endpoint,
|
||||
buildFormData: config.buildFormData,
|
||||
filePrefix: config.filePrefix,
|
||||
responseHandler: config.responseHandler
|
||||
};
|
||||
processedFiles = await processFiles(
|
||||
params,
|
||||
validFiles,
|
||||
apiCallsConfig,
|
||||
actions.setProgress,
|
||||
actions.setStatus
|
||||
);
|
||||
break;
|
||||
|
||||
case 'multiFile':
|
||||
// Multi-file processing - single API call with all files
|
||||
actions.setStatus('Processing files...');
|
||||
const formData = (config.buildFormData as (params: TParams, files: File[]) => FormData)(params, validFiles);
|
||||
const formData = config.buildFormData(params, validFiles);
|
||||
const endpoint = typeof config.endpoint === 'function' ? config.endpoint(params) : config.endpoint;
|
||||
|
||||
const response = await axios.post(endpoint, formData, { responseType: 'blob' });
|
||||
@ -169,22 +198,12 @@ export const useToolOperation = <TParams = void>(
|
||||
processedFiles = await extractAllZipFiles(response.data);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Individual file processing - separate API call per file
|
||||
const apiCallsConfig: ApiCallsConfig<TParams> = {
|
||||
endpoint: config.endpoint,
|
||||
buildFormData: config.buildFormData as (params: TParams, file: File) => FormData,
|
||||
filePrefix: config.filePrefix,
|
||||
responseHandler: config.responseHandler
|
||||
};
|
||||
processedFiles = await processFiles(
|
||||
params,
|
||||
validFiles,
|
||||
apiCallsConfig,
|
||||
actions.setProgress,
|
||||
actions.setStatus
|
||||
);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'custom':
|
||||
actions.setStatus('Processing files...');
|
||||
processedFiles = await config.customProcessor(params, validFiles);
|
||||
break;
|
||||
}
|
||||
|
||||
if (processedFiles.length > 0) {
|
||||
|
@ -12,11 +12,11 @@ export const buildSingleLargePageFormData = (parameters: SingleLargePageParamete
|
||||
|
||||
// Static configuration object
|
||||
export const singleLargePageOperationConfig = {
|
||||
toolType: 'singleFile',
|
||||
buildFormData: buildSingleLargePageFormData,
|
||||
operationType: 'single-large-page',
|
||||
endpoint: '/api/v1/general/pdf-to-single-page',
|
||||
buildFormData: buildSingleLargePageFormData,
|
||||
filePrefix: 'single_page_', // Will be overridden in hook with translation
|
||||
multiFileEndpoint: false,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
|
||||
@ -28,4 +28,4 @@ export const useSingleLargePageOperation = () => {
|
||||
filePrefix: t('pdfToSinglePage.filenamePrefix', 'single_page') + '_',
|
||||
getErrorMessage: createStandardErrorHandler(t('pdfToSinglePage.error.failed', 'An error occurred while converting to single page.'))
|
||||
});
|
||||
};
|
||||
};
|
||||
|
@ -57,11 +57,11 @@ export const getSplitEndpoint = (parameters: SplitParameters): string => {
|
||||
|
||||
// Static configuration object
|
||||
export const splitOperationConfig = {
|
||||
toolType: 'multiFile',
|
||||
buildFormData: buildSplitFormData,
|
||||
operationType: 'splitPdf',
|
||||
endpoint: getSplitEndpoint,
|
||||
buildFormData: buildSplitFormData,
|
||||
filePrefix: 'split_',
|
||||
multiFileEndpoint: true, // Single API call with all files
|
||||
defaultParameters,
|
||||
} as const;
|
||||
|
||||
|
@ -12,11 +12,11 @@ export const buildUnlockPdfFormsFormData = (parameters: UnlockPdfFormsParameters
|
||||
|
||||
// Static configuration object
|
||||
export const unlockPdfFormsOperationConfig = {
|
||||
toolType: 'singleFile',
|
||||
buildFormData: buildUnlockPdfFormsFormData,
|
||||
operationType: 'unlock-pdf-forms',
|
||||
endpoint: '/api/v1/misc/unlock-pdf-forms',
|
||||
buildFormData: buildUnlockPdfFormsFormData,
|
||||
filePrefix: 'unlocked_forms_', // Will be overridden in hook with translation
|
||||
multiFileEndpoint: false,
|
||||
defaultParameters,
|
||||
} as const;
|
||||
|
||||
@ -28,4 +28,4 @@ export const useUnlockPdfFormsOperation = () => {
|
||||
filePrefix: t('unlockPDFForms.filenamePrefix', 'unlocked_forms') + '_',
|
||||
getErrorMessage: createStandardErrorHandler(t('unlockPDFForms.error.failed', 'An error occurred while unlocking PDF forms.'))
|
||||
});
|
||||
};
|
||||
};
|
||||
|
@ -10,13 +10,13 @@ import { ResourceManager } from './resourceManager';
|
||||
* Execute a tool operation directly without using React hooks
|
||||
*/
|
||||
export const executeToolOperation = async (
|
||||
operationName: string,
|
||||
parameters: any,
|
||||
operationName: string,
|
||||
parameters: any,
|
||||
files: File[],
|
||||
toolRegistry: ToolRegistry
|
||||
): Promise<File[]> => {
|
||||
console.log(`🔧 Executing tool: ${operationName}`, { parameters, fileCount: files.length });
|
||||
|
||||
|
||||
const config = toolRegistry[operationName]?.operationConfig;
|
||||
if (!config) {
|
||||
console.error(`❌ Tool operation not supported: ${operationName}`);
|
||||
@ -34,17 +34,17 @@ export const executeToolOperation = async (
|
||||
return resultFiles;
|
||||
}
|
||||
|
||||
if (config.multiFileEndpoint) {
|
||||
if (config.toolType === 'multiFile') {
|
||||
// Multi-file processing - single API call with all files
|
||||
const endpoint = typeof config.endpoint === 'function'
|
||||
? config.endpoint(parameters)
|
||||
const endpoint = typeof config.endpoint === 'function'
|
||||
? config.endpoint(parameters)
|
||||
: config.endpoint;
|
||||
|
||||
|
||||
console.log(`🌐 Making multi-file request to: ${endpoint}`);
|
||||
const formData = (config.buildFormData as (params: any, files: File[]) => FormData)(parameters, files);
|
||||
console.log(`📤 FormData entries:`, Array.from(formData.entries()));
|
||||
|
||||
const response = await axios.post(endpoint, formData, {
|
||||
|
||||
const response = await axios.post(endpoint, formData, {
|
||||
responseType: 'blob',
|
||||
timeout: AUTOMATION_CONSTANTS.OPERATION_TIMEOUT
|
||||
});
|
||||
@ -53,11 +53,11 @@ export const executeToolOperation = async (
|
||||
|
||||
// Multi-file responses are typically ZIP files, but may be single files
|
||||
const result = await AutomationFileProcessor.extractAutomationZipFiles(response.data);
|
||||
|
||||
|
||||
if (result.errors.length > 0) {
|
||||
console.warn(`⚠️ File processing warnings:`, result.errors);
|
||||
}
|
||||
|
||||
|
||||
console.log(`📁 Processed ${result.files.length} files from response`);
|
||||
return result.files;
|
||||
|
||||
@ -65,18 +65,18 @@ export const executeToolOperation = async (
|
||||
// Single-file processing - separate API call per file
|
||||
console.log(`🔄 Processing ${files.length} files individually`);
|
||||
const resultFiles: File[] = [];
|
||||
|
||||
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
const endpoint = typeof config.endpoint === 'function'
|
||||
? config.endpoint(parameters)
|
||||
const endpoint = typeof config.endpoint === 'function'
|
||||
? config.endpoint(parameters)
|
||||
: config.endpoint;
|
||||
|
||||
|
||||
console.log(`🌐 Making single-file request ${i+1}/${files.length} to: ${endpoint} for file: ${file.name}`);
|
||||
const formData = (config.buildFormData as (params: any, file: File) => FormData)(parameters, file);
|
||||
console.log(`📤 FormData entries:`, Array.from(formData.entries()));
|
||||
|
||||
const response = await axios.post(endpoint, formData, {
|
||||
|
||||
const response = await axios.post(endpoint, formData, {
|
||||
responseType: 'blob',
|
||||
timeout: AUTOMATION_CONSTANTS.OPERATION_TIMEOUT
|
||||
});
|
||||
@ -85,7 +85,7 @@ export const executeToolOperation = async (
|
||||
|
||||
// Create result file
|
||||
const resultFile = ResourceManager.createResultFile(
|
||||
response.data,
|
||||
response.data,
|
||||
file.name,
|
||||
AUTOMATION_CONSTANTS.FILE_PREFIX
|
||||
);
|
||||
@ -107,7 +107,7 @@ export const executeToolOperation = async (
|
||||
* Execute an entire automation sequence
|
||||
*/
|
||||
export const executeAutomationSequence = async (
|
||||
automation: any,
|
||||
automation: any,
|
||||
initialFiles: File[],
|
||||
toolRegistry: ToolRegistry,
|
||||
onStepStart?: (stepIndex: number, operationName: string) => void,
|
||||
@ -117,7 +117,7 @@ export const executeAutomationSequence = async (
|
||||
console.log(`🚀 Starting automation sequence: ${automation.name || 'Unnamed'}`);
|
||||
console.log(`📁 Initial files: ${initialFiles.length}`);
|
||||
console.log(`🔧 Operations: ${automation.operations?.length || 0}`);
|
||||
|
||||
|
||||
if (!automation?.operations || automation.operations.length === 0) {
|
||||
throw new Error('No operations in automation');
|
||||
}
|
||||
@ -126,25 +126,25 @@ export const executeAutomationSequence = async (
|
||||
|
||||
for (let i = 0; i < automation.operations.length; i++) {
|
||||
const operation = automation.operations[i];
|
||||
|
||||
|
||||
console.log(`📋 Step ${i + 1}/${automation.operations.length}: ${operation.operation}`);
|
||||
console.log(`📄 Input files: ${currentFiles.length}`);
|
||||
console.log(`⚙️ Parameters:`, operation.parameters || {});
|
||||
|
||||
|
||||
try {
|
||||
onStepStart?.(i, operation.operation);
|
||||
|
||||
|
||||
const resultFiles = await executeToolOperation(
|
||||
operation.operation,
|
||||
operation.parameters || {},
|
||||
operation.operation,
|
||||
operation.parameters || {},
|
||||
currentFiles,
|
||||
toolRegistry
|
||||
);
|
||||
|
||||
|
||||
console.log(`✅ Step ${i + 1} completed: ${resultFiles.length} result files`);
|
||||
currentFiles = resultFiles;
|
||||
onStepComplete?.(i, resultFiles);
|
||||
|
||||
|
||||
} catch (error: any) {
|
||||
console.error(`❌ Step ${i + 1} failed:`, error);
|
||||
onStepError?.(i, error.message);
|
||||
@ -154,4 +154,4 @@ export const executeAutomationSequence = async (
|
||||
|
||||
console.log(`🎉 Automation sequence completed: ${currentFiles.length} final files`);
|
||||
return currentFiles;
|
||||
};
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user