In this post, you will learn how to implement all operations for entities.

This is a post from a series Building OData Services. Earlier posts can be accessed through the below links.

  1. Building OData Services [1]: OData Overview
  2. Building OData Services [2]: Understanding OData Service
  3. Building OData Services [3]: Create OData Service
  4. Building OData Services [4]: Register and Test OData Service
  5. Building OData Services [5]: Working With OData URIs

In the previous post, we understood how URIs work. Now let us implement the URIs / Service operations in SAP ABAP.

When OData Project is generated in the SEGW transaction, a Data Provider Base Class and Data Provider Extension Class are created. We will use the extension class to implement all the operations.

This is the extension class – usually called as DPC_EXT class.

Each of the operations will have a dedicated method in the data provider class. The method names indicate which operation they should be used to implement.

For example, CARRIERSET_CREATE_ENTITY is for Creating a Carrier. Code examples for each of the operations are as below.

Read – GET ENTITY 

Method io_tech_request_context->get_converted_keys is used to get the key passed in URI.

/sap/opu/odata/sap/ZJP_DEMO_SRV/CarrierSet(‘AA’)

METHOD carrierset_get_entity.
  DATA ls_keys TYPE zcl_zjp_demo_mpc=>ts_carrier.

  io_tech_request_context->get_converted_keys(
    IMPORTING
      es_key_values = ls_keys ).
  SELECT SINGLE carrid, carrname, currcode
    FROM scarr
    INTO CORRESPONDING FIELDS OF @er_entity
    WHERE carrid = @ls_keys-carrid.
  IF sy-subrc NE 0.
    RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception
      EXPORTING
        message = 'No Data Found'.
  ENDIF.
ENDMETHOD.

Query: Selection of data and Sorting – GET ENTITYSET

In query operation, some filters can be applied by default as per business logic. Here is an example without filter operation implemented

/sap/opu/odata/sap/ZJP_DEMO_SRV/CarrierSet?$format=json

METHOD carrierset_get_entityset.

  SELECT carrid, carrname, currcode
    FROM scarr
    INTO TABLE @et_entityset.
  IF sy-subrc NE 0.
    RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception.
  ENDIF.

ENDMETHOD.

However, in the Get Entity Set method we need to handle the implementation of Filter, Search String, Sort Order, and paging as well.

Example with filter & search string

The below code is useful when all the filter values can be used in a single query.

/sap/opu/odata/sap/ZJP_DEMO_SRV/CarrierSet?$filter=CarrierID eq ‘AA’

/sap/opu/odata/sap/ZJP_DEMO_SRV/CarrierSet?search=’Ame’

METHOD carrierset_get_entityset.

  DATA(lv_search) = io_tech_request_context->get_search_string( ).
  IF lv_search IS INITIAL.
    DATA(osql_where_clause) = io_tech_request_context->get_osql_where_clause_convert( ).
  ELSE.
    REPLACE ALL OCCURRENCES OF '''' IN lv_search WITH '%'.
    osql_where_clause = |( CARRID LIKE '{ lv_search }' ) OR ( CARRNAME LIKE '{ lv_search }' )|.
  ENDIF.

  SELECT carrid, carrname, currcode
    FROM scarr
    WHERE (osql_where_clause)
    INTO CORRESPONDING FIELDS OF TABLE @et_entityset.
  IF sy-subrc NE 0.
    RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception.
  ENDIF.

ENDMETHOD.

The below code is useful when we need to use each field as a different range.

/sap/opu/odata/sap/ZJP_DEMO_SRV/CarrierSet?$filter=CarrierID eq ‘AA’

METHOD carrierset_get_entityset.

  DATA : lr_carrid TYPE RANGE OF scarr-carrid.

  DATA(lt_filters) = io_tech_request_context->get_filter( )->get_filter_select_options( ).
  lr_carrid = VALUE #( FOR ls_row IN lt_filters[ property = 'CARRID' ]-select_options
                       ( sign   = ls_row-sign
                         option = ls_row-option
                         low    = ls_row-low
                         high   = ls_row-high ) ).

  SELECT carrid, carrname, currcode
    FROM scarr
    WHERE carrid IN @lr_carrid
    INTO CORRESPONDING FIELDS OF TABLE @et_entityset.
  IF sy-subrc NE 0.
    RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception.
  ENDIF.

ENDMETHOD.

Code for Sorting

/sap/opu/odata/sap/ZJP_DEMO_SRV/CarrierSet?$orderby=Currency,CarrierID

*-- This is additional code after the data selection for sorting
  DATA(lt_tech_order) = io_tech_request_context->get_orderby( ).
  IF lt_tech_order IS NOT INITIAL.
    DATA(lt_otab) = VALUE abap_sortorder_tab(
                      FOR ls_otab IN lt_tech_order
                      ( name = to_upper( ls_otab-property )
                        descending = COND #( WHEN ls_otab-order = 'desc' THEN abap_true
                                             ELSE abap_false ) ) ).
    SORT et_entityset BY (lt_otab).
  ENDIF.

Code for Paging

/sap/opu/odata/sap/ZJP_DEMO_SRV/CarrierSet?$top=2&$skip=4

*-- Handle paging – provide number of records
  IF io_tech_request_context->has_inlinecount( ) = abap_true.
    es_response_context-inlinecount = lines( et_entityset ).
  ENDIF.

*-- Handle paging – keep only required records 
  DATA(lv_skip) = io_tech_request_context->get_skip( ).
  DATA(lv_top)  = io_tech_request_context->get_top( ).

  IF lv_skip IS NOT INITIAL OR lv_top IS NOT INITIAL.

    IF lv_top IS INITIAL.
      lv_top = lines( et_entityset ).
    ENDIF.

    IF lv_skip IS INITIAL.
      lv_skip = 1.
    ENDIF.

    DATA(lv_to) = lv_skip.
    DATA(lv_from) = lv_skip + lv_top + 1.

    IF lv_from LT lines( et_entityset ).
      DELETE et_entityset FROM lv_from.
    ENDIF.
    IF lv_to GT 0.
      DELETE et_entityset TO lv_to.
    ENDIF.
  ENDIF.

Query: Association – GET ENTITYSET

Any entity-set query operation can have two sources. It can be called directly or through association. So both the calling source should be handled.

When a query is called by Association, the association referential constraints field information is passed with it. Otherwise, filters can be passed.

The import parameter iv_source_name tells from where it is called.

/sap/opu/odata/sap/ZJP_DEMO_SRV/CarrierSet(‘DL’)/ToFlightSchedule

METHOD flightschedulese_get_entityset.

  DATA ls_keys TYPE zcl_zjp_demo_mpc=>ts_carrier.

  CASE io_tech_request_context->get_source_entity_type_name( ).
    WHEN 'Carrier'.  "Called from Association
      io_tech_request_context->get_converted_source_keys(
        IMPORTING es_key_values = ls_keys ).
      SELECT * FROM spfli INTO TABLE @et_entityset
        WHERE carrid = @ls_keys-carrid.
      IF sy-subrc NE 0.
        RAISE EXCEPTION TYPE /iwbep/cx_mgw_tech_exception.
      ENDIF.
  ENDCASE.

ENDMETHOD.

Function Import – Method to Implement: /IWBEP/IF_MGW_APPL_SRV_RUNTIME~EXECUTE_ACTION 

All function imports trigger the same method in the data provider class. The method has importing parameter iv_action_name which has the function import name as value.

This example depicts the scenario when a function import is created to get a number of flights for a carrier, and how the code can be implemented.

Here, it is important to note that the Function Import always returns a single entity or entity set. As it can pass back both entity and entity sets, a special method copy_data_to_ref is used to copy result data to er_data which is the exported data reference.

‘GetFlightNumbers’ is the name of the function import. The types for function import parameter gets automatically created in MPC class.

METHOD /iwbep/if_mgw_appl_srv_runtime~execute_action.

  DATA : ls_out  TYPE zcl_zjp_demo_mpc=>ts_flights,
         ls_pars TYPE zcl_zjp_demo_mpc=>ts_getflightnumbers.

  CASE io_tech_request_context->get_function_import_name( ).
    WHEN 'GetFlightNumbers'.
      io_tech_request_context->get_converted_parameters(
        IMPORTING es_parameter_values = ls_pars ).

      ls_out-carrierid = ls_pars-carrierid.
      SELECT COUNT( * ) FROM sflight
        INTO @ls_out-flightnos
        WHERE carrid = @ls_pars-carrierid.
      IF sy-subrc EQ 0.
        me->copy_data_to_ref( EXPORTING is_data = ls_out
                              CHANGING cr_data = er_data ).
      ELSE.
        RAISE EXCEPTION TYPE /iwbep/cx_mgw_tech_exception.
      ENDIF.
  ENDCASE.

ENDMETHOD.

The remaining CRUD operations can be implemented as below.

Create – CREATE ENTITY

The data to be created is fetched from io_data_provider.

METHOD carrierset_create_entity.

  DATA ls_header TYPE zcl_zjp_demo_mpc=>ts_carrier.
  io_data_provider->read_entry_data( IMPORTING es_data = ls_header ).

  INSERT scarr FROM ls_header.  "Business Logic to create record
  IF sy-subrc EQ 0.
    er_entity = ls_header.
  ELSE.
    RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception.
  ENDIF.

ENDMETHOD.

Update – UPDATE ENTITY

The data to be updated is fetched from object io_data_provider.

METHOD carrierset_update_entity.

  DATA ls_header TYPE zcl_zjp_demo_mpc=>ts_carrier.
  io_data_provider->read_entry_data( IMPORTING es_data = ls_header ).

  UPDATE scarr FROM ls_header.  "Business Logic to create record
  IF sy-subrc EQ 0.
    er_entity = ls_header.
  ELSE.
    RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception.
  ENDIF.

ENDMETHOD.

Delete – DELETE ENTITY

For deletion, the keys are sent in it_key_tab. A single entry is deleted at a time.

METHOD carrierset_delete_entity.

  TRY.
      DATA(lt_keys) = io_tech_request_context->get_keys( ).
      DATA(lv_carrid) = VALUE s_carr_id( lt_keys[ 1 ]-value ).
      DELETE FROM scarr WHERE carrid = @lv_carrid.
    CATCH cx_sy_itab_line_not_found.
      RAISE EXCEPTION TYPE /iwbep/cx_mgw_tech_exception.
  ENDTRY.

ENDMETHOD.

These are code examples that work only when the entity names and properties match exactly, so when you create a service, use these code samples as a reference and implement the service.

Visit OData Development in SAP to explore all articles on OData.


If you like the content, please subscribe…

Join 2,648 other followers