Skip to Content

Introduction

Consume S/4HANA Cloud APIs indirectly via SAP Cloud Platform Integration

In this blog we will focus on indirect S/4HANA Cloud API consumption via SAP Cloud Platform Integration (in this blog referred to as CPI).

Ideally you have read through part 1: https://blogs.sap.com/2018/04/10/s4hana-cloud-integration-commercial-project-part-1

You might not want to address the API directly in S/4HANA Cloud but indirectly via a Middleware. This is the use case if you plan to connect S/4HANA Cloud to another system and an extensive mapping and routing operation is required. In our scenario we will use SAP Cloud Platform Integration (CPI). In CPI you are able to exploit an OData API, which both read and create project APIs are, as an Integration Flow or as an OData Service. In our case we will create an OData Service, and Integration Flow with an OData sender adapter and an Integration Flow with a HTTPS sender adapter. To do this you need to have activated the relevant APIs in S/4HANA Cloud (read part 1 for further information).

OData Catalog Service

If you are unsure of the URL to your OData service and want to view all deployed OData services in CPI, you can use the catalog service:

https://<CPI-tenant>/gw/CATALOGSERVICE;v=1/ServiceCollection

OData Service

In SAP Cloud Platform Integration you can expose existing S/4HANA Cloud SOAP and OData APIs as OData Services. The benefit here is that it creates relevant default mappings, while the Integration Flow does not. To do that instead of creating an Integration Flow in your integration package, create an OData Service:

Then you define a service name and namespace, which will be used in the URL to address the OData service. Once the top right click on “Import Model Wizard” and import the EDMX file you downloaded for your API from the API Business Hub. Select the OData Hierarchy that you want to include in your OData Service, in our case we included all four service nodes:

Once you check the structure and pressed on “finished”, you can view your OData model in the Graphical Model Viewer on the top right corner:

It will display you the structure of your OData service graphically:

Now we want to define the method, with which you want to address the OData service. Because we want to create a project in S/4HC we choose the method CREATE and press on “bind”:

Here we set the entity set to “ProjectSet and our End Point is the S/4HC API:

Once we press on OK, it will create an Integration Flow automatically for the CREATE method with these parameters. You need to check if both sender and receiver OData adapter have been configured correctly and if both mappings are correct:

Once this is done, you can deploy the OData service. In our case we use “CREATEPROJECTS” as a service name and “SAP” as namespace, which means our URL will have following format:

https://<CPI-tenant>/gw/odata/SAP/CREATEPROJECTS;v=1/ProjectSet

Here again, you can use the same payload structure from part 1 of my blog series and just change the project ID and project name:

{
  "ProjectCategory": "C",
  "OrgID": "1020",
  "CostCenter": "0010201903",
  "ProfitCenter": "YB102",
  "Customer": "IC1020",
  "Currency": "EUR",
  "ProjectID": "API",
  "ProjectName": "ProjectAPI",
  "ProjectStage": "P001",
  "ProjManagerExtId": "<User_name>",
  "StartDate": "2018-03-29T00:00:00.0000000",
  "EndDate": "2018-03-29T00:00:00.0000000"
}

If successful, you should receive a successful message in the CPI monitor:

Once you check the S/4HANA Cloud system you will see that a new project has been created:

Integration Flow with OData sender adapter

In this case we create an Integration Flow with both OData sender and receiver adapter.

In the OData sender adapter

  • include the EDMX file of the OData API, which you can download from API Business Hub
  • set the Entity Set to ProjectSet:

In the receiver OData adapter

  • enter the URL to your S/4HANA endpoint, then insert your name of your communication user and set resource path to “ProjectSet”:

In our scenario our integration flow consists of

  • 2 scripts
  • 1 request-reply
  • 1 message mapping

Firstly we add a script that let us read the request payload

  • On the left hand side navigate to Script
  • create a GroovyScript
  • replace the default script with the following:
/*
 * The integration developer needs to create the method processData 
 * This method takes Message object of package com.sap.gateway.ip.core.customdev.util
 * which includes helper methods useful for the content developer:
 * 
 * The methods available are:
    public java.lang.Object getBody()
    //This method helps User to retrieve message body as specific type ( InputStream , String , byte[] ) - e.g. message.getBody(java.io.InputStream)
    public java.lang.Object getBody(java.lang.String fullyQualifiedClassName)
    public void setBody(java.lang.Object exchangeBody)
    public java.util.Map<java.lang.String,java.lang.Object> getHeaders()
    public void setHeaders(java.util.Map<java.lang.String,java.lang.Object> exchangeHeaders)
    public void setHeader(java.lang.String name, java.lang.Object value)
    public java.util.Map<java.lang.String,java.lang.Object> getProperties()
    public void setProperties(java.util.Map<java.lang.String,java.lang.Object> exchangeProperties) 
    public void setProperty(java.lang.String name, java.lang.Object value)
 * 
 */
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
def Message processData(Message message) {
	def messageLog = messageLogFactory.getMessageLog(message);
	def bodyAsString = message.getBody(String.class);
	messageLog.addAttachmentAsString("Request Message", bodyAsString, "text/xml");	
	return message;
}

Then the Request Reply

  • on the left hand side go to external call, then create a request reply:

Then we will add a message mapping for the response message from S/4HANA Cloud. Here we do a 1-to-1 mapping of the entity set ProjectSet.

  • Create a message mapping
  • Go to “edit message”
  • under “Source Messages” go to “edit message”
  • choose the EDMX file of your OData API
  • select “ProjectSet” as Element
  • under “Target Messages” go to “edit message”
  • choose the EDMX file of your OData API
  • select “ProjectSet” as Element
  • press ok

  • click on the ProjectSet node on both source and target structure
  • click on the AB mapping button on the top right corner, it will automatically do your 1-to-1 mapping

Then we include a script to display our response payload:

  • On the left hand side navigate to Script
  • create a GroovyScript
  • replace the default script with the following:
/*
 * The integration developer needs to create the method processData 
 * This method takes Message object of package com.sap.gateway.ip.core.customdev.util
 * which includes helper methods useful for the content developer:
 * 
 * The methods available are:
    public java.lang.Object getBody()
    //This method helps User to retrieve message body as specific type ( InputStream , String , byte[] ) - e.g. message.getBody(java.io.InputStream)
    public java.lang.Object getBody(java.lang.String fullyQualifiedClassName)
    public void setBody(java.lang.Object exchangeBody)
    public java.util.Map<java.lang.String,java.lang.Object> getHeaders()
    public void setHeaders(java.util.Map<java.lang.String,java.lang.Object> exchangeHeaders)
    public void setHeader(java.lang.String name, java.lang.Object value)
    public java.util.Map<java.lang.String,java.lang.Object> getProperties()
    public void setProperties(java.util.Map<java.lang.String,java.lang.Object> exchangeProperties) 
    public void setProperty(java.lang.String name, java.lang.Object value)
 * 
 */
import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
def Message processData(Message message) {
	def messageLog = messageLogFactory.getMessageLog(message);
	def bodyAsString = message.getBody(String.class);
	messageLog.addAttachmentAsString("Response Message", bodyAsString, "text/xml");	
	return message;
}

Now we can test this integration flow. Below is the payload structure for posting. Please include your relevant data into this payload before posting.

{
  "ProjectCategory": "C",
  "OrgID": "your org ID",
  "CostCenter": "your 10 digit cost center",
  "ProfitCenter": "your profit center",
  "Customer": "your customer ID",
  "Currency": "EUR",
  "ProjectID": "API3",
  "ProjectName": "ProjectAPI3",
  "ProjectStage": "P001",
  "ProjManagerExtId": "your user name",
  "StartDate": "2018-04-29T00:00:00.0000000",
  "EndDate": "2018-05-29T00:00:00.0000000",
  "WorkPackageSet": {
    "results": [
      {
        "ProjectID": "API3",
        "WorkPackageID": "API3.1.1",
        "WorkPackageName": "WorkPackageAPI3",
        "WPStartDate": "2018-04-29T00:00:00.0000000",
        "WPEndDate": "2018-05-29T00:00:00.0000000",
        "WorkItemSet": {
          "results": [
            {
              "ProjectID": "API3",
             "WorkPackageID": "API3.1.1",
              "WorkPackageName": "WorkPackageAPI3",
              "Workitem": "your work item",
              "Workitemname": "your work item name"
            }
          ]
        },
        "DemandSet": {
          "results": [
            {
              "ProjectID": "API3",
              "WorkPackageID": "API3.1.1",
              "ResType": "your resource type",
              "ResourceId": "your resource type ID",
              "Effort": "10",
              "Uom": "H",
              "Workitem": "your work item",
              "WorkPackageName": "WorkPackageAPI3",
              "DelvryServOrg": "your delivery service org"
            }
          ]
        }
      }
    ]
  }
}

Firstly we need to get the x-csrf-token, check part 1 of this blog series.

Once we have the token in Postman

  • we select the POST method
  • set URL, it depends on the namespace and integration flow name, in our case the URL:
    • https://<CPI>/gw/odata/SAP/CREATEPROJECT;v=1/ProjectSet
    • replace <CPI> with your CPI tenant
  • include our communication user for basic authentication
  • include x-csrf-token
  • include payload in message body

If successful a response message is displayed:

In S/4HANA Cloud a new project is created including a workpackage, work item and role assigned:

In the CPI monitor you will see a successful message:

Under the tap “Attachments” you can view your request payload:

And your response payload:

Integration Flow HTTP sender adapter

If you do not have an EDMX file of an OData API from your source system, but just a JSON payload with which you have to build the integration. For this scenario you can use the HTTP sender adapter.

In our scenario our integration flow will look like this:

The scripts are only to view the payload, you can leave them out if not desired.

Following steps need to be done:

  • Configure HTTP sender adapter
  • Configure OData receiver adapter
  • Add Content Modifier
  • Add JSON to XML Converter
  • Configure Message Mapping

The other steps remain the same in the earlier scenario.

Configure HTTP sender adapter

  • Add a http endpoint
  • In our scenario we use /CreateProject

Configure OData receiver adapter

  • connect to your SAP S/4HANA Cloud system with your communication user
  • include the URL to your API endpoint
  • Choose Operation: Create (POST)
  • Choose Sub Levels: 5
  • Select All Fields
  • Under Fields, select all fields of sub levels
  • Save
  • A XSD is generated, this will be used in our message mapping as target message

Add Content Modifier

For mapping purposes the source message needs a header node. With content modifier we will add two header nodes.

  • Create Content Modifier
  • Under Message Body add following:
{
"ProjectSet": 
{
"Project":
${in.body}
}
}

Add JSON to XML Converter

  • Add the JSON to XML Converter
  • no additional step required

Configure Message Mapping

Before we start we need the xsd file of the source message.

However we only have a JSON payload without the two header nodes, which means we need:

  • add the two header nodes to the JSON payload
  • convert JSON to XML
  • convert XML to XSD
  • import XSD to message mapping

Simply add the content which we added into our content modifier to your payload.

Payload should look like this:

{
  "ProjectSet": {
    "Project": {
      "ProjectCategory": "C",
      "OrgID": "your org ID",
      "CostCenter": "your 10 digit cost center",
      "ProfitCenter": "your profit center",
      "Customer": "your customer ID",
      "Currency": "EUR",
      "ProjectID": "API3",
      "ProjectName": "ProjectAPI3",
      "ProjectStage": "P001",
      "ProjManagerExtId": "your user name",
      "StartDate": "2018-04-29T00:00:00.0000000",
      "EndDate": "2018-05-29T00:00:00.0000000",
      "WorkPackageSet": {
        "results": {
          "ProjectID": "API3",
          "WorkPackageID": "API3.1.1",
          "WorkPackageName": "WorkPackageAPI3",
          "WPStartDate": "2018-04-29T00:00:00.0000000",
          "WPEndDate": "2018-05-29T00:00:00.0000000",
          "WorkItemSet": {
            "results": {
              "ProjectID": "API3",
              "WorkPackageID": "API3.1.1",
              "WorkPackageName": "WorkPackageAPI3",
              "Workitem": "your work item",
              "Workitemname": "your work item name"
            }
          },
          "DemandSet": {
            "results": {
              "ProjectID": "API3",
              "WorkPackageID": "API3.1.1",
              "ResType": "your resource type",
              "ResourceId": "your resource type ID",
              "Effort": "10",
              "Uom": "H",
              "Workitem": "your work item",
              "WorkPackageName": "WorkPackageAPI3",
              "DelvryServOrg": "your delivery service org"
            }
          }
        }
      }
    }
  }
}

Use a JSON to XML converter, for example: www.utilities-online.info/xmltojson/

You should receive following XML:

<?xml version="1.0" encoding="UTF-8" ?>
	<ProjectSet>
		<Project>
			<ProjectCategory>C</ProjectCategory>
			<OrgID>your org ID</OrgID>
			<CostCenter>your 10 digit cost center</CostCenter>
			<ProfitCenter>your profit center</ProfitCenter>
			<Customer>your customer ID</Customer>
			<Currency>EUR</Currency>
			<ProjectID>API3</ProjectID>
			<ProjectName>ProjectAPI3</ProjectName>
			<ProjectStage>P001</ProjectStage>
			<ProjManagerExtId>your user name</ProjManagerExtId>
			<StartDate>2018-04-29T00:00:00.0000000</StartDate>
			<EndDate>2018-05-29T00:00:00.0000000</EndDate>
			<WorkPackageSet>
				<results>
					<ProjectID>API3</ProjectID>
					<WorkPackageID>API3.1.1</WorkPackageID>
					<WorkPackageName>WorkPackageAPI3</WorkPackageName>
					<WPStartDate>2018-04-29T00:00:00.0000000</WPStartDate>
					<WPEndDate>2018-05-29T00:00:00.0000000</WPEndDate>
					<WorkItemSet>
						<results>
							<ProjectID>API3</ProjectID>
							<WorkPackageID>API3.1.1</WorkPackageID>
							<WorkPackageName>WorkPackageAPI3</WorkPackageName>
							<Workitem>your work item</Workitem>
							<Workitemname>your work item name</Workitemname>
						</results>
					</WorkItemSet>
					<DemandSet>
						<results>
							<ProjectID>API3</ProjectID>
							<WorkPackageID>API3.1.1</WorkPackageID>
							<ResType>your resource type</ResType>
							<ResourceId>your resource type ID</ResourceId>
							<Effort>10</Effort>
							<Uom>H</Uom>
							<Workitem>your work item</Workitem>
							<WorkPackageName>WorkPackageAPI3</WorkPackageName>
							<DelvryServOrg>your delivery service org</DelvryServOrg>
						</results>
					</DemandSet>
				</results>
			</WorkPackageSet>
		</Project>
	</ProjectSet>
	

Use a XML to XSD converter, for example: https://www.freeformatter.com/xsd-generator.html

It will generate you following XSD:

<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="ProjectSet">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="Project">
          <xs:complexType>
            <xs:sequence>
              <xs:element type="xs:string" name="ProjectCategory"/>
              <xs:element type="xs:string" name="OrgID"/>
              <xs:element type="xs:string" name="CostCenter"/>
              <xs:element type="xs:string" name="ProfitCenter"/>
              <xs:element type="xs:string" name="Customer"/>
              <xs:element type="xs:string" name="Currency"/>
              <xs:element type="xs:string" name="ProjectID"/>
              <xs:element type="xs:string" name="ProjectName"/>
              <xs:element type="xs:string" name="ProjectStage"/>
              <xs:element type="xs:string" name="ProjManagerExtId"/>
              <xs:element type="xs:dateTime" name="StartDate"/>
              <xs:element type="xs:dateTime" name="EndDate"/>
              <xs:element name="WorkPackageSet">
                <xs:complexType>
                  <xs:sequence>
                    <xs:element name="results">
                      <xs:complexType>
                        <xs:sequence>
                          <xs:element type="xs:string" name="ProjectID"/>
                          <xs:element type="xs:string" name="WorkPackageID"/>
                          <xs:element type="xs:string" name="WorkPackageName"/>
                          <xs:element type="xs:dateTime" name="WPStartDate"/>
                          <xs:element type="xs:dateTime" name="WPEndDate"/>
                          <xs:element name="WorkItemSet">
                            <xs:complexType>
                              <xs:sequence>
                                <xs:element name="results">
                                  <xs:complexType>
                                    <xs:sequence>
                                      <xs:element type="xs:string" name="ProjectID"/>
                                      <xs:element type="xs:string" name="WorkPackageID"/>
                                      <xs:element type="xs:string" name="WorkPackageName"/>
                                      <xs:element type="xs:string" name="Workitem"/>
                                      <xs:element type="xs:string" name="Workitemname"/>
                                    </xs:sequence>
                                  </xs:complexType>
                                </xs:element>
                              </xs:sequence>
                            </xs:complexType>
                          </xs:element>
                          <xs:element name="DemandSet">
                            <xs:complexType>
                              <xs:sequence>
                                <xs:element name="results">
                                  <xs:complexType>
                                    <xs:sequence>
                                      <xs:element type="xs:string" name="ProjectID"/>
                                      <xs:element type="xs:string" name="WorkPackageID"/>
                                      <xs:element type="xs:string" name="ResType"/>
                                      <xs:element type="xs:string" name="ResourceId"/>
                                      <xs:element type="xs:byte" name="Effort"/>
                                      <xs:element type="xs:string" name="Uom"/>
                                      <xs:element type="xs:string" name="Workitem"/>
                                      <xs:element type="xs:string" name="WorkPackageName"/>
                                      <xs:element type="xs:string" name="DelvryServOrg"/>
                                    </xs:sequence>
                                  </xs:complexType>
                                </xs:element>
                              </xs:sequence>
                            </xs:complexType>
                          </xs:element>
                        </xs:sequence>
                      </xs:complexType>
                    </xs:element>
                  </xs:sequence>
                </xs:complexType>
              </xs:element>
            </xs:sequence>
          </xs:complexType>
        </xs:element>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Save this as a file & import it to your message mapping as a source message.

Use the XSD which was generated after you finished configuring your OData receiver adapter as your target message.

Because both source and target message have the same field names, you can select the header node and press map all on the right side. This will automatically map all the fields with the same names automatically. Repeat this step for your underlying nodes (Workpackage, Workitem & Demand)

Testing

Now we can test, send your payload into your http endpoint.

HTTP endpoint: https://<CPI-tenant>/http/CreateProject

Payload:

{
  "ProjectCategory": "C",
  "OrgID": "your org ID",
  "CostCenter": "your 10 digit cost center",
  "ProfitCenter": "your profit center",
  "Customer": "your customer ID",
  "Currency": "EUR",
  "ProjectID": "API3",
  "ProjectName": "ProjectAPI3",
  "ProjectStage": "P001",
  "ProjManagerExtId": "your user name",
  "StartDate": "2018-04-29T00:00:00.0000000",
  "EndDate": "2018-05-29T00:00:00.0000000",
  "WorkPackageSet": {
    "results": [
      {
        "ProjectID": "API3",
        "WorkPackageID": "API3.1.1",
        "WorkPackageName": "WorkPackageAPI3",
        "WPStartDate": "2018-04-29T00:00:00.0000000",
        "WPEndDate": "2018-05-29T00:00:00.0000000",
        "WorkItemSet": {
          "results": [
            {
              "ProjectID": "API3",
             "WorkPackageID": "API3.1.1",
              "WorkPackageName": "WorkPackageAPI3",
              "Workitem": "your work item",
              "Workitemname": "your work item name"
            }
          ]
        },
        "DemandSet": {
          "results": [
            {
              "ProjectID": "API3",
              "WorkPackageID": "API3.1.1",
              "ResType": "your resource type",
              "ResourceId": "your resource type ID",
              "Effort": "10",
              "Uom": "H",
              "Workitem": "your work item",
              "WorkPackageName": "WorkPackageAPI3",
              "DelvryServOrg": "your delivery service org"
            }
          ]
        }
      }
    ]
  }
}

You will receive a successful message in SAP Cloud Platform Integration. Under Attachments you can see each payload during the process steps of the integration flow:

 

Conclusion

And that is it! Leave a comment below if you have any questions!

To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

Leave a Reply