SAP ABAP, NW ABAP Gateway (OData)

Gateway Runtime Artifacts Behind The Curtains

I have spent good amount of time developing lots of gateway projects using transaction SEGW.

As programmer, I always been curious to know purpose behind the generated runtime artifacts.

And over period of time I learned a way to develop gateway service without SEGW transaction. In this article I would be walk you through

Gateway Service Development without SEGW

Which is not a recommended way; but best for connecting dots between all generated runtime artifacts ( i.e. Model Provider Classes, Data Provider Classes, Model and Service).

Let’s get started..

We will take Sales Order from Enterprise Procurement Model as use case and consume data from SNWD* tables. We want to expose Sales Order data as Odata service thus we will need 2 entities – Sales Order & Sales Order Items and an association between them.

So we can define model as a ABAP class ZODATA_MODEL_CLASS inherited from super class IWBEP/CL_MGW_ABS_MODEL.

First we will create the entities structures as ABAP structures and the entity sets as ABAP table types in Public section for the class.

Save and activate those declarations. You can see them in Types tab as below

Now refine method ‘DEFINE’ to define the entity, entity sets and associations along with it’s annotations.

Copy below code snippet which is self-explanatory in DEFINE Method.

method DEFINE.

       "Declarations
        Data :
           lo_entity_type TYPE REF TO /iwbep/if_mgw_odata_entity_typ,
           lo_property TYPE REF TO /iwbep/if_mgw_odata_property,
           lo_association TYPE REF TO /iwbep/if_mgw_odata_assoc,
           lo_ref_constraint type ref to /iwbep/if_mgw_odata_ref_constr,
           lo_assoc_set      type ref to /iwbep/if_mgw_odata_assoc_set,
           lo_nav_property   type ref to /iwbep/if_mgw_odata_nav_prop.


        "Define Sales Order Entity
        lo_entity_type = model->create_entity_type('SalesOrder').
        "Define Properties
        lo_property = lo_entity_type->create_property( iv_property_name = 'Id'
                                                       iv_abap_fieldname = 'SO_ID').
        lo_property->set_is_key( ).                  "Mark property as entity key
        lo_property->set_conversion_exit( 'ALPHA' ). "Specify Conversion Exit


        lo_property = lo_entity_type->create_property( iv_property_name = 'BuyerId'
                                                       iv_abap_fieldname = 'BUYER_ID').

        lo_property = lo_entity_type->create_property( iv_property_name = 'GrossAmount'
                                                       iv_abap_fieldname = 'GROSS_AMOUNT').

        "Bind Structure
        lo_entity_type->bind_structure('ZODATA_MODEL_CLASS=>TS_SALES_ORDER').

        "Create Entity Set
        lo_entity_type->create_entity_set('SalesOrderCollection').


        "Define Sales Order Item Entity
        lo_entity_type = model->create_entity_type('SalesOrderItem').
        "Define Properties
        lo_property = lo_entity_type->create_property( iv_property_name = 'SalesOrderId'
                                                       iv_abap_fieldname = 'SO_ID').
        lo_property->set_is_key( ). " Mark property as entity key

        lo_property = lo_entity_type->create_property( iv_property_name = 'ItemPosition'
                                                       iv_abap_fieldname = 'ITEM_POS').
        lo_property->set_is_key( ). " Mark property as entity key

        lo_property = lo_entity_type->create_property( iv_property_name = 'ProductId'
                                                       iv_abap_fieldname = 'PRODUCT_ID').

        "Bind Structure
        lo_entity_type->bind_structure('ZODATA_MODEL_CLASS=>TS_SALES_ORDER_ITEM').

        "Create Entity Set
        lo_entity_type->create_entity_set('SalesOrderItemCollection').

       "Define association
        lo_association = model->create_association(  iv_association_name = 'SalesOrderSalesOrderItem'
                                                     iv_left_type        = 'SalesOrder'
                                                     iv_right_type       = 'SalesOrderItem'
                                                     iv_right_card       = 'N'
                                                     iv_left_card        = '1'  ).
        "Referencial cardianlity
        lo_ref_constraint = lo_association->create_ref_constraint( ).
        lo_ref_constraint->add_property( iv_principal_property = 'Id'   iv_dependent_property = 'SalesOrderId' ).

        "Association Set
        lo_assoc_set = model->create_association_set( iv_association_set_name  = 'SalesOrderSalesOrderItemAssocSet'
                                                    iv_left_entity_set_name  = 'SalesOrderCollection'
                                                    iv_right_entity_set_name = 'SalesOrderItemCollection'
                                                    iv_association_name      = 'SalesOrderSalesOrderItem' ).

        "Navigation Property
        lo_entity_type = model->get_entity_type( iv_entity_name = 'SalesOrder' ).
        lo_nav_property = lo_entity_type->create_navigation_property( iv_property_name  = 'SalesOrderItemCollection'
                                                                      iv_association_name = 'SalesOrderSalesOrderItem' ).


        endmethod.

Now we will be implementing the data handling logic. So we will create another ABAP class ZODATA_DATA_CLASS inherited from super class /IWBEP/CL_MGW_ABS_DATA

For now we will only implement Odata GET operation for sales order header data.

Refined the method /iwbep/if_mgw_appl_srv_runtime~get_entity and add below code snippet which is self-explanatory in it.

METHOD /iwbep/if_mgw_appl_srv_runtime~get_entity.
	    CASE iv_entity_name.
	      WHEN 'SalesOrder'.
	*-------------------------------------------------------------------
	*   Local data definitions
	*-------------------------------------------------------------------
	        DATA :
	          ls_inputdata  TYPE zodata_model_class=>ts_sales_order,
	          ls_outputdata TYPE bapi_epm_so_header,
	          li_return     TYPE bapiret2_t.
	
	*-------------------------------------------------------------------
	*   Process Gateway Request + parameters (if any)
	*-------------------------------------------------------------------
	* Read converted keys for entity using follwing method
	*  -> This method invokes conversion exit respective to key fields implicitly and converts key values to desired format
	        CALL METHOD io_tech_request_context->get_converted_keys
	          IMPORTING
	            es_key_values = ls_inputdata.
	
	        DATA(lv_so_id) = CONV bapi_epm_so_id( ls_inputdata-so_id ).
	*-------------------------------------------------------------------
	*   Perform backend functionality using RFC / Z Function Module / BAPI / Method /...
	*-------------------------------------------------------------------
	* Read Sales Order data based on SoId
	        CALL FUNCTION 'BAPI_EPM_SO_GET_DETAIL'
	          EXPORTING
	            so_id      = lv_so_id
	          IMPORTING
	            headerdata = ls_outputdata
	          TABLES
	            return     = li_return.
	
	*--------------- End of backend functionality ---------------------------------*
	*-------------------------------------------------------------------
	*   Error / Message Handling
	*-------------------------------------------------------------------
	        IF line_exists( li_return[ type = 'E' ] ).
	* Raise Exception on error
	        ELSE.
	* On Success - Send back Entity data
	          ls_inputdata-buyer_id     = ls_outputdata-buyer_id.
	          ls_inputdata-gross_amount = ls_outputdata-gross_amount.
	
	          copy_data_to_ref(
	            EXPORTING
	             is_data = ls_inputdata
	            CHANGING
	              cr_data = er_entity ).
	        ENDIF.
	
	      WHEN OTHERS.
	    ENDCASE.
	
	  ENDMETHOD.

Now we have Model class containing Sales Order Entity definition and Data class containing Read Sales Order logic.

Let’s do the binding of these ABAP classes in Odata Service.

Go to SPRO transaction and perform following configurations

1. Maintain Model

2. Maintain Service

Now let’s register the service using transaction /iwfnd/maint_service

1. Add Service

2. Verify the Odata ICF node is active and the system alias is properly maintained.

We are all set to test the service with SAP Gateway Client.

Verify the metadata for the service

Now let’s perform GET Sales Order Data operation

You can see the sales order data in response.

So much of effort for a simple Sales Order GET operation!!! We could implement the same operation within 5 minutes with Gateway Project created in SEGW transaction.

Now we understand the Generate Runtime Artifacts Button taking care of creating the Model and Data Class along with Model and Service as per framework recommendation making odata developer’s life easy….