Skip to Content
Technical Articles
Author's profile photo Jens Schroeder

How To Connect SAP Cloud for Real Estate to SAP BW/4HANA

This post will share my experiences with you and guide you through the steps to connect your SAP Cloud for Real Estate API published by the SAP API Business Hub for Cloud for Real Estate to your SAP Cloud for Real Estate standard content available in SAP BW/4HANA since SP09.

Overview Architecture (Source: own representation)

Prerequisites are:

Steps are:

  1. Get the certificate of your SAP BTP and import it to your SAP BW/4HANA system.
  2. Create and configure a OAuth2 profile described in this blog post. Be sure, you managed your authorization objects (PFCG) before-hand.
  3. Configure a OAuth2 profile described in this blog post.
  4. Establish a connection to your SAP Cloud for Real Estate API either
    1. by destination (SM59) or
    2. by URL directly in the ABAP coding.
  5. Parse your JSON by using the ABAP JSON deserializer also described in this blog post and map your JSON attributes to your ABAP target structures.
  6. Write the results to ADSO type of Direct Update to the entry layer of the SAP Cloud for Real Estate standard content.

 

The steps in detail:

  1. Export the certificate from your SAP BTP instance. Best way is by calling the SAP Cloud for Real Estate REST API in your browser. You will get an 4xx error but you can download the certificate then from your browser, anyway. Next step is import this certificate via transaction STRUST in your SAP BW/4HANA System in your Anonym folder.
  2. First create a OAuth 2.0 profile in transaction SE80 as shown below and manage your scopes. You will get them (scopes) by calling the API endpoint to get the who am I information: <your_space.your _host>/core/api/v0/whoami
    oauth2_profile
  3. Now, configure your OAuth 2.0 profile via the transaction /nOA2C_CONFIG by putting in your <spaceid and tenantid> and also manage your OAuth 2.0 credentials with client id and client secrets.
    oauth_config1
    oauth2_config

Steps 4 to 6 you can find in the following coding example. Here you can find a simple quick and dirty ABAP report for getting all buildings and its time-depended attributes:

*&---------------------------------------------------------------------*
*& Report ZREC4RE
*&---------------------------------------------------------------------*
*&
*&---------------------------------------------------------------------*
report zrec4re.

data: profile                    type oa2c_profile,
      target                     type string,
      method                     type string,
      lo_http_client             type ref to if_http_client,
      lo_oa2c_client             type ref to if_oauth2_client,
      lv_status_code             type i,
      lv_json_data               type string,
      lt_fields                  type tihttpnvp,
      lx_oa2c                    type ref to cx_oa2c,
      lr_data                    type ref to data,
      ls_startdate               type char10,
      ls_enddate                 type char10,
      ls_building                type /b1h/ac_cre1202,           "Buildings - structure of active table of ADSO /IMO/C_CRE120
      lt_buildings               type table of /b1h/ac_cre1202,  "Buildings - active table of ADSO /IMO/C_CRE120
      ls_statustimeline          type /b1h/ac_cre2102,           "StatusTimeline - structure of active table of ADSO /IMO/C_CRE210
      lt_statustimeline          type table of /b1h/ac_cre2102,  "StatusTimeline - active table of ADSO /IMO/C_CRE210
      ls_ownershipstatustimeline type /b1h/ac_cre2202,           "OwnershipStatusTimeline - structure of active table of ADSO /IMO/C_CRE220
      lt_ownershipstatustimeline type table of /b1h/ac_cre2202,  "OwnershipStatusTimeline - active table of ADSO /IMO/C_CRE220
      ls_usagetypetimeline       type /b1h/ac_cre2402,           "UsageTypeTimeline - structure of active table of ADSO /IMO/C_CRE240
      lt_usagetypetimeline       type table of /b1h/ac_cre2402.  "UsageTypeTimeline - active table of ADSO /IMO/C_CRE240

field-symbols:
  <ls_field>        like line of lt_fields,
  <data>            type data,
  <inner_data>      type data,
  <results>         type any,
  <inner_results>   type any,
  <structure>       type any,
  <inner_structure> type any,
  <table>           type any table,
  <inner_table>     type any table,
  <field>           type any,
  <field_value>     type data.

start-of-selection.

  profile = 'ZREC4RE4'.
  target  = 'https://your_tenant.cfapps.sap.hana.ondemand.com/core/api/v0/buildings'.
  method  = 'GET'.

**********************************************************************
* Step 4: Create HTTP client by URL
**********************************************************************
  call method cl_http_client=>create_by_url
    exporting
      url                = target
      ssl_id             = 'ANONYM'
    importing
      client             = lo_http_client
    exceptions
      argument_not_found = 1
      plugin_not_active  = 2
      internal_error     = 3
      others             = 4.
  if sy-subrc <> 0.
    message id sy-msgid type sy-msgty number sy-msgno
               with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  endif.

  lo_http_client->propertytype_logon_popup = 0.

  call method lo_http_client->request->set_method
    exporting
      method = method.

**********************************************************************
* Set OAuth 2.0 Token
**********************************************************************
  try.

      call method cl_oauth2_client=>create
        exporting
          i_profile        = profile
        receiving
          ro_oauth2_client = lo_oa2c_client.

    catch cx_oa2c into lx_oa2c.
      write: `Error calling CREATE.`.
      write: / lx_oa2c->get_text( ).
      return.
  endtry.

  try.

      call method lo_oa2c_client->set_token
        exporting
          io_http_client = lo_http_client.

    catch cx_oa2c into lx_oa2c.
      try.

          "CALL METHOD lo_oa2c_client->execute_refresh_flow.
          call method lo_oa2c_client->execute_cc_flow.
        catch cx_oa2c into lx_oa2c.
          write: `Error calling EXECUTE_REFRESH_FLOW.`.
          write: / lx_oa2c->get_text( ).
          return.
      endtry.
      try.
          call method lo_oa2c_client->set_token
            exporting
              io_http_client = lo_http_client.
        catch cx_oa2c into lx_oa2c.
          write: `Error calling SET_TOKEN.`.
          write: / lx_oa2c->get_text( ).
          return.
      endtry.
  endtry.

**********************************************************************
* Send / Receive Request
**********************************************************************
  call method lo_http_client->send
    exceptions
      http_communication_failure = 1
      http_invalid_state         = 2
      http_processing_failed     = 3
      http_invalid_timeout       = 4
      others                     = 5.
  if sy-subrc <> 0.
    message id sy-msgid type sy-msgty number sy-msgno
               with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  endif.

  call method lo_http_client->receive
    exceptions
      http_communication_failure = 1
      http_invalid_state         = 2
      http_processing_failed     = 3
      others                     = 4.
  if sy-subrc <> 0.
    message id sy-msgid type sy-msgty number sy-msgno
               with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  endif.

**********************************************************************
* Display result
**********************************************************************
  call method lo_http_client->response->get_status
    importing
      code = lv_status_code.

  if lv_status_code = 200.
    call method lo_http_client->response->get_cdata
      receiving
        data = lv_json_data.

    call method /ui2/cl_json=>deserialize
      exporting
        json         = lv_json_data
        pretty_name  = /ui2/cl_json=>pretty_mode-user
        assoc_arrays = abap_true
      changing
        data         = lr_data.

  else.
    call method lo_http_client->response->get_header_fields
      changing
        fields = lt_fields.

    loop at lt_fields assigning <ls_field>.
      write: / <ls_field>-name, 25 <ls_field>-value.
    endloop.
  endif.

**********************************************************************
* Step 5: parse JSON result to target structure by using /ui2/cl_data_access
**********************************************************************
  if lr_data is bound.

    assign lr_data->* to <data>.
    assign component `content` of structure <data> to <results>.
    assign <results>->* to <table>.

    loop at <table> assigning <structure>.

      assign <structure>->* to <data>.

      /ui2/cl_data_access=>create( iv_data = <data> iv_component = `irn`)->value( importing ev_data = ls_building-irn ).
      /ui2/cl_data_access=>create( iv_data = <data> iv_component = `type`)->value( importing ev_data = ls_building-type ).
      /ui2/cl_data_access=>create( iv_data = <data> iv_component = `siteIrn`)->value( importing ev_data = ls_building-siteirn ).
      /ui2/cl_data_access=>create( iv_data = <data> iv_component = `constructionYear`)->value( importing ev_data = ls_building-constructionyear ).
      /ui2/cl_data_access=>create( iv_data = <data> iv_component = `shortName`)->value( importing ev_data = ls_building-shortname ).
      /ui2/cl_data_access=>create( iv_data = <data> iv_component = `longName`)->value( importing ev_data = ls_building-longname ).
      /ui2/cl_data_access=>create( iv_data = <data> iv_component = `longitude`)->value( importing ev_data = ls_building-longitude ).
      /ui2/cl_data_access=>create( iv_data = <data> iv_component = `latitude`)->value( importing ev_data = ls_building-latitude ).

      assign component `statusTimeline` of structure <data> to <inner_results>.
      assign <inner_results>->* to <inner_table>.

      loop at <inner_table> assigning <inner_structure>.

        assign <inner_structure>->* to <inner_data>.

        /ui2/cl_data_access=>create( iv_data = <data> iv_component = `irn`)->value( importing ev_data = ls_statustimeline-irn ).
        /ui2/cl_data_access=>create( iv_data = <inner_data> iv_component = `name`)->value( importing ev_data = ls_statustimeline-name ).
        /ui2/cl_data_access=>create( iv_data = <inner_data> iv_component = `startDate`)->value( importing ev_data = ls_startdate ).
        ls_statustimeline-startdate = |{ ls_startdate+0(4) }{ ls_startdate+5(2) }{ ls_startdate+8(2) }|.
        /ui2/cl_data_access=>create( iv_data = <inner_data> iv_component = `endDate`)->value( importing ev_data = ls_enddate ).
        ls_statustimeline-enddate = |{ ls_enddate+0(4) }{ ls_enddate+5(2) }{ ls_enddate+8(2) }|.

        append ls_statustimeline to lt_statustimeline.
        clear ls_statustimeline.
      endloop.

      assign component `ownershipStatusTimeline` of structure <data> to <inner_results>.
      assign <inner_results>->* to <inner_table>.

      loop at <inner_table> assigning <inner_structure>.

        assign <inner_structure>->* to <inner_data>.

        /ui2/cl_data_access=>create( iv_data = <data> iv_component = `irn`)->value( importing ev_data = ls_ownershipstatustimeline-irn ).
        /ui2/cl_data_access=>create( iv_data = <inner_data> iv_component = `name`)->value( importing ev_data = ls_ownershipstatustimeline-name ).
        /ui2/cl_data_access=>create( iv_data = <inner_data> iv_component = `startDate`)->value( importing ev_data = ls_ownershipstatustimeline-startdate ).
        ls_ownershipstatustimeline-startdate = |{ ls_startdate+0(4) }{ ls_startdate+5(2) }{ ls_startdate+8(2) }|.
        /ui2/cl_data_access=>create( iv_data = <inner_data> iv_component = `endDate`)->value( importing ev_data = ls_ownershipstatustimeline-enddate ).
        ls_ownershipstatustimeline-enddate = |{ ls_enddate+0(4) }{ ls_enddate+5(2) }{ ls_enddate+8(2) }|.

        append ls_ownershipstatustimeline to lt_ownershipstatustimeline.
        clear ls_ownershipstatustimeline.
      endloop.

      assign component `usageTypeTimeline` of structure <data> to <inner_results>.
      assign <inner_results>->* to <inner_table>.

      loop at <inner_table> assigning <inner_structure>.

        assign <inner_structure>->* to <inner_data>.

        /ui2/cl_data_access=>create( iv_data = <data> iv_component = `irn`)->value( importing ev_data = ls_usagetypetimeline-irn ).
        /ui2/cl_data_access=>create( iv_data = <inner_data> iv_component = `name`)->value( importing ev_data = ls_usagetypetimeline-name ).
        /ui2/cl_data_access=>create( iv_data = <inner_data> iv_component = `startDate`)->value( importing ev_data = ls_usagetypetimeline-startdate ).
        ls_usagetypetimeline-startdate = |{ ls_startdate+0(4) }{ ls_startdate+5(2) }{ ls_startdate+8(2) }|.
        /ui2/cl_data_access=>create( iv_data = <inner_data> iv_component = `endDate`)->value( importing ev_data = ls_usagetypetimeline-enddate ).
        ls_usagetypetimeline-enddate = |{ ls_enddate+0(4) }{ ls_enddate+5(2) }{ ls_enddate+8(2) }|.

        append ls_usagetypetimeline to lt_usagetypetimeline.
        clear ls_usagetypetimeline.
      endloop.

      append ls_building to lt_buildings.
      clear ls_building.
    endloop.

***********************************************************************
*    "Step 6a: Write result to ADSO /IMO/C_CRE120
***********************************************************************
    call function 'RSDSO_DU_WRITE_API'
      exporting
        i_adsonm      = '/IMO/C_CRE120'
        it_data       = lt_buildings
        i_insert_only = abap_false.

***********************************************************************
*    "Step 6b: Write result to ADSO /IMO/C_CRE210
***********************************************************************
    call function 'RSDSO_DU_WRITE_API'
      exporting
        i_adsonm      = '/IMO/C_CRE210'
        it_data       = lt_statustimeline
        i_insert_only = abap_false.

***********************************************************************
*    "Step 6c: Write result to ADSO /IMO/C_CRE220
***********************************************************************
    call function 'RSDSO_DU_WRITE_API'
      exporting
        i_adsonm      = '/IMO/C_CRE220'
        it_data       = lt_ownershipstatustimeline
        i_insert_only = abap_false.

***********************************************************************
*    "Step 6d: Write result to ADSO /IMO/C_CRE240
***********************************************************************
    call function 'RSDSO_DU_WRITE_API'
      exporting
        i_adsonm      = '/IMO/C_CRE240'
        it_data       = lt_usagetypetimeline
        i_insert_only = abap_false.


  endif.

**********************************************************************
* Close HTTP client
**********************************************************************
  call method lo_http_client->close
    exceptions
      http_invalid_state = 1
      others             = 2.
  if sy-subrc <> 0.
    message id sy-msgid type sy-msgty number sy-msgno
               with sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4.
  endif.
**********************************************************************

 

Lessons learned:

In summary it was a easy walkover. The only thing was that at some point was not able to get a connection anymore. So I looked into the transaction logs via SMICM and found out that the certificate was expired. Just renew it and all works.

Additionally, I also want to mentioned if you have already SAP Cloud Platform Integration or SAP Data Intelligence in place then you also can connect the REST API to SAP BW/4HANA by those tools.

 

Assigned Tags

      1 Comment
      You must be Logged on to comment or reply to a post.
      Author's profile photo Frank Riesner
      Frank Riesner

      Hi Jens, very interesting and well documented how-to-guide. I have added your blog as reference to this blog post, which covers integration of other SAP clouds to SAP BW/4HANA.
      https://blogs.sap.com/2021/09/22/sap-cloud-integration-into-sap-bw-4hana/

      Regards, Frank