Skip to Content
Technical Articles
Author's profile photo Andre Fischer

How to perform a deep update with SAP Gateway OData V4 framework?

Introduction

The SAP Gateway OData V4 framework does not support to perform a deep update.

This is also true for the OData V2 framework. And as a result also RAP based services do currently not support deep updates.

Update:

As an alternative you can use in RAP OData V4 bindings actions with parameters that contain a hierarchical structure, since actions in OData V4 send the parameters in a json payload.

See the documentation: Actions – ABAP Keyword Documentation (sap.com)

However today I learned about the need of such a support for an integration scenario where the consuming OData (or rather REST) client only supported updates using a payload which required a deep update support.

Since this support was only needed for a few API’s and since the requirement was not a generic support for deep updates I came to the conclusion (after some internal discussions) that a custom http handler would be an option in this case.

When checking the ICF node used by the SAP Gateway OData V4 framework we see that this uses the http handler class /IWBEP/CL_OD_ICF_HANDLER.

 

Custom http handler

For my POC I copied the class to a transportable class in the customer namespace and added a check in the code that checks

  1. if the incoming request is a PUT request and
  2. if the URL of the incoming request matches the URL of the entity that is to be updated

The copied http handler class ZIWBEP_CL_OD_ICF_HANDLER is registered in SICF for the OData V4 node.

Method if_http_extension~handle_request

The important method is the method if_http_extension~handle_request.

In this method I have added a check for the http method that is used by the incoming request.

DATA(http_method_used) = to_lower( server->request->get_method( ) ).

IF lv_uri_path_after_handler_org = '/iwbep/v4_sample/default/iwbep/v4_gw_sample_basic/0001/salesorderlist(''0500000001'')' AND 
         http_method_used = 'put'.

If this check is not successful the standard implementation is called.

  METHOD if_http_extension~handle_request.
*    URL segments:
*      foo://example.com:8042/over/there?name=ferret
*      \_/   \______________/\_________/ \_________/
*       |           |            |            |
*    scheme     authority       path        query

    DATA:
      lo_request_handler            TYPE REF TO if_http_extension,
      "!" Path part of the URL behind the segment where the ICF handler (this class) is registered - AFTER alias resolution
      "! <br/>E.g. '/iwbep/tea/default/iwbep/tea_busi/0001/TEAMS'
      lv_uri_path_after_handler     TYPE string,
      "!" Path part of the URL behind the segment where the ICF handler (this class) is registered - BEFORE alias resolution
      lv_uri_path_after_handler_org TYPE string,
      "! Path part of the URL up to segment where the ICF handler (this class) is registered - AFTER alias resolution
      "! <br/>This must be '/sap/opu/odata4'
      lv_uri_path_up_to_handler     TYPE string.


    lv_uri_path_up_to_handler     = to_lower( server->request->get_header_field( if_http_header_fields_sap=>script_name_expanded ) ).
    lv_uri_path_after_handler     = to_lower( server->request->get_header_field( if_http_header_fields_sap=>path_info_expanded ) ).
    lv_uri_path_after_handler_org = to_lower( server->request->get_header_field( if_http_header_fields_sap=>path_info ) ).

    " This handler must only be registered under the ICF node '/sap/opu/odata4'
    ASSERT lv_uri_path_up_to_handler = /iwbep/cl_v4_url_util=>gc_icf_path_odata4.
    " An alias must not point behind an ICF node after node '/sap/opu/odata4'
    ASSERT lv_uri_path_after_handler = lv_uri_path_after_handler_org.
    DATA(http_method_used) = to_lower( server->request->get_method( ) ).


    IF lv_uri_path_after_handler_org = '/iwbep/v4_sample/default/iwbep/v4_gw_sample_basic/0001/salesorderlist(''0500000001'')' AND
           http_method_used = 'put'.

      DATA(payload) = server->request->get_cdata( ).
      "do something with payload

      server->response->set_cdata(
        EXPORTING
          data               = cl_demo_output=>get( 'Hello World' )
      ).
      server->response->set_status(
        EXPORTING
          code          = if_web_http_status=>ok
          reason        = 'success_deep_update'
      ).

    ELSE.



      IF lv_uri_path_after_handler = /iwbep/cl_v4_url_util=>gc_icf_path_async_monitor
      OR lv_uri_path_after_handler CP /iwbep/cl_v4_url_util=>gc_icf_path_async_monitor && '*'.
        lo_request_handler = NEW /iwbep/cl_od_http_req_hand_arm( ).

      ELSE.
        lo_request_handler = NEW /iwbep/cl_od_http_req_handler( ).

      ENDIF.

      lo_request_handler->handle_request( server ).
    ENDIF.
  ENDMETHOD.


  METHOD if_oauth2_consumer~provide_tadir_key_for_request.

    CONSTANTS:
      lc_program_id_r3tr           TYPE pgmid      VALUE 'R3TR',
      lc_object_type_service_group TYPE trobjtype  VALUE 'G4BA'.

    DATA:
      lv_uri_path_after_handler TYPE string,   " E.g. '/iwbep/tea/default/iwbep/tea_busi/0001/TEAMS'
      lv_service_group_id       TYPE /iwbep/v4_med_group_id.



    lv_uri_path_after_handler = i_http_request->get_header_field( if_http_header_fields_sap=>path_info_expanded ).
    /iwbep/cl_v4_url_util=>parse_path_after_handler(
      EXPORTING
        iv_uri = lv_uri_path_after_handler
      IMPORTING
        ev_service_group_id = lv_service_group_id ).

    e_tadir_key-obj_name = lv_service_group_id.
    e_tadir_key-object   = lc_object_type_service_group.
    e_tadir_key-pgmid    = lc_program_id_r3tr.

  ENDMETHOD.
ENDCLASS.

 

Result

When running a deep update request we can see in the debug mode that one is able to retrieve the payload of the request.

So it will be possible to parse the JSON file and to extract the data so that one can perform a custom implementation for the deep update.

 

 

The result in the SAP Gateway Client then looks as follows:

As a reason “success_deep_update” is shown alongside with the response “Hello World” as it has been set in the coding of our custom http handler class.

server->response->set_cdata(
    EXPORTING
       data               = cl_demo_output=>get( 'Hello World' )
      ).
server->response->set_status(
    EXPORTING
          code          = if_web_http_status=>ok
          reason        = 'success_deep_update'
      ).

 

Assigned Tags

      11 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Michael Fritz
      Michael Fritz

      Hi Andre and sorry for my ignorance, but what is a "deep update"?

      Reading your blog, I guess it has nothing to to with a code or system upgrade, but rather how to access resources via OData?

      Author's profile photo Andre Fischer
      Andre Fischer
      Blog Post Author

      Hi Michael,

      your guess is correct.

      A deep update request would be a PUT / PATCH operation on an entity .../salesorder(45) and in the payload alongside with the salesorder information you would be able to pass data for the items that are part of this salesorder.

      Right now you would have to send several PUT / PATCH requests for each items that is to be maintained and for the sales order header data.

      Currenty it is only possible to create such objects using a so called deep insert in one request.

      I will work further on this post and show how one can use an action as a workaround as well since actions in V4 accept an arbritrary payload so that the deep structure could be passed as a payload on action.

      This would eliminate the need of changing the http handler which is not nice and clean.

      Kind regards,

      Andre

       

       

      Author's profile photo Michael Fritz
      Michael Fritz

      Thanks Andre for this explanation and keep going on posting 😉

      Author's profile photo Christian Kniesel
      Christian Kniesel

      Thanks so much for this Blog.

      Did you already have a chance to look deeper in how to use actions to enable deep updates?

      We are using oData to provide services to an external system based on MS .net / c#. As oData 4.01 (2017) supports deep update the customer is questioning why SAP is not supporting it - so we are looking for ways to provide this functionality.

      Basically one example would be the reimplementation of API_PRODUCTION_ORDER_2_SRV (oData 2.0) to support create/update with components / items on S/4HANA 2022 using oData 4.0.
      So this is similar to the Sales Order example shown here - only "a bit" more complex to impliment because of missing functionality in legacy APIs.

      Kind regards,
      Christian

      Author's profile photo Stefan Haslinger
      Stefan Haslinger

      Hi Andre,

      did you ever manage to find some time to write the post about the action approach without the http handler change?

      I was looking for it here in the blogs, but failed to find it.

      Warm regards, and thanks for all your work, you taught me a lot of the past years!

      Stefan

      Author's profile photo Andre Fischer
      Andre Fischer
      Blog Post Author

      See documentation for deep parameters

      RAP - InputParameter - ABAP Keyword Documentation (sap.com)

      Author's profile photo Kaschirin Sascha
      Kaschirin Sascha

      Correct me if I'm wrong, but I could use deep insert in order to update data just by adjusting the abap code in CREATE_DEEP_ENTITY, couldn't I?

      Author's profile photo Nitish Chawla
      Nitish Chawla

      Hi Andre,

      I might be missing something here. How this is different from the requests we handle in Deep_Entity method of dpc_ext class ?

      BR,

      Nitish

      Author's profile photo Andre Fischer
      Andre Fischer
      Blog Post Author

      It is different because it is

      a) a deep update and not a deep_insert

      b) V4 and not V2 which you use when implementing DPC_EXT in SEGW.

      Kind regards,

      Andre

       

      Author's profile photo Ankit Verma
      Ankit Verma

      Andre Fischer  - Thanks a lot for your informative post. We were trying to use Odata V4  for performing create/update/delete/mass operations and we are facing a lot of issues in trying to implement it through code based implementation specially for deep operations.

      Please can you also share how we can achieve for Deep Create scenario since we are not able to find any SAP blog with information on this using ABAP code based implementation.

      Also with so many constraints for Odata V4 using ABAP implementation please advise if its the correct path forward for building complex Fiori apps "On Premise" with this approach or is there a better approach we can take for building such services like using RAP frameworks or some other approach.

      Author's profile photo Andre Fischer
      Andre Fischer
      Blog Post Author

      Hi Ankit,

      when possible I would recommend to build an application based on RAP instead of using a code based implementation.

      It does however depend on which platform you are using but OData V4 services based on RAP can be build in principle as of SAP S/4HANA 2021.

      Kind regards,

      Andre