Skip to Content
Author's profile photo Andre Fischer

OData service development with SAP Gateway using CDS via Referenced Data Sources

Updates

  • 02.06.2016 Added link to blog that describes how to implement updates
  • 09.06.2016 Added section how to create the CDS view used for generating an OData service via Referenced Data Sources
  • 26.04.2017 Replaced screen shots that now show the latest version of ADT tools

 

Introduction

 

In a previous blog OData service development with SAP Gateway – code-based service development – Part I I described how to implement as basic OData Service using code based implementation.

Since the recommended development approach as of SAP NetWeaver 750 is to use CDS views I would like to show how a service having the same capabilities as the service in the blog mentioned above can be generated based on CDS views.

 

It will turn out that from a development perspective in the ABAP stack this is much less effort if appropriate CDS views are in place and if your backend system is based on SAP NetWeaver 750.

 

If however no appropriate CDS view is in place instead of having the effort of developing an OData service via code based implementation you would have the effort to develop the appropriate DDL source code.

 

Prerequisites

As a prerequisite we are using a consumption view ZSEPM_C_SALESORDER_<#> since it is not good practice to access core CDS views directly.

This consumption view is then also not annotated as an analytical view as the underlying core CDS view SEPM_I_SALESORDER_E.

How to create this consumption view ZSEPM_C_SALESORDER_<#> is shown in the following.

 

If you are using a system for a SAP CodeJam you can skip this step or create your own CDS view by replacing the hash tag <#> with your group number.

 


Creating the consumption view

 

Please note:

The creation of a consumption view is only necessary if you want to follow this how-to-guide on your own system or if you want to use your own CDS view.

For a SAP CodeJam or other SAP event we will use a system where this view has already been created.

 

 

 

  • Start the ABAP Development Tools

 

From the menu choose New –> ABAP Project

Select the system (here A4H) from the list of system connections and choose Next02 Select A4H from the list of system connections.png

  • In the “Connection Settings” screen press Next
    System Connection Details.png
  • Enter your credentials
    04 logon to system.png
  • Right click on your system connection and select New –> Other …

Select Data Definition and press Next

  • Enter the following details for the new DDL source and press Finish

    Package: $TMP
    Name: ZSEPM_C_SALESORDER_<#>
    Description: SalesOrders – EPM Demo Data

Cut and paste the source code and press the Activate button. This will create the CDS view that we will use in this how to guide

You will have to adapt the name of the SQL view, the name of the CDS view and you should maintain the annotation for @EndUserText.label so that it contains your group number.

 

 

DDL source code

@AbapCatalog.sqlViewName: 'ZSEPM_ISOE_<#>'

@AbapCatalog.compiler.compareFilter: true

@AccessControl.authorizationCheck: #CHECK

@EndUserText.label: 'SalesOrders – EPM Demo Data'

define view Zsepm_C_Salesorder_<#>
  as select from SEPM_I_SalesOrder_E
{

      @ObjectModel.text.association: '_Text'

  key SalesOrder,

      @ObjectModel.readOnly: true

      CreationDateTime,

      @ObjectModel.readOnly: true

      LastChangedDateTime,

      @ObjectModel.readOnly: true

      IsCreatedByBusinessPartner,

      @ObjectModel.readOnly: true

      IsLastChangedByBusinessPartner,

      @ObjectModel.readOnly: true

      Customer,

      @ObjectModel.readOnly: true

      TransactionCurrency,

      @ObjectModel.readOnly: true

      GrossAmountInTransacCurrency,

      @ObjectModel.readOnly: true

      NetAmountInTransactionCurrency,

      @ObjectModel.readOnly: true

      TaxAmountInTransactionCurrency,

      @ObjectModel.readOnly: true

      SalesOrderLifeCycleStatus,

      @ObjectModel.readOnly: true

      SalesOrderBillingStatus,

      @ObjectModel.readOnly: true

      SalesOrderDeliveryStatus,

      @ObjectModel.readOnly: true

      SalesOrderOverallStatus,

      @ObjectModel.readOnly: true

      Opportunity,

      /* Associations */

      _Customer,

      _Item,

      _Text


} 
  
 

 

 

 

 

Generating an OData service via Referenced Data Source

 

  • We first have to start with creating a new Service Builder project called ZE2E100_<#>_2 since the project in the previous blog mentioned above was called ZE2E100_<#>

    Note:
    Replace <#> with your group number
  • Right click on the folder Data Model.Choose Reference –> Modeled Data Source Reference from the context menu

/wp-content/uploads/2016/06/image082_966055.png

  • The Reference Data Source Wizard opens.

On the first screen choose:
CDS Core Data Services for the field Modeled Data Source Type.
ZSEPM_C_SALESORDER_<#> for the field Modeled Data Source Name

/wp-content/uploads/2016/06/rds01_966286.jpg

  • In the second window of the wizard select the following associations’_CUSTOMER and _ITEM.Please note that the cds views SEPM_I_BUSINESSPATNER_E and SEPM_I_SALESORDERITEM_E are automatically selected as well.Press Finish

/wp-content/uploads/2016/06/rds02_966356.jpg

 

  • Press the Generate Runtime Objects button

 

  • In the Model and Service Definition screen leave the default values unchanged and press Continue.

/wp-content/uploads/2016/06/image086_966097.png

  • In the Create Object Directory Entry dialogue press Local Object or enter $TMP.

 

  • Expand the folder Service Maintenance right click on the entry GW_HUB and choose Register.

/wp-content/uploads/2016/06/image088_966104.png

  • In the Add Service dialogue enter $TMP for the package assignment or press Local Object.

/wp-content/uploads/2016/06/image089_966105.png

  • Expand the folder Service Maintenance right click on the entry GW_HUB and choose SAP Gateway Client.Press <Execute>

/wp-content/uploads/2016/06/image090_966106.png

  • Check the Service Document

    It shall contain three entity sets:
    1. Zsepm_C_Salesorder_<#>
    2. SEPM_I_SalesOrderItem_E
    3. SEPM_I_BusinessPartner_E

/wp-content/uploads/2016/06/rds04_966357.jpg

  • Check the metadata document by selecting <Add URI option> and select $metadata

Check the Metadata Document
Please note that the entity type ZSEPM_I_SalesOrder_EType contains:

    • Two navigation properties to_Customer and to_Item
    • A property SalesOrder_Text that has been generated by the SADL framework based on the annotation @ObjectModel.text.association: ‘_Text’.

      Please note this property is annotated as sap:updatable=”false”;

 

/wp-content/uploads/2016/06/rds06_966109.jpg

  • Now we can test the service and we will see that it supports various query options, $expand and selecting single entities out of the box.

    To do so execute the following URI’s in the SAP Gateway Client (transaction /n/IWFND/GW_CLIENT):
  • /sap/opu/odata/SAP/ZE2E100__2_SRV/Zsepm_C_Salesorder_?$filter=GrossAmountInTransacCurrency ge 100000&$select=SalesOrder,GrossAmountInTransacCurrency,TransactionCurrency&$format=json

 

    • $skip and $top together with $inlinecount work out of the box as well

      /sap/opu/odata/SAP/ZE2E100__2_SRV/Zsepm_C_Salesorder_?$filter=GrossAmountInTransacCurrency ge 100000&$select=SalesOrder,GrossAmountInTransacCurrency,TransactionCurrency&$top=2&$skip=1&$inlinecount=allpages&$format=json

 

Please note that via &$inlinecount=allpages we retrieve the number of entries that would be returned without using $skip and $top

/wp-content/uploads/2016/06/rds07_966358.jpg

    • Read a single entity from the list of the sales order/sap/opu/odata/SAP/ZE2E100_<#>_2_SRV/Zsepm_C_Salesorder_<#>(‘5000000<X>‘)?$format=jsonPlease note that the weird looking key stems from the fact that the CDS view is annotated as an analytical view.
    • Read the list of items of a single sales order via the navigation property to_Item /sap/opu/odata/SAP/ZE2E100_<#>_2_SRV/Zsepm_C_Salesorder_<#>(‘5000000<#>‘)/to_Item?$format=json

 

 

 

How to implement updates in a service that has been generated using referenced data sources will be described in the following blog

 

OData service development with SAP Gateway using CDS via Referenced Data Sources – How to implement updates

 

 

Assigned Tags

      22 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Jibin Joy
      Jibin Joy

      Hi Andre ,

        Thanks for Information !!!

      Please share ur valuable suggestion .

          Consider 2 User can access this Sales Order oData Service "User1" and "User2" . Now "User1" can view Sales Order of particular set of Customer like "C1" and "C2" and "User2" can view other set of Customer like "C3" and "C4".

        how can we achieve like this Authorization !!!

      Regards,

      Jibin Joy

      Author's profile photo Krishna Kishor Kammaje
      Krishna Kishor Kammaje

      HI Jibin,

      Use this space (comment section) only to discuss about topics directly related to the blog. Use the forum for any questions.

      Regards

      Krishna

      Author's profile photo Jibin Joy
      Jibin Joy

      Hi All,

       

      Above Clarification has been moved

         Authorization Mechanism : OData service development using CDS via Referenced Data Sources

      Regards,

      Jibin Joy

      Author's profile photo Former Member
      Former Member

      Hi Andre,

      e.g. i decide to use Gateway with the deployment option "Hub architecture with development on the server" as you described here. Will the automatic generation via an ABAP CDS View by using the annotation @OData.publish work?

      I'm asking because i'm not sure whether the description here with "..SAP Gateway hub system. ..." considers both possible Hub deployment options.

      BR
      Eugen

      Author's profile photo Mynyna Chau
      Mynyna Chau

      Hi Andre, thanks for your blog post.

      I got notified that comments weren't possible, so I wanted to check back and see if it is possible to post comments now, since there might be people who would like to give additional feedback or get questions on your blog cleared. Have a nice day!

      Regards, Mynyna

      Author's profile photo Gregor Wolf
      Gregor Wolf

      Hi André,

      I've followed the described steps in a SAP NetWeaver ABAP 7.50 SP5 system and can query the service. But I want to make the service subscribable. For this I've followed the steps described in Configurations on the SAP Backend System for Push Oriented Scenarios. In the MPC class I've used the following coding to set the subscribable flag for the Entity Set:

      CLASS ZCL_ZSEPM_C_SALESORDER_MPC_EXT IMPLEMENTATION.
      
        METHOD define.
          super->define( ).
          DATA(lo_so) = model->get_entity_set( 'Zsepm_C_Salesorder_Tpl' ).
          lo_so->set_subscribable( ).
        ENDMETHOD.
      
      ENDCLASS.

      When I now call the Service via the URL "/sap/opu/odata/sap/ZSEPM_C_SALESORDER_SRV/?$format=json" I get the following result:

      {
        "d" : {
          "EntitySets" : [
            "SEPM_I_SalesOrderItem_E",
            "Zsepm_C_Salesorder_Tpl",
            "SEPM_I_BusinessPartner_E"
          ]
        }
      }

      So you see that the Entities that should be returned for a subscribable service:

      NotificationCollection
      SubscriptionCollection

      do not exist. Hope you can give some guidance.

      Best regards
      Gregor

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

       

      Hi Gregor,

      you have to enter a few more lines of coding that I have copied from the define method of /IWBEP/CL_MGW_PUSH_ABS_MODEL.

      Reason is that the DEFINE method of the generated MPC class

        method DEFINE.
      *&---------------------------------------------------------------------*
      *&           Generated code for the MODEL PROVIDER BASE CLASS         &*
      *&                                                                     &*
      *&  !!!NEVER MODIFY THIS CLASS. IN CASE YOU WANT TO CHANGE THE MODEL  &*
      *&        DO THIS IN THE MODEL PROVIDER SUBCLASS!!!                   &*
      *&                                                                     &*
      *&---------------------------------------------------------------------*
      
      model->set_schema_namespace( 'ZE2E100_XX_2_SRV' ).
      
      define_rds_4( ).
      get_last_modified_rds_4( ).
        endmethod.

      does not call the DEFINE method of the Superclass.

      So if your DEFINE method looks like this the two entity sets

      NotificationCollection

      SubscriptionCollection

      will show up and can be called since the CRUD methods are handled by the framework for example by calling /IWBEP/IF_MGW_CORE_SRV_RUNTIME~READ_ENTITYSET in the base class /IWBEP/CL_MGW_PUSH_ABS_DATA.

      Please note that the methods of the interface /IWBEP/IF_MGW_CORE_SRV_RUNTIME must not be redefined since they belong to the framework.

      Only methods of the application interface /IWBEP/IF_MGW_APPL_SRV_RUNTIME may be redefined

        super->define( ).
      
             DATA(lo_so) = model->get_entity_set( 'Zsepm_C_Salesorder_Tpl' ).
          lo_so->set_subscribable( ).
      
      
      
         DATA:
              lo_data_object  TYPE REF TO /iwbep/if_mgw_odata_entity_typ,
              lo_do_conv      TYPE REF TO /iwbep/if_mgw_odata_etype_conv,
              lo_property     TYPE REF TO /iwbep/if_mgw_odata_property.
      
      
        lo_data_object = model->create_entity_type( 'Subscription' ). "#EC NOTEXT
        lo_data_object->set_creatable( abap_true ).
        lo_data_object->set_updatable( abap_true ).
        lo_data_object->set_deletable( abap_true ).
      
        " todo consumer type + connectivity server
        lo_do_conv ?=  lo_data_object.
      
        lo_do_conv->create_properties(
           iv_comma_sep_abap_names =
            'ID,USERNAME,CHANGED_TIMESTMP,TITLE,DELIVERY_ADDRESS,PERSIST_NOTIF,COLLECTION_NAME,FILTER_PARAMETER,SELECT_PARAMETER,CHANGE_TYPE'
           iv_comma_sep_ext_names =
            'ID,user,updated,title,deliveryAddress,persistNotifications,collection,filter,select,changeType'
        ).
      
        lo_data_object->bind_structure( '/IWBEP/S_MGW_SUB_DATA' ).
        lo_property =  lo_data_object->get_property( 'ID' ).
        lo_property->set_is_key( ).
        lo_property->set_creatable( abap_false ).
        lo_property->set_updatable( abap_false ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'updated' ).
        lo_property->set_as_updated( abap_false ).
        lo_property->set_creatable( abap_false ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'user' ).
        lo_property->set_as_author( abap_false ).
        lo_property->set_creatable( abap_false ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'title' ).
        lo_property->set_as_title( abap_false ).
        lo_property->set_creatable( ).
        lo_property->set_updatable( ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'deliveryAddress' ).
        lo_property->set_creatable( ).
        lo_property->set_updatable( ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'persistNotifications' ).
        lo_property->set_creatable( ).
        lo_property->set_updatable( ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'collection' ).
        lo_property->set_creatable( ).
        lo_property->set_updatable( ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'filter' ).
        lo_property->set_creatable( ).
        lo_property->set_updatable( ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'select' ).
        lo_property->set_creatable( ).
        lo_property->set_updatable( ).
        lo_property->set_filterable( abap_false ).
      
        lo_property = lo_data_object->get_property( 'changeType' ).
        lo_property->set_creatable( ).
        lo_property->set_updatable( ).
      
      *------set semantics
        lo_data_object->set_semantic( 'subscriptions' ).
      
      
      * ------Notification----------------------------------------------------------------------
        lo_data_object = model->create_entity_type( 'Notification' ). "#EC NOTEXT
        lo_data_object->set_creatable( abap_false ).
        lo_data_object->set_updatable( abap_false ).
        lo_data_object->set_deletable( abap_false ).
        lo_data_object->set_addressable( abap_false ).
      
        lo_do_conv ?=  lo_data_object.
      
        lo_do_conv->create_properties(
           iv_comma_sep_abap_names =
            'ID,COLLECTION,TEXT,UPDATED,OPERATION_TYPE,ENTRIES_OF_INTEREST'
           iv_comma_sep_ext_names =
            'ID,collection,title,updated,changeType,entriesOfInterest'
        ).
        lo_data_object->bind_structure( '/IWBEP/S_MGW_NOTIF_DATA' ).
      
        lo_property = lo_data_object->create_property( 'recipient' ).
        lo_property->set_creatable( abap_false ).
        lo_property->set_updatable( abap_false ).
        lo_property->bind_data_element( '/IWBEP/MGW_NOTIF_RECIPIENT' ).
      
        lo_property =  lo_data_object->get_property( 'ID' ).
        lo_property->set_is_key( ).
      
        lo_property = lo_data_object->get_property( 'title' ).
        lo_property->set_as_title( abap_false ).
      
        lo_property = lo_data_object->get_property( 'updated' ).
        lo_property->set_as_updated( abap_false ).
      
      *-set semantics
        lo_data_object->set_semantic( 'notifications' ).
      
      
      Author's profile photo Gregor Wolf
      Gregor Wolf

      Hi André,

      I've just tested your suggestion in my system and yes it solves my issue. Thank you!

      But is there a valid reason why the define method does not call it's super class?

      Best regards
      Gregor

      Author's profile photo Gregor Wolf
      Gregor Wolf

      Hi André,

      unfortunately I've never tried the URI that I get back from the Path:

      /sap/opu/odata/iwfnd/NOTIFICATIONSTORE/NotificationCollection

      The Path part of the URI looks like that:

      /sap/opu/odata/sap/ZMATERIALS_SRV;mo/ZDDL_MATERIALS(SAP__Origin='LOCAL',MaterialNumber='000000000000004711')

      When I call this URL the following error is returned:

      {
        "code": "CX_SADL_VIEW_ELEMENT_UNDEFINED",
        "message": "'SAP__ORIGIN' is not a view element",
        "propertyref": "",
        "severity": "error",
        "target": ""
      },
      {
        "code": "CX_SADL_GW_PROPERTY_NOT_MAPPED",
        "message": "Property 'SAP__ORIGIN' is not mapped to a SADL data source and must be handled programmatically",
        "propertyref": "",
        "severity": "error",
        "target": ""
      }

      What changes do I have to add to the coding so that this issue does not occur?

      When I use the Path:

      /sap/opu/odata/sap/ZMATERIALS_SRV/ZDDL_MATERIALS('000000000000004711')

      everything is just fine.

      Best regards
      Gregor

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

      you could use the following URL:

      /sap/opu/odata/sap/ZMATERIALS_SRV;o=LOCAL/ZDDL_MATERIALS(‘000000000000004711’)

      So adding the name of the system alias of your backend after the root URL of your service.

      Best Regards,

      Andre

       

       

      Author's profile photo Gregor Wolf
      Gregor Wolf

      Hi André,

      but shouldn't the URI in the Notification be correctly generated or evaluated by SAP Gateway? I think it's the best to raise an incident in the service marketplace anyway. What do you think?

      Best regards
      Gregor

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

      Hi Gregor,

      you are right. This seems to be a bug.

      In my Sandbox System however it works to run the following requests:
      /sap/opu/odata/sap/ZE2E100_XX_2_SRV;mo/Zsepm_C_Salesorder_Tpl(SAP__Origin='LOCAL2',SalesOrder='500000000')

      /sap/opu/odata/sap/ZE2E100_XX_2_SRV;mo/Zsepm_C_Salesorder_Tpl(SAP__Origin='LOCAL',SalesOrder='500000000')

      after having created a second system alias LOCAL2.

      Author's profile photo Alexander Karpov
      Alexander Karpov

      Hi, Andre.

      I create oData service using cds+referenced data Source in SEGW. But flag filterable in SEGW for entity type fields is unchecked and i cant filter oData service.

      How can i set flag filterable for one of field of entityType ?

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

      Hi Alexander,

      the filterable flag has no influence on the runtime of an OData service. It is just metadata information that shall inform the client whether an entity set supports the filtering of data or not.

      An entity set which is based on a CDS view and that have been generated using the referenced data source approach should support filtering out out of the box.

      Have you tried to filter the data using the SAP Gateway client as described above or have you just checked the flag in SEGW?

      If there is still a problem please post your question in the question section or raise a customer ticket.

      Best Regards,

      Andre

       

      Author's profile photo Alexander Karpov
      Alexander Karpov

      Thanks, Andre.

      I will try filtering in GW client

       

      Author's profile photo Timo Matz
      Timo Matz

      Hi Andre,

      is it possibly to set NOT „filterable“ or NOT „sortable“ per CDS-Annotation?

      That means without coding these in the define method.

      Thank you in advance.

      Best regards,

      Timo

       

      Author's profile photo Santhosh Kadiyala
      Santhosh Kadiyala

      excellent blog andre.

      Author's profile photo Ramin Shafai
      Ramin Shafai

      Sorry Andre, I deleted my message before had a chance to read your reply. So I'm posting it again for others' benefit.

      My question was whether changing a cds view (ie adding a field) would refresh the odata entity. And your answer was:

       

      Hi Ramin, this method is a workaround and though you would have to redo the step of creating a new merged XML document I still find it practical since the alternative is that you don't have any solution....

       

      Yes, it is easy to recreate the reference in SEGW (that's why I decided to remove my original message 🙂 ). Thanks for your answer, appreciate it.

      Ramin.

      Author's profile photo Ramin Shafai
      Ramin Shafai

      Wouldn't the alternative be to create the data entity manually in SEGW and associate it to the CDS view's ddic structure, instead of a Reference to CDS?

      In the case of updates to the cds view then, you'd just have to drag and drop the new fields into the Service Implementation of the data entity.

      I'm trying to decide which alternative is easier, less prone to errors.

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

      Hi Ramin,

      I wouldn't recommend to use the mapped data source approach. The mapped data source approach is the predecessor of the reference data source approach.

      Using the reference data source approach you don't even have to regenerate the SEGW project when adding additional fields to the underlying CDS view (see also chapter 7.3.2 in our book).

      Only when adding additional CDS views to the hierarchy or value helps the SEGW project has to regenerated.

      The RDS approach is definitely the future proof approach.

      Which release are you working on?

      When working on SAP S/4HANA 1909 or later you should check out the ABAP RESTful Programming Model instead of using the RDS approach.

      Kind regards,

      Andre

       

      Author's profile photo Ramin Shafai
      Ramin Shafai

      We are on 7.5 Business Suite system, not upgraded to S4 yet.

      We have a lot of mapped odata services unfortunately.

      Thanks for your advice, I will let other developers in our team know.

       

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

      This might be because mapped data source was introduced in 740 and RDS only in 750.

      It should however be quite easy to create new RDS based services alongside to the existing services that are based on mapped data source since you just have to select the CDS views rather than having to create entity types via DDIC import and associations manually.

      The $metadata documents of the new services will slightly differ though.

      On the other hand you have to judge whether it is worth the effort to change existing services just for the sake of using the latest runtime implementation ;-).

      When upgrading to SAP S/4HANA 2020 FSP1 you should definitly check if you would want to create RAP based services using an unmanaged implementation.