Technical Articles
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 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.
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:
Thanks
Gang
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
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
Thank you Andre, that makes sense !
Looking forward for that blog 🙂
Thanks.
Anurag
Hi Andre,
That is very informative. could you please confirm if custom action is allowed over Custom entity BO?