Skip to Content
Technical Articles

SharePoint Integration using SAP CPI (Without SAP Open Connector)

Introduction

Sometimes documents need to be copied to the SharePoint team site so that it can be made accessible to broader recipients within an organization.

Main

This integration requires configuration changes in SharePoint as well as SAP CPI.

SharePoint App Configuration

Register Add-In

On the initial stage, we have to register the Add-In in SharePoint, where we want to access the information. Follow the steps below to register the Add-In in SharePoint site.

  • Navigate and login to SharePoint online site.
  • Then navigate to the Register Add-In page by entering the url as
  • https://<siteURL>/_layouts/15/appregnew.aspx
  • On the App Information section, click the Generate button next to the Client Id and Client Secret textboxes to generate the respective values.
  • Enter Add-In Title in Title textbox
  • Enter AppDomian as a localhost
  • Enter RedirectUri as a https://localhost
  • Click Create button, which registers the add-in and returns the success message with created information.

Register%20SharePoint%20Add-ins

Register SharePoint Add-ins

 

Grant Permissions to Add-In

Once the Add-In is registered, we have to set the permissions for that add-in to access the SharePoint data. We will set the Read permission level to the web scope, so that we will be able to read the web information.

  • Navigate to the SharePoint site
  • Then enter the URL https://<siteURL>/_layouts/15/appinv.aspx in the browser. This will redirect to the Grant permission page.
  • Enter the Client ID(which we have generated earlier), in the AppId textbox and click the Lookup button. That will populate the value to other textboxes in Title, App Domain and Redirect Url.

Grant%20Permission%20to%20Add-In

Grant Permissions to Add-In

  • Now enter the below permission request in XML format.
<AppPermissionRequests AllowAppOnlyPolicy="true">
    <AppPermissionRequest Scope="http://sharepoint/content/sitecollection/web" Right="Write" />
</AppPermissionRequests>
  • Then click the Create button. This will redirect to your page, where we have to trust the add-in to read items from the website.

Note: If we want to access site collection or tenant level, we have add the xml accordingly.

 

Retrieve the Tenant ID

Once we register the Client Id and Secret with the permissions, we are ready to access the SharePoint information from external systems or tools.

First, we have to know the Tenant ID. Follow the below steps to obtain that information from the postman. Postman helps to get the tenant Id by requesting the below url with Authorization header.

  • Launch Postman.
  • Select Get Method
  • Enter the below URL in the “Request URL” textbox
  • https://<siteURL>/_vti_bin/client.svc/
  • Configure the below information in the header section to send along with the url requestMethod = Get

Get%20Call%20for%20Tenant%20ID

Get Call for Tenant ID

  • After applying the configuration, click the Send button. The response returns a lot of headers but ends with unauthorized access.

Response%20Headers

Response Headers

In response header, we will get WWW-Authenticate as one of the headers and that contains the necessary information required for the next step. The realm value contains the tenant id for the SharePoint Online site and clientid value contains the resource information (we’ll use it later in our SAP CPI iFlow).

 

SAP CPI iFlow Configuration

iFlow

iFlow

This iFlow is creating one webservice which can be triggered by any HTTP Client and upload files to SharePoint using REST api V1 of SharePoint.

Here we have used parallel multicast to get the OAuth access token from Microsoft Access Control Services.

 

OAuth Call Branch

OAuth Request

First of all we are using a content modifier step to create the request. 

  • Request Header: We have added one header with key and value pair as “Content-Type” and “application/x-www-form-urlencoded”.
  • Request Body: We have to add the below string to the message body section with appropriate replacement.
grant_type=client_credentials&client_id=ClientID@TenantID&client_secret=ClientSecret&resource=resource/SiteDomain@TenantID
  • ClientID: Created in the Add In section
  • ClientSecret: Created in the Add In section
  • TenantID: Fetch from “WWW-Authenticate” header in the last section
  • resource: Fetch from “WWW-Authenticate” header in the last section
  • SiteDomain: Domain name

 

Call to MACS and HTTP Receiver

Then we have a request reply step which will call the Microsoft Access Control Services and fetch the response. HTTP receiver channel should be updated with the below address and “POST” Method.

Address: https://accounts.accesscontrol.windows.net/<TenantID>/tokens/OAuth/2

 

JSON to XML Converter and Fetch Access Token

As we are getting one JSON response we need to convert it to XML format and then we have used another content modifier to fetch the access token and assign it to a property called “access_token”.

 

Access_Token

Access_Token

 

Remove Body

We have used one simple Groovy script to remove the body of the OAuth call branch.

import com.sap.gateway.ip.core.customdev.util.Message
import groovy.json.JsonOutput

Message processData(Message message) {
    def body = "";
    message.setBody(body) 
    return message;
}

 

Final Call to SharePoint

 

Join and Gather

We have used Join and Gather to merge both the branches. So that we can get an OAuth access token as well as the original message body from the request.

 

Set Header

We have used one content modifier to create 2 headers with a key value pair as below. We have also deleted the unused headers.

Key :: Value

Content-Type :: application/xml (You can change the value as per your requirement)

Authorization :: Bearer ${property.access_token}

 

Headers

Headers

 

We have also created one exchange property to transmit the filename.

Property

Property

 

Convert Body to outputStream

As per our understanding the request body should be passed to the HTTP adapter but as per our testing the content modifier or ${body} is not giving required results when the request body is passed directly (SharePoint REST API is giving 500 error with I/O error). So we have used one simple groovy script to convert the payload to ByteArrayOutputStream and again set it to the message body. (Not sure why it is behaving like this as we are still investigating this issue.)

import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
def Message processData(Message message) {
    //Body 
       def body = message.getBody(String.class);
       ByteArrayOutputStream outputStream = new ByteArrayOutputStream()
       outputStream.write(body.getBytes())
       message.setBody(outputStream.toString())
       return message;
}

 

Call to SharePoint

We are making a POST call to SharePoint REST api using request reply and HTTP receiver channel.

HTTP%20Receiver

HTTP Receiver

 

Test 

We have called the HTTP endpoint of the iFlow from a Postman client.

Call%20from%20Postman

Call from Postman

 

Result

We can see the files being sent to the SharePoint.

Result

Result

 

Conclusion

With this you have successfully created and tested an integration flow that is uploading documents  to SharePoint, created a team site in SharePoint, leveraged the SAP CPI to connect seamlessly to SharePoint site. Basically we need to take care of the HTTP request payload while calling the rest api of SharePoint. Please go through the Microsoft documentation for API overview.

 

 

8 Comments
You must be Logged on to comment or reply to a post.
  • Hi Asutosh,

    Did u check uploading binary (ppt,pdf,word..) files.

    I am unable to post binary data to Sharepoint eventhough my headers and request payload is correct

    Input Attachment

    Input%20attachment

    Input attachment

    Groovy script code to read attachments and set header data(tried with ByteArrayOutputStream also)

    Map<String, DataHandler> attachments = message.getAttachments()

    List<String> aList = new ArrayList<String>();

    for (String x : attachments.keySet())

    {

    aList.add(x);

    }

    def attch=attachments.get(aList.get(0))

    message.setHeader("fName",attch.getName())

    def xcn=attch.getInputStream().getBytes()

    message.setBody(xcn)

    message.setHeader("Content-Length",xcn.size())

    def ctype=attch.getContentType().indexOf(';')

    message.setHeader("Content-Type",attch.getContentType().substring(0,ctype))

    return message

     

    Message Content from trace step

    Message Header

    Message%20Headers

    Message Headers

    Body(Binary data)

    Binary data which is downloaded and saved with the extension is of actual file

    Msg%20before%20Step

    Msg before Step

    Error in SAP CPI

    Error%20in%20CPI

    Error in CPI

    Exception from sharepoint 

    <?xml version="1.0" encoding="utf-8"?>
    <m:error xmlns:m="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata">
    <m:code>-1, System.IO.IOException</m:code>
    <m:message xml:lang="en-US">I/O error occurred.</m:message>
    </m:error>

    Could you please help me with this.

    • Dear Yeswanth,

       

      I have also tried to modify the script to assign binary payload to the request body but it's giving same I/O error. It seems like conversion to string only working fine as of now.

       

      I have observed that HTTP adapter is converting the type of request body to "inputStream" but it seems like there is some issue with the SAP CPI HTTP receiver adapter which is not able to pass byte streams correctly. I have tried using Postman it is working fine.

       

      I am trying to create a custom HTTP adapter from Apache components and try to use that adapter to connect to SharePoint.

       

      BR,

      Asutosh Maharana

  • Hi Asutosh,

    I also feel it is issue with HTTP Adapter based on below screenshots. I think issue is also with encoding, as per HTTP adapter default encoding is UTF-8.

    Input Attachment

    Input%20Attachment

    Message Before Step in Adapter

    Msg%20Before%20Step

     

    Downloaded and opened above binary data in Notepad++(length is as expected and encoding in ANSI) and saved it with .pptx as is working as expected.

    Before%20Step_Notepad

    Message before Receiver Adapter(here is the step where the file encoding is getting changed)
    Message%20before%20Receiver%20Adapter

    Downloaded and opened above binary data in Notepad++(length is not as expected and encoding in UTF-8 as per HTTP adapter standards)

    Encoding change as per below SAP Documentation

    https://help.sap.com/viewer/368c481cd6954bdfa5d0435479fd4eaf/Cloud/en-US/2da452effb764b3bb28f8e0a2f5bd480.html

    Encoding property as per above SAP blog (changed CamelCharsetName to overwrite standard UTF-8 encoding)

    File in Notepad++ after above encoding change (almost original input attachment data expect few characters unable to find the exact encoding for ANSI as ANSI is not an encoding type)

     

     

    • Dear Yeswanth,

       

      I have a workaround for this issue not sure if it will help you or not.

      We can first convert the binary payload to base64 encoded string then transfer it to SharePoint and create one text file. Then we can use Power Automate flow to convert the base64 encoded text file to binary file again.

      Power Automate flow: It's a manually triggered flow but you can create an automated flow as well. Let me know if you are facing any issue with power automate.

      Base64%20to%20binary

      Base64 to binary

      Will this suffice your requirement ?

      I am also looking into the charset conversion issue of the HTTP receiver adapter.

       

      BR,

      Asutosh Maharana

      /
      Base64%20to%20binary
    • Hi Yeswanth, Asutosh,

      Were you able to find a solution for the binary files? I'm also suffering from this problem.

       

      Regards,

      Erkam

  • Hi all,

    For the upload of binary files to a SharePoint site, I've just come up with such a solution; Instead of CPI's own HTTP receiver adapter, I'm uploading the files via HTTP call from the groovy.  That works fine. Here is my code:

    import com.sap.gateway.ip.core.customdev.util.Message;
    import java.util.HashMap;
    
    def Message processData(Message message) {
        String contentLength = message.getBodySize();
        def Content = message.getBody(byte[])
        
        
        def messageLog = messageLogFactory.getMessageLog(message);
        def propertiesMap = message.getProperties();
        def headersMap = message.getHeaders();
        
        def siteDomain = propertiesMap.get("siteDomain");
        def site = propertiesMap.get("site");
        def pathSpaceCorrected = propertiesMap.get("pathSpaceCorrected");
        def fileName = propertiesMap.get("fileName");
        
        String SharePointURL = 'https://' + siteDomain + '/sites/' + site + '/_api/web/GetFolderByServerRelativeUrl(\'' + '/sites/' + site  + pathSpaceCorrected + '\')/Files/add(url=\'' + fileName + '\',overwrite=true)';
        
        // POST
        def post = new URL(SharePointURL).openConnection();
        
        
        post.setRequestMethod("POST")
        post.setDoOutput(true)
        post.setRequestProperty("Content-Type", headersMap.get("Content-Type") as String);
        post.setRequestProperty("Content-Length", contentLength ); 
        post.setRequestProperty("Authorization", headersMap.get("Authorization") as String);
        post.getOutputStream().write(Content);
        
        def postRC = post.getResponseCode();
        if(postRC.equals(200)) {
            message.setBody("<?xml version=\"1.0\" encoding=\"utf-8\"?><root><status>Uploaded</status></root>");       
            messageLog.addAttachmentAsString("Post result:", post.getInputStream().getText() as String, "text/plain")
        }else{
            message.setBody("<?xml version=\"1.0\" encoding=\"utf-8\"?><root><status>Failed</status></root>");
            def exceptionMsg = "HTTP" + postRC.toString() + ", " + post.getInputStream().getText()
            throw new Exception(exceptionMsg as String)
        }
        
        return message
    }
    

    Reference articles:

    Nick Yang's solution, and Joao Miguel Gomes's contribution:

    https://answers.sap.com/questions/12963572/send-zip-file-and-form-data-through-cpi-and-http-a.html

    Rajath Kodandaramu's article:

    https://blogs.sap.com/2019/04/17/cpi-http-calls-from-groovy/

     

    Regards,

    Erkam Ozturk

    • Dear Erkam,

       

      I think it's not recommended to do HTTP call directly from scripts without using any adapter from SAP CPI or any integration platform as it will cause issues for tracing and logging. We discourage developers to call the webservices from Java mapping as well in SAP PO for this reason.

      And there is no point in having all the processing steps or adapters in SAP CPI as well because we can do all the operations using Groovy scripts basically.

      The real culprit is SAP CPI's HTTP receiver adapter where we are getting the error due to charset conversion. Because we are already aware of this solution to call the APIs directly from script.

       

      You can go through the Power Automate flow alternative as well.

      BR,

      Asutosh Maharana