Skip to Content
Technical Articles
Author's profile photo Sounak Basu

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.

DWB REST API Documentation

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

  1. The DWB API REST calls should be made sequentially for a project.
  2. A new token should be fetched for each call to the DWB REST API; Token expires after 30 seconds.
  3. 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.
  4. 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

Assigned tags

      8 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Former Member
      Former Member

      Very well documented!

      Author's profile photo Tobias Schellinger
      Tobias Schellinger

      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?

      Author's profile photo Sounak Basu
      Sounak Basu
      Blog Post Author

      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

       

      Author's profile photo Tobias Schellinger
      Tobias Schellinger

      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?

      Author's profile photo Sounak Basu
      Sounak Basu
      Blog Post Author

      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

      Author's profile photo Tobias Schellinger
      Tobias Schellinger

      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

      Author's profile photo Sounak Basu
      Sounak Basu
      Blog Post Author

      Hi Tobias Schellinger

      Yes, data import using DWB API is consistent with frontend with following limitations:

      1. Templates downloaded from frontend cannot be used to import data using API
      2. Format validation is not done
      3. Data mapping and Codelist mapping is not possible
      4. Import data using migration templates is not supported
      5. Update operation is not supported
      6. Import of Attachments & ID Mapping is not supported

      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

      Author's profile photo Manuel Seeger
      Manuel Seeger

      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.