Skip to Content
Technical Articles
Author's profile photo Andre Fischer

How to generate RAP BOs with custom entities ?

With the latest version of my open source project the RAP Generator the tool is able to generate RAP business objects whose data model consist out of custom entities.

As a datasource you can use one of the follwoing datasource types:

  • Abstract entities
  • DDIC structures
  • ABAP types (which currently have to based on DDIC structures as well)

In this blog post I would like to show how you can generate an unmanaged RAP business object based on an abstract entity.

Abstract entities are typically been generated when you create a service consumption model.

How to create a service consumption model based on an OData service that contains SAP Netweaver demo product data is described as part of exercise 2 of the SAP TechEd session DEV268.

teched2020-DEV268/exercises/ex2 at main · SAP-samples/teched2020-DEV268 (github.com)

So I assume that a service consumption model called ZSC_RAP_PRODUCTS_DEMO has been created. (If you want to create a service consumption model with this name you have to replace the four hashtags #### in the script with DEMO or any other four characters or numbers such as 0002.

Alongside with the service consumption model an abstract entity will be generated that has the name ZDEMO_SEPMRA_I_PRODUCT_E.

 

Caution – Bug in XCO libary – will be fixed with 2011

After the abstract entity has been generated you have to replace the definition of the property Currency from

Currency : abap.cuky;

to

Currency : abap.cuky( 5 );

If you don’t add the length for the field abap.cuky the RAP Generator will throw an exception. This bug is already fixed and will be delivered with the upcoming release 2011.

 

The service consumption model has been created in the package ZRAP_EPM_PRODUCTS_DEMO.

Now you can create a class called zrap_cl_create_custom_entity by copying the class /dmo/cl_rap_generator_console and by replacing the JSON string in the method get_json_string( ) with the following JSON string.

{
    "$schema": "https://raw.githubusercontent.com/SAP-samples/cloud-abap-rap/main/json_schemas/RAPGenerator-schema-all.json",
    "namespace": "Z",
    "bindingType": "odata_v2_ui",
    "dataSourceType": "cds_view",
    "implementationtype": "unmanaged_semantic",
    "package": "ZRAP_EPM_PRODUCTS_DEMO",
    "draftenabled": false,
    "prefix": "ABS_",
    "suffix": "_DEMO",
    "hierarchy": {
        "entityName": "Product",
        "dataSource": "ZDEMO_SEPMRA_I_PRODUCT_E",
        "objectId": "Product",
        "etagMaster": "LastChangedDateTime"        
    }
}

 

Please note that the data source type is cds_view. The RAP Generator automatically detects that the CDS view is of type abstract entity. As a result it will generate a custom entity rather than a view entity.

The datasource is the abstract entity ZDEMO_SEPMRA_I_PRODUCT_E.

When we now run this console application it will generate a RAP business object based on a custom entity ZI_ABS_PRODUCT_DEMO.

Generated%20objects

Generated objects

 

Since it is not possible to create projection views and metadata extension views on top or alongside with custom entities the RAP Generator adds all semantic annotations and UI annotations into the DDL source code of the custom entity.

 

 

@ObjectModel.query.implementedBy: 'ABAP:ZCL_CE_ABS_Product_DEMO'
@UI: {
  headerInfo: {
    typeName: 'Product', 
    typeNamePlural: 'Products', 
    title: {
      type: #STANDARD, 
      label: 'Product', 
      value: 'PRODUCT'
    }
  }
}
define root custom entity ZI_ABS_PRODUCT_DEMO
{
  @UI.facet: [ {
    id: 'idCollection', 
    type: #COLLECTION, 
    label: 'Product', 
    position: 10 
  }, {
    id: 'idIdentification', 
    parentId: 'idCollection', 
    type: #IDENTIFICATION_REFERENCE, 
    label: 'General Information', 
    position: 10 
  } ]
  @UI.lineItem: [ {
    position: 10 , 
    importance: #HIGH, 
    label: 'PRODUCT'
  } ]
  @UI.identification: [ {
    position: 10 , 
    label: 'PRODUCT'
  } ]
  @UI.selectionField: [ {
    position: 10 
  } ]
  key PRODUCT : abap.char( 10 );
  @UI.lineItem: [ {
    position: 20 , 
    importance: #HIGH, 
    label: 'PRODUCTTYPE'
  } ]
  @UI.identification: [ {
    position: 20 , 
    label: 'PRODUCTTYPE'
  } ]
  PRODUCTTYPE : abap.char( 2 );
  @UI.lineItem: [ {
    position: 30 , 
    importance: #HIGH, 
    label: 'PRODUCTCATEGORY'
  } ]
  @UI.identification: [ {
    position: 30 , 
    label: 'PRODUCTCATEGORY'
  } ]
  PRODUCTCATEGORY : abap.char( 40 );
 
....
  
}

In addition to the custom entity the RAP Generator generates a class ZCL_CE_ABS_Product_DEMO that implements the interface  if_rap_query_provider.

CLASS zcl_ce_abs_product_demo DEFINITION
  PUBLIC
  FINAL
  CREATE PUBLIC .

  PUBLIC SECTION.

    INTERFACES if_rap_query_provider .
  PROTECTED SECTION.
  PRIVATE SECTION.
ENDCLASS.



CLASS zcl_ce_abs_product_demo4 IMPLEMENTATION.


  METHOD if_rap_query_provider~select.
    DATA business_data TYPE TABLE OF ZI_ABS_Product_DEMO.
    DATA query_result  TYPE TABLE OF zdemo_sepmra_i_product_e.
    DATA total_number_of_records TYPE int8.
    DATA(top)               = io_request->get_paging( )->get_page_size( ).
    DATA(skip)              = io_request->get_paging( )->get_offset( ).
    DATA(requested_fields)  = io_request->get_requested_elements( ).
    DATA(sort_order)        = io_request->get_sort_elements( ).
    TRY.
        DATA(filter_condition) = io_request->get_filter( )->get_as_ranges( ).
        "Here you have to implement your custom query
        "and store the result in the internal table query_result
        IF io_request->is_total_numb_of_rec_requested(  ).
          io_response->set_total_number_of_records( total_number_of_records ).
        ENDIF.
        io_response->set_data( business_data ).
      CATCH cx_root INTO DATA(exception).
        DATA(exception_message) = cl_message_helper=>get_latest_t100_exception( exception )->if_message~get_longtext( ).
    ENDTRY.
  ENDMETHOD.
ENDCLASS.

 

The only thing that is left to do is to implement the call of the OData client proxy that calls the remote OData service. Here we use the same approach as described in my script where a method get_products( ) is added that contains the remote OData client proxy call.

https://github.com/SAP-samples/teched2020-DEV268/tree/main/exercises/ex2#class-zcl_ce_rap_products_—implementation

As a result you will get an OData service that calls remotely an OData service in the SAP Gateway demo system ES5.

Assigned Tags

      5 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Gang Wu
      Gang Wu

      Hi Andre, thanks for you sharing!

      I have follow up your steps in this article and generated the RAP BOs,  but I received below error:

      Caution: Exception occured
      Check repository objects of RAP BO ZI_ABS_Product_0306.
      Messages from framework:
      {SEVERITY: I, OBJECT_TYPE: , OBJECT_NAME: , MESSAGE: Messages and warnings from ADT:}
      {SEVERITY: W, OBJECT_TYPE: BDEF, OBJECT_NAME: ZI_ABS_PRODUCT_0306, MESSAGE: Operations need to be implemented for the behavior definition, which means an implementation class needs to be specified.}
      {SEVERITY: , OBJECT_TYPE: , OBJECT_NAME: , MESSAGE: PUT operation failed:}
      {SEVERITY: E, OBJECT_TYPE: , OBJECT_NAME: , MESSAGE: Activation failed.}

      I have already seen all the BOs generated and looks like the same as the BO structure in your article.  Please see below: Could you suggest which activation failed?

      Generator class:

      CLASS zcl_create_custom_entity_0306 DEFINITION
        PUBLIC
        INHERITING FROM cl_xco_cp_adt_simple_classrun
        FINAL
        CREATE PUBLIC .
      
        PUBLIC SECTION.
        PROTECTED SECTION.
          METHODS: main REDEFINITION.
          METHODS get_json_string
            RETURNING VALUE(json_string) TYPE string.
        PRIVATE SECTION.
      ENDCLASS.
      
      
      
      CLASS zcl_create_custom_entity_0306 IMPLEMENTATION.
        METHOD get_json_string.
          json_string = '{' && |\r\n|  &&
                        '    "$schema": "https://raw.githubusercontent.com/SAP-samples/cloud-abap-rap/main/json_schemas/RAPGenerator-schema-all.json",' && |\r\n|  &&
                        '    "namespace": "Z",' && |\r\n|  &&
                        '    "bindingType": "odata_v2_ui",' && |\r\n|  &&
                        '    "dataSourceType": "cds_view",' && |\r\n|  &&
                        '    "implementationtype": "unmanaged_semantic",' && |\r\n|  &&
                        '    "package": "ZRAP_EPM_PRODUCTS_0306",' && |\r\n|  &&
                        '    "draftenabled": false,' && |\r\n|  &&
                        '    "prefix": "ABS_",' && |\r\n|  &&
                        '    "suffix": "_0306",' && |\r\n|  &&
                        '    "hierarchy": {' && |\r\n|  &&
                        '        "entityName": "Product",' && |\r\n|  &&
                        '        "dataSource": "ZRAP_0306SEPMRA_I_PRODUCT_E",' && |\r\n|  &&
                        '        "objectId": "Product",' && |\r\n|  &&
                        '        "etagMaster": "LastChangedDateTime"' && |\r\n|  &&
                        '    }' && |\r\n|  &&
                        '}'.
        ENDMETHOD.
        METHOD main.
          TRY.
              DATA(json_string) = get_json_string(  ).
              DATA(rap_generator) = /dmo/cl_rap_generator=>create_for_cloud_development( json_string ).
              "DATA(rap_generator) = /dmo/cl_rap_generator=>create_for_S4_2020_development( json_string ).
              DATA(framework_messages) = rap_generator->generate_bo( ).
              IF rap_generator->exception_occured( ).
                out->write( |Caution: Exception occured | ).
                out->write( |Check repository objects of RAP BO { rap_generator->get_rap_bo_name(  ) }.| ).
              ELSE.
                out->write( |RAP BO { rap_generator->get_rap_bo_name(  ) }  generated successfully| ).
              ENDIF.
              out->write( |Messages from framework:| ).
              LOOP AT framework_messages INTO DATA(framework_message).
                out->write( framework_message ).
              ENDLOOP.
            CATCH /dmo/cx_rap_generator INTO DATA(rap_generator_exception).
              out->write( 'RAP Generator has raised the following exception:' ).
              out->write( rap_generator_exception->get_text(  ) ).
          ENDTRY.
        ENDMETHOD.
      
      ENDCLASS.

      Thanks

      Gang

      Author's profile photo Anurag Srivastava1
      Anurag Srivastava1

      Hi Andre,

      Thanks for Sharing this!

      I wanted to know if this supports Transactional behavior as well like Create/Update/Delete on the remote Service created via Service Consumption Model ?

      I read on the below link that Transactional activities are only supported on the locally created DB but not on remote services.

      https://help.sap.com/docs/BTP/923180ddb98240829d935862025004d6/aa2b11fa73c846cdb733ba8b797d350c.html

       

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

      Hi Anurag,

      the answer to your question is unfortunately not a clear yes or no.

      The RAP framework wants to govern the transactional consistency. When you perform two updates in a remote system and the first call works and the second does not then how would you roll back the changes of your first update call?

      So technically it will be possible to call one or more RFC function modules or OData services from whithin your behavior implementation but you might run into issues with your implementation.

      Colleagues of mine are preparing a blog post that will talk in depth about the problem of transactional consistency when working with legacy code. And I will update my answer once this blog post is out and published. ;-).

       

      Kind regards,

      Andre

      Author's profile photo Anurag Srivastava1
      Anurag Srivastava1

      Thank you Andre, that makes sense !

      Looking forward for that blog 🙂

      Thanks.

      Anurag

      Author's profile photo Himanshu Gupta
      Himanshu Gupta

      Hi Andre,

       

      That is very informative. could you please confirm if custom action is allowed over Custom entity BO?