Utilities
Work with File Storage
ODF Framework provides Java API for S3 operations. Here is the list of the most important methods.
Prior to ODF v10.0, the com.workfusion.rpa.core.storage.S3Manager class is used for S3 operations:
String putFile(String bucket, String filePath, InputStream input, String contentType);
String putFile(String filePath, InputStream input, String contentType);
String uploadFileToS3(String bucket, byte[] newDocumentToUpload, String fileName, String contentType);
String uploadFileToS3(byte[] newDocumentToUpload, String fileName, String contentType);
boolean deleteFile(String path);
List<String> listFiles(String path, String filter);
String getFullResourcePath(String resourcePath);
InputStream getFile(String bucket, String path);
InputStream getFile(String path);
Here, you can find S3 API usage examples.
To be able to use S3Manager, you can simply inject it into the target object as shown in the example below:
private final S3Manager s3Manager;
@Inject
public ExcelToHtmlProcessor(S3Manager s3Manager) {
this.s3Manager = s3Manager;
}
Be aware that S3Manager expects the defaultS3Bucket parameter to be injected, which means it has to be defined in one of the custom
modules.
@Inject
public S3Manager(Binding binding, Logger logger, @Named("defaultS3Bucket") String defaultS3Bucket) {
if (StringUtils.isEmpty(defaultS3Bucket)) {
throw new IllegalArgumentException("S3 bucket cannot be empty");
}
}
The example of the Module with the S3 default bucket provider:
public class ExampleModuleWithS3 implements Module {
@Provides
@Named("defaultS3Bucket")
public String s3Bucket() {
return CommonConstants.INTAKE_S3_BUCKET;
}
}
Work with Data Stores
ODF provides Java API for Data Stores operations.
There are two main classes involved:
com.workfusion.rpa.core.datastore.DataStoreInsertwith the following methods available:long insertRow(String dsName, Map<String, String> row); long insertRow(String dsName, String json); long insertRow(String dsName, DataStoreRow dsRow);com.workfusion.rpa.core.datastore.DataStoreQuerywith the following methods available:QueryResult executeQuery(String dsName, String query);
Example of work with ODF Transactions
public class DatastoreTransactionService implements TransactionService {
private static final String TRANSACTION_DS_NAME = "my_datastore";
private static final String READ_TRANSACTION_QUERY_TEMPLATE = "select * from @this where transaction_id = '%s'";
private static final String UPDATE_TRANSACTION_QUERY_TEMPLATE = "update @this set transaction_data = '%s' where transaction_id = '%s'";
private static final String DELETE_TRANSACTION_QUERY_TEMPLATE = "delete from @this where transaction_id = '%s'";
private final DataStoreInsert dataStoreInsert;
private final DataStoreQuery dataStoreQuery;
private final Logger logger;
private final Binding binding;
@Inject
public DatastoreTransactionService(DataStoreInsert dataStoreInsert, DataStoreQuery dataStoreQuery, Logger logger, Binding binding) {
this.dataStoreInsert = dataStoreInsert;
this.dataStoreQuery = dataStoreQuery;
this.logger = logger;
this.binding = binding;
}
@Override
public void save(Transaction transaction) {
String id = transaction.getId();
String runId = BindingUtils.getBpRunId(binding);
Map<String, String> transactionRow = new HashMap<>();
transactionRow.put("transaction_id", id);
transactionRow.put("bp_run_id", runId);
transactionRow.put("transaction_data", InternalStorageFormats.toJson(transaction));
dataStoreInsert.insertRow(TRANSACTION_DS_NAME, transactionRow);
}
@Override
public Transaction byId(String transactionId) {
DataStoreQuery.QueryResult result = dataStoreQuery.executeQuery(TRANSACTION_DS_NAME, String.format(READ_TRANSACTION_QUERY_TEMPLATE, transactionId));
List<Map<String, String>> rows = result.getSelectResultAsMapRows().orElse(Collections.emptyList());
if (rows.isEmpty()) {
logger.error("ODF transaction with id={} does not exist", transactionId);
return null;
}
return rows.stream()
.map(row -> row.get("transaction_data"))
.map(InternalStorageFormats::fromJson)
.findFirst()
.get();
}
@Override
public void update(Transaction transaction) {
try {
String id = transaction.getId();
String updateTransactionQuery = String.format(UPDATE_TRANSACTION_QUERY_TEMPLATE,
StringEscapeUtils.escapeSql(InternalStorageFormats.toJson(transaction)), StringEscapeUtils.escapeSql(id));
dataStoreQuery.executeQuery(TRANSACTION_DS_NAME, updateTransactionQuery);
} catch (Exception e) {
throw new IllegalStateException("Cannot update the transaction", e);
}
}
@Override
public void delete(String transactionId) {
dataStoreQuery.executeQuery(TRANSACTION_DS_NAME, String.format(DELETE_TRANSACTION_QUERY_TEMPLATE, transactionId));
}
}
Example of reading from regular Data Store
The SimpleDatastoreExample class:
public class SimpleDatastoreExample {
private static final String DATASTORE_NAME = "my_datastore";
private static final String READ_QUERY_TEMPLATE = "select * from @this";
private final DataStoreQuery dataStoreQuery;
@Inject
public SimpleDatastoreExample(DataStoreQuery dataStoreQuery) {
this.dataStoreQuery = dataStoreQuery;
}
public List<Map<String, String>> readData() {
DataStoreQuery.QueryResult result = dataStoreQuery.executeQuery(DATASTORE_NAME, String.format(READ_QUERY_TEMPLATE, DATASTORE_NAME));
return result.getSelectResultAsMapRows().get();
}
}
The TransactionSupplierExample class:
public class TransactionSupplierExample implements TransactionSupplier {
private final DataStoreQuery dataStoreQuery;
@Inject
public TransactionSupplierExample(DataStoreQuery dataStoreQuery) {
this.dataStoreQuery = dataStoreQuery;
}
@Override
public Collection<Transaction> get() {
SimpleDatastoreExample dsExample = new SimpleDatastoreExample(dataStoreQuery);
return transformRecordsIntoTransactions(dsExample.readData());
}
private Collection<Transaction> transformRecordsIntoTransactions(List<Map<String, String>> result) {
Collection<Transaction> transactions = new ArrayList<>();
for (int index = 0; index < result.size(); index++) {
Transaction transaction = new Transaction();
transaction.setId(uuid());
transaction.setDocs(Arrays.asList(createSampleDocument(result.get(index))));
transactions.add(transaction);
}
return transactions;
}
private Document createSampleDocument(Map<String, String> row) {
Document document = new Document();
document.setId(uuid());
for (String column_name : row.keySet()) {
document.putAttribute(column_name, row.get(column_name));
}
return document;
}
private String uuid() {
return UUID.randomUUID().toString();
}
}
Bot Task to invoke the TransactionSupplierExample class:
<config xmlns="http://web-harvest.sourceforge.net/schema/1.0/config" scriptlang="groovy">
<script></script>
<export include-original-data="false">
<multi-column list="${result}" split-results="true">
<put-to-column-getter name="_sys_transaction_id" property="transaction_id"/>
</multi-column>
</export>
</config>
Transaction Service
Prior to ODF v10.0, the com.workfusion.odf.service.DBTransactionService class is used for database transactional operations:
/**
* Starts database transaction: returns existing one or creates new one if needed.
*
* @return <code>DataStoreTransaction</code> instance to be used for transactional updates.
* @throws SQLException In case of DB errors during transaction creation.
*/
DataStoreTransaction startTransaction() throws SQLException;
/**
* Commits existing transaction.
*
* @throws SQLException In case of Db errors during transaction commit.
*/
void commitTransaction() throws SQLException;
/**
* Rolls back existing transaction.
*/
void rollbackTransaction();
/**
* Returns existing transaction or <code>null</code> if none is started.
* @return Existing transaction or <code>null</code> if none is started.
*/
DataStoreTransaction getTransaction();
The usage example:
public class DBTransactionServiceExample {
private final DBTransactionService transactionService;
@Inject
public DBTransactionServiceExample(DBTransactionService transactionService) {
this.transactionService = transactionService;
}
public <T> T process(Supplier<T> transactionalOperation) {
try {
transactionService.startTransaction();
T result = transactionalOperation.get();
transactionService.commitTransaction();
return result;
} catch (Exception e) {
transactionService.rollbackTransaction();
throw new IllegalStateException("Exception occurred while trying to perform transactional operation", e);
}
}
}
Work with Secrets Vault
ODF provides Java API for Secret Vault operations.
The com.workfusion.rpa.core.security.SecurityUtils class provides the following methods:
com.workfusion.bot.service.SecureEntryDTO getSecureEntry(String aliasString);
The example of usage with injection:
public class SecretVaultExample {
private final SecurityUtils securityUtils;
@Inject
public SecretVaultExample(SecurityUtils securityUtils) {
this.securityUtils = securityUtils;
}
public void doSomething() {
SecureEntryDTO secureEntry = securityUtils.getSecureEntry("my_secret");
String key = secureEntry.getKey();
String value = secureEntry.getValue();
}
}
Work with Emails
ODF provides Java API for sending notification emails:
public interface EmailSender {
/**
* Send e-mail message.
*
* @param message Message to send.
* @throws EmailMessagingException E-mail sending problems.
*/
void sendEmail(EmailMessage message) throws EmailMessagingException;
/**
* Send e-mail message.
*
* @param from Sender e-mail address.
* @param to Recipient e-mail address.
* @param subject Message subject.
* @param body Message body text.
* @throws EmailMessagingException E-mail sending problems.
*/
void sendEmail(String from, String to, String subject, String body) throws EmailMessagingException;
/**
* Send e-mail message.
*
* @param from Sender e-mail address.
* @param to List of recipient e-mail addresses.
* @param subject Message subject.
* @param body Message body text.
* @throws EmailMessagingException E-mail sending problems.
*/
void sendEmail(String from, List<String> to, String subject, String body) throws EmailMessagingException;
}
Configuration is done via SMTPEmailSender which you can instantiate and fill with credentials stored in Secure Storage and Global Variables:
/**
* @param host
* @param port
* @param user
* @param password
*/
public SMTPEmailSender(final SupportedProtocol protocol, final String host, final String port, final String user, final String password) {
this.protocol = protocol;
this.host = host;
this.port = port;
this.user = user;
this.password = password;
initStore();
}
The example of usage with injection:
public class EmailNotificationExample {
private final SMTPEmailSender smtpEmailSender;
@Inject
public EmailNotificationExample(SMTPEmailSender smtpEmailSender) {
this.smtpEmailSender = smtpEmailSender;
}
public void sendNotificationEmail() throws EmailMessagingException {
String from = "<from e-mail>";
String to = "<to e-mail>";
String subject = "Test message";
String body = "Hello,\n The batch is executed successfully.";
smtpEmailSender.sendMail(from, to, subject, body);
}
}
Sample send Email solution
try {
SecureEntryDTO secureEntry = securityUtils.getSecureEntry("GmailAccountAlias");
smtpEmailSender = new SMTPEmailSender(supportedProtocol, "smtp.gmail.com", "465", secureEntry.getKey(), secureEntry.getValue());
smtpEmailSender.sendEmail(secureEntry.getKey(), secureEntry.getKey(), topic, message);
transaction.putAttribute("status", "SUCCESS");
} catch (EmailMessagingException e) {
transaction.putAttribute("status", "FAILED");
}
Configure EmailTransactionConnector to use nested S3 folder
To set up EmailTransactionConnector to use the nested folder, the mail_s3_folder parameter (or EmailTransactionConnector.EmailSettings.MAIL_S3_FOLDER constant) should be specified:
def params = ['mail_s3_folder': 'my/super/folder/'];
def transactions = Intake.init(binding).params(params).get().loadTransactions(EmailTransactionConnector.class);