Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

OData V4 code based implementation I (basic interface, read access)

Introduction

In this first blog about OData V4 code based implementation I want to show how to build a simple service that shows sales order header data alongside with its items.

The service implementation will leverage two CDS consumption views ZE2E001_C_SalesOrder and ZE2E001_C_SalesOrderItem that read their data from the CDS interface views SEPM_I_SalesOrder_E and SEPM_I_SalesOrderItem_E respectively.

Please note:

By leveraging CDS views you will be able to reuse the data modelling part to a large extent in the new ABAP programming model once this supports OData V4 even if you are currently using AS ABAP 750. In addition your service will support most query options out of the box by using the code samples shown in this blog.

In addition to the data provider class and the model provider class which are mandatory for an OData V4 service we will in create three additional repository objects for convenience (an interface) and as a best practice (an error and an message class).

The interface is used to define types and constants that are used in both, the data provider class as well as in the model provider class. Examples are the types of the two CDS consumption views mentioned above or the ABAP internal and external names of our entity types, entity sets, navigation property, etc.

How to test the service

This sample service has been implemented for your convenience in the new demo system ES5. System details can be found in my following blog.New SAP Gateway Demo System available.

The $metadata document of the service can than be called via the following URL

https://sapes5.sapdevcenter.com/sap/opu/odata4/sap/ze2e001/default/sap/ze2e001_salesorder/0001/$metadata?sap-statistics=true

Using the implementation of the basic interface methods our service already supports the following requests:

Read a single sales order

https://sapes5.sapdevcenter.com/sap/opu/odata4/sap/ze2e001/default/sap/ze2e001_salesorder/0001/SalesOrder(‘500000000’)?sap-ds-debug=true

Get the first three sales orders

https://sapes5.sapdevcenter.com/sap/opu/odata4/sap/ze2e001/default/sap/ze2e001_salesorder/0001/SalesOrder?$top=3&sap-ds-debug=true

Navigate from the sales order head to the items and filter the result set for items with a grossamount larger than 1100$.

https://sapes5.sapdevcenter.com/sap/opu/odata4/sap/ze2e001/default/sap/ze2e001_salesorder/0001/SalesOrder(‘500000000’)/_Item?$filter=Grossamountintransaccurrency ge 1100 and Transactioncurrency eq ‘USD’&sap-ds-debug=true

Read a sales order header and expand the items therby filtering on the expanded items.

https://sapes5.sapdevcenter.com/sap/opu/odata4/sap/ze2e001/default/sap/ze2e001_salesorder/0001/SalesOrder(‘500000000’)?$expand=_Item($filter=Grossamountintransaccurrency ge 1100 and Transactioncurrency eq ‘USD’)&sap-ds-debug=true

Repository objects

(listed in the order of implementation)

In the end our ABAP project will contain the following 7 repository objects:

 # name role Details:
 1 ZE2E001_C_SalesOrder CDS consumption view – sales order reads data from SEPM_I_SALESORDER_E
 2 ZE2E001_C_SalesOrderItem CDS consumption view – sales order item reads data from SEPM_I_SALESORDERITEM_E
 3 zif_e2e001_odata_v4_so_types Interface  is used by the data provider class as well as the model provider class
4 zcm_e2e001odatav4_so Message class contains 2 messages
5 zcx_e2e001_odata_v4_so Exception class inherits from:
/iwbep/cx_gateway
 6 zcl_e2e001_odata_v4_so_model Model provider class inherits from:
/iwbep/cl_v4_abs_model_prov
 7 zcl_e2e001_odata_v4_so_data Data provider class inherits from:
/iwbep/cl_v4_abs_data_provider

And our project explorer structure in the ABAP Developmen Tools in Eclipse will look like follows:

 

CDS consumption views ZE2E001_C_SalesOrder and ZE2E001_C_SalesOrderItem

We will start to create two CDS consumption views on top of two existing CDS Interface views that are delivered by SAP as part of the EPM demo data model.

@AbapCatalog.sqlViewName: 'ZE2E001CSO'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'OData V4 Demo Service - SalesOrder Root'
define view ZE2E001_C_SalesOrder
  as select from SEPM_I_SalesOrder_E

  association [1..*] to ZE2E001_C_SALESORDERITEM as _Item on _Item.SalesOrder = $projection.SalesOrder
  association [0..*] to SEPM_I_SalesOrderText_E  as _Text on $projection.SalesOrder = _Text.SalesOrder


{
      //SEPM_I_SalesOrder_E
  key SalesOrder,
      CreatedByUser,
      CreationDateTime,
      LastChangedByUser,
      LastChangedDateTime,
      IsCreatedByBusinessPartner,
      IsLastChangedByBusinessPartner,
      Customer,
      CustomerContact,
      TransactionCurrency,
      @Semantics.amount.currencyCode: 'TransactionCurrency'
      GrossAmountInTransacCurrency,
      @Semantics.amount.currencyCode: 'TransactionCurrency'
      NetAmountInTransactionCurrency,
      @Semantics.amount.currencyCode: 'TransactionCurrency'
      TaxAmountInTransactionCurrency,
      SalesOrderLifeCycleStatus,
      SalesOrderBillingStatus,
      SalesOrderDeliveryStatus,
      SalesOrderOverallStatus,
      Opportunity,
      SalesOrderPaymentMethod,
      SalesOrderPaymentTerms,
      BillToParty,
      BillToPartyRole,
      ShipToParty,
      ShipToPartyRole,
      /* Associations */
      _Item,
      _Text

}
@AbapCatalog.sqlViewName: 'ZE2E001CSOI'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'OData V4 Demo Service - SalesOrder Items'
define view Ze2e001_C_Salesorderitem 
 as select from SEPM_I_SalesOrderItem_E
  association [0..*] to SEPM_I_SalesOrderItemText_E as _Text on  $projection.SalesOrder     = _Text.SalesOrder
                                                             and $projection.SalesOrderItem = _Text.SalesOrderItem
{
      //SEPM_I_SalesOrderItem_E
  key SalesOrder,
  key SalesOrderItem,
      Product,
      TransactionCurrency,
      @Semantics.amount.currencyCode: 'TransactionCurrency'
      GrossAmountInTransacCurrency,
      @Semantics.amount.currencyCode: 'TransactionCurrency'
      NetAmountInTransactionCurrency,
      @Semantics.amount.currencyCode: 'TransactionCurrency'
      TaxAmountInTransactionCurrency,
      ProductAvailabilityStatus,
      OpportunityItem,
      /* Associations */
      _Text
} 
 
 

Please note that you have to activate both views together (Ctrl+Shift+F3). Afterwards you can select the sales order view and use (F8) to get a preview of the data.

Interface – zif_e2e001_odata_v4_so_types

In the second step we create an interface zif_e2e001_odata_v4_so_types that will be used to store data types and constants that will be used in both, the model provider and the data provider class.

It can also be used for other service implementations that reside in the same service group and that might want to leverage the information being defined in the interface to implement cross service references.

interface zif_e2e001_odata_v4_so_types
  public .
  types:
    begin of gty_cds_views,
      salesorderitem type ze2e001_c_salesorderitem,
      salesorder     type ze2e001_c_salesorder,
    end of gty_cds_views.

  types: begin of gty_s_so_soi .
      include type gty_cds_views-salesorder.
  types:
    _item type standard table of gty_cds_views-salesorderitem with default key,
    end of gty_s_so_soi .

  types:
    begin of gt_key_range,
      salesorder   type range of gty_cds_views-salesorder-salesorder,
      itemposition type range of gty_cds_views-salesorderitem-salesorderitem,
    end of gt_key_range.

  constants:

    begin of gcs_cds_view_names,
      salesorderitem type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'SEPM_ODATA_C_SALESORDERITEM',
      salesorder     type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'SEPM_ODATA_C_SALESORDER',
    end of gcs_cds_view_names,

    begin of gcs_entity_type_names,
      begin of internal,
        salesorderitem type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'SEPM_ODATA_C_SALESORDERITEM',
        salesorder     type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'SEPM_ODATA_C_SALESORDER',
      end of internal,
      begin of edm,
        salesorderitem type /iwbep/if_v4_med_element=>ty_e_med_edm_name value 'SalesOrderItemType',
        salesorder     type /iwbep/if_v4_med_element=>ty_e_med_edm_name value 'SalesOrderType',
      end of edm,
    end of gcs_entity_type_names,

    begin of gcs_entity_set_names,
      begin of internal,
        salesorderitem type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'SEPM_ODATA_C_SALESORDERITEM',
        salesorder     type /iwbep/if_v4_med_element=>ty_e_med_internal_name value 'SEPM_ODATA_C_SALESORDER',
      end of internal,
      begin of edm,
        salesorderitem type /iwbep/if_v4_med_element=>ty_e_med_edm_name value 'SalesOrderItem',
        salesorder     type /iwbep/if_v4_med_element=>ty_e_med_edm_name value 'SalesOrder',
      end of edm,
    end of gcs_entity_set_names ,

    begin of gcs_nav_prop_names,
      begin of internal,
        salesorder_to_items type /iwbep/if_v4_med_element=>ty_e_med_internal_name value '_ITEM',
      end of internal,
      begin of edm,
        salesorder_to_items type /iwbep/if_v4_med_element=>ty_e_med_edm_name value '_Item',
      end of edm,
    end of gcs_nav_prop_names.

endinterface.

Message class

The message class contains three messages that are used in the exception class to raise error messages specific to your OData service.

 Message Number Short Text  Self Explanatory
050  Entity &1 not found in entiy set &2  X
051  filter, top or navigation must be used to access entity set &1  X
052  Entity key &1 does not match key in payload  X

The message class will look like follows in ADT.

Exception class

It is a good practice to use your own exception classes. The class inherits from the basic exception class provided by the SAP Gateway framework and contains exceptions that are raised if the following errors occur:

  • the application tries to read header data of a sales order that does not exist
  • the application tries to read a list of sales orders or sales order items without providing any query options to avoid a full table scan

Please note:

SAP Fiori applications for example by default use query options such as $top and $skip. A single read on a sales order will also use the correct key that has been retrieved beforehand.

class zcx_e2e001_odata_v4_so definition
  public
  inheriting from /iwbep/cx_gateway
  final
  create public .

  public section.

    constants:
      begin of entity_not_found,
        msgid type symsgid value 'ZCM_E2E001ODATAV4_SO',
        msgno type symsgno value '050',
        attr1 type scx_attrname value 'ENTITY_KEY',
        attr2 type scx_attrname value 'EDM_ENTITY_SET_NAME',
        attr3 type scx_attrname value '',
        attr4 type scx_attrname value '',
      end of entity_not_found.
    constants:
      begin of use_filter_top_or_nav,
        msgid type symsgid value 'ZCM_E2E001ODATAV4_SO',
        msgno type symsgno value '051',
        attr1 type scx_attrname value 'EDM_ENTITY_SET_NAME',
        attr2 type scx_attrname value '',
        attr3 type scx_attrname value '',
        attr4 type scx_attrname value '',
      end of use_filter_top_or_nav .
    constants:
      begin of entity_keys_do_not_match,
        msgid type symsgid value 'ZCM_E2E001ODATAV4_SO',
        msgno type symsgno value '052',
        attr1 type scx_attrname value 'EDM_ENTITY_KEY',
        attr2 type scx_attrname value '',
        attr3 type scx_attrname value '',
        attr4 type scx_attrname value '',
      end of entity_keys_do_not_match .

    data: entity_set_name     type /iwbep/if_v4_med_element=>ty_e_med_internal_name,
          edm_entity_set_name type /iwbep/if_v4_med_element=>ty_e_med_edm_name,
          user_name           type syuname,
          entity_key          type string read-only.



    methods constructor
      importing
        !textid              like if_t100_message=>t100key optional
        !previous            like previous optional
        !exception_category  type ty_exception_category default gcs_excep_categories-provider
        !http_status_code    type ty_http_status_code default gcs_http_status_codes-sv_internal_server_error
        !is_for_user         type abap_bool default abap_true
        !message_container   type ref to /iwbep/if_v4_message_container optional
        !sap_note_id         type ty_sap_note_id optional
        !edm_entity_set_name type /iwbep/if_v4_med_element=>ty_e_med_edm_name optional
        !entity_set_name     type /iwbep/if_v4_med_element=>ty_e_med_internal_name optional
        !user_name           type syuname optional
        !entity_key          type string optional.
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.



CLASS ZCX_E2E001_ODATA_V4_SO IMPLEMENTATION.


METHOD constructor ##ADT_SUPPRESS_GENERATION.
    CALL METHOD super->constructor
      EXPORTING
        previous           = previous
        exception_category = exception_category
        http_status_code   = http_status_code
        is_for_user        = is_for_user
        message_container  = message_container
        sap_note_id        = sap_note_id.

    me->entity_key = entity_key.
    me->user_name = user_name.
    me->entity_set_name = entity_set_name .
    me->edm_entity_set_name = edm_entity_set_name.

    CLEAR me->textid.
    IF textid IS INITIAL.
      if_t100_message~t100key = if_t100_message=>default_textid.
    ELSE.
      if_t100_message~t100key = textid.
    ENDIF.
  ENDMETHOD.
ENDCLASS.

Model provider class – zcl_e2e001_odata_v4_so_model – coding explained

Now that we have worked on the prerequisites we can finally start to implement our model provider class. As in V2 the model provider class contains a DEFINE method that is amongst others responsible to create the definition of our entity types and entity sets.

The define method will call two entity type specific methods define_salesorder and define_salesorderitem that create the entity types and entity sets for sales order header and items.

  method /iwbep/if_v4_mp_basic~define.
    define_salesorder( io_model ).
    define_salesorderitem( io_model ).
  endmethod.

The most important method call is to call the method create_entity_type_by_struct which works similar to the DDIC import in SEGW in V2.

Please note that we make use of our interface here since the ABAP internal name of the entity type is retrieved via the constant gcs_entity_type_names-internal-salesorder and the DDIC type from the type gty_cds_views-salesorder.

method define_salesorder.
    data: lt_primitive_properties type /iwbep/if_v4_med_element=>ty_t_med_prim_property,
          lo_entity_set           type ref to /iwbep/if_v4_med_entity_set,
          lo_nav_prop             type ref to /iwbep/if_v4_med_nav_prop,
          lo_entity_type          type ref to /iwbep/if_v4_med_entity_type,
          lv_referenced_cds_view  type gty_cds_views-salesorder  . 
          " As internal ABAP name we use the name of the CDS view


    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    "   Create entity type
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_entity_type = io_model->create_entity_type_by_struct(
                      exporting
                        iv_entity_type_name          = gcs_entity_type_names-internal-salesorder
                        is_structure                 = lv_referenced_cds_view
                        iv_add_conv_to_prim_props    = abap_true
                        iv_add_f4_help_to_prim_props = abap_true
                        iv_gen_prim_props            = abap_true ).

Here we also set the key field of our entity type.

    lo_primitive_property = lo_entity_type->get_primitive_property( 'SALESORDER' ).
    lo_primitive_property->set_is_key( ).

And we create a navigation property in our entity type that points to the sales order items. Also here we use our interface to retrieve the ABAP internal and the external name of the navigation property gcs_nav_prop_names-internal-salesorder_to_items and  gcs_nav_prop_names-edm-salesorder_to_items as well as the ABAP internal name of the target entity type gcs_entity_type_names-internal-salesorderitem.

lo_nav_prop = lo_entity_type->create_navigation_property( gcs_nav_prop_names-internal-salesorder_to_items ).
lo_nav_prop->set_edm_name( gcs_nav_prop_names-edm-salesorder_to_items ).

lo_nav_prop->set_target_entity_type_name( gcs_entity_type_names-internal-salesorderitem ).
lo_nav_prop->set_target_multiplicity( /iwbep/if_v4_med_element=>gcs_med_nav_multiplicity-to_many_optional ).
lo_nav_prop->set_on_delete_action( /iwbep/if_v4_med_element=>gcs_med_on_delete_action-none ).

Model provider class – zcl_e2e001_odata_v4_so_model – Complete source code

The complete code of the model provider classs can be found here:

class zcl_e2e001_odata_v4_so_model definition
  public
  inheriting from /iwbep/cl_v4_abs_model_prov
  final
  create public .

  public section.
    interfaces zif_e2e001_odata_v4_so_types.

    methods /iwbep/if_v4_mp_basic~define redefinition.
  protected section.
  private section.

    aliases gty_cds_views
      for zif_e2e001_odata_v4_so_types~gty_cds_views.
    aliases gcs_entity_set_names
      for zif_e2e001_odata_v4_so_types~gcs_entity_set_names .
    aliases gcs_entity_type_names
      for zif_e2e001_odata_v4_so_types~gcs_entity_type_names .
    aliases gcs_nav_prop_names
      for zif_e2e001_odata_v4_so_types~gcs_nav_prop_names.

    methods define_salesorder
      importing
        io_model type ref to /iwbep/if_v4_med_model
      raising
        /iwbep/cx_gateway .
    methods define_salesorderitem
      importing
        io_model type ref to /iwbep/if_v4_med_model
      raising
        /iwbep/cx_gateway .
endclass.



class zcl_e2e001_odata_v4_so_model implementation.


  method /iwbep/if_v4_mp_basic~define.
    define_salesorder( io_model ).
    define_salesorderitem( io_model ).
  endmethod.


  method define_salesorder.
    data: lt_primitive_properties type /iwbep/if_v4_med_element=>ty_t_med_prim_property,
          lo_entity_set           type ref to /iwbep/if_v4_med_entity_set,
          lo_nav_prop             type ref to /iwbep/if_v4_med_nav_prop,
          lo_entity_type          type ref to /iwbep/if_v4_med_entity_type,
          lv_referenced_cds_view  type gty_cds_views-salesorder  . " As internal ABAP name we use the name of the CDS view


    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    "   Create entity type
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_entity_type = io_model->create_entity_type_by_struct(
                      exporting
                        iv_entity_type_name          = gcs_entity_type_names-internal-salesorder
                        is_structure                 = lv_referenced_cds_view
                        iv_add_conv_to_prim_props    = abap_true
                        iv_add_f4_help_to_prim_props = abap_true
                        iv_gen_prim_props            = abap_true ).

    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    " Set external EDM name for entity type
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_entity_type->set_edm_name( gcs_entity_type_names-edm-salesorder ).



    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    " Rename external EDM names of properties so that CamelCase notation is used
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_entity_type->get_primitive_properties( importing et_property = lt_primitive_properties ).

    loop at lt_primitive_properties into data(lo_primitive_property).
      lo_primitive_property->set_edm_name( to_mixed( val = lo_primitive_property->get_internal_name( ) ) ).
    endloop.


    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    " Set key field(s)
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_primitive_property = lo_entity_type->get_primitive_property( 'SALESORDER' ).
    lo_primitive_property->set_is_key( ).


    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    "   Create navigation property
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_nav_prop = lo_entity_type->create_navigation_property( gcs_nav_prop_names-internal-salesorder_to_items ).
    lo_nav_prop->set_edm_name( gcs_nav_prop_names-edm-salesorder_to_items ).

    lo_nav_prop->set_target_entity_type_name( gcs_entity_type_names-internal-salesorderitem ).
    lo_nav_prop->set_target_multiplicity( /iwbep/if_v4_med_element=>gcs_med_nav_multiplicity-to_many_optional ).
    lo_nav_prop->set_on_delete_action( /iwbep/if_v4_med_element=>gcs_med_on_delete_action-none ).


    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    "   Create entity set
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_entity_set = lo_entity_type->create_entity_set( gcs_entity_set_names-internal-salesorder ).
    lo_entity_set->set_edm_name( gcs_entity_set_names-edm-salesorder ).

    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    " Add the binding of the navigation path
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_entity_set->add_navigation_prop_binding( iv_navigation_property_path = conv #( gcs_nav_prop_names-internal-salesorder_to_items )
                                                iv_target_entity_set        = gcs_entity_set_names-internal-salesorderitem ).

  endmethod.


  method define_salesorderitem.
    data: lo_entity_type          type ref to /iwbep/if_v4_med_entity_type,
          lo_entity_set           type ref to /iwbep/if_v4_med_entity_set,
          lt_primitive_properties type /iwbep/if_v4_med_element=>ty_t_med_prim_property,
          lv_referenced_cds_view  type gty_cds_views-salesorderitem  . " As internal ABAP name we use the name of the CDS view


    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    " Create entity type
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_entity_type = io_model->create_entity_type_by_struct(
                      exporting
                        iv_entity_type_name          = gcs_entity_type_names-internal-salesorderitem
                        is_structure                 = lv_referenced_cds_view
                        iv_add_conv_to_prim_props    = abap_true
                        iv_add_f4_help_to_prim_props = abap_true
                        iv_gen_prim_props            = abap_true ).

    lo_entity_type->set_edm_name( gcs_entity_type_names-edm-salesorderitem ).


    lo_entity_type->get_primitive_properties( importing et_property = lt_primitive_properties ).

    loop at lt_primitive_properties into data(lo_primitive_property).
      lo_primitive_property->set_edm_name( to_mixed( val = lo_primitive_property->get_internal_name( ) ) ).
    endloop.

    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    " Set key field(s)
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_primitive_property = lo_entity_type->get_primitive_property( 'SALESORDER' ).
    lo_primitive_property->set_is_key( ).

    lo_primitive_property = lo_entity_type->get_primitive_property( 'SALESORDERITEM'  ).
    lo_primitive_property->set_is_key( ).


    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    "   Create entity set
    """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
    lo_entity_set = lo_entity_type->create_entity_set( gcs_entity_set_names-internal-salesorderitem ).
    lo_entity_set->set_edm_name( gcs_entity_set_names-edm-salesorderitem ).

  endmethod.
endclass.

Data Provider class

The data provider class contains the generic implementation of the basic interface methods for read access. These are the following methods.

  • /iwbep/if_v4_dp_basic~read_entity_list
  • /iwbep/if_v4_dp_basic~read_entity
  • /iwbep/if_v4_dp_basic~read_ref_target_key_data_list

that  have to be redefined.

In addition it contains entity set specific impelmentations for both entity sets Salesorder and Salesorderitems. These are the following methods:

  • read_entity_salesorder
  • read_entity_salesorderitem
  • read_list_salesorder
  • read_list_salesorderitem
  • read_ref_key_list_salesorder

I will explain the functionality of these methods in the following. The complete code is shown in the following section.

/iwbep/if_v4_dp_basic~read_entity_list.

We start with the method /iwbep/if_v4_dp_basic~read_entity_list.which is for example called if a client accesses an entity set with a Get request, for example GET …./Salesorder.

We first have to retrieve the todo list via the io_request object.

io_request->get_todos( importing es_todo_list = ls_todo_list ).

With this todo list we can check whether our service implementation has to retrieve parameters such as $top or $skip.

    " $skip / $top handling
    if ls_todo_list-process-skip = abap_true.
      ls_done_list-skip = abap_true.
      io_request->get_skip( importing ev_skip = lv_skip ).
    endif.
    if ls_todo_list-process-top = abap_true.
      ls_done_list-top = abap_true.
      io_request->get_top( importing ev_top = lv_top ).
    endif.

The implementation /iwbep/if_v4_dp_basic~read_entity_list contains generic coding to retrieve the following query options:

  • $orderby
  • $select
  • $filter
  • $skip
  • $top

for all entity sets, so that they can be passed as parameters to the entity set specific private methods.read_list_salesorder and read_list_salesorderitem,

Please note that the name of the entity set is retrieved via the method io_request->get_entity_set and checked against the constant gcs_entity_set_names-internal-salesorder that has been defined in our interface.

    io_request->get_entity_set( importing ev_entity_set_name = lv_entityset_name ).

    case lv_entityset_name.

      when gcs_entity_set_names-internal-salesorder.

        read_list_salesorder(
          exporting
            io_request        = io_request
            io_response       = io_response
            iv_orderby_string = lv_orderby_string
            iv_select_string  = lv_select_string
            iv_where_clause   = lv_where_clause
            iv_skip           = lv_skip
            iv_top            = lv_top
            is_done_list      = ls_done_list ).

read_list_salesorder

The entity type specifc methods read_list_salesorder and read_list_salesorderitem both start with a definition of entity type specific data types that will hold the data being returned and (if being provided) the list of key fields retrieved via navigation.

lt_key_range_salesorder type zif_e2e001_odata_v4_so_types=>gt_key_range-salesorder,
ls_key_range_salesorder type line of zif_e2e001_odata_v4_so_types=>gt_key_range-salesorder,
lt_salesorder type standard table of gty_cds_views-salesorder,
lt_key_salesorder  type standard table of gty_cds_views-salesorder.

"generic data types
data: ls_todo_list type /iwbep/if_v4_requ_basic_list=>ty_s_todo_list,
      ls_done_list type /iwbep/if_v4_requ_basic_list=>ty_s_todo_process_list,
      lv_count     type i,
      lv_max_index type i.

The code also uses our exception class since an exception will be raised if  the client would try to send a Get request   GET…/Salesorder without limiting the result set using either $filter, navigation or $top.

    if  ls_todo_list-process-filter = abap_false
    and ls_todo_list-process-key_data = abap_false
    and iv_top = 0.
      raise exception type zcx_e2e001_odata_v4_so
        exporting
          textid              = zcx_e2e001_odata_v4_so=>use_filter_top_or_nav
          http_status_code    = zcx_e2e001_odata_v4_so=>gcs_http_status_codes-bad_request
          edm_entity_set_name = gcs_entity_set_names-edm-salesorder.
    endif.

In the end we are able to run a generic OpenSQL statement against our CDS view which supports most query options out of the box.

An exception is for example $skip since offset is only supported in OpenSQL as of AS ABAP 751. If you are using AS ABAP 750  you have to work with first selecting  lv_max_index = iv_top + iv_skip table entries and later delete those from the response that have to be skipped (see complete coding at the end of this section)

Please note that the V4 API uses the method io_response->set_busi_data to return the business data to the SAP Gateway framework.

      "OFFSET is only supported as of NW751
      select (iv_select_string) from ze2e001_c_salesorder
      where (iv_where_clause)
      and   salesorder in @lt_key_range_salesorder
      order by (iv_orderby_string)
      into corresponding fields of table @lt_salesorder
      up to @iv_top rows
      offset @iv_skip.

      io_response->set_busi_data( it_busi_data = lt_salesorder ).

/iwbep/if_v4_dp_basic~read_entity

We continue with the method /iwbep/if_v4_dp_basic~read_entity which is for example called if a client accesses a single entity with a Get request, for example
GET …./Salesorder(‘50000000’).

This time we first retrieve the name of the entity set that has been accessed using the method io_request->get_entity_set.

    io_request->get_entity_set( importing ev_entity_set_name = lv_entityset_name ).

    case lv_entityset_name.

      when gcs_entity_set_names-internal-salesorder.
        read_entity_salesorder(
          exporting
            io_request  = io_request
            io_response = io_response ).

read_entiy_salesorder and  read_entiy_salesorderitem

Here we have to retrieve the todo list via the io_request object. This todo list will contain different flags than the todo list used by the read_entity_list method. We can for example retrieve the key from the incoming Get request in the ABAP internal representation. We then can simply read the data via a select single statement from the CDS view. If data has been found it is returned to the framework via the method io_response->set_busi_data. If no table entry is found we raise an error message.using our exception class.

Please note that our coding is very generic so that  it can easily be adapted to the CDS viewyou will use.

  method read_entity_salesorder.

    "entity type specific data types
    data: ls_salesorder         type gty_cds_views-salesorder,
          ls_key_salesorder     type gty_cds_views-salesorder,
          lv_salesorder_key_edm type string,
          lv_helper_int         type i.
    "generic data types
    data: ls_todo_list type /iwbep/if_v4_requ_basic_read=>ty_s_todo_list,
          ls_done_list type /iwbep/if_v4_requ_basic_read=>ty_s_todo_process_list.

    io_request->get_todos( importing es_todo_list = ls_todo_list ).

    " read the key data
    io_request->get_key_data( importing es_key_data = ls_key_salesorder ).
    ls_done_list-key_data = abap_true.

    select single * from ze2e001_c_salesorder
    into corresponding fields of @ls_salesorder
    where salesorder = @ls_key_salesorder-salesorder.

    if ls_salesorder is not initial.
      io_response->set_busi_data( is_busi_data = ls_salesorder ).
    else.
      "Move data first to an integer to remove leading zeros from the response
      lv_salesorder_key_edm = lv_helper_int = ls_key_salesorder-salesorder.

      raise exception type zcx_e2e001_odata_v4_so
        exporting
          textid              = zcx_e2e001_odata_v4_so=>entity_not_found
          http_status_code    = zcx_e2e001_odata_v4_so=>gcs_http_status_codes-not_found
          edm_entity_set_name = gcs_entity_set_names-edm-salesorder
          entity_key          = lv_salesorder_key_edm.

    endif.

    " Report list of request options handled by application
    io_response->set_is_done( ls_done_list ).
  endmethod.

/iwbep/if_v4_dp_basic~read_ref_target_key_data_list

This method is called by the framework to determine the list of key fields in case navigation is used.

  method /iwbep/if_v4_dp_basic~read_ref_target_key_data_list.

    data: lv_source_entity_name type /iwbep/if_v4_med_element=>ty_e_med_internal_name.


    io_request->get_source_entity_type( importing ev_source_entity_type_name = lv_source_entity_name ).

    case lv_source_entity_name.

      when gcs_entity_type_names-internal-salesorder.
        read_ref_key_list_salesorder(
           exporting
            io_request  = io_request
            io_response = io_response ).

      when others.
        super->/iwbep/if_v4_dp_basic~read_ref_target_key_data_list(
          exporting
            io_request  = io_request
            io_response = io_response ).

    endcase.

  endmethod.

Here we check for the source entity type io_request->get_source_entity_type and call an entity type specific method read_ref_key_list_salesorder.

read_ref_key_list_salesorder

This method retrieves the ABAP internal value of the key field of our source entity (here 0500000000), if a requests such as

SalesOrder(‘500000000’)/_Item or

SalesOrder(‘500000000’)?$expand=_Item 

have been called.

Please  note that we are following the naming convention that uses the same name for the navigation property that is used as an association in the CDS view.

After having checked the ABAP internal name of the navigation property (here: _ITEM) that is defined in the interface we retrieve the key fields (salesorder and salesorderitem) from the CDS view ze2e001_c_salesorderitem where the key field salesorder is equal to the source key field (here 0500000000).

The key data is sent back to the framework which provides it to the read_entity_list method.

  method read_ref_key_list_salesorder.

    "entity type specific data types
    data: ls_salesorder_key_data     type  gty_cds_views-salesorder,
          lt_salesorderitem_key_data type standard table of gty_cds_views-salesorderitem,
          ls_todo_list               type /iwbep/if_v4_requ_basic_ref_l=>ty_s_todo_list.
    "generic data types
    data: ls_done_list         type /iwbep/if_v4_requ_basic_ref_l=>ty_s_todo_process_list,
          lv_nav_property_name type /iwbep/if_v4_med_element=>ty_e_med_internal_name.

    " Get the request options the application should/must handle
    io_request->get_todos( importing es_todo_list = ls_todo_list ).

    if ls_todo_list-process-source_key_data = abap_true.
      io_request->get_source_key_data( importing es_source_key_data =  ls_salesorder_key_data ).
      ls_done_list-source_key_data = abap_true.
    endif.

    io_request->get_navigation_prop( importing ev_navigation_prop_name = lv_nav_property_name ).

    case lv_nav_property_name.
      when gcs_nav_prop_names-internal-salesorder_to_items.

        select salesorder , salesorderitem from ze2e001_c_salesorderitem
        into corresponding fields of table @lt_salesorderitem_key_data
        where salesorder = @ls_salesorder_key_data-salesorder.

        io_response->set_target_key_data( lt_salesorderitem_key_data ).

      when others.

        raise exception type zcx_e2e001_odata_v4_so
          exporting
            http_status_code = zcx_e2e001_odata_v4_so=>gcs_http_status_codes-sv_not_implemented.

    endcase.

    " Report list of request options handled by application
    io_response->set_is_done( ls_done_list ).


  endmethod.

Data provider class – zcl_e2e001_odata_v4_so_data – Complete source code

class zcl_e2e001_odata_v4_so_data definition
  public
  inheriting from /iwbep/cl_v4_abs_data_provider
  final
  create public .

  public section.
    interfaces zif_e2e001_odata_v4_so_types.

    methods /iwbep/if_v4_dp_basic~read_entity redefinition .
    methods /iwbep/if_v4_dp_basic~read_entity_list redefinition.
    methods /iwbep/if_v4_dp_basic~read_ref_target_key_data_list redefinition .
    methods /iwbep/if_v4_dp_basic~create_entity redefinition.
    methods /iwbep/if_v4_dp_basic~update_entity redefinition.
    methods /iwbep/if_v4_dp_basic~delete_entity redefinition.

  protected section.
  private section.
    aliases gcs_entity_set_names
       for zif_e2e001_odata_v4_so_types~gcs_entity_set_names .
    aliases gcs_entity_type_names
      for zif_e2e001_odata_v4_so_types~gcs_entity_type_names .
    aliases gty_cds_views
      for zif_e2e001_odata_v4_so_types~gty_cds_views.
    aliases gcs_nav_prop_names
      for zif_e2e001_odata_v4_so_types~gcs_nav_prop_names.

    methods read_list_salesorder
      importing
        io_request        type ref to /iwbep/if_v4_requ_basic_list
        io_response       type ref to /iwbep/if_v4_resp_basic_list
        iv_orderby_string type string
        iv_where_clause   type string
        iv_select_string  type string
        iv_skip           type i
        iv_top            type i
        is_done_list      type /iwbep/if_v4_requ_basic_list=>ty_s_todo_process_list
      raising
        /iwbep/cx_gateway.
    methods read_entity_salesorder
      importing
        io_request  type ref to /iwbep/if_v4_requ_basic_read
        io_response type ref to /iwbep/if_v4_resp_basic_read
      raising
        /iwbep/cx_gateway.
    methods read_ref_key_list_salesorder
      importing
        io_request  type ref to /iwbep/if_v4_requ_basic_ref_l
        io_response type ref to /iwbep/if_v4_resp_basic_ref_l
      raising
        /iwbep/cx_gateway.

    methods read_list_salesorderitem
      importing
        io_request        type ref to /iwbep/if_v4_requ_basic_list
        io_response       type ref to /iwbep/if_v4_resp_basic_list
        iv_orderby_string type string
        iv_where_clause   type string
        iv_select_string  type string
        iv_skip           type i
        iv_top            type i
        is_done_list      type /iwbep/if_v4_requ_basic_list=>ty_s_todo_process_list
      raising
        /iwbep/cx_gateway.

*    METHODS read_list_adv_salesorderitem
*      IMPORTING
*        io_request   TYPE REF TO /iwbep/if_v4_requ_adv_list
*        io_response  TYPE REF TO /iwbep/if_v4_resp_adv_list
*        is_done_list TYPE /iwbep/if_v4_requ_adv_list=>ty_s_todo_process_list
*      RAISING
*        /iwbep/cx_gateway.

    methods read_entity_salesorderitem
      importing
        io_request  type ref to /iwbep/if_v4_requ_basic_read
        io_response type ref to /iwbep/if_v4_resp_basic_read
      raising
        /iwbep/cx_gateway.
    methods create_salesorder
      importing
        io_request  type ref to /iwbep/if_v4_requ_basic_create
        io_response type ref to /iwbep/if_v4_resp_basic_create
      raising
        /iwbep/cx_gateway.
    methods create_salesorderitem
      importing
        io_request  type ref to /iwbep/if_v4_requ_basic_create
        io_response type ref to /iwbep/if_v4_resp_basic_create
      raising
        /iwbep/cx_gateway.
    methods update_salesorder
      importing
        io_request  type ref to /iwbep/if_v4_requ_basic_update
        io_response type ref to /iwbep/if_v4_resp_basic_update
      raising
        /iwbep/cx_gateway.

endclass.



class zcl_e2e001_odata_v4_so_data implementation.

  method /iwbep/if_v4_dp_basic~create_entity.
    data: lv_entity_type_name type /iwbep/if_v4_med_element=>ty_e_med_internal_name.


    io_request->get_entity_type( importing ev_entity_type_name = lv_entity_type_name ).

    case lv_entity_type_name.

      when gcs_entity_type_names-internal-salesorder.
        create_salesorder(
            io_request  = io_request
            io_response = io_response ).

      when gcs_entity_type_names-internal-salesorderitem.
        create_salesorderitem(
            io_request  = io_request
            io_response = io_response ).

      when others.

        super->/iwbep/if_v4_dp_basic~create_entity(
            exporting
              io_request  = io_request
              io_response = io_response ).

    endcase.

  endmethod.

  method /iwbep/if_v4_dp_basic~delete_entity.

  endmethod.

  method /iwbep/if_v4_dp_basic~read_entity.

    data: lv_entityset_name type /iwbep/if_v4_med_element=>ty_e_med_internal_name.


    io_request->get_entity_set( importing ev_entity_set_name = lv_entityset_name ).

    case lv_entityset_name.

      when gcs_entity_set_names-internal-salesorder.
        read_entity_salesorder(
          exporting
            io_request  = io_request
            io_response = io_response ).

      when gcs_entity_set_names-internal-salesorderitem.
        read_entity_salesorderitem(
          exporting
            io_request  = io_request
            io_response = io_response ).

      when others.
        super->/iwbep/if_v4_dp_basic~read_entity(
          exporting
            io_request  = io_request
            io_response = io_response ).

    endcase.

  endmethod.

  method /iwbep/if_v4_dp_basic~read_entity_list.

    data lv_entityset_name type /iwbep/if_v4_med_element=>ty_e_med_internal_name.

    data: ls_todo_list         type /iwbep/if_v4_requ_basic_list=>ty_s_todo_list,
          ls_done_list         type /iwbep/if_v4_requ_basic_list=>ty_s_todo_process_list,
          lv_where_clause      type string,
          lv_select_string     type string,
          lv_orderby_string    type string,
          lt_selected_property type /iwbep/if_v4_runtime_types=>ty_t_property_path,
          lv_skip              type i value 0,
          lv_top               type i value 0,
          lt_orderby_property  type abap_sortorder_tab.


    io_request->get_todos( importing es_todo_list = ls_todo_list ).


    " $orderby was called
    if ls_todo_list-process-orderby = abap_true.
      ls_done_list-orderby = abap_true.
      "** only supported as of 751 or 752
      "get Open SQL Order by Clause
      "io_request->get_osql_orderby_clause( IMPORTING ev_osql_orderby_clause = lv_orderby_string ).
*        CATCH /iwbep/cx_gateway.    "

      io_request->get_orderby( importing et_orderby_property = lt_orderby_property ).
      clear lv_orderby_string.
      loop at lt_orderby_property into data(ls_orderby_property).
        if ls_orderby_property-descending = abap_true.
          concatenate lv_orderby_string ls_orderby_property-name 'DESCENDING' into lv_orderby_string separated by space.
        else.
          concatenate lv_orderby_string ls_orderby_property-name 'ASCENDING' into lv_orderby_string separated by space.
        endif.
      endloop.

    else.
      " lv_orderby_string must not be empty.
      lv_orderby_string = 'PRIMARY KEY'.
    endif.


    " $skip / $top handling
    if ls_todo_list-process-skip = abap_true.
      ls_done_list-skip = abap_true.
      io_request->get_skip( importing ev_skip = lv_skip ).
    endif.
    if ls_todo_list-process-top = abap_true.
      ls_done_list-top = abap_true.
      io_request->get_top( importing ev_top = lv_top ).
    endif.


    " $select handling
    if ls_todo_list-process-select = abap_true.
      ls_done_list-select = abap_true.
      io_request->get_selected_properties(  importing et_selected_property = lt_selected_property ).
      concatenate lines of lt_selected_property into lv_select_string  separated by ','.
    else.
      "check coding. If no columns are specified via $select retrieve all columns from the model instead?
      lv_select_string = '*'.
      "or better to throw an exception instead?
    endif.


    " specific sales orders based on $filter?
    if ls_todo_list-process-filter = abap_true.
      ls_done_list-filter = abap_true.
      io_request->get_filter_osql_where_clause( importing ev_osql_where_clause = lv_where_clause ).
    endif.


    io_request->get_entity_set( importing ev_entity_set_name = lv_entityset_name ).

    case lv_entityset_name.

      when gcs_entity_set_names-internal-salesorder.

        read_list_salesorder(
          exporting
            io_request        = io_request
            io_response       = io_response
            iv_orderby_string = lv_orderby_string
            iv_select_string  = lv_select_string
            iv_where_clause   = lv_where_clause
            iv_skip           = lv_skip
            iv_top            = lv_top
            is_done_list      = ls_done_list ).

      when gcs_entity_set_names-internal-salesorderitem.

        read_list_salesorderitem(
          exporting
            io_request        = io_request
            io_response       = io_response
            iv_orderby_string = lv_orderby_string
            iv_select_string  = lv_select_string
            iv_where_clause   = lv_where_clause
            iv_skip           = lv_skip
            iv_top            = lv_top
            is_done_list      = ls_done_list ).

      when others.

        super->/iwbep/if_v4_dp_basic~read_entity_list( io_request  = io_request
                                                       io_response = io_response ).
    endcase.

  endmethod.

  method /iwbep/if_v4_dp_basic~read_ref_target_key_data_list.

    data: lv_source_entity_name type /iwbep/if_v4_med_element=>ty_e_med_internal_name.


    io_request->get_source_entity_type( importing ev_source_entity_type_name = lv_source_entity_name ).

    case lv_source_entity_name.

      when gcs_entity_type_names-internal-salesorder.
        read_ref_key_list_salesorder(
           exporting
            io_request  = io_request
            io_response = io_response ).

      when others.
        super->/iwbep/if_v4_dp_basic~read_ref_target_key_data_list(
          exporting
            io_request  = io_request
            io_response = io_response ).

    endcase.

  endmethod.

  method /iwbep/if_v4_dp_basic~update_entity.

    data: lv_entity_type_name type /iwbep/if_v4_med_element=>ty_e_med_internal_name.

    io_request->get_entity_type( importing ev_entity_type_name = lv_entity_type_name ).

    case lv_entity_type_name.

      when gcs_entity_type_names-internal-salesorder.
        update_salesorder(
          exporting
            io_request  = io_request
            io_response = io_response ).

      when others.

        super->/iwbep/if_v4_dp_basic~update_entity(
         exporting
           io_request  = io_request
           io_response = io_response ).

    endcase.

  endmethod.

  method read_entity_salesorder.

    "entity type specific data types
    data: ls_salesorder         type gty_cds_views-salesorder,
          ls_key_salesorder     type gty_cds_views-salesorder,
          lv_salesorder_key_edm type string,
          lv_helper_int         type i.
    "generic data types
    data: ls_todo_list type /iwbep/if_v4_requ_basic_read=>ty_s_todo_list,
          ls_done_list type /iwbep/if_v4_requ_basic_read=>ty_s_todo_process_list.

    io_request->get_todos( importing es_todo_list = ls_todo_list ).

    " read the key data
    io_request->get_key_data( importing es_key_data = ls_key_salesorder ).
    ls_done_list-key_data = abap_true.

    select single * from ze2e001_c_salesorder
    into corresponding fields of @ls_salesorder
    where salesorder = @ls_key_salesorder-salesorder.

    if ls_salesorder is not initial.
      io_response->set_busi_data( is_busi_data = ls_salesorder ).
    else.
      "Move data first to an integer to remove leading zeros from the response
      lv_salesorder_key_edm = lv_helper_int = ls_key_salesorder-salesorder.

      raise exception type zcx_e2e001_odata_v4_so
        exporting
          textid              = zcx_e2e001_odata_v4_so=>entity_not_found
          http_status_code    = zcx_e2e001_odata_v4_so=>gcs_http_status_codes-not_found
          edm_entity_set_name = gcs_entity_set_names-edm-salesorder
          entity_key          = lv_salesorder_key_edm.

    endif.

    " Report list of request options handled by application
    io_response->set_is_done( ls_done_list ).
  endmethod.

  method read_entity_salesorderitem.
    "entity type specific data types
    data: ls_salesorderitem         type gty_cds_views-salesorderitem,
          ls_key_salesorderitem     type gty_cds_views-salesorderitem,
          lv_key_edm_salesorderitem type string,
          lv_helper_int             type i.
    "generic data types
    data: ls_todo_list type /iwbep/if_v4_requ_basic_read=>ty_s_todo_list,
          ls_done_list type /iwbep/if_v4_requ_basic_read=>ty_s_todo_process_list.

    io_request->get_todos( importing es_todo_list = ls_todo_list ).

    " read the key data
    io_request->get_key_data( importing es_key_data = ls_key_salesorderitem ).
    ls_done_list-key_data = abap_true.

    select single * from ze2e001_c_salesorderitem
    into corresponding fields of @ls_salesorderitem
    where salesorder = @ls_key_salesorderitem-salesorder
    and  salesorderitem = @ls_key_salesorderitem-salesorderitem.

    if ls_salesorderitem is not initial.
      io_response->set_busi_data( is_busi_data = ls_salesorderitem ).
    else.
      "Move data first to an integer to remove leading zeros from the response
      lv_key_edm_salesorderitem = lv_helper_int = ls_key_salesorderitem-salesorder.
      lv_key_edm_salesorderitem = lv_key_edm_salesorderitem && ','.
      lv_helper_int = ls_key_salesorderitem-salesorderitem.
      lv_key_edm_salesorderitem = lv_key_edm_salesorderitem && lv_helper_int.

      raise exception type zcx_e2e001_odata_v4_so
        exporting
          textid              = zcx_e2e001_odata_v4_so=>entity_not_found
          http_status_code    = zcx_e2e001_odata_v4_so=>gcs_http_status_codes-not_found
          edm_entity_set_name = gcs_entity_set_names-edm-salesorderitem
          entity_key          = lv_key_edm_salesorderitem.

    endif.

    " Report list of request options handled by application
    io_response->set_is_done( ls_done_list ).
  endmethod.



  method read_list_salesorder.

    "entity type specific data types
    data : lt_key_range_salesorder type zif_e2e001_odata_v4_so_types=>gt_key_range-salesorder,
           ls_key_range_salesorder type line of zif_e2e001_odata_v4_so_types=>gt_key_range-salesorder,
           lt_salesorder           type standard table of gty_cds_views-salesorder,
           lt_key_salesorder       type standard table of gty_cds_views-salesorder.

    "generic data types
    data: ls_todo_list type /iwbep/if_v4_requ_basic_list=>ty_s_todo_list,
          ls_done_list type /iwbep/if_v4_requ_basic_list=>ty_s_todo_process_list,
          lv_count     type i,
          lv_max_index type i.

    " Get the request options the application should/must handle
    io_request->get_todos( importing es_todo_list = ls_todo_list ).

    " Get the request options the application has already handled
    ls_done_list = is_done_list.

    " specific sales orders based on navigation?
    if ls_todo_list-process-key_data = abap_true.
      io_request->get_key_data( importing et_key_data = lt_key_salesorder ).
      loop at lt_key_salesorder into data(ls_key_entity).
        append value #( sign = 'I' option = 'EQ' low = ls_key_entity-salesorder ) to lt_key_range_salesorder.
      endloop.
      ls_done_list-key_data = abap_true.
    endif.

    "================================================================
    " read_list must either be called with a filter or via navigation
    " or $top has to be used to avoid a full table scan
    if  ls_todo_list-process-filter = abap_false
    and ls_todo_list-process-key_data = abap_false
    and iv_top = 0.
      raise exception type zcx_e2e001_odata_v4_so
        exporting
          textid              = zcx_e2e001_odata_v4_so=>use_filter_top_or_nav
          http_status_code    = zcx_e2e001_odata_v4_so=>gcs_http_status_codes-bad_request
          edm_entity_set_name = gcs_entity_set_names-edm-salesorder.
    endif.

    " Return business data if requested
    if ls_todo_list-return-busi_data = abap_true.

      "code for NW750 where OFFSET is not supported
      "read data from the CDS view
      "value for max_index must only be calculated if the request also contains a $top
*      if iv_top is not initial.
*        lv_max_index = iv_top + iv_skip.
*      else.
*        lv_max_index = 0.
*      endif.
*
*      select (iv_select_string) from ze2e001_c_salesorder
*      where (iv_where_clause)
*      and   salesorder in @lt_key_range_salesorder
*      order by (iv_orderby_string)
*      into corresponding fields of table @lt_salesorder
*      up to @lv_max_index rows.
*
*      "skipping entries specified by $skip
*      "not needed as of NW751 where OFFSET is supported in Open SQL
*      if iv_skip is not initial.
*        delete lt_salesorder to iv_skip.
*      endif.

      "OFFSET is only supported as of NW751
      select (iv_select_string) from ze2e001_c_salesorder
      where (iv_where_clause)
      and   salesorder in @lt_key_range_salesorder
      order by (iv_orderby_string)
      into corresponding fields of table @lt_salesorder
      up to @iv_top rows
      offset @iv_skip.

      io_response->set_busi_data( it_busi_data = lt_salesorder ).

    else.
      "if business data is requested count will be calculated by
      "the framework
      if ls_todo_list-return-count = abap_true.

        select count( * ) from ze2e001_c_salesorder
            where (iv_where_clause) and
           salesorder in @lt_key_range_salesorder
            into @lv_count.

        io_response->set_count( lv_count ).
      endif.
    endif.

    " Report list of request options handled by application
    io_response->set_is_done( ls_done_list ).
  endmethod.

  method read_list_salesorderitem.

    "entity type specific data types
    data : lt_key_


This post first appeared on Web Service Testing A Blackjack GUI And API, please read the originial post: here

Share the post

OData V4 code based implementation I (basic interface, read access)

×

Subscribe to Web Service Testing A Blackjack Gui And Api

Get updates delivered right to your inbox!

Thank you for your subscription

×