Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
kallolathome
Active Participant

Introduction


In this new world of BTP, I was wondering whether I can run python applications in the cloud foundry & perform several things like the followings:

  1. accessing the data of different systems(S4H On-Premise, ECC, BTP CAP OData services, etc.) via destinations.

  2. running python flask applications having multiple functionalities/pages.


I have checked a few places & found bits and pieces of information. I have tried & failed a few times to achieve the same. At last, I succeeded. So, I am consolidating everything in this blog post. Feel free to add information if I miss something. 

What is a python flask application? Ref.


There are many modules or frameworks which allow building your webpage using python like bottle, Django, Flask, etc. But the really popular ones are Flask and Django. Django is easy to use as compared to Flask but Flask provides you with the versatility to program with.

To understand what Flask is you have to understand a few general terms.

  1. WSGI: Web Server Gateway Interface (WSGI) has been adopted as a standard for Python web application development. WSGI is a specification for a universal interface between the web server and the web applications.

  2. Werkzeug: It is a WSGI toolkit, which implements requests, response objects, and other utility functions. This enables building a web framework on top of it. The Flask framework uses Werkzeug as one of its bases.

  3. jinja2: jinja2 is a popular templating engine for Python. A web templating system combines a template with a certain data source to render dynamic web pages.


Flask is a web application framework written in Python. Flask is based on the Werkzeug WSGI toolkit and Jinja2 template engine. Both are Pocco projects.

If you want to create your first local python flask application then follow this link.

 

Solution


There is a need for 6 files each having special functions. They are the following.



    1. python_app_filename.py: It is the main python application file containing all the necessary codes.
      # importing modules
      # ---------------------------------------------------------------------
      # import all the necessary modules here that will be used in the program
      from flask import Flask, request, jsonify
      import os


      # importing local module: BTPservices: containing generic codes
      # ---------------------------------------------------------------------
      import BTPservices


      # application initialization
      # ---------------------------------------------------------------------
      app = Flask(__name__)

      # fetching port details
      # ---------------------------------------------------------------------
      cf_port = os.getenv("PORT")


      # fetching of records having OAuth2ClientCredentials authorization
      # ---------------------------------------------------------------------
      def get_records_01(lv_dest, lv_string):
      lv_destination = BTPservices.getDestination(sDestinationService=service_name_001, sDestinationName=lv_dest)
      lv_url = lv_destination['destinationConfiguration']['URL'] + lv_string
      try:
      lv_request = requests.get(lv_url, headers={'Accept': 'application/json'})
      except requests.exceptions.HTTPError as err:
      return jsonify(replies=err)
      lv_request = lv_request.json()
      # clearing variables
      del lv_destination, lv_url
      return lv_request.json()


      # fetching of records having OAuth2Password/No authorizartion
      # ---------------------------------------------------------------------
      def get_records_02(lv_dest, lv_string, lv_flag):
      lv_destination = BTPservices.getDestination(sDestinationService=service_name_001, sDestinationName=lv_dest)
      # having authorization
      if lv_flag == 'True':
      lv_token = str(lv_destination['authTokens'][0]['http_header']['value'])
      lv_headers = {'Authorization': lv_token}
      lv_response = requests.get(lv_destination['destinationConfiguration']['URL'] + lv_string, headers=lv_headers)
      else:
      # having no authorization
      lv_response = requests.get(lv_destination['destinationConfiguration']['URL'] + lv_string )
      # clearing the value
      del lv_destination, lv_flag, lv_token
      # returns the data
      return lv_response.json()


      # data fetch using the destination
      # ---------------------------------------------------------------------
      @app.route('/fetch_data_01', methods=['GET'])
      def fetch_data_01():
      lv_request = get_records_01('destination_name', 'entity_name')
      return lv_request


      @app.route('/fetch_data_02', methods=['GET'])
      def fetch_data_02():
      lv_request = get_records_02('destination_name', 'entity_name', 'True')
      return lv_request


      # homepage
      # ---------------------------------------------------------------------
      @app.route('/', methods=['GET'])
      def home():
      lv_return = {
      "result": [
      {
      'message': 'Welcome to the homepage!',
      'author': 'Kallol Chakraborty',
      'date': '11/10/2022 ',
      'description': 'The is a demo application'
      }
      ]
      }
      return jsonify(status='200', replies=lv_return)


      # main
      # ---------------------------------------------------------------------
      if __name__ == '__main__':
      # If the app is running locally
      if cf_port is None:
      # Use port 5000
      app.run(host='0.0.0.0', port=5000, debug=True)
      else:
      # Else use cloud foundry default port
      app.run(host='0.0.0.0', port=int(cf_port), debug=False)




    2. manifest.yml: The manifest.yml file represents the configuration describing your application and how it will be deployed to Cloud Foundry.IMPORTANT: Make sure you don’t have another application with the name app_name in your space. If you do, use a different name and adjust the whole tutorial according to it. Also bear in mind that your application’s technical name (in the route) must be unique in the whole Cloud Foundry landscape. The advice is that you use, for example, your subdomain name or part of your subaccount ID to construct the technical name. In this tutorial, we use: application-1234-abcd-007. The path to access the application will be https://app-1234-abcd-007.cfapps.eu20.hana.ondemand.com
      ---
      applications:
      - name: app_name
      routes:
      - route: app-1234-abcd-007.cfapps.us10.hana.ondemand.com
      path: ./
      memory: 128M
      disk_quota: 512M
      buildpack: python_buildpack
      command: python python_app_filename.py
      services:
      - service_name_001

      service_name_001 is bound to the application now but it is not created yet. Please create the service: service_name_001 using the CF CLI command provided below.
      cf create-service destination lite service_name_001 


    3. ProcFile: The Procfile specifies the command line to execute the application at runtime.
      web: python my_app.py


    4. runtime.txt: Specify the Python runtime version that your application will run on.
      python-3.10.1​


    5. requirements.txt: This application will be a web server utilizing the Flask web framework. To specify flask, requests, cfenv, etc. as an application dependencies, please check the below sample code.
      flask==2.0.1
      requests
      cfenv​

      You can provide the versions also if you like.

    6. BTP Service.py:This is a very important file containing some generic codes & is responsible for fetching the details of the BTP destinations.
      # modules
      # -----------------------------------------------------------------------------------------------------
      from cfenv import AppEnv
      import requests

      def getDestination(sDestinationService, sDestinationName):

      # Read the environment variables
      # -----------------------------------------------------------------------------------------------------
      env = AppEnv()
      dest_service = env.get_service(name=sDestinationService)
      sUaaCredentials = dest_service.credentials["clientid"] + ':' + dest_service.credentials["clientsecret"]

      # Request a JWT token to access the destination service
      # -----------------------------------------------------------------------------------------------------
      headers = {'Authorization': 'Basic '+sUaaCredentials, 'content-type': 'application/x-www-form-urlencoded'}
      form = [('client_id', dest_service.credentials["clientid"] ),('client_secret', dest_service.credentials["clientsecret"] ), ('grant_type', 'client_credentials')]

      url = dest_service.credentials['url'] +"/oauth/token"
      headers = {
      'Content-Type': 'application/x-www-form-urlencoded'
      }

      response = requests.request("POST", url, headers=headers, data=form)

      # Search your destination in the destination service
      # -----------------------------------------------------------------------------------------------------
      token = response.json()["access_token"]
      headers= { 'Authorization': 'Bearer ' + token }

      r = requests.get(dest_service.credentials["uri"] + '/destination-configuration/v1/destinations/'+sDestinationName, headers=headers)
      print("DEST URI:",dest_service.credentials['uri'])

      # Access the destination securely
      # -----------------------------------------------------------------------------------------------------
      destination = r.json()

      return destination






Now, you need to create the destinations in the BTP account & try to access them via the python flask application.

That's it. Happy coding 🙂

 

Ref. Using the Destination service in SAP BTP, Cloud Foundry Environment. Please check out this blogpost for more information.

 
1 Comment
Labels in this area