Backup_FME_personal/force-app/main/default/classes/SFTPFeature.cls

712 lines
26 KiB
OpenEdge ABL

public with sharing class SFTPFeature extends CloudStorageBase {
private static String SERVER_URL = System.Label.Ngrok;
public override OperationResult listFiles(Map<String, Object> params) {
List<Map<String, Object>> resultFiles = new List<Map<String, Object>>();
System.debug('Inside listFiles');
try {
Map<String, Object> payload = (Map<String, Object>) params.get('payload');
String endpoint = 'https://hopelessly-upright-platypus.ngrok-free.app/api/v1/sftp';
System.debug('Endpoint: ' + endpoint);
System.debug('Payload sftp: ' + JSON.serializePretty(payload));
String requestBody = JSON.serialize(payload);
Map<String, String> headers = new Map<String, String>{
'Content-Type' => 'application/json'
};
HttpResponse res = CloudStorageUtlilty.makeCallout(requestBody,endpoint,headers,'POST');
System.debug('Status Code: ' + res.getStatusCode());
System.debug('Response Body: ' + res.getBody());
if (res.getStatusCode() == 200) {
List<Object> fileList = (List<Object>) JSON.deserializeUntyped(res.getBody());
System.debug('fileList: ' + JSON.serializePretty(fileList));
// Build full tree
List<Map<String,Object>> tree = buildTree(fileList);
// Folder filtering logic
String folderId = (String) params.get('folderId');
if (String.isBlank(folderId)) {
resultFiles = tree; // root
} else {
Map<String,Object> folder = findFolderByPath(tree, folderId);
if (folder != null && folder.containsKey('children')) {
resultFiles = (List<Map<String,Object>>) folder.get('children');
} else {
resultFiles = new List<Map<String,Object>>();
}
}
} else {
System.debug('SFTP call failed!');
throw new AuraHandledException('Error from SFTP service: ' + res.getBody());
}
} catch (Exception e) {
throw new AuraHandledException('Exception: ' + e.getMessage());
}
System.debug('Final resultFiles: ' + JSON.serializePretty(resultFiles));
return OperationResult.success(resultFiles);
}
private List<Map<String,Object>> buildTree(List<Object> items){
List<Map<String,Object>> output = new List<Map<String,Object>>();
for(Object obj : items){
Map<String,Object> node = (Map<String,Object>) obj;
String name = (String) node.get('name');
String type = (String) node.get('type');
String path = (String) node.get('path');
Boolean isFolder =
type != null &&
type.toLowerCase() == 'folder';
//added
// Set mimeType based on type (similar to S3 logic)
String mimeType;
if (isFolder) {
mimeType = 'doctype:folder';
} else {
// Try to determine mimeType from file extension, default to attachment
mimeType = getMimeTypeFromFileName(name);
}
Map<String,Object> entry =
new Map<String,Object>{
'name' => name,
'type' => type,
'path' => path,
'id' => path,
'fileId' => path,
'iconName' => isFolder
? 'doctype:folder'
: 'doctype:attachment',
'isFolder' => String.valueOf(isFolder),
'mimeType' => mimeType // ADD THIS LINE
};
// recurse children
if(node.containsKey('children')){
List<Object> children =
(List<Object>) node.get('children');
entry.put(
'children',
children != null
? buildTree(children)
: new List<Map<String,Object>>()
);
} else if(isFolder){
entry.put('children', new List<Map<String,Object>>());
}
output.add(entry);
}
return output;
}
// Add this helper method to determine mimeType from filename
private String getMimeTypeFromFileName(String fileName) {
if (String.isBlank(fileName)) return 'application/octet-stream';
String ext = '';
Integer lastDot = fileName.lastIndexOf('.');
if (lastDot > 0 && lastDot < fileName.length() - 1) {
ext = fileName.substring(lastDot + 1).toLowerCase();
}
Map<String, String> mimeMap = new Map<String, String>{
'pdf' => 'application/pdf',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'xls' => 'application/vnd.ms-excel',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'ppt' => 'application/vnd.ms-powerpoint',
'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'txt' => 'text/plain',
'csv' => 'text/csv',
'json' => 'application/json',
'xml' => 'application/xml',
'html' => 'text/html',
'htm' => 'text/html',
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'bmp' => 'image/bmp',
'svg' => 'image/svg+xml',
'mp3' => 'audio/mpeg',
'mp4' => 'video/mp4',
'avi' => 'video/x-msvideo',
'mov' => 'video/quicktime',
'zip' => 'application/zip',
'rar' => 'application/x-rar-compressed',
'7z' => 'application/x-7z-compressed'
};
return mimeMap.containsKey(ext) ? mimeMap.get(ext) : 'application/octet-stream';
}
private Map<String,Object> findFolderByPath(List<Map<String,Object>> nodes, String path){
for(Map<String,Object> node : nodes){
if((String)node.get('path') == path){
return node;
}
if(node.containsKey('children')){
Map<String,Object> found =findFolderByPath((List<Map<String,Object>>)node.get('children'),path);
if(found != null) return found;
}
}
return null;
}
//create folder
public override OperationResult createFolder(Map<String, Object> params) {
Map<String, Object> resultMap = new Map<String, Object>();
System.debug('Inside SFTP createFolder');
System.debug('Params: ' + params);
try {
String parentFolderId = (String) params.get('parentFolderId');
String folderName = (String) params.get('folderName');
Map<String, Object> payload =
(Map<String, Object>) params.get('payload');
// Default root path if null
String path = String.isBlank(parentFolderId)
? '/var/sftp/shared'
: parentFolderId;
// Encode folder name + path
String encodedFolderName =
EncodingUtil.urlEncode(folderName, 'UTF-8');
String encodedPath =
EncodingUtil.urlEncode(path, 'UTF-8');
// Endpoint expected by SFTP API
String endpoint = SERVER_URL +
'/api/v1/sftp/folder?folderName=' +
encodedFolderName +
'&path=' +
encodedPath;
System.debug('Endpoint: ' + endpoint);
// Request body expected by API
String requestBody = JSON.serialize(payload);
Map<String, String> headers = new Map<String, String>{
'Content-Type' => 'application/json',
'accept' => '*/*'
};
HttpResponse res = CloudStorageUtlilty.makeCallout(
requestBody,
endpoint,
headers,
'POST'
);
System.debug('Response code: ' + res.getStatusCode());
System.debug('Response body: ' + res.getBody());
// Handle error responses
if (res.getStatusCode() != 200 &&
res.getStatusCode() != 201) {
return OperationResult.error(
'SFTP folder creation failed: ' +
res.getStatusCode() + ' - ' + res.getBody()
);
}
// Handle empty response safely
String responseBody = res.getBody();
if (String.isNotBlank(responseBody)) {
resultMap =
(Map<String, Object>) JSON.deserializeUntyped(responseBody);
} else {
resultMap.put('code', res.getStatusCode());
resultMap.put('message', 'Folder created successfully');
}
return OperationResult.success(resultMap);
} catch (Exception e) {
System.debug('SFTP create error: ' + e.getMessage());
throw new AuraHandledException(
'SFTP Folder Creation Failed: ' + e.getMessage()
);
}
}
@AuraEnabled
public static void storeSftpCredentials(Boolean isAdminCred, Map<String, Object> payload){
try {
String username = (String) payload.get('username');
String password = (String) payload.get('password');
String host = (String) payload.get('host');
System.debug('Inside storeSftpCredentials');
System.debug('username: ' + username);
System.debug('password: ' + password);
System.debug('host: ' + host);
String userId = SalesforceEnvironmentUtil.getUserId();
Date todayDate = System.today();
Map<String, Object> tokenMap = new Map<String, Object>();
tokenMap.put('HIC_FilesME__Auth_Provider_Name__c', 'Sftp');
tokenMap.put('ConnectedDate', todayDate);
tokenMap.put('HIC_FilesME__Provider__c', 'Sftp');
tokenMap.put('HIC_FilesME__username__c', username);
tokenMap.put('HIC_FilesME__Password__c', password);
tokenMap.put('HIC_FilesME__Hostname__c', host);
tokenMap.put('HIC_FilesME__Is_Admin_level_Credentials__c', isAdminCred);
tokenMap.put('HIC_FilesME__User__c', userId);
System.debug('Storing token');
OAuthTokenManager.storeToken(tokenMap);
}
catch (Exception e) {
throw new AuraHandledException(e.getMessage());
}
}
private static void processItems(List<Object> items,List<Map<String, String>> resultFiles) {
System.debug('Inside processItems' + JSON.serializePretty(items));
System.debug('Inside processItems' + JSON.serializePretty(resultFiles));
for (Object item : items) {
Map<String, Object> fileMap = (Map<String, Object>) item;
String type = (String) fileMap.get('type');
Boolean isFolder =
type != null && type.toLowerCase() == 'folder';
String iconName =
isFolder ? 'doctype:folder' : 'doctype:txt';
String path = (String) fileMap.get('path');
Map<String, String> fileData = new Map<String, String>{
'fileId' => path,
'id' => path,
'name' => (String) fileMap.get('name'),
'type' => type,
'path' => path,
'iconName' => iconName,
'isFolder' => String.valueOf(isFolder)
};
resultFiles.add(fileData);
if (isFolder && fileMap.containsKey('children')) {
List<Object> children =
(List<Object>) fileMap.get('children');
if (children != null && !children.isEmpty()) {
processItems(children, resultFiles);
}
}
}
}
//to delete
public override OperationResult deleteFile(Map<String, Object> params) {
try {
System.debug('>>>entered deleteFile SFTP');
String fileId = (String) params.get('fileId');
Boolean isServiceMode = (Boolean) params.get('isServiceMode');
Map<String, Object> payload = (Map<String, Object>) params.get('payload');
String endpoint = 'https://hopelessly-upright-platypus.ngrok-free.app/api/v1/sftp';
Map<String, Object> requestBody = new Map<String, Object>{
'targetPaths' => new List<String>{ fileId },
'sftpConnection' => payload
};
String jsonBody = JSON.serialize(requestBody);
System.debug('Create Request Body: ' + jsonBody);
Map<String, String> headers = new Map<String, String>{
'Content-Type' => 'application/json',
'accept' => '*/*'
};
HttpResponse res = CloudStorageUtlilty.makeCallout(jsonBody, endpoint, headers, 'DELETE');
System.debug('>>>res=' + res.getBody());
System.debug('>>>res.getStatusCode()=' + res.getStatusCode());
if (res.getStatusCode() == 200 || res.getStatusCode() == 204) {
return OperationResult.success(res.getBody());
} else {
return OperationResult.error(res.getBody());
}
} catch (Exception e) {
System.debug('>>>error=' + e.getMessage());
throw new AuraHandledException(e.getMessage());
}
}
// Rename file/folder (SFTP)
public override OperationResult renameFile(Map<String, Object> params) {
Map<String, Object> resultMap = new Map<String, Object>();
System.debug('Inside SFTP renameFile');
System.debug('Params: ' + params);
try {
String sourcePath = (String) params.get('fileId');
String newFileName = (String) params.get('newFileName');
Boolean overwrite = (Boolean) params.get('overwrite');
Map<String, Object> payload =
(Map<String, Object>) params.get('payload');
if (overwrite == null) {
overwrite = true;
}
System.debug('Source Path: ' + sourcePath);
System.debug('New Name: ' + newFileName);
// Build request body as expected by API
Map<String, Object> requestMap = new Map<String, Object>{
'sourcePath' => sourcePath,
'newName' => newFileName,
'payload' => payload,
'overwrite' => overwrite
};
String requestBody = JSON.serialize(requestMap);
String endpoint = SERVER_URL + '/api/v1/sftp/rename';
Map<String, String> headers = new Map<String, String>{
'Content-Type' => 'application/json',
'accept' => '*/*'
};
HttpResponse res = CloudStorageUtlilty.makeCallout(
requestBody,
endpoint,
headers,
'POST'
);
System.debug('Rename response: ' + res.getBody());
if (res.getStatusCode() != 200 && res.getStatusCode() != 201) {
return OperationResult.error(
'SFTP rename failed: ' +
res.getStatusCode() + ' - ' + res.getBody()
);
}
String responseBody = res.getBody();
if (String.isNotBlank(responseBody)) {
resultMap =
(Map<String, Object>) JSON.deserializeUntyped(responseBody);
} else {
resultMap.put('message', 'Rename successful');
}
return OperationResult.success(resultMap);
} catch (Exception e) {
System.debug('Rename error: ' + e.getMessage());
throw new AuraHandledException(
'SFTP Rename Failed: ' + e.getMessage()
);
}
}
//to upload
@AuraEnabled(cacheable=false)
public static OperationResult uploadFile(String host,String username,String password,String uploadPath,String fileName,String base64Data) {
try {
System.debug('>>> Inside SFTP upload');
String endpoint = 'https://hopelessly-upright-platypus.ngrok-free.app/api/v1/sftp/upload';
Map<String, Object> sftpConnection = new Map<String, Object>{
'user' => username,
'hostname' => host,
'password' => password,
'encryptionType' => 'SFTP',
'folderName' => ''
};
List<Object> items = new List<Object>{
new Map<String, Object>{
'name' => fileName,
'type' => 'FILE',
'value' => base64Data
}
};
// Payload
Map<String, Object> payload = new Map<String, Object>{
'sftpConnection' => sftpConnection,
'uploadPath' => uploadPath,
'items' => items
};
String requestBody = JSON.serialize(payload);
System.debug('Upload Payload: ' + requestBody);
Map<String, String> headers = new Map<String, String>{
'Content-Type' => 'application/json',
'accept' => '*/*'
};
HttpResponse res = CloudStorageUtlilty.makeCallout(requestBody,endpoint,headers,'POST');
System.debug('Upload Response: ' + res.getBody());
System.debug('Status: ' + res.getStatusCode());
if (res.getStatusCode() == 200 ||
res.getStatusCode() == 201) {
return OperationResult.success(res.getBody());
}
return OperationResult.error(res.getBody());
} catch (Exception e) {
throw new AuraHandledException('SFTP Upload Failed: ' + e.getMessage());
}
}
// move file/folder
public override OperationResult moveFileAndFolder(Map<String, Object> params) {
Map<String, Object> resultMap = new Map<String, Object>();
System.debug('Inside SFTP moveFileandfolder');
System.debug('Params: ' + params);
try {
String sourcePath = (String) params.get('sourceKey');
System.debug('sourcepath:' + sourcePath );
String destinationPath = (String) params.get('destinationKey');
System.debug('destinationpath:' + destinationPath);
Boolean overwrite = (Boolean) params.get('overwrite');
Map<String, Object> payload = (Map<String, Object>) params.get('payload');
if (overwrite == null) {
overwrite = true;
}
System.debug('Source Path: ' + sourcePath);
System.debug('Destination Path: ' + destinationPath);
// API request body
Map<String, Object> requestMap = new Map<String, Object>{
'sourcePath' => sourcePath,
'destinationPath' => destinationPath,
'payload' => payload,
'overwrite' => overwrite
};
String requestBody = JSON.serialize(requestMap);
System.debug('Move Request Body: ' + requestBody);
String endpoint = SERVER_URL + '/api/v1/sftp/move';
System.debug('endpoint: ' + endpoint);
Map<String, String> headers = new Map<String, String>{
'Content-Type' => 'application/json',
'accept' => '*/*'
};
HttpResponse res = CloudStorageUtlilty.makeCallout(requestBody,endpoint,headers,'POST');
System.debug('Move response: ' + res.getBody());
if (res.getStatusCode() != 200 && res.getStatusCode() != 201) {
return OperationResult.error('SFTP move failed: ' +res.getStatusCode() + ' - ' + res.getBody());
}
String responseBody = res.getBody();
System.debug('responseBody: ' + responseBody);
System.debug('res.getStatusCode(): ' + res.getStatusCode());
if (String.isNotBlank(responseBody)) {
resultMap = (Map<String, Object>) JSON.deserializeUntyped(responseBody);
} else {
resultMap.put('message', 'Move successful');
}
System.debug('resultMap: ' + resultMap);
return OperationResult.success(resultMap);
} catch (Exception e) {
System.debug('Move error: ' + e.getMessage());
throw new AuraHandledException('SFTP Move Failed: ' + e.getMessage());
}
}
// change custom object -- linkedFolder
public override OperationResult linkFiles(String recordId) {
List<Map<String, String>> resultFolders = new List<Map<String, String>>();
try {
List<HIC_FilesME__Linked_Folder__c> linkedFolders = [
SELECT Id,Name,CreatedDate,CreatedBy.Name,HIC_FilesME__External_Folder_ID__c,
HIC_FilesME__Folder_Name__c,
HIC_FilesME__Folder_URL__c,
HIC_FilesME__Storage_Provider__c,
HIC_FilesME__Access_Level__c,
HIC_FilesME__Connected_Username__c
FROM HIC_FilesME__Linked_Folder__c
WHERE HIC_FilesME__Linked_Record__c = :recordId
AND HIC_FilesME__Is_Active__c = true
AND HIC_FilesME__Storage_Provider__c = 'SFTP'
];
System.debug('linkedFolders >>>> ' + linkedFolders);
for (HIC_FilesME__Linked_Folder__c folder : linkedFolders) {
Map<String, String> folderData = new Map<String, String>();
folderData.put('id', folder.HIC_FilesME__External_Folder_ID__c);
folderData.put('recordId', folder.Id);
folderData.put('name', folder.HIC_FilesME__Folder_Name__c);
folderData.put('externalStorageType', folder.HIC_FilesME__Storage_Provider__c);
folderData.put('accessLevel', folder.HIC_FilesME__Access_Level__c);
folderData.put('createdTime', String.valueOf(folder.CreatedDate));
folderData.put('createdBy', folder.CreatedBy != null ? folder.CreatedBy.Name : '-');
folderData.put('linkedBy', folder.HIC_FilesME__Connected_Username__c);
folderData.put('url', folder.HIC_FilesME__Folder_URL__c);
resultFolders.add(folderData);
System.debug('folderData line378 >>>> ' + folderData);
}
System.debug('resultFolders >>>> ' + JSON.serializePretty(resultFolders));
return OperationResult.success(resultFolders);
} catch (Exception e) {FME_Logger.error(e.getMessage(),'LinkedFolderFeature' + e.getLineNumber(),'linkFiles');
return OperationResult.error('Failed to fetch linked folders. ' + e.getMessage());
}
}
// change custom object -- linkedFolder
public override OperationResult toLinkFiles(String recordId,String folderDetails,String providerType) {
try {
System.debug('providerType >>>> ' + providerType);
System.debug('recordId >>>> ' + recordId);
System.debug('JSON.deserializeUntyped folderDetails >>>> ' + JSON.deserializeUntyped(folderDetails));
Map<String, Object> folderMap = (Map<String, Object>) JSON.deserializeUntyped(folderDetails);
HIC_FilesME__Linked_Folder__c linkedFolder = new HIC_FilesME__Linked_Folder__c();
linkedFolder.HIC_FilesME__Linked_Record__c = recordId;
linkedFolder.HIC_FilesME__External_Folder_ID__c = (String) folderMap.get('id');
linkedFolder.HIC_FilesME__Folder_Name__c = (String) folderMap.get('name');
//add
linkedFolder.HIC_FilesME__Created__c = UserInfo.getUserId();
linkedFolder.HIC_FilesME__Created_Date__c = System.now();
linkedFolder.HIC_FilesME__Connected_Username__c = (String) folderMap.get('linkedBy');
linkedFolder.HIC_FilesME__Folder_URL__c = (String) folderMap.get('url');
linkedFolder.HIC_FilesME__Storage_Provider__c = providerType;
// linkedFolder.HIC_FilesME__Access_Level__c = (String) folderMap.get('accessLevel');
linkedFolder.HIC_FilesME__Is_Active__c = true;
insert linkedFolder;
System.debug('linkedFolder >>>> ' + linkedFolder);
return OperationResult.success(linkedFolder);
} catch (Exception e) {
FME_Logger.error(e.getMessage(),'LinkedFolderFeature' + e.getLineNumber(),'toLinkFiles' );
return OperationResult.error('Error linking folder: ' + e.getMessage());
}
}
//to unlinkfiles
public override OperationResult unlinkFiles(String recordId, String filesId, Boolean deleteFromDrive) {
System.debug('recordId >>>> ' + recordId);
System.debug('filesId >>>> ' + filesId);
System.debug('deleteFromDrive >>>> ' + deleteFromDrive);
OperationResult result;
try {
List<HIC_FilesME__Linked_Folder__c> fileLinks = [SELECT Id FROM HIC_FilesME__Linked_Folder__c WHERE HIC_FilesME__Linked_Record__c = :recordId AND HIC_FilesME__External_Folder_ID__c = :filesId];
if (fileLinks.size() > 0) {
delete fileLinks;
// if (deleteFromDrive) {
// deleteFile(filesId, true, null);
// }
result = OperationResult.success(null);
} else {
result = OperationResult.error('No record found');
}
} catch (Exception e) {
result = OperationResult.error('Error unlinking file: ' + e.getMessage());
FME_Logger.error(e.getMessage(), 'GoogleDriveFeature' + e.getLineNumber(), 'unlinkFiles');
}
return result;
}
}