Technical Articles
Consuming SAP Cloud for Customer Data Workbench REST API from Java Applications
SAP Data Workbench (DWB) offers REST APIs that enable customers to import bulk data into SAP Cloud for Customer. It includes the following APIs:
- DWB Authentication API
- DWB Template API
- DWB Import API
Purpose
Use the SAP Cloud for Customer Data Workbench (DWB) REST API, to import bulk data into SAP Cloud for Customer.
This article walks through the techniques for consuming DWB REST API in Java applications.
Accessing the Authentication API
You can access the DWB Authentication API of your tenant, with the following URL pattern:
https://<your_C4C_tenant>/sap/c4c/dwb/singleusetoken/
Add the necessary code to invoke the DWB Authentication API using the user name & password. Consider wrapping the invocation to the DWB Authentication API in a try-catch block to capture any exceptions that are thrown by the authentication attempt.
The following example is applicable for Java 8.
Import Statements:
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Entity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.apache.chemistry.opencmis.commons.impl.json.JSONObject;
import org.apache.chemistry.opencmis.commons.impl.json.parser.JSONParseException;
import org.apache.chemistry.opencmis.commons.impl.json.parser.JSONParser;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.glassfish.jersey.client.authentication.HttpAuthenticationFeature;
import org.junit.Assert;
Initializing the REST Client:
HttpAuthenticationFeature feature = HttpAuthenticationFeature.basic(username, password);
Client client = ClientBuilder.newClient();
client.register(feature);
Request Token:
private Token requestToken() {
Token token = null;
try {
String tokenResponse = client
.target("https://" + c4cTenantURL
+ "/sap/c4c/dwb/singleusetoken/")
.request(MediaType.APPLICATION_JSON).get()
.readEntity(String.class);
if (tokenResponse == null || tokenResponse.isEmpty()
|| tokenResponse.startsWith("<html>")) {
Assert.fail("Request Token Failed");
}
JSONObject tokenResponseJSON = (JSONObject) new JSONParser()
.parse(tokenResponse);
if (tokenResponseJSON == null) {
Assert.fail("Request Token Failed");
}
String hostName = (String) tokenResponseJSON.get("HostName");
String tokenString = (String) tokenResponseJSON.get("Token");
if (hostName == null || hostName.isEmpty() || tokenString == null
|| tokenString.isEmpty()) {
Assert.fail("Request Token Failed");
}
token = new Token();
token.setHostName(hostName);
token.setToken(tokenString);
} catch (JSONParseException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
return token;
}
Assuming that the authentication API succeeds, the resulting payload contains the DWB API service endpoint URL (HostName) and Token. Note that the Token expires after 30 seconds.
Accessing the Data Workbench Template API to download Template
The Template API is used to download the template file to prepare data for submitting via DWB API.
Request URL: <HostName>/api/v1/template?token=<token>&serviceName=<service_name>&entityName=<rootEntityName>&isCustom=false&type=1&operation=1
private boolean downloadTemplate(Token token,
TemplateRequest templateRequest) {
try {
String url = token.getHostName() + "/api/v1/template?token="
+ token.getToken() + "&serviceName="
+ templateRequest.getServiceName() + "&entityName="
+ templateRequest.getEntityName() + "&isCustom="
+ templateRequest.isCustom() + "&type="
+ templateRequest.getType() + "&operation="
+ templateRequest.getOperation();
Response response = client.target(url).request().get();
if (response.getStatus() == 200) {
try (final InputStream is = (InputStream) response.getEntity()) {
File templateFile = new File("src/main/test/data/"
+ templateRequest.getTemplateFileName());
Files.copy(is, templateFile.toPath(),
StandardCopyOption.REPLACE_EXISTING);
}
} else {
Assert.fail("Error: " + Integer.toString(response.getStatus())
+ " " + response.getStatusInfo());
}
} catch (Exception e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
return true;
}
Accessing the Data Workbench REST API to Import Data
You can access the DWB API to Import Data, with the following URL pattern:
<HostName> + “/api/v1/import?token=” + <Token>
Add the necessary code to invoke the DWB API to Import Data using the HostName & Token. Consider wrapping the invocation to the DWB REST API in a try-catch block to capture any exceptions that are thrown by the data import attempt.
private String importData(Token token, Task task) {
String taskName = null;
try {
File dataFile = new File(task.getDataFile());
byte[] fileContent = IOUtils.toByteArray(new FileInputStream(
dataFile));
String data = new String(Base64.encodeBase64(fileContent, true),
"UTF-8");
String body = "<task>" + "<customService>"
+ task.getCustomService() + "</customService>"
+ "<dataFile>" + data + "</dataFile>"
+ "<rootEntitySetName>" + task.getEntitySetName()
+ "</rootEntitySetName>" + "<ignoreBlankValues>"
+ task.isIgnoreBlankValues() + "</ignoreBlankValues>"
+ "<initialLoad>" + task.isMigrationMode()
+ "</initialLoad>" + "<operation>" + task.getOperation()
+ "</operation>" + "<projectName>" + task.getProjectName()
+ "</projectName>" + "<sequence>" + task.getSequence()
+ "</sequence>" + "<serviceName>" + task.getServiceName()
+ "</serviceName>" + "<simulation>" + task.isSimulation()
+ "</simulation>" + "<taskType>" + task.getTaskType()
+ "</taskType>" + "<errorThreshold>" + task.getThreshold()
+ "</errorThreshold>" + "<lastTask>" + task.isLastTask()
+ "</lastTask>" + "</task>";
String url = token.getHostName() + "/api/v1/import?token="
+ token.getToken();
Response dwbAPIResponse = client
.target(url)
.request(MediaType.APPLICATION_JSON)
.post(Entity.entity(body, MediaType.APPLICATION_XML),
Response.class);
if (dwbAPIResponse.getStatus() == 200) {
JSONObject dwbAPIResponseJSON = null;
dwbAPIResponseJSON = (JSONObject) new JSONParser()
.parse(dwbAPIResponse.readEntity(String.class));
taskName = (String) dwbAPIResponseJSON.get("taskName");
} else {
Assert.fail("Error: "
+ Integer.toString(dwbAPIResponse.getStatus()) + " "
+ dwbAPIResponse.getStatusInfo());
}
} catch (JSONParseException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
} catch (FileNotFoundException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
} catch (IOException e) {
e.printStackTrace();
Assert.fail(e.getMessage());
}
return taskName;
}
Sample POST Request XML Body:
Multiple DWB import tasks can be created for a single project, by invoking the DWB REST API in a loop.
Project dwbProject = prepareData();
for (Task task : dwbProject.getTasks()) {
Token token = this.requestToken();
Assert.assertNotNull(token);
String taskName = this.importData(token, task);
Assert.assertNotNull(taskName);
}
For reference, Class Project, Task, Token & TemplateRequest:
public class Project {
private String name;
private List<Task> tasks = new ArrayList<Task>();
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Task> getTasks() {
return tasks;
}
public void addTask(Task task) {
this.tasks.add(task);
}
}
public class Task {
private String serviceName;
private boolean customService;
private String dataFile;
private String entitySetName;
private boolean ignoreBlankValues;
private boolean migrationMode;
private int operation;
private int sequence;
private boolean simulation;
private int taskType;
private int threshold;
private boolean lastTask;
private String projectName;
public String getProjectName() {
return projectName;
}
public void setProjectName(String projectName) {
this.projectName = projectName;
}
public String getServiceName() {
return serviceName;
}
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
public boolean getCustomService() {
return customService;
}
public void setCustomService(boolean customService) {
this.customService = customService;
}
public String getDataFile() {
return dataFile;
}
public void setDataFile(String dataFile) {
this.dataFile = dataFile;
}
public String getEntitySetName() {
return entitySetName;
}
public void setEntitySetName(String entitySetName) {
this.entitySetName = entitySetName;
}
public boolean isIgnoreBlankValues() {
return ignoreBlankValues;
}
public void setIgnoreBlankValues(boolean ignoreBlankValues) {
this.ignoreBlankValues = ignoreBlankValues;
}
public boolean isMigrationMode() {
return migrationMode;
}
public void setMigrationMode(boolean migrationMode) {
this.migrationMode = migrationMode;
}
public int getOperation() {
return operation;
}
public void setOperation(int operation) {
this.operation = operation;
}
public int getSequence() {
return sequence;
}
public void setSequence(int sequence) {
this.sequence = sequence;
}
public boolean isSimulation() {
return simulation;
}
public void setSimulation(boolean simulation) {
this.simulation = simulation;
}
public int getTaskType() {
return taskType;
}
public void setTaskType(int taskType) {
this.taskType = taskType;
}
public int getThreshold() {
return threshold;
}
public void setThreshold(int threshold) {
this.threshold = threshold;
}
public boolean isLastTask() {
return lastTask;
}
public void setLastTask(boolean lastTask) {
this.lastTask = lastTask;
}
}
public class Token {
private String token;
private String hostName;
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public String getHostName() {
return hostName;
}
public void setHostName(String hostName) {
this.hostName = hostName;
}
}
public class TemplateRequest {
private String serviceName;
private String entityName;
private boolean isCustom;
private int type;
private int operation;
private String templateFileName;
public String getTemplateFileName() {
return templateFileName;
}
public void setTemplateFileName(String templateFileName) {
this.templateFileName = templateFileName;
}
public String getServiceName() {
return serviceName;
}
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
public String getEntityName() {
return entityName;
}
public void setEntityName(String entityName) {
this.entityName = entityName;
}
public boolean isCustom() {
return isCustom;
}
public void setCustom(boolean isCustom) {
this.isCustom = isCustom;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public int getOperation() {
return operation;
}
public void setOperation(int operation) {
this.operation = operation;
}
}
Assuming that the DWB REST API succeeds, the resulting payload contains the DWB Task Name for the submitted data file.
Monitoring Data Workbench Project/Tasks submitted using the DWB REST API
Logon to SAP Cloud for Customer front-end and navigate to Data Workbench Monitor work center view to monitor the submitted tasks for a project:
The submitted DWB task can also be monitored using the following OData endpoint:
https://<Your_C4C_Tenant>/sap/c4c/odata/v1/dwbmonitor/ScenarioCollection?$filter=ScenarioName%20eq%20%27<Task_Name>%27
Note: Your C4C Tenant username and password is required to Sign In to this endpoint.
Tips & Tricks
- The DWB API REST calls should be made sequentially for a project.
- A new token should be fetched for each call to the DWB REST API; Token expires after 30 seconds.
- The last task of the project must be indicated by <lastTask>true</lastTask>, as the last call to the DWB REST API for that project.
- Submitted tasks for a project are processed in DWB only when the last task is submitted using the DWB REST API.
Summary
- Use the DWB Authentication API to fetch token, and the DWB Import API endpoint
- Use the DWB Template API to download the template for an object, and prepare data using the template
- Using DWB Import API, create mass data into SAP Cloud for Customer
Very well documented!
Thanks for the thorough Instructions. I have one question regarding the Import. Since the "Complete Business Object" Template seems to be the default DWB Template type, it is unclear to me whether the import of "Single Object" Templates is also supported. Do you have any Experience in uploading those in this manner?
Hello Tobias Schellinger ,
Yes, import of "Single Object" Templates is also supported.
In Data Workbench Template API, use "type" code "2" for Individual Business Object.
In Data Workbench Import API, use "taskType" code "2" for Individual Business Object.
For details, please refer Data Workbench API Documentation:
https://help.sap.com/viewer/c52d3ba13f56403ba116560148fd2071/CLOUD/en-US/3ee40995eb704c7f8e778676a2cac520.html
Regards,
Sounak
Hello Sounak Basu,
thank you for your quick reply on my previous Inquiry.
It would be greatly appreciated if you could help me with this one as well.
I have tried importing multiple Files via the Import API as described here and in the Documentation of the DWB APIs. For some reason, the Information for those Imports is hidden on the DWB Front End. The Information is accessible via the ODATA Service, but they have IsHidden set to true and remain in queue. Are there any prerequisites or rules on using the Import API that are not mentioned in the Blog Post or the documentation?
The Data I used for testing was definitely wrong, but I expected to receive the Error log within the DWB Front End.
I have one more question. The Documentation refers to 2 different Operation types, Insert and Upsert. Is there no Update Method when Importing Individual Business Objects? I am asking since it is available within the DWB Import Front End.
Kind Regards,
Tobias Schellinger
Edit:
I have figured out how to enable the project view and can now see the tasks via the Front end. It is noted how to achieve this within the Documentations, however, i have regrettably missed that part. The other issues still remain, Tasks are not processed, even though I sent the last Task, and Rest Requests sporadically return internal errors. I am working with a heavily developed on Test Tenant, could it just be, that something is not right in the Backend?
Hello Tobias Schellinger
Glad to know you could enable the projects in DWB Monitor.
Update operation via DWB API is not yet implemented for Individual Business Objects.
If the tasks submitted via DWB API remain unprocessed or results in errors, please consider creating an incident on following component: LOD-CRM-INT-DWB-API
Best Regards,
Sounak Basu
Hello Sounak Basu,
thank you for your reply!
I've created an Incident, just as you have suggested.
I assumed that the APIs offers the same amount of functionality as the Front End Workcenter in terms of importing.
I am trying to streamline the Process of data migration Projects via the APIs, are there any other Limitations on the APIs compared to the Workcenter that I should be aware of?
Are there any Plans on implementing the update Operartion or continuing development on the APIs?
Best Regards,
Tobias Schellinger
Hi Tobias Schellinger
Yes, data import using DWB API is consistent with frontend with following limitations:
Update data using DWB API is planned for future releases. To fast track the requirement, please add this use case in SAP Influence Portal.
Regards,
Sounak
Thanks for this guide - honestly, without this blog the workbench API is not usable. The official documentation on help.sap.com is written poorly, is misleading, and is plain wrong in a few places.
Hi Sounak
Thank you for your howto!
In SAP Help page I saw that the data file needs to be in zip format.
Do you know if we really need tho have the data file in zip format if we would use task type 2 (Individual Business Object).
When importing individual business objects using the c4c data workbench, we would need to upload a csv file.
thx and regards
Christian
Hi Christian Langmann ,
For task type 2 (Individual Business Object), we can select CSV file; the API accepts base64 encoded byte array of the CSV data file.
Regards,
Sounak
Hi Sounak Basu
Do you know, if this List of Objects Available in Data Workbench is up-to-date?
I am missing a way to automatically create call lists using this (or any other) API.
Can you name the relevant OData Service Name in Data Workbench.
There is the option to import call lists using Data Workbench manually.
thx and regards!
Hi Christian Langmann ,
The list of objects is generated and should be up-to-date.
Link - https://help.sap.com/viewer/c52d3ba13f56403ba116560148fd2071/CLOUD/en-US/04a358aaf14c42aebdec2bd75e72e872.html
Maybe you can refer the "activity" OData Service for Data Workbench Object "Phone Call":
https://<tenant_host_name>/sap/c4c/odata/v1/activity/PhoneCallCollection
Regards,
Sounak