420 lines
19 KiB
OpenEdge ABL
420 lines
19 KiB
OpenEdge ABL
public with sharing class DocusignESignatureService {
|
|
public class ESignatureAccountConfig {
|
|
@AuraEnabled public String accountCode;
|
|
@AuraEnabled public String accountDisplayName;
|
|
@AuraEnabled public String environment;
|
|
@AuraEnabled public String eSignatureAuthNamedCredential;
|
|
@AuraEnabled public String eSignatureRestNamedCredential;
|
|
@AuraEnabled public String eSignatureAccountId;
|
|
}
|
|
|
|
public class ApiResponse {
|
|
@AuraEnabled public Boolean success;
|
|
@AuraEnabled public Integer statusCode;
|
|
@AuraEnabled public String message;
|
|
@AuraEnabled public String requestPath;
|
|
@AuraEnabled public String responseBody;
|
|
}
|
|
|
|
public class ESignatureAccountSummary {
|
|
@AuraEnabled public String accountId;
|
|
@AuraEnabled public String accountName;
|
|
@AuraEnabled public String baseUri;
|
|
@AuraEnabled public Boolean isDefault;
|
|
@AuraEnabled public String rawJson;
|
|
}
|
|
|
|
public class TemplateSummary {
|
|
@AuraEnabled public String templateId;
|
|
@AuraEnabled public String name;
|
|
@AuraEnabled public String description;
|
|
@AuraEnabled public String shared;
|
|
@AuraEnabled public String lastModified;
|
|
@AuraEnabled public String rawJson;
|
|
}
|
|
|
|
public class EnvelopeSummary {
|
|
@AuraEnabled public String envelopeId;
|
|
@AuraEnabled public String emailSubject;
|
|
@AuraEnabled public String status;
|
|
@AuraEnabled public String createdDateTime;
|
|
@AuraEnabled public String sentDateTime;
|
|
@AuraEnabled public String completedDateTime;
|
|
@AuraEnabled public String rawJson;
|
|
}
|
|
|
|
public class EnvelopeCreateResult {
|
|
@AuraEnabled public Boolean success;
|
|
@AuraEnabled public String envelopeId;
|
|
@AuraEnabled public String status;
|
|
@AuraEnabled public String envelopeUri;
|
|
@AuraEnabled public String errorMessage;
|
|
}
|
|
|
|
@AuraEnabled(cacheable=true)
|
|
public static ESignatureAccountConfig getAccountConfig(String accountCode) {
|
|
CLM_Account_Setting__mdt row = requireAccountSetting(accountCode);
|
|
ESignatureAccountConfig config = new ESignatureAccountConfig();
|
|
config.accountCode = String.isNotBlank(row.Account_Code__c) ? row.Account_Code__c : row.DeveloperName;
|
|
config.accountDisplayName = String.isNotBlank(row.Account_Display_Name__c) ? row.Account_Display_Name__c : row.DeveloperName;
|
|
config.environment = row.Environment_Code__c;
|
|
config.eSignatureAuthNamedCredential = row.ESignature_Auth_Named_Credential__c;
|
|
config.eSignatureRestNamedCredential = row.ESignature_Rest_Named_Credential__c;
|
|
config.eSignatureAccountId = row.ESignature_Account_Id__c;
|
|
return config;
|
|
}
|
|
|
|
@AuraEnabled(cacheable=false)
|
|
public static ApiResponse probe(String accountCode, String relativePath) {
|
|
CLM_Account_Setting__mdt row = requireAccountSetting(accountCode);
|
|
String normalizedPath = normalizePath(relativePath);
|
|
|
|
HttpRequest req = new HttpRequest();
|
|
req.setEndpoint(buildEndpoint(normalizedPath, row.ESignature_Rest_Named_Credential__c));
|
|
req.setMethod('GET');
|
|
req.setTimeout(30000);
|
|
|
|
HttpResponse res = new Http().send(req);
|
|
ApiResponse response = new ApiResponse();
|
|
response.success = res.getStatusCode() >= 200 && res.getStatusCode() < 300;
|
|
response.statusCode = res.getStatusCode();
|
|
response.message = response.success ? 'eSignature request succeeded.' : 'eSignature request failed.';
|
|
response.requestPath = normalizedPath;
|
|
response.responseBody = res.getBody();
|
|
return response;
|
|
}
|
|
|
|
@AuraEnabled(cacheable=false)
|
|
public static List<ESignatureAccountSummary> listAccounts(String accountCode) {
|
|
ApiResponse response = getLoginInformation(accountCode);
|
|
if (!response.success) {
|
|
throw new AuraHandledException('eSignature API Error (HTTP ' + response.statusCode + '): ' + response.responseBody);
|
|
}
|
|
return parseAccountList(response.responseBody);
|
|
}
|
|
|
|
@AuraEnabled(cacheable=false)
|
|
public static ApiResponse getLoginInformation(String accountCode) {
|
|
return probe(accountCode, '/v2.1/login_information');
|
|
}
|
|
|
|
@AuraEnabled(cacheable=false)
|
|
public static ApiResponse getUserInfo(String accountCode) {
|
|
CLM_Account_Setting__mdt row = requireAccountSetting(accountCode);
|
|
|
|
HttpRequest req = new HttpRequest();
|
|
req.setEndpoint(buildEndpoint('/oauth/userinfo', authNamedCredential(row)));
|
|
req.setMethod('GET');
|
|
req.setTimeout(30000);
|
|
|
|
HttpResponse res = new Http().send(req);
|
|
ApiResponse response = new ApiResponse();
|
|
response.success = res.getStatusCode() >= 200 && res.getStatusCode() < 300;
|
|
response.statusCode = res.getStatusCode();
|
|
response.message = response.success ? 'eSignature user info request succeeded.' : 'eSignature user info request failed.';
|
|
response.requestPath = '/oauth/userinfo';
|
|
response.responseBody = res.getBody();
|
|
return response;
|
|
}
|
|
|
|
@AuraEnabled(cacheable=false)
|
|
public static ApiResponse getAccountInformation(String accountCode, String eSignatureAccountId) {
|
|
CLM_Account_Setting__mdt row = requireAccountSetting(accountCode);
|
|
String targetAccountId = requireESignatureAccountId(row, accountCode, eSignatureAccountId);
|
|
return probe(accountCode, '/v2.1/accounts/' + targetAccountId);
|
|
}
|
|
|
|
@AuraEnabled(cacheable=false)
|
|
public static List<TemplateSummary> listTemplates(String accountCode) {
|
|
CLM_Account_Setting__mdt row = requireAccountSetting(accountCode);
|
|
String targetAccountId = requireESignatureAccountId(row, accountCode, null);
|
|
ApiResponse response = probe(accountCode, '/v2.1/accounts/' + targetAccountId + '/templates');
|
|
if (!response.success) {
|
|
throw new AuraHandledException('eSignature API Error (HTTP ' + response.statusCode + '): ' + response.responseBody);
|
|
}
|
|
return parseTemplateList(response.responseBody);
|
|
}
|
|
|
|
@AuraEnabled(cacheable=false)
|
|
public static List<EnvelopeSummary> listEnvelopes(String accountCode, String fromDate) {
|
|
CLM_Account_Setting__mdt row = requireAccountSetting(accountCode);
|
|
String targetAccountId = requireESignatureAccountId(row, accountCode, null);
|
|
String normalizedFromDate = String.isBlank(fromDate)
|
|
? DateTime.newInstanceGMT(Date.today().addDays(-30), Time.newInstance(0, 0, 0, 0)).formatGMT('yyyy-MM-dd')
|
|
: fromDate.trim();
|
|
ApiResponse response = probe(
|
|
accountCode,
|
|
'/v2.1/accounts/' + targetAccountId + '/envelopes?from_date=' + EncodingUtil.urlEncode(normalizedFromDate, 'UTF-8')
|
|
);
|
|
if (!response.success) {
|
|
throw new AuraHandledException('eSignature API Error (HTTP ' + response.statusCode + '): ' + response.responseBody);
|
|
}
|
|
return parseEnvelopeList(response.responseBody);
|
|
}
|
|
|
|
@AuraEnabled(cacheable=false)
|
|
public static EnvelopeCreateResult createEnvelope(Id caseId, String accountCode, String templateId) {
|
|
EnvelopeCreateResult result = new EnvelopeCreateResult();
|
|
result.success = false;
|
|
try {
|
|
if (String.isBlank(templateId)) {
|
|
result.errorMessage = 'Template ID is required.';
|
|
return result;
|
|
}
|
|
|
|
CLM_Account_Setting__mdt row = requireAccountSetting(accountCode);
|
|
String targetAccountId = requireESignatureAccountId(row, accountCode, null);
|
|
|
|
Appraiser_Case__c caseRecord = [
|
|
SELECT Appraiser_Email__c,
|
|
Appraiser_Name__c,
|
|
Appraiser_Last_Name__c,
|
|
Appraiser_Salutation__c
|
|
FROM Appraiser_Case__c
|
|
WHERE Id = :caseId
|
|
LIMIT 1
|
|
];
|
|
|
|
if (String.isBlank(caseRecord.Appraiser_Email__c)) {
|
|
result.errorMessage = 'Appraiser email is blank on this case. Cannot create envelope.';
|
|
return result;
|
|
}
|
|
|
|
String appraiserName = String.isNotBlank(caseRecord.Appraiser_Name__c)
|
|
? caseRecord.Appraiser_Name__c
|
|
: ((String.isNotBlank(caseRecord.Appraiser_Salutation__c)
|
|
? caseRecord.Appraiser_Salutation__c + ' '
|
|
: '') + String.valueOf(caseRecord.Appraiser_Last_Name__c)).trim();
|
|
|
|
Map<String, Object> roleMap = new Map<String, Object>{
|
|
'email' => caseRecord.Appraiser_Email__c,
|
|
'name' => appraiserName,
|
|
'roleName' => 'Signer'
|
|
};
|
|
Map<String, Object> bodyMap = new Map<String, Object>{
|
|
'templateId' => templateId,
|
|
'templateRoles' => new List<Object>{ roleMap },
|
|
'status' => 'sent'
|
|
};
|
|
|
|
HttpRequest req = new HttpRequest();
|
|
req.setEndpoint(buildEndpoint(
|
|
'/v2.1/accounts/' + targetAccountId + '/envelopes',
|
|
row.ESignature_Rest_Named_Credential__c
|
|
));
|
|
req.setMethod('POST');
|
|
req.setHeader('Content-Type', 'application/json');
|
|
req.setTimeout(30000);
|
|
req.setBody(JSON.serialize(bodyMap));
|
|
|
|
HttpResponse res = new Http().send(req);
|
|
if (res.getStatusCode() >= 200 && res.getStatusCode() < 300) {
|
|
Map<String, Object> responseMap = (Map<String, Object>) JSON.deserializeUntyped(res.getBody());
|
|
result.success = true;
|
|
result.envelopeId = (String) responseMap.get('envelopeId');
|
|
result.status = (String) responseMap.get('status');
|
|
result.envelopeUri = (String) responseMap.get('uri');
|
|
} else {
|
|
result.errorMessage = 'eSignature API Error (HTTP ' + res.getStatusCode() + '): ' + res.getBody();
|
|
}
|
|
} catch (Exception e) {
|
|
result.success = false;
|
|
result.errorMessage = e.getMessage();
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@TestVisible
|
|
private static List<ESignatureAccountSummary> parseAccountList(String body) {
|
|
Object root = JSON.deserializeUntyped(body);
|
|
List<Object> records = new List<Object>();
|
|
if (root instanceof List<Object>) {
|
|
records = (List<Object>) root;
|
|
} else if (root instanceof Map<String, Object>) {
|
|
Object loginAccounts = ((Map<String, Object>) root).get('loginAccounts');
|
|
if (loginAccounts instanceof List<Object>) {
|
|
records = (List<Object>) loginAccounts;
|
|
}
|
|
Object accounts = ((Map<String, Object>) root).get('accounts');
|
|
if (records.isEmpty() && accounts instanceof List<Object>) {
|
|
records = (List<Object>) accounts;
|
|
}
|
|
}
|
|
|
|
List<ESignatureAccountSummary> summaries = new List<ESignatureAccountSummary>();
|
|
for (Object record : records) {
|
|
if (!(record instanceof Map<String, Object>)) {
|
|
continue;
|
|
}
|
|
Map<String, Object> row = (Map<String, Object>) record;
|
|
ESignatureAccountSummary summary = new ESignatureAccountSummary();
|
|
summary.accountId = firstString(row, new List<String>{ 'accountId', 'account_id' });
|
|
summary.accountName = firstString(row, new List<String>{ 'accountName', 'account_name', 'name' });
|
|
summary.baseUri = firstString(row, new List<String>{ 'baseUri', 'base_uri', 'baseUrl', 'base_url' });
|
|
summary.isDefault = parseBoolean(row.get('isDefault'));
|
|
summary.rawJson = JSON.serialize(row);
|
|
summaries.add(summary);
|
|
}
|
|
return summaries;
|
|
}
|
|
|
|
@TestVisible
|
|
private static List<TemplateSummary> parseTemplateList(String body) {
|
|
List<Object> records = extractCollection(body, new List<String>{ 'envelopeTemplates', 'templates' });
|
|
List<TemplateSummary> summaries = new List<TemplateSummary>();
|
|
for (Object record : records) {
|
|
if (!(record instanceof Map<String, Object>)) {
|
|
continue;
|
|
}
|
|
Map<String, Object> row = (Map<String, Object>) record;
|
|
TemplateSummary summary = new TemplateSummary();
|
|
summary.templateId = firstString(row, new List<String>{ 'templateId', 'template_id' });
|
|
summary.name = firstString(row, new List<String>{ 'name' });
|
|
summary.description = firstString(row, new List<String>{ 'description' });
|
|
summary.shared = firstString(row, new List<String>{ 'shared' });
|
|
summary.lastModified = firstString(row, new List<String>{ 'lastModified', 'last_modified', 'lastModifiedDateTime' });
|
|
summary.rawJson = JSON.serialize(row);
|
|
summaries.add(summary);
|
|
}
|
|
return summaries;
|
|
}
|
|
|
|
@TestVisible
|
|
private static List<EnvelopeSummary> parseEnvelopeList(String body) {
|
|
List<Object> records = extractCollection(body, new List<String>{ 'envelopes' });
|
|
List<EnvelopeSummary> summaries = new List<EnvelopeSummary>();
|
|
for (Object record : records) {
|
|
if (!(record instanceof Map<String, Object>)) {
|
|
continue;
|
|
}
|
|
Map<String, Object> row = (Map<String, Object>) record;
|
|
EnvelopeSummary summary = new EnvelopeSummary();
|
|
summary.envelopeId = firstString(row, new List<String>{ 'envelopeId', 'envelope_id' });
|
|
summary.emailSubject = firstString(row, new List<String>{ 'emailSubject', 'email_subject' });
|
|
summary.status = firstString(row, new List<String>{ 'status' });
|
|
summary.createdDateTime = firstString(row, new List<String>{ 'createdDateTime', 'created_datetime' });
|
|
summary.sentDateTime = firstString(row, new List<String>{ 'sentDateTime', 'sent_datetime' });
|
|
summary.completedDateTime = firstString(row, new List<String>{ 'completedDateTime', 'completed_datetime' });
|
|
summary.rawJson = JSON.serialize(row);
|
|
summaries.add(summary);
|
|
}
|
|
return summaries;
|
|
}
|
|
|
|
@TestVisible
|
|
private static String buildEndpoint(String relativePath, String namedCredential) {
|
|
if (String.isBlank(namedCredential)) {
|
|
throw new AuraHandledException('No eSignature named credential is configured for this account.');
|
|
}
|
|
return 'callout:' + namedCredential + normalizePath(relativePath);
|
|
}
|
|
|
|
private static String normalizePath(String relativePath) {
|
|
if (String.isBlank(relativePath)) {
|
|
throw new AuraHandledException('A relative path is required.');
|
|
}
|
|
return relativePath.startsWith('/') ? relativePath : '/' + relativePath;
|
|
}
|
|
|
|
private static CLM_Account_Setting__mdt requireAccountSetting(String accountCode) {
|
|
String normalizedCode = String.isBlank(accountCode) ? null : accountCode.trim();
|
|
List<CLM_Account_Setting__mdt> rows = new List<CLM_Account_Setting__mdt>();
|
|
if (String.isNotBlank(normalizedCode)) {
|
|
rows = [
|
|
SELECT DeveloperName,
|
|
Account_Code__c,
|
|
Account_Display_Name__c,
|
|
Environment_Code__c,
|
|
ESignature_Auth_Named_Credential__c,
|
|
ESignature_Rest_Named_Credential__c,
|
|
ESignature_Account_Id__c,
|
|
Active__c
|
|
FROM CLM_Account_Setting__mdt
|
|
WHERE Active__c = true
|
|
AND DeveloperName = :normalizedCode
|
|
LIMIT 1
|
|
];
|
|
if (rows.isEmpty()) {
|
|
rows = [
|
|
SELECT DeveloperName,
|
|
Account_Code__c,
|
|
Account_Display_Name__c,
|
|
Environment_Code__c,
|
|
ESignature_Auth_Named_Credential__c,
|
|
ESignature_Rest_Named_Credential__c,
|
|
ESignature_Account_Id__c,
|
|
Active__c
|
|
FROM CLM_Account_Setting__mdt
|
|
WHERE Active__c = true
|
|
AND Account_Code__c = :normalizedCode
|
|
LIMIT 1
|
|
];
|
|
}
|
|
}
|
|
|
|
if (rows.isEmpty()) {
|
|
throw new AuraHandledException('No active CLM account setting was found for ' + accountCode + '.');
|
|
}
|
|
|
|
CLM_Account_Setting__mdt row = rows[0];
|
|
if (String.isBlank(row.ESignature_Rest_Named_Credential__c)) {
|
|
throw new AuraHandledException('No eSignature named credential is configured for ' + accountCode + '.');
|
|
}
|
|
return row;
|
|
}
|
|
|
|
private static String authNamedCredential(CLM_Account_Setting__mdt row) {
|
|
return String.isNotBlank(row.ESignature_Auth_Named_Credential__c)
|
|
? row.ESignature_Auth_Named_Credential__c
|
|
: row.ESignature_Rest_Named_Credential__c;
|
|
}
|
|
|
|
private static String requireESignatureAccountId(CLM_Account_Setting__mdt row, String accountCode, String eSignatureAccountId) {
|
|
String targetAccountId = String.isNotBlank(eSignatureAccountId) ? eSignatureAccountId : row.ESignature_Account_Id__c;
|
|
if (String.isBlank(targetAccountId)) {
|
|
throw new AuraHandledException('No eSignature account id is configured for ' + accountCode + '.');
|
|
}
|
|
return targetAccountId;
|
|
}
|
|
|
|
private static List<Object> extractCollection(String body, List<String> keys) {
|
|
Object root = JSON.deserializeUntyped(body);
|
|
if (root instanceof List<Object>) {
|
|
return (List<Object>) root;
|
|
}
|
|
if (root instanceof Map<String, Object>) {
|
|
Map<String, Object> source = (Map<String, Object>) root;
|
|
for (String key : keys) {
|
|
Object records = source.get(key);
|
|
if (records instanceof List<Object>) {
|
|
return (List<Object>) records;
|
|
}
|
|
}
|
|
}
|
|
return new List<Object>();
|
|
}
|
|
|
|
private static String firstString(Map<String, Object> source, List<String> keys) {
|
|
for (String key : keys) {
|
|
Object value = source.get(key);
|
|
if (value != null) {
|
|
String text = String.valueOf(value);
|
|
if (String.isNotBlank(text)) {
|
|
return text;
|
|
}
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static Boolean parseBoolean(Object value) {
|
|
if (value == null) {
|
|
return false;
|
|
}
|
|
if (value instanceof Boolean) {
|
|
return (Boolean) value;
|
|
}
|
|
return String.valueOf(value).toLowerCase() == 'true';
|
|
}
|
|
}
|