Skip to Content
Technical Articles

How to connect On-Premise system to SAP Cloud Foundry Environment using SAP Cloud Connector

The best way to learn is by doing. The only way to build a strong work ethic is getting your hands dirty.

                                                                                                                – Alex Spanos

In this blog I’ll demonstrate how to connect services that are deployed on an on-premise system to a Flask application that is deployed on Cloud Foundry. For this exercise, we will make use of the SAP Cloud Connector.

While working on a project that required me to connect CF and On-Premise systems, I came across two very wonderful blogs written by –  Matthieu Pelatan and  Marius Obert . While Matthieu’s blog shows end to end connection between the two systems, he relies on JAVA to demonstrate his example. Since I am not very familiar with JAVA, I decided to follow – Marius article. Marius shows how to call destinations endpoints from Flask application, which is great but it did not solve my cross-system connectivity issue. So, I decided to write this blog to show how we can call services from different systems (on-prem) via Flask (Python) application in CF environment. I thank Marius and Matthieu for providing such wonderful resources.

1. On-Premise

For the purpose of this blog, we will have a service (XSJS) deployed in an on-premise system as follows –

process_query.xsjs

// Function that return current balance ( hard coded )
function returnBalance(){
    try{
        var output = { 
                        status:200,
                        replies:[{
                          'type': 'text',
                          'content': 'Hi, current balance is: $ 23,900'
                        }]
                    };
    
        var body = JSON.stringify(output);
        $.response.contentType = 'application/json';
        $.response.setBody(body);
        $.response.status = $.net.http.OK;
    }
     
    catch (e) {
        var output2 = {
                        status:500,
                        replies:[{
                          'type': 'text',
                          'content': e.message
                        }]

                    };
        
        $.response.contentType = 'application/json';
        var body2 = JSON.stringify(output2);
        $.response.setBody(body2);
        $.response.status = $.net.http.OK;
        
    }
    
}

returnBalance();

 

Service status – 

 

The service is available now as seen above. Now we shall expose this service via SAP Cloud connector.

 

2. SAP Cloud Connector

 

Our SAP CF account looks as follows –

 

Now, let’s make the Connection between our CF system and On-premise system using Cloud Connector –

 

2.1. Create connection to CF

2.2. Add the On-Premise system

 

2.3. Add the service endpoint.

 

3. Cloud Foundry

Now that we have set up connection between the Cloud Foundry environment and the On-premise system, let’s deploy our Flask application.

3.1 Create Destination

Let’s add a new Destination entry which will point to the <VirtualHost>:<VirtualPort> corresponding to the <ActualHost>:<ActualPort> that we have mapped in SAP Cloud connector.

 

3.2 Add Destination, XSUAA and Connectivity service.

Lets’s add the Destination , XSUAA and Connectivity service to out CF Sub-account.The purpose of these services are as follows –

  • Destination – Provides a uniform way to add service endpoints in an application.
  • XSUAA – Provides authentication service.
  • Connectivity – This will invoke the Cloud Connector service which in turn will call our services deployed in On-premise system.
3.2.1 Service

 

For the services – Just provide the plan and the name of the service. DO NOT provide anything in the Specify Parameters/ Assign Application section.

After adding all the required services, Navigate to service instance tab –

3.3 Flask Application

 

Let’s add our Flask Application now

app.py –

from flask import Flask, request, jsonify
import requests
import json
import base64
from cfenv import AppEnv


app = Flask(__name__)
env = AppEnv()

# Creating instances of the Destiantion, XSUAA and Connectivity service
UAA_SERVICE = env.get_service(name='uaa_service')
DESTINATION_SERVICE = env.get_service(name='destination_service')
CONNECTIVITY_SERVICE = env.get_service(name='connectivity_service')
# Destination name
DESTINATION = 'demoService'
#connectivity proxy
CONNECTIVITY_PROXY = CONNECTIVITY_SERVICE.credentials["onpremise_proxy_host"]+":"
                    + CONNECTIVITY_SERVICE.credentials["onpremise_proxy_port"]

# Connectivity and Destination Authentication tokens
CONNECTIVITY_SECRET = CONNECTIVITY_SERVICE.credentials["clientid"] + \
    ':' + CONNECTIVITY_SERVICE.credentials["clientsecret"]

DESTINATION_SECRET = DESTINATION_SERVICE.credentials["clientid"] + \
    ':' + DESTINATION_SERVICE.credentials["clientsecret"]

CONNECTIVITY_CREDENTIALS = base64.b64encode(
    CONNECTIVITY_SECRET.encode()).decode('ascii')

DESTINATION_CREDENTIALS = base64.b64encode(
    DESTINATION_SECRET.encode()).decode('ascii')


def getAccessToken(credentials, serviceName):
    #Getting access token for Connectivity service.
    headers = {'Authorization': 'Basic ' + credentials,
               'content-type': 'application/x-www-form-urlencoded'}

    form = [('client_id', serviceName.credentials["clientid"]),
            ('grant_type', 'client_credentials')]

    r = requests.post(
        UAA_SERVICE.credentials["url"] + '/oauth/token', data=form, headers=headers)

    token = r.json()['access_token']
    return token


# Helper that Returns the URL of the destination.
def _getDestinationURL(token):
    headers = {'Authorization': 'Bearer ' + token}

    r = requests.get(DESTINATION_SERVICE.credentials["uri"] +
                     '/destination-configuration/v1/destinations/' + DESTINATION, headers=headers)

    destination = r.json()
    return destination["destinationConfiguration"]["URL"]


def getURL():
    # Fetch URL of the Destination
    destination_token = getAccessToken(
        DESTINATION_CREDENTIALS, DESTINATION_SERVICE)
    url = _getDestinationURL(destination_token)
    return url


def getProxy():
    data = {}
    connectivity_token = getAccessToken(
        CONNECTIVITY_CREDENTIALS, CONNECTIVITY_SERVICE)

    # Setting proxies and header for the Destination that needs to be called.
    headers = {
        'Proxy-Authorization': 'Bearer ' + connectivity_token}
    # connection['headers'] = str(headers)
    # proxy
    proxies = {
        "http": CONNECTIVITY_PROXY
    }
    # connection['proxies'] = str(proxies)
    data['headers'] = headers
    data['proxies'] = proxies
    return data

def makeRequest(request,endpoint):
    # Get destination URL
    url = getURL()
    #Get proxy parameters
    connection = getProxy()

    # Call the on-prem process_query service.
    r = requests.post(url+endpoint,
                      proxies=connection['proxies'], headers=connection['headers'],
                      verify=False, timeout=10)
    return json.loads(r.text)


# Routes
@app.route('/process_query', methods=['POST', 'GET'])
def process_query():
    responseText = makeRequest(request,'/process_query.xsjs')
    return jsonify(responseText)



if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8080, debug= True)
	

 

Let’s break down the code and see what’s going on –

  • We have created an endpoint /process_query which shall call the process_query.xsjs service in On-Premise system.
  • getAccessToken() – This function provides a JWT token from the XSUAA service. For the connectivity and destination service, we generate two different JWT Token.
  • getURL() – This function invokes the destination service and fetches the Endpoint defined under the demoService destination.
  • getProxy() –  This function provides a proxy for the connectivity service to call the SAP Cloud Connector and in turn invoke the on-premise service.

 

We shall bind all the 3 services – Destination, Connectivity and XSUAA service in the manifest.yml  file –

---
applications:
- memory: 128MB
  name: demo_app
  disk_quota: 256MB
  random-route: true
  services:
  - destination_service
  - uaa_service
  - connectivity_service

 

We shall freeze the Flask application’s dependencies in a requirements.txt  file –

Flask
requests
cfenv

 

We shall also add a Procfle  – 

web: python app.py

Finally, we will add the command needed to start the application in a  runtime.txt  as follows –

python-3.6.6

Our application directory will be as follows –

Now that we are ready with our application, let’s deploy it-

cf push demo_app -u none

Deployment logs –

 

 

4. Test

 

Let’s test our application and see what we get –

Hit the URL- demoapp-silly-buffalo.cfapps.sap.hana.ondemand.com/process_query

SUCCESS !!!

 

Now, our application deployed in Cloud Foundry is able to communicate with XSJS services deployed in an On-Premise system.

I hope this helps anyone who wants to connect systems using SAP Cloud Connector.

 

Regards,

Boudhayan Dev

 

 

EDIT –

The sample On-Premise service shown above does not require any authentication. However, it is very common to have some sort of authentication for your backend services. If that is the case, please use the following modified code –

Calling On-premise service with authentication

 

Do not forget to update the credentials in destination tab before this.

5 Comments
You must be Logged on to comment or reply to a post.
  • Hi Boudhayan Dev,

    thanks for the blog.

    Two question from a neo developer in SCP Cloud Foundry : is this connection possible with trial account ? When I try to create a new destination instance with the service there is this error :

    Are Destination, XSUAA and Connectivity service necessary for create a simple python application connected with on-premise CDS?

    Thank you again,

    Sebastiano

    • Hi Sebastiano Marchesini ,

       

      1. Yes, it works in trial as well. The error in the above image is due to insufficient quota for connectivity service. Increase in by going into the subaccount level and then the quota plans section.
      2. Yes, XSUAA, Destination and Connectivity service is required if you want to communicate with your on-premise services. These services are available in CF.

       

      • Thank you so much Boudhayan Dev, !!! 🙂

        Now I’ve the destination, uaa and connection service and the application has been deployed.

         

        But I can’t use the backend system for an error in the request for token. I copied your code in my app:

        def getAccessToken(credentials, serviceName):
            #Getting access token for Connectivity service.
            headers = {'Authorization': 'Basic ' + credentials,
                       'content-type': 'application/x-www-form-urlencoded'}
        
            form = [('client_id', serviceName.credentials["clientid"]),
                    ('grant_type', 'client_credentials')]
        
            r = requests.post(UAA_SERVICE.credentials["url"] + '/oauth/token', data=form, headers=headers)
        
            token = r.json()['access_token']
            return token

         

        The system tell me it’s a {“error”:”unauthorized”,”error_description”:”Bad credentials”} . But I set all the instance and key of uaa_service, connectivity_service, and destination_service. Where am I wrong ?

        Thanks again 🙂

        PS: I’m following your blog and the Pranav Blog, but in my case the system expose an oData service from simply CDS.

        —– EDIT —-

        Ok, I never followed this blog, maybe is for that the error :

        https://blogs.sap.com/2019/01/25/sap-cloud-platform-backend-service-tutorial-0.1-preparation/#enablebeta

         

        but when I’m initializing the service SAP Cloud Platform Backend Service I’ve got this error :

         

        Could not subscribe to SAP Cloud Platform Backend service [BETA]. Please try again. If the problem persists, open a ticket using the BCP component OPU-GW-OD-EBS and include the job ID 32ed3584-5c0b-4c9f-ba6f-7af9615d11d1 and the correlation ID 32ed3584-5c0b-4c9f-ba6f-7af9615d11d1

        Like of this question:

        https://answers.sap.com/questions/12716212/sap-cloud-platform-backend-service-beta-version-on.html

        any solution? Is it useful for my intention ?