Technical Articles
Consuming RAP generated OData from BTP ABAP environment in AppGyver Part 1 – READ operation
The main audiences of this blog are:
- Experienced AppGyver citizen developers who are keen to learn how to implement OData in SAP backend using CDS technology and RAP framework.
- Experienced ABAPers who are also AppGyver beginners and are keen to learn how RAP generated OData is integrated in Appgyver.
- Experienced in neither but are interested in the topic.
BTP ABAP Environment is chosen as backend because:
- As common as consuming from S4, it’s also common scenario to use BTP ABAP environment as backend
- No need for S4(on-prem or cloud) to do this hands-on and everything is done in BTP front to back.
Prerequisite:
- BTP non-trial subscription
- ABAP Environment instance & AppGyver service in BTP.
1. Create OData data source and Service
1.1 Create Ztable and populate with data
The detailed steps of 1.1 are also in this tutorial.
1.2 CDS basic view, Projection view and Service definition/binding
The detailed steps of 1.2 are also in this tutorial.
Create CDS basic view ZUSER based on the Ztable. Date fields are modified to simple DD.MM.YYYY format. Without this, AppGyver will receive the date in Unix Epoch format and conversion on frontend is required. This is not ideal for low code/no code because we want to keep as much logic in the backend as possible.
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'User info'
define root view entity ZUSER as select from zuser_data as user
{
key user.bname as userID,
concat(
concat(substring( cast(user.date_from as abap.char(10)), 7, 2 ), '.'),
concat(substring( cast(user.date_from as abap.char(10)), 5, 2 ),
concat('.', substring( cast(user.date_from as abap.char(10)), 1, 4 )))
) as date_from,
concat(
concat(substring( cast(user.date_to as abap.char(10)), 7, 2 ), '.'),
concat(substring( cast(user.date_to as abap.char(10)), 5, 2 ),
concat('.', substring( cast(user.date_to as abap.char(10)), 1, 4 )))
) as date_to,
user.name_first as name_first,
user.name_last as name_last,
/*-- Admin data --*/
@Semantics.user.createdBy: true
created_by,
@Semantics.systemDateTime.createdAt: true
created_at,
@Semantics.user.lastChangedBy: true
last_changed_by,
@Semantics.systemDateTime.lastChangedAt: true
last_changed_at
}
Create a projection view projecting the basic view. UI annotations are only needed for Fiori element app, which is not relevant for this blog so they can be removed upon your choice.
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Projection view for user info'
@UI: {
headerInfo: { typeName: 'User', typeNamePlural: 'Users', title: { type: #STANDARD, value: 'userID' } } }
@Search.searchable: true
define root view entity ZC_USER
provider contract transactional_query
as projection on ZUSER
{
@UI.facet: [ { id: 'User',
purpose: #STANDARD,
type: #IDENTIFICATION_REFERENCE,
label: 'User info',
position: 10 } ]
@UI: {
lineItem: [ { position: 10, importance: #HIGH } ],
identification: [ { position: 10, label: 'User ID'} ],
selectionField: [ { position: 10 } ] }
key userID as userID,
@UI: {
lineItem: [ { position: 20, importance: #HIGH } ],
identification: [ { position: 20, label: 'Valid From' } ],
selectionField: [ { position: 20 } ] }
date_from as date_from,
@UI: {
lineItem: [ { position: 30, importance: #HIGH } ],
identification: [ { position: 30, label: 'Valid to' } ],
selectionField: [ { position: 30 } ] }
@Search.defaultSearchElement: true
date_to as date_to,
@UI: {
lineItem: [ { position: 40, importance: #HIGH } ],
identification: [ { position: 40, label: 'First name' } ],
selectionField: [ { position: 40 } ] }
name_first as name_first,
@UI: {
lineItem: [ { position: 50, importance: #HIGH } ],
identification: [ { position: 50, label: 'Last name' } ],
selectionField: [ { position: 50 } ] }
name_last as name_last,
@UI.hidden: true
last_changed_at as LastChangedAt
}
Finally create service definition from the projection view and create service binding from the service definition. After publishing the service, access the Service URL + Entity set to see if the data stored in your table is displayed. So in my case, the URL is “https://yourBTPaccount/sap/opu/odata/sap/ZSRVBIN_USER/C_USER”.
@EndUserText.label: 'Service definition for ZC_USER'
define service ZSRVDEF_USER {
expose ZC_USER as C_USER;
}
2. Add behaviors to CDS views
The detailed steps of 2 are also in this tutorial.
Defining behavior to the basic view and projection view will allow the OData to perform CRUD operation performing direct update of source database table. Create New Behavior Definition from basic view ZUSER. It’s important to set the source database table(ZUSER_DATA) as the persistent table and mapping of the fields. The implementation class zbp_user is implemented and activated but it’s in the default state and no logic is added.
managed implementation in class zbp_user unique;
strict;
define behavior for ZUSER alias USER
persistent table ZUSER_DATA
lock master
authorization master ( instance )
etag master last_changed_at
//late numbering
{
// administrative fields (read only)
field ( numbering : managed, readonly ) userID;
// administrative fields (read only)
field ( readonly ) last_changed_at, last_changed_by, created_at, created_by;
// mandatory fields
field ( mandatory : create ) date_from, date_to, name_first, name_last;
create;
update;
delete;
mapping for ZUSER_DATA
{
userID = bname;
date_from = date_from;
date_to = date_to;
name_first = name_first;
name_last = name_last;
}
}
Create New Behavior Definition for projection view ZC_USER as well.
projection;
strict;
define behavior for ZC_USER alias C_USER
use etag
{
use create;
use update;
use delete;
}
3. Communication configuration
The detailed steps of 3.1 are also in this tutorial step 9~11.
The detailed steps of 3.2 are also in this tutorial.
3.1 Create a communication scenario
3.2 Communication arrangement
Go to ABAP environment Dashboard by accessing ADT and right click the project, click properties, under ABAP Development access the System URL. Alternatively, you can go to BTP subaccount and click on the ABAP environment instance.
3.3 Configure BTP destination
Go to your BTP subaccount and navigate to Destination under Connectivity on the left side pane. Click on New Destination. Setup the connection as below. Make sure to set the user and password created in step 3.2 and add HTML5.DynamicDestination = true” and “WebIDEEnabled = true” as Additional Properties. Make sure to set the service URL from the communication arrangement as well. Save the connection and click on Check Connection. It should return “Connection to “XXXX” established. Response returned: “200: OK”.
4. AppGyver setup
4.1 Receive service in AppGyver
4.2 Create list app to display data
Next step..
My next blog will go through the steps to implement AppGyver Create/Update/Delete process to handle RAP generated OData.
However, it seems that Create/Update are not supported yet by AppGyver BTP authentication for handling SAP OData service(haven’t tested Delete). There might be some configuration I’ve missed to make them work but I’ve reported this issue and still waiting for the analysis.
https://appgyver.canny.io/bug-reports/p/btp-authentication-csrf-token-error-with-create-record
Excellent blog!! waiting for the part -2
Hi, part 2 is out covering all CRUD operations.
https://blogs.sap.com/2023/02/01/new-alternative-for-building-transactional-report-is-nocode-crud-now-supported-for-sap-appgyver-classic/