Monday, 4 October 2021

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.

SAP ABAP Exam Prep, SAP ABAP Tutorial and Materials, SAP ABAP Certification, SAP ABAP Learning, SAP ABAP Preparation, SAP ABAP Career, SAP ABAP

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.

SAP ABAP Exam Prep, SAP ABAP Tutorial and Materials, SAP ABAP Certification, SAP ABAP Learning, SAP ABAP Preparation, SAP ABAP Career, SAP ABAP
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.


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

Source: sap.com

No comments:

Post a Comment