@makolabs/ripple
Version:
Simple Svelte 5 powered component library ✨
172 lines (171 loc) • 6.85 kB
JavaScript
/**
* Base storage adapter with common functionality
*/
export class BaseAdapter {
// Common implementation of import and getImportStatus
async import(file, options) {
try {
const endpoint = `/api/${this.getApiPath()}/import`;
const response = await fetch(endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
key: file.key,
fileId: file.id, // For Google Drive
contentType: options.contentType,
fileFormat: options.fileFormat,
reference: options.reference || file.name,
insurer: options.contentType === 'insurer_statement' ? options.insurer : null
})
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || 'Failed to import file');
}
const result = await response.json();
return {
success: true,
message: `File ${file.name} imported successfully`,
fileId: result.fileId || result.importResponse?.uuid,
status: 'pending',
progress: 10
};
}
catch (err) {
console.error(`Error importing ${this.getName()} file:`, err);
return {
success: false,
message: `Failed to import file from ${this.getName()}`,
error: err instanceof Error ? err.message : 'Unknown error'
};
}
}
async getImportStatus(importId) {
try {
const response = await fetch(`/api/v1/uploads/${importId}`);
if (!response.ok) {
if (response.status === 404) {
throw new Error(`404: File ${importId} not found on server`);
}
throw new Error(`Failed to get file status: ${response.status} ${response.statusText}`);
}
const fileData = await response.json();
return {
fileId: importId,
status: fileData.processing_status,
progress: this.calculateProgress(fileData.processing_status, fileData.processed_rows, fileData.total_rows),
processedRows: fileData.processed_rows,
totalRows: fileData.total_rows,
detailedStatus: this.getDetailedStatus(fileData.processing_status),
error: fileData.error_details?.message,
created_at: fileData.created_at
};
}
catch (err) {
console.error('Error polling file status:', err);
throw err;
}
}
// Common implementation of batch import
async batchImport(files, options) {
// Process files sequentially
const individualResults = [];
for (const file of files) {
try {
const result = await this.import(file, options);
individualResults.push(result);
}
catch (err) {
individualResults.push({
success: false,
message: `Failed to import ${file.name}`,
error: err instanceof Error ? err.message : 'Unknown error',
fileId: file.id
});
}
}
// Count successes and failures
const succeeded = individualResults.filter((r) => r.success).length;
const failed = individualResults.length - succeeded;
// Return batch import result
return {
success: failed === 0, // Overall success if all files succeeded
message: `Imported ${succeeded} of ${individualResults.length} files`,
summary: {
total: individualResults.length,
succeeded,
failed
},
results: individualResults.map((r) => ({
key: r.fileId || '',
success: r.success,
fileId: r.fileId,
error: r.error
}))
};
}
// Set a flag to reopen the browser after authentication
setReopenFlag() {
// Default implementation does nothing
// Subclasses should override this method to implement storage-specific reopening flags
}
// Check if the browser should be reopened
shouldReopenBrowser() {
// Default implementation returns false
// Subclasses should override this method to check their specific reopening flags
return false;
}
// Clear the browser reopening flag
clearReopenFlag() {
// Default implementation does nothing
// Subclasses should override this method to clear their specific reopening flags
}
// Helper method to get detailed status
getDetailedStatus(status) {
const statusMap = {
pending: 'Waiting to start',
pending_import: 'Preparing import',
initializing: 'Initializing import',
pending_file_retrieval: 'Retrieving file',
retrieving_file: 'Downloading file',
file_retrieved: 'File downloaded',
loading_file_contents_to_db: 'Loading data',
file_contents_loaded: 'Data loaded',
ready_to_process: 'Ready for processing',
validating_source_file_rows: 'Validating file',
converting_to_business_objects: 'Converting data',
processing: 'Processing data',
completed: 'Import completed',
failed: 'Import failed',
partially_processed: 'Partially processed'
};
return statusMap[status] || status;
}
// Helper method to calculate progress
calculateProgress(status, processedRows, totalRows) {
// If we have row counts, use them for more accurate progress
if (processedRows !== undefined && totalRows && totalRows > 0) {
return Math.min(95, Math.round((processedRows / totalRows) * 100));
}
// Otherwise, use status-based progress estimates
const progressMap = {
pending: 5,
pending_import: 10,
initializing: 15,
pending_file_retrieval: 20,
retrieving_file: 30,
file_retrieved: 40,
loading_file_contents_to_db: 50,
file_contents_loaded: 60,
ready_to_process: 70,
validating_source_file_rows: 80,
converting_to_business_objects: 90,
completed: 100,
failed: 0,
partially_processed: 95
};
return progressMap[status] || 50;
}
}