Use Case configuration guide
A crucial aspect of installation and further support is implementing a Use Case so that you can change any setting outside of the implementation code without recompiling, updating, or re-deploying your Use Case. Typically, this is achieved by making your configuration external to the source code.
The Use Case configuration is a set of parameters that can change over time, depending on the environment or client. In ODF 2, it is implemented in a similar way. Generally, an ODF 2 Use Case configuration consists of the following:
- The uc_[UC_CODE]_config_[UC_DATA_MODEL_VERSION] configuration Data Store in the Use Case Data Model (the Data Store has only two properties:
nameandvalue). - Access to the configuration provided by the
com.workfusion.odf2.core.settings.Configurationmethods.
Perform configuration steps
To work with the out-of-the-box ODF 2 configuration, do as follows:
- To define all configuration points you want to keep external to the code, add the default values to uc_[UC_CODE]_config_[UC_DATA_MODEL_VERSION]. You can do it in several ways:
- Modify an initial migration by adding more values into
usecase_config.csvloaded by the migration defined inrecords.xml. - Add a new migration step if your project is already in Production.
- Modify an initial migration by adding more values into
- Use configuration points, for example, in your Bot Task code. Inject
com.workfusion.odf2.core.settings.Configurationto the constructor of your Bot Task so that APIs become available.
View configuration APIs
Data Store properties can be either always defined or optional. That is why two pairs of methods are provided by the framework.
The four methods of com.workfusion.odf2.core.settings.Configuration are as follows:
public Optional<String> getProperty(String name) {
return configRepository.findByName(name).map(ConfigEntity::getValue);
}
public String getRequiredProperty(String name) {
return getProperty(name).orElseThrow(() -> new IllegalStateException(String.format("Configuration does not contain property '%s'", name)));
}
public Optional<String> getProperty(OdfConfigurationItem item) {
return getProperty(item.getPropertyName());
}
public String getRequiredProperty(OdfConfigurationItem item) {
return getRequiredProperty(item.getPropertyName());
}
where:
public enum OdfConfigurationItem {
OCR_S3_BUCKET("odf.ocr.s3.bucket"),
OCR_CACHE_ENABLED("odf.ocr.cache.enabled");
private final String propertyName;
OdfConfigurationItem(String propertyName) {
this.propertyName = propertyName;
}
public String getPropertyName() {
return propertyName;
}
}
note
Be careful when working with the properties. It is highly recommended to work with them as optional and hardcode the default value.
If you hardcode any value, list it explicitly in the Use Case documentation so that those who perform setup are aware of it.
View configuration example
Now, you are all set to read any configuration point value stored in the configuration Data Store. Below is a sample Bot Task that uses the configuration to store an RPA application URL and an alias for Secrets Vault.
important
Store all credentials and other security-related data securely. In IA Cloud, the proper component for the purpose is Secrets Vault used to keep the credentials for accessing the application.
The example below provides the best practice to leverage the Use Case configuration with Secrets Vault.
Your Bot Task code where you use configuration values is as follows:
package com.example.intake.task;
import javax.inject.Inject;
import com.workfusion.bot.service.SecureEntryDTO;
import com.workfusion.odf2.core.settings.Configuration;
import com.workfusion.odf2.core.webharvest.service.vault.SecretsVaultService;
import org.slf4j.Logger;
import com.workfusion.odf2.compiler.BotTask;
import com.workfusion.odf2.core.task.generic.GenericTask;
import com.workfusion.odf2.core.task.rpa.RpaDriver;
import com.workfusion.odf2.core.task.rpa.RpaFactory;
import com.workfusion.odf2.core.task.rpa.RpaRunner;
import static com.workfusion.rpa.helpers.RPA.$;
import static com.workfusion.rpa.helpers.RPA.open;
import static com.workfusion.rpa.helpers.UiSelectors.byXpath;
@BotTask(requireRpa=true)
public class RPASampleTask implements GenericTask {
public static final String APP_URL = "app.invoiceplain.url";
public static final String APP_CREDENTIALS_ALIAS = "app.invoiceplain.credentials.alias";
private final Logger logger;
private final RpaRunner runner;
private final Configuration configuration;
private final SecretsVaultService secretsVault;
@Inject
public RPASampleTask(RpaFactory rpaFactory, Logger logger, Configuration configuration, SecretsVaultService secretsVault) {
this.logger = logger;
this.configuration = configuration;
this.secretsVault = secretsVault;
runner = rpaFactory.builder(RpaDriver.CHROME)
.closeOnCompletion(true)
.maximizeOnStartup(true)
.startInPrivate(true)
.capability("cleanSession", true)
.build();
}
@Override
public void run() {
logger.info("Hello World");
SecureEntryDTO credentials = secretsVault.getEntry(configuration.getRequiredProperty(APP_CREDENTIALS_ALIAS));
runner.execute(driver -> {
open(configuration.getRequiredProperty(APP_URL));
$("#email").sendKeys(credentials.getKey());
$("#password").sendKeys(credentials.getValue());
$(byXpath("//input[@type='submit']")).click();
});
}
}
To test, use the following Bot Task JUnit test:
package com.example.intake.task;
import com.workfusion.odf.test.launch.BotTaskUnit;
import com.workfusion.odf2.core.orm.model.internal.ConfigEntity;
import com.workfusion.odf2.junit.OrmSupport;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import com.workfusion.odf.test.junit.IacDeveloperJUnitConfig;
import com.workfusion.odf2.junit.BotTaskFactory;
import java.util.ArrayList;
import java.util.Collection;
import static org.assertj.core.api.Assertions.assertThatCode;
@IacDeveloperJUnitConfig
class RpaTaskTest {
@BeforeEach
void setUp(OrmSupport ormSupport) {
ormSupport.createTables(ConfigEntity.class);
// let's pre-create a config datastore with two required values
Collection<ConfigEntity> cfg = new ArrayList<ConfigEntity>() {{
add(new ConfigEntity("app.invoiceplain.url", "https://train-invoiceplane.workfusion.com"));
add(new ConfigEntity("app.invoiceplain.credentials.alias", "ipalias"));
}};
ormSupport.getConfigRepository().createAll(cfg);
}
@Test
@DisplayName("should run RPA to open and log in into InvoicePlane app in Chrome")
void shouldRunRPAService(BotTaskFactory botTaskFactory) {
// given
BotTaskUnit rpaDriverTask = botTaskFactory
.fromClass(RPASampleTask.class)
// provide credentials directly in the test code or via the properties file
.withSecureEntries(cfg -> cfg.withEntry("ipalias", "wf-robot@mail.com", "BotsRock4ever!"));
// then
assertThatCode(rpaDriverTask::buildAndRun).doesNotThrowAnyException();
}
}
Execute Bot Task in Control Tower
Before you execute the above example in Control Tower, perform a few preparation steps:
In Secrets Vault, create an
ipaliasitem to securely keep the credentials for https://train-invoiceplane.workfusion.com:
Deploy your Use Case project to Control Tower. Then, find the uc_[UC_CODE]_config_[UC_DATA_MODEL_VERSION] Data Store and verify there are two new configuration items available:

At this point, you should have the r-p-a-sample Bot Task available in Control Tower. Drag and drop this Bot Task into your Business Process and execute it for testing purposes.