Technology Blog Posts by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
Andre_Fischer
Product and Topic Expert
Product and Topic Expert
21,762

Updates



  • 07.10.2022 - Added the information that the code of MPC_EXT and DPC_EXT MUST use the name of the original service


Introduction


Extenstions of OData Services that have been created using the Reference Data Source (RDS) approach do not work out of the box. When you try to add additional entities to the SEGW project that redefines an RDS based OData service these entities are not part of the $metadata document.

This is quite unfortunate since most of the OData services hat have been delivered with SAP S/4HANA are based on RDS.

It is however possible to redefine a reference data source based OData service and perform certain adjustments in the MPC_EXT and DPC_EXT class to achieve this goal.

In the following I will describe the steps that are needed in order to redefine a reference datasource based OData service and how you can use the reference datasource approach in order to add additional entities that are handled by the SADL framework as well.

To make the live of the ABAP developer easier I have provided a report that lets you select the source and the extended SEGW project that will generate the aforementioned boiler plate coding.

The source code of the report can be found on GitHub

There I have also posted a code sample that has been generated by this report for the simple extension example described in this blog post.

Example of generated MPC_EXT and DPC_EXT code

Summary


In the following I will use a simple example of an RDS based source SEGW project Z_SRC_RDS that uses two CDS views: SEPM_I_SALESORDER_E and SEPM_I_SALESORDERITEM_E.


This project is redefined in a second SEGW project called Z_REDEF_RDS where two additional CDS views ZI_Currency and I_CurrencyText have beend added.



Adjust the MPC_EXT class


IF_SADL_GW_MODEL_EXPOSURE_DATA~GET_MODEL_EXPOSURE


In the method IF_SADL_GW_MODEL_EXPOSURE_DATA~GET_MODEL_EXPOSURE, the SADL framework generates the SADL definition, an XML file that contains the structure and the datasources that have been added to your SEGW project using RDS.

The following code snippet shows the sadl definition of the source SEGW project.
method IF_SADL_GW_MODEL_EXPOSURE_DATA~GET_MODEL_EXPOSURE.
CONSTANTS: co_gen_timestamp TYPE timestamp VALUE '20211208161947'.
DATA(lv_sadl_xml) =
|<?xml version="1.0" encoding="utf-16"?>| &
|<sadl:definition xmlns:sadl="http://sap.com/sap.nw.f.sadl" syntaxVersion="" >| &
| <sadl:dataSource type="CDS" name="SEPM_I_SALESORDERITEM_E" binding="SEPM_I_SALESORDERITEM_E" />| &
| <sadl:dataSource type="CDS" name="SEPM_I_SALESORDER_E" binding="SEPM_I_SALESORDER_E" />| &
|<sadl:resultSet>| &
|<sadl:structure name="SEPM_I_SalesOrderItem_E" dataSource="SEPM_I_SALESORDERITEM_E" maxEditMode="RO" exposure="TRUE" >| &
| <sadl:query name="SADL_QUERY">| &
| </sadl:query>| &
|</sadl:structure>| &
|<sadl:structure name="SEPM_I_SalesOrder_E" dataSource="SEPM_I_SALESORDER_E" maxEditMode="RO" exposure="TRUE" >| &
| <sadl:query name="SADL_QUERY">| &
| </sadl:query>| &
| <sadl:association name="TO_ITEM" binding="_ITEM" target="SEPM_I_SalesOrderItem_E" cardinality="zeroToMany" />| &
|</sadl:structure>| &
|</sadl:resultSet>| &
|</sadl:definition>| .

ro_model_exposure = cl_sadl_gw_model_exposure=>get_exposure_xml( iv_uuid = CONV #( 'Z_SRC_RDS' )
iv_timestamp = co_gen_timestamp
iv_sadl_xml = lv_sadl_xml ).
endmethod.
ENDCLASS.

When you create the extension project Z_REDEF_RDS via redefinition and then add two additional CDS views using the reference data source approach you will find that the MPC class also contains a SADL definition. But this SADL definition only contains the newly added entities.
method IF_SADL_GW_MODEL_EXPOSURE_DATA~GET_MODEL_EXPOSURE.
CONSTANTS: co_gen_timestamp TYPE timestamp VALUE '20211208162527'.
DATA(lv_sadl_xml) =
|<?xml version="1.0" encoding="utf-16"?>| &
|<sadl:definition xmlns:sadl="http://sap.com/sap.nw.f.sadl" syntaxVersion="V2" >| &
| <sadl:dataSource type="CDS" name="I_CURRENCYTEXT" binding="I_CURRENCYTEXT" />| &
| <sadl:dataSource type="CDS" name="ZI_CURRENCY" binding="ZI_CURRENCY" />| &
|<sadl:resultSet>| &
|<sadl:structure name="I_CurrencyText" dataSource="I_CURRENCYTEXT" maxEditMode="RO" exposure="TRUE" >| &
| <sadl:query name="SADL_QUERY">| &
| </sadl:query>| &
|</sadl:structure>| &
|<sadl:structure name="zI_currency" dataSource="ZI_CURRENCY" maxEditMode="RO" exposure="TRUE" >| &
| <sadl:query name="SADL_QUERY">| &
| </sadl:query>| &
| <sadl:association name="TO_TEXT" binding="_TEXT" target="I_CurrencyText" cardinality="zeroToMany" />| &
|</sadl:structure>| &
|</sadl:resultSet>| &
|</sadl:definition>| .

ro_model_exposure = cl_sadl_gw_model_exposure=>get_exposure_xml( iv_uuid = CONV #( 'Z_REDEF_RDS' )
iv_timestamp = co_gen_timestamp
iv_sadl_xml = lv_sadl_xml ).
endmethod.

In order to leverag all reference data source based entities one has to merge both SADL definitions. The merged SADL xml file has than to be provided in the redefined method IF_SADL_GW_MODEL_EXPOSURE_DATA~GET_MODEL_EXPOSURE of the MPC_EXT class of the project Z_REDEF_RDS.

In addition we have to redefine the DEFINE method so that it calls the DEFINE method of its parent class. So will have to create the following:
CLASS ZCL_Z_REDEF_RDS_MPC_EXT IMPLEMENTATION.
METHOD define.
super->define( ).
ENDMETHOD.
METHOD if_sadl_gw_model_exposure_data~get_model_exposure.
CONSTANTS: co_gen_timestamp TYPE timestamp VALUE '20211208180352'.
DATA(lv_sadl_xml) =
|<?xml version="1.0" encoding="utf-16"?>| &
|<sadl:definition xmlns:sadl="http://sap.com/sap.nw.f.sadl" syntaxVersion="V2" >| &
| <sadl:dataSource type="CDS" name="SEPM_I_SALESORDERITEM_E" binding="SEPM_I_SALESORDERITEM_E" />| &
| <sadl:dataSource type="CDS" name="SEPM_I_SALESORDER_E" binding="SEPM_I_SALESORDER_E" />| &
|<sadl:dataSource type="CDS" name="I_CURRENCYTEXT" binding="I_CURRENCYTEXT" />| &
| <sadl:dataSource type="CDS" name="ZI_CURRENCY" binding="ZI_CURRENCY" />| &
|<sadl:resultSet>| &
|<sadl:structure name="SEPM_I_SalesOrderItem_E" dataSource="SEPM_I_SALESORDERITEM_E" maxEditMode="RO" exposure="TRUE" >| &
| <sadl:query name="SADL_QUERY">| &
| </sadl:query>| &
|</sadl:structure>| &
|<sadl:structure name="SEPM_I_SalesOrder_E" dataSource="SEPM_I_SALESORDER_E" maxEditMode="RO" exposure="TRUE" >| &
| <sadl:query name="SADL_QUERY">| &
| </sadl:query>| &
| <sadl:association name="TO_ITEM" binding="_ITEM" target="SEPM_I_SalesOrderItem_E" cardinality="zeroToMany" />| &
|</sadl:structure>| &
|<sadl:structure name="I_CurrencyText" dataSource="I_CURRENCYTEXT" maxEditMode="RO" exposure="TRUE" >| &
| <sadl:query name="SADL_QUERY">| &
| </sadl:query>| &
|</sadl:structure>| &
|<sadl:structure name="zI_currency" dataSource="ZI_CURRENCY" maxEditMode="RO" exposure="TRUE" >| &
| <sadl:query name="SADL_QUERY">| &
| </sadl:query>| &
| <sadl:association name="TO_TEXT" binding="_TEXT" target="I_CurrencyText" cardinality="zeroToMany" />| &
|</sadl:structure>| &
|</sadl:resultSet>| &
|</sadl:definition>| .
ro_model_exposure = cl_sadl_gw_model_exposure=>get_exposure_xml( iv_uuid = CONV #( 'Z_SRC_RDS' )
iv_timestamp = co_gen_timestamp
iv_sadl_xml = lv_sadl_xml ).
ENDMETHOD.
ENDCLASS.

This process can become cumbersome and error prone when dealing with large SEGW source projects. That's why I have created a report that automates this process.

Adjust DPC_EXT class




IF_SADL_GW_DPC_UTIL~GET_DPC


The method IF_SADL_GW_DPC_UTIL~GET_DPC contains (for historical) reasons the same sadl definition. We have hence to provide the merged SADL xml file in the redefined method in the DPC_EXT class of the extended SEGW project as well.
 METHOD if_sadl_gw_dpc_util~get_dpc.
CONSTANTS: co_gen_timestamp TYPE timestamp VALUE '20211208223150'.
DATA(lv_sadl_xml) =
|<?xml version="1.0" encoding="utf-16"?>| &
|<sadl:definition xmlns:sadl="http://sap.com/sap.nw.f.sadl" syntaxVersion="V2" >| &
| <sadl:dataSource type="CDS" name="SEPM_I_SALESORDERITEM_E" binding="SEPM_I_SALESORDERITEM_E" />| &
| <sadl:dataSource type="CDS" name="SEPM_I_SALESORDER_E" binding="SEPM_I_SALESORDER_E" />| &
|<sadl:dataSource type="CDS" name="I_CURRENCYTEXT" binding="I_CURRENCYTEXT" />| &
| <sadl:dataSource type="CDS" name="ZI_CURRENCY" binding="ZI_CURRENCY" />| &
|<sadl:resultSet>| &
|<sadl:structure name="SEPM_I_SalesOrderItem_E" dataSource="SEPM_I_SALESORDERITEM_E" maxEditMode="RO" exposure="TRUE" >| &
| <sadl:query name="SADL_QUERY">| &
| </sadl:query>| &
|</sadl:structure>| &
|<sadl:structure name="SEPM_I_SalesOrder_E" dataSource="SEPM_I_SALESORDER_E" maxEditMode="RO" exposure="TRUE" >| &
| <sadl:query name="SADL_QUERY">| &
| </sadl:query>| &
| <sadl:association name="TO_ITEM" binding="_ITEM" target="SEPM_I_SalesOrderItem_E" cardinality="zeroToMany" />| &
|</sadl:structure>| &
|<sadl:structure name="I_CurrencyText" dataSource="I_CURRENCYTEXT" maxEditMode="RO" exposure="TRUE" >| &
| <sadl:query name="SADL_QUERY">| &
| </sadl:query>| &
|</sadl:structure>| &
|<sadl:structure name="zI_currency" dataSource="ZI_CURRENCY" maxEditMode="RO" exposure="TRUE" >| &
| <sadl:query name="SADL_QUERY">| &
| </sadl:query>| &
| <sadl:association name="TO_TEXT" binding="_TEXT" target="I_CurrencyText" cardinality="zeroToMany" />| &
|</sadl:structure>| &
|</sadl:resultSet>| &
|</sadl:definition>| .
ro_dpc = cl_sadl_gw_dpc_factory=>create_for_sadl( iv_sadl_xml = lv_sadl_xml
iv_timestamp = co_gen_timestamp
iv_uuid = CONV #( 'Z_SRC_RDS' )
io_context = me->mo_context ).
ENDMETHOD.

*************************************************************


!!! CAUTION - IV_UUID !!!


It is important that in both classes, the MPC_EXT and the DPC_EXT class the value for the unique ID of the SADL model is the name of the original model.
iv_uuid = CONV #( 'Z_SRC_RDS' )

(If you use the name of the redefined model instead the newly added entities will not show up).

In addition it is important that the time stamp has been set to a value near "now" so that it is newer than the time stamps used in the rest of the coding generated by SEGW and delivered by SAP.

The report that I wrote takes care of this, so I would strongly recommend to use this report to create the sample code for MPC_EXT and DPC_EXT.

=> RESTRICTION


In addition it is important to note that by using the original model name also the original RDS based model now would show the newly added entities.

So the orginal service would not be accessible in its original version.

But since you are interested to extend the original RDS based service delivered by SAP I assume that this restriction is not a problem.

*************************************************************


Add entity specific _get_entity, _get_entityset methods.


When adding additional CDS views to your redefined RDS based SEGW project Z_RDS_SRC you will find that no entity specifc methods are generated in your extended SEGW project Z_REDEF_RDS.

It is thus necessary to add manually entity specific methods such as:

i_currencytex_get_entityi_currencytex_get_entitysetzi_currency_get_entity and zi_currency_get_entityset.

Please note that we only use one importing parameter io_tech_request_context since the rest of the importing parameters have been deprecated and are thus not used by the SADL framework.
CLASS zcl_z_redef_rds_dpc_ext DEFINITION
PUBLIC
INHERITING FROM zcl_z_redef_rds_dpc
CREATE PUBLIC .
PUBLIC SECTION.
METHODS if_sadl_gw_dpc_util~get_dpc REDEFINITION .
METHODS /iwbep/if_mgw_appl_srv_runtime~get_entityset REDEFINITION .
METHODS /iwbep/if_mgw_appl_srv_runtime~get_entity REDEFINITION .
PROTECTED SECTION.
METHODS i_currencytex_get_entity
IMPORTING
!io_tech_request_context TYPE REF TO /iwbep/if_mgw_req_entity OPTIONAL
EXPORTING
!er_entity TYPE zcl_z_redef_rds_mpc=>ts_i_currencytexttype "use code completion to select correct type from MPC .
!es_response_context TYPE /iwbep/if_mgw_appl_srv_runtime=>ty_s_mgw_response_entity_cntxt
RAISING
/iwbep/cx_mgw_busi_exception
/iwbep/cx_mgw_tech_exception .
METHODS i_currencytex_get_entityset
IMPORTING
!io_tech_request_context TYPE REF TO /iwbep/if_mgw_req_entityset OPTIONAL
EXPORTING
!et_entityset TYPE zcl_z_redef_rds_mpc=>tt_i_currencytexttype "use code completion to select correct type from MPC .
!es_response_context TYPE /iwbep/if_mgw_appl_srv_runtime=>ty_s_mgw_response_context
RAISING
/iwbep/cx_mgw_busi_exception
/iwbep/cx_mgw_tech_exception .
METHODS zi_currency_get_entity
IMPORTING
!io_tech_request_context TYPE REF TO /iwbep/if_mgw_req_entity OPTIONAL
EXPORTING
!er_entity TYPE zcl_z_redef_rds_mpc=>ts_zi_currencytype "use code completion to select correct type from MPC .
!es_response_context TYPE /iwbep/if_mgw_appl_srv_runtime=>ty_s_mgw_response_entity_cntxt
RAISING
/iwbep/cx_mgw_busi_exception
/iwbep/cx_mgw_tech_exception .
METHODS zi_currency_get_entityset
IMPORTING
!io_tech_request_context TYPE REF TO /iwbep/if_mgw_req_entityset OPTIONAL
EXPORTING
!et_entityset TYPE zcl_z_redef_rds_mpc=>tt_zi_currencytype "use code completion to select correct type from MPC .
!es_response_context TYPE /iwbep/if_mgw_appl_srv_runtime=>ty_s_mgw_response_context
RAISING
/iwbep/cx_mgw_busi_exception
/iwbep/cx_mgw_tech_exception .
ENDCLASS .

Please note that the implementation of the entity specific methods ultimatively only contains generic calls of the underlying SADL framework.
  METHOD i_currencytex_get_entity.
if_sadl_gw_dpc_util~get_dpc( )->get_entity( EXPORTING io_tech_request_context = io_tech_request_context
IMPORTING es_data = er_entity ).
ENDMETHOD.
METHOD i_currencytex_get_entityset.
if_sadl_gw_dpc_util~get_dpc( )->get_entityset( EXPORTING io_tech_request_context = io_tech_request_context
IMPORTING et_data = et_entityset
es_response_context = es_response_context ).
ENDMETHOD.
METHOD zi_currency_get_entity.
if_sadl_gw_dpc_util~get_dpc( )->get_entity( EXPORTING io_tech_request_context = io_tech_request_context
IMPORTING es_data = er_entity ).
ENDMETHOD.
METHOD zi_currency_get_entityset.
if_sadl_gw_dpc_util~get_dpc( )->get_entityset( EXPORTING io_tech_request_context = io_tech_request_context
IMPORTING et_data = et_entityset
es_response_context = es_response_context ).
ENDMETHOD.

/IWBEP/IF_MGW_APPL_SRV_RUNTIME~GET_ENTITYSET


The generic method /IWBEP/IF_MGW_APPL_SRV_RUNTIME~GET_ENTITYSET controls which entities can be reached in the service at runtime. It contains a CASE statement that will call the entity specific methods if an OData request for ths same has reached the SAP Gateway OData framework.

Please note that the entity specific methods ultimately only contain a delegation to the SADL runtime. In the generic methods.

If none of the newly added entity sets is called the generic GET_ENTITYSET of the parent class and thus the DPC_EXT implementation of the source service is called.
 METHOD /iwbep/if_mgw_appl_srv_runtime~get_entityset.
DATA(my_entity_name) = io_tech_request_context->get_entity_set_name( ).
CASE io_tech_request_context->get_entity_set_name( ).
WHEN 'I_CurrencyText'.
i_currencytex_get_entityset( EXPORTING io_tech_request_context = io_tech_request_context
IMPORTING et_entityset = DATA(i_currencytex_entityset)
es_response_context = es_response_context ).
IF i_currencytex_entityset IS NOT INITIAL.
copy_data_to_ref( EXPORTING is_data = i_currencytex_entityset
CHANGING cr_data = er_entityset ).
ENDIF.
WHEN 'zI_currency'.
zi_currency_get_entityset( EXPORTING io_tech_request_context = io_tech_request_context
IMPORTING et_entityset = DATA(zi_currency_entityset)
es_response_context = es_response_context ).
IF zi_currency_entityset IS NOT INITIAL.
copy_data_to_ref( EXPORTING is_data = zi_currency_entityset
CHANGING cr_data = er_entityset ).
ENDIF.
WHEN OTHERS.
super->/iwbep/if_mgw_appl_srv_runtime~get_entityset( EXPORTING iv_entity_name = iv_entity_name
iv_entity_set_name = iv_entity_set_name
iv_source_name = iv_source_name
it_filter_select_options = it_filter_select_options
it_order = it_order
is_paging = is_paging
it_key_tab = it_key_tab
it_navigation_path = it_navigation_path
iv_filter_string = iv_filter_string
iv_search_string = iv_search_string
io_tech_request_context = io_tech_request_context
IMPORTING er_entityset = er_entityset
es_response_context = es_response_context ).
ENDCASE.
ENDMETHOD.

/IWBEP/IF_MGW_APPL_SRV_RUNTIME~GET_ENTITY


Also for the GET_ENTITY requests we have to provide appropriate code in the generic GET_ENTITY method /IWBEP/IF_MGW_APPL_SRV_RUNTIME~GET_ENTITY.
 METHOD /iwbep/if_mgw_appl_srv_runtime~get_entity.
DATA(my_entity_name) = io_tech_request_context->get_entity_set_name( ).
CASE io_tech_request_context->get_entity_set_name( ).
WHEN 'I_CurrencyText'.
i_currencytex_get_entity( EXPORTING io_tech_request_context = io_tech_request_context
IMPORTING er_entity = DATA(i_currencytex_entity)
es_response_context = es_response_context ).
IF i_currencytex_entity IS NOT INITIAL.
copy_data_to_ref( EXPORTING is_data = i_currencytex_entity
CHANGING cr_data = er_entity ).
ENDIF.
WHEN 'zI_currency'.
zi_currency_get_entity( EXPORTING io_tech_request_context = io_tech_request_context
IMPORTING er_entity = DATA(zi_currency_entity)
es_response_context = es_response_context ).
IF zi_currency_entity IS NOT INITIAL.
copy_data_to_ref( EXPORTING is_data = zi_currency_entity
CHANGING cr_data = er_entity ).
ENDIF.
WHEN OTHERS.
super->/iwbep/if_mgw_appl_srv_runtime~get_entity( EXPORTING iv_entity_name = iv_entity_name
iv_entity_set_name = iv_entity_set_name
iv_source_name = iv_source_name
it_key_tab = it_key_tab
it_navigation_path = it_navigation_path
io_tech_request_context = io_tech_request_context
IMPORTING er_entity = er_entity
es_response_context = es_response_context ).
ENDCASE.
ENDMETHOD.

Add missing annotations


The following blog post of my colleague mrajasekarana will give you details on how to link the annotations of the Odata Service created  based on CDS  with the redefined service.

How to redefine a CDS based Odata Service with Annotations ? | SAP Blogs

See also SAP Note:

3091281 - Extended OData Service throws error :No Service found for Annotation File ‘XXXXXXXXXXXXXXX...

Result


When having performed the aforementioned adjustments the OData service of the redefined SEGW project now contains four CDS based entities.



Problems when redefining large OData Services


While the described process works find for our simple example you might encounter problems when dealing with large RDS based OData services as they have been delivered as part of SAP S/4HANA.

Failures during consistency checks


When redefining an SAP Gateway based OData service SEGW nevertheless performs consistency checks. It has been observed that some of these consistency checks fail though the underlying service works fine.


To overcome this issue the following notes have been created that are also part of the latest deliveries of the SAP Gateway framework.

2853065 - SEGW Tool update - Association & Referential Constraints

3115221 - SEGW Tool - Covert Error Messages to Warnings during check

Time out problems


Redefinition of large OData service might take several minutes so that the maximum runtime of a workprocess is exceeded. The profile parameter rdisp/scheduler/prio_high/max_runtime is used to configure the dialog request run-time limit, i.e., the time that a request is allowed to run in a dialog work process without interruption.

Fortunately it is possible to change the setting of this profile parameter online using transaction RZ11. So you can adapt this setting without the need to reboot your development system.


 

Tool support


Since merging XML files and creating additional methods in the DPC_EXT class is a time consuming and errorprone task I have developed a report that lets you select two SEGW projects. A source project and an extension project.


The report will generate the source code in a separate text file that has to be implemented in the MPC_EXT and the DPC_EXT class of your SEGW extension project.

The source code of the report can be found on GitHub:

SAP-samples / abap-platform-code-samples-standard

Simply copy and paste the code from the text file that you have dowloaded into the MPC_EXT and DPC_EXT classes of your extended SEGW project.

The only thing that is left to do for the developer is that he or she has to copy and paste the generated code into the MPC_EXT and the DPC_EXT class.


At the moment it is also necessary to select the TYPES that have been generated in the MPC class via code completion.

Technical restrictions


Since we are merging the SADL xml files at some point in time you have to redo parts of this when

a) the underlying source service changes and

b) if you add additional entities via the reference data source approach

Hopefully we will be able to add (parts) of this functionality into the standard ;-).

Outlook


I am currently testing code snippets that can be added to your MPC_EXT and DPC_EXT class that are calling the so called flex classes. Flex classes are part of SEGW projects where the OData services have been prepared to support the extension by key user tools.

Flex classes are parent classes of the MPC_EXT and DPC_EXT class of an OData service and they are in turn inheriting from the MPC and DPC class of the SEGW project delivered by SAP.

This code is needed so that fields that have been added using the custom fields key user tool are also visible in the SAP FIori application if the underlying OData service has been redefined.

 

 

 

 
21 Comments