Updates
- 19.05.2020 – added link to GitHub repo
- 31.05.2020 – added the demo from my SAP Online Track talk [SOT114] The RAP generator
- 05.06.2020 – changed the code of the RAP Generator so that it works again after HFC3 has been applied
- 10.06.2020 – Published new version that offers the following
- Object oriented design
- BO node class
- BO generator class
- Support for multiple child, grandchild, … nodes
- Unit tests
- Exception class
- Object oriented design
- 31.08.2020 – New version that offers the following
- Structure of the BO can be defined in a JSON file
- Support for CDS views as a data source
- 11.09.2020 – Added a link to a how to a click through guide
Introduction
When preparing hands-on sessions for the SAP Fiori Programming Model (in the past) and now for the ABAP RESTful Programming Model (RAP) I always faced the problem that we had to start from scratch.
That means that the participants were forced to create all objects of the stack such as table(s), Interface CDS View(s), Projection View(s), Metadata Extension View(s), Behavior Definition for interface view and projection view and Behavior Implementations, service definition and service binding.
Most of what a developer has to do here is to write boiler plate coding such the use of the insert all elements option when creating a CDS view based on a table or another CDS view.
For educational purposes it’s OK that you learn how to develop all the objects step by step. But if you want to provide a deep dive session it is not much fun if you have to create lots of boiler plate coding before having the chance to dive into the more interesting topics.
Also for a normal developer it would be nice to have the chance to speed up the development process by getting an up- and running starter business object.
So I was very pleased when I saw in an SAP internal review the new XCO framework (“Extension Components”) which allows you to generate various ABAP repository objects via ABAP API’s.
So I started to develop a class zcl_rap_bo_generator that allows you to generate nearly the complete stack of a RAP business object based on one (header) and multiple child and grandchild tables which is available on GitHub https://github.com/SAP-samples/cloud-abap-rap.
With the 2008 release the third version is now available and can be tested on the trial systems. As of the release 2008 the XCO framework is now also available in customer systems.
Special thanks for several discussions go to Soeren Schlegel who suggested to use an object oriented approach to get to a scalable architecture as we have it now.
While the first version was only able to generate a header item scenario the second version became much more flexible and allowed to generate more complex business object trees.
The third version now allows to define the specifics of your business object tree in a JSON file.
This way you do not have to write lots of service specific coding and you can concentrate on the structure of your BO.
I won’t explain the XCO framework as such in great detail because this will be done by my colleague Sebastian Michler who is the product owner of this library.
Click-through guide
When you just want to try it out in a trial system you find a click-through guide in my following blog
What’s new with 2008
- Structure of the BO can be conveniently entered as a JSON file
- Support for CDS views as a data source
- Generation of service definition and service binding
- Generation of control structures for unmanaged scenarios
I moved the technical documentation to the readme.md file of the GitHub repository.
Limitations
- there are currently no known limitations
What does it do?
Lets have a look at the famous flight sample and take the two tables that are part of the GitHub repository
- ZRAP_TRAVEL_DEMO
- ZRAP_BOOK_DEMO
I assume that you have created a package ZRAP_DEMO and within this package you have created a class zcl_rap_generator_demo that is a clone of the class ZCL_RAP_GENERATOR that is part of this repo.
On the trial systems I have installed the generator for your convenience, so no need to download the repo from GitHub on these systems.
You now only have to create a JSON file like the following
{
"implementationType": "managed_uuid",
"namespace": "Z",
"suffix": "_####",
"prefix": "RAP",
"package": "ZRAP_####",
"datasourcetype": "table",
"hierarchy": {
"entityName": "Travel",
"dataSource": "zrap_travel_demo",
"objectId": "travel_id",
"children": [
{
"entityName": "Booking",
"dataSource": "zrap_book_demo",
"objectId": "booking_id"
}
]
}
}
Based on the two tables and the above mentioned JSON file the generator will create the following repository objects:
- CDS interface views
- CDS projection views
- Metadata Extensions
- Behavior Definition
- Behavior implementation classes
- Service definition
- Service binding
as shown in the following screen shot
Please note that the generated artefacts follow the naming convention which is used for the development in SAP S/4HANA.
How does it work?
Create a package
The generator needs a package where the ABAP artefacts that are going to be generated are stored.
In the above mentioned demo I created a package ZRAP_DEMO.
Create the table(s) (for managed scenarios)
If you want to develop a managed scenario for a green field implementation you will want to create new objects. Here it is recommended to use tables with UUID based key fields since these can be changed such that they also support draft.
For a managed scenario where UUID’s are used as key fields for both, the header as well as the item table, the generator expects that the tables fulfill certain prerequisites:
The key field uuid must be a Universally Unique Identifier (UUID). This mandatory for a managed scenario where early numbering is used. That means where the ABAP framework automatically generates values for the key field when creating the data.
The last four fields of the header table
- created_by
- created_at
- last_changed_by
- last_changed_at
are also mandatory in a managed scenario for a root object. The framework expects these type of fields (with appropriate annotations in the root CDS interface view) so that it is able to check when data has been created and changed.
Download the classes from Github (only needed for non-trial systems)
The tool consists out of 5 classes and a message class that you can download from Github. The package also contains three sample tables for a managed scenario with uuid based key fields.
Call the generator using a console application
Simply duplicate the class ZCL_RAP_GENERATOR. In the above shown screen shots I created a new class called ZCL_RAP_GENERATOR_DEMO.
Copy and paste the content of a json sample file (e.g.) json_managed_uuid_table_demo.txt between the two single quotes.
Be sure to have activated the settings inADT that will wrap and escape the JSON string as I have described in this blog How to wrap long strings automatically in ADT.
Replace the placeholders #### so that they fit to the name of your package and the suffix if you want to use one for your objects.
The edited code should look like follows
When you press F9 the generator should simply tell you Finished.
What happens if I did a typo?
If you for example forget to replace the placeholders an exception will be raised.
This exception is caught by our class since it inherits from the new class cl_xco_cp_adt_simple_classrun which iis provided by the XCO framework and that shows the complete stack of the exception as you know it from ADT.
Roadmap
I plan to add the following features when the next release of SAP Cloud Platform ABAP Environment is available.
- Generation of CDS views for domain fixed values
- Objects will be generated inactive, that means you will have to activate them manually.
- Support for on premise systems
Looking behind the scenes
If you are interested how the RAP Generator works in detail, here is a short technical overview.
The objects of the RAP Business Object tree are generated by the class zcl_rap_bo_generator.
The constructor of this class expects two parameters. The name of the package in which all the objects shall be generated and a root node of type zcl_rap_node that itself can have child nodes that also can have child nodes.
The tricky thing is now how to create the node hierarchy of node proxy classes.
When creating the root node there is the need to specify certain parameters such as the name space, the type of data source that is used (table or CDS view), the implementation type (managed or unmanaged), the type of keys (uuid or semantic and optionally a suffix and a prefix.
These root node specific parameters are inherited by the child nodes when these are added using the method add_child( ).
For each node (the root node and all child nodes) certain node specific parameters have to be set as well, such as the name of the entity and the name of the data source. Based on the data the node objects will contain data such as the names of repository objects taht shall be generated, name of key fields, etc.
Though you can create this node hierarchy programmatically yourself I came to the conclusion that it is better to provide all these parameters in a JSON file.
With the version 2008 of the ABAP stack the XCO libraries provide a powerful way to read and manage JSON files.
By developing a JSON visitor class zcl_rap_xco_json_visitor I was now able to minimize the programmatic effort for the user (the developer).
When having created an appropriate JSON file this file has only to be pasted in a class.
DATA rap_bo_visitor TYPE REF TO zcl_rap_xco_json_visitor .
DATA(json_string) = '<enter your json string here>'.
rap_bo_visitor = NEW zcl_rap_xco_json_visitor( ).
DATA(json_data) = xco_cp_json=>data->from_string( json_string ).
json_data->traverse( rap_bo_visitor ).
The visitor will be used when traversing the JSON file using the class xco_cp_json. It will create the node hierarchy and will pass this data in a final call if_xco_json_tree_visitor~on_end to the class zcl_rap_bo_generator.
METHOD if_xco_json_tree_visitor~on_end.
LOOP AT root_node->all_childnodes INTO DATA(childnode).
childnode->finalize( ).
ENDLOOP.
root_node->finalize( ).
DATA(my_bo_generator) = NEW zcl_rap_bo_generator(
iv_package = root_node->package
io_rap_bo_root_node = root_node
).
DATA(lt_todos) = my_bo_generator->generate_bo( ).
ENDMETHOD.
Since the class zcl_rap_generator (that you should copy beforehand) inherits from the new class cl_xco_cp_adt_simple_classrun that is provided by the XCO framework all exceptions that are thrown by the generator are conviently catched and written to the console log in the very same way as you know it from ADT.
Where can I get the source code?
The source code for RAP Generator can be found on GitHub
https://github.com/SAP-samples/cloud-abap-rap
What is generated?
CDS – Interface view(s)
The interface view are generated such that based on the ABAP field names aliases have been created such that the ABAP field name was converted into camelCase notation.
@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
@EndUserText.label: 'CDS View forTravel'
define root view entity ZI_RAPTRAVEL_DEMO
as select from zrap_travel_demo
composition [0..*] of ZI_RAPBooking_demo as _Booking
{
key UUID as UUID,
TRAVEL_ID as TravelID,
AGENCY_ID as AgencyID,
CUSTOMER_ID as CustomerID,
BEGIN_DATE as BeginDate,
END_DATE as EndDate,
@Semantics.amount.currencyCode: 'CurrencyCode'
BOOKING_FEE as BookingFee,
@Semantics.amount.currencyCode: 'CurrencyCode'
TOTAL_PRICE as TotalPrice,
CURRENCY_CODE as CurrencyCode,
DESCRIPTION as Description,
OVERALL_STATUS as OverallStatus,
@Semantics.user.createdBy: true
CREATED_BY as CreatedBy,
@Semantics.systemDateTime.createdAt: true
CREATED_AT as CreatedAt,
@Semantics.user.lastChangedBy: true
LAST_CHANGED_BY as LastChangedBy,
@Semantics.systemDateTime.lastChangedAt: true
LAST_CHANGED_AT as LastChangedAt,
_Booking
}
CDS – Projection view(s)
The projection view is created with annotations added for currency code.
@AccessControl.authorizationCheck: #CHECK
@Metadata.allowExtensions: true
@EndUserText.label: 'Projection View forTravel'
define root view entity ZC_RAPTRAVEL_DEMO
as projection on ZI_RAPTravel_demo
{
key UUID,
TravelID,
AgencyID,
CustomerID,
BeginDate,
EndDate,
@Semantics.amount.currencyCode: 'CurrencyCode'
BookingFee,
@Semantics.amount.currencyCode: 'CurrencyCode'
TotalPrice,
CurrencyCode,
Description,
OverallStatus,
CreatedBy,
CreatedAt,
LastChangedBy,
LastChangedAt,
_Booking : redirected to composition child ZC_RAPBooking_demo
}
Behavior Definition
The behavior implementation was generated such that the semantic key was marked as read-only. Also a mapping was added that maps the ABAP field names to the field names of the CDS views.And we find a determination that was generated for the objectId field.
implementation managed;
define behavior for ZI_RAPTRAVEL_DEMO alias Travel
implementation in class ZBP_I_RAPTravel_demo unique
persistent table zrap_travel_demo
etag master LastChangedAt
lock master
{
field ( readonly ) TravelID;
field ( numbering : managed ) UUID;
create;
update;
delete;
mapping for zrap_travel_demo
{
UUID = UUID;
TravelID = TRAVEL_ID;
AgencyID = AGENCY_ID;
CustomerID = CUSTOMER_ID;
BeginDate = BEGIN_DATE;
EndDate = END_DATE;
BookingFee = BOOKING_FEE;
TotalPrice = TOTAL_PRICE;
CurrencyCode = CURRENCY_CODE;
Description = DESCRIPTION;
OverallStatus = OVERALL_STATUS;
CreatedBy = CREATED_BY;
CreatedAt = CREATED_AT;
LastChangedBy = LAST_CHANGED_BY;
LastChangedAt = LAST_CHANGED_AT;
}
association _Booking { create; }
determination Calculate on modify { create; }
}
Behavior Definition Projection
implementation projection;
define behavior for ZC_RAPTRAVEL_DEMO alias Travel
{
use create;
use update;
use delete;
use association _Booking { create; }
}
define behavior for ZC_RAPBOOKING_DEMO alias Booking
{
use update;
use delete;
use association _Travel;
}
Metadata Extension View(s)
Last not least you will find it handy that also a Metadata Extension View has been generated that automatically publishes all field on the list page as well as on the object page by setting appropriate @UI annotations.
@Metadata.layer: #CUSTOMER
@UI: {
headerInfo: {
typeName: 'Travel',
typeNamePlural: 'Travels',
title: {
type: #STANDARD,
label: 'Travel',
value: 'TravelID'
}
}
}
annotate view ZC_RAPTravel_demo with
{
@UI.facet: [ {
id: 'idCollection',
type: #COLLECTION,
label: 'Travel',
position: 10
},
{
id: 'idIdentification',
parentId: 'idCollection',
type: #IDENTIFICATION_REFERENCE,
label: 'General Information',
position: 10
},
{
id: 'idLineitem',
type: #LINEITEM_REFERENCE,
label: 'Booking',
position: 20 ,
targetElement: '_Booking'
} ]
@UI.hidden: true
UUID;
@UI.lineItem: [ {
position: 20 ,
importance: #HIGH
} ]
@UI.identification: [ {
position: 20
} ]
TravelID;
@UI.lineItem: [ {
position: 30 ,
importance: #HIGH
} ]
@UI.identification: [ {
position: 30
} ]
AgencyID;
@UI.lineItem: [ {
position: 40 ,
importance: #HIGH
} ]
@UI.identification: [ {
position: 40
} ]
CustomerID;
@UI.lineItem: [ {
position: 50 ,
importance: #HIGH
} ]
@UI.identification: [ {
position: 50
} ]
BeginDate;
@UI.lineItem: [ {
position: 60 ,
importance: #HIGH
} ]
@UI.identification: [ {
position: 60
} ]
EndDate;
@UI.lineItem: [ {
position: 70 ,
importance: #HIGH
} ]
@UI.identification: [ {
position: 70
} ]
BookingFee;
@UI.lineItem: [ {
position: 80 ,
importance: #HIGH
} ]
@UI.identification: [ {
position: 80
} ]
TotalPrice;
@UI.lineItem: [ {
position: 90 ,
importance: #HIGH
} ]
@UI.identification: [ {
position: 90
} ]
CurrencyCode;
@UI.lineItem: [ {
position: 100 ,
importance: #HIGH
} ]
@UI.identification: [ {
position: 100
} ]
Description;
@UI.lineItem: [ {
position: 110 ,
importance: #HIGH
} ]
@UI.identification: [ {
position: 110
} ]
OverallStatus;
@UI.hidden: true
CreatedBy;
@UI.hidden: true
CreatedAt;
@UI.hidden: true
LastChangedBy;
@UI.hidden: true
LastChangedAt;
}
Service Definition
define service ZUI_RAPTRAVEL_M_DEMO {
expose ZC_RAPTRAVEL_DEMO as Travel;
expose ZC_RAPBOOKING_DEMO as Booking;
}
Feel free to check out more of the generated code.
Original Article:
https://blogs.sap.com/2020/05/17/the-rap-generator/