Salvation after 5+ years and variety of technological marriages, ABAP RAP is here finally. Fiori and WebAPI development can take a deep breath. This post is about common and uncommon snippets for ABAP RAP. This post is updated on regular manner including some facts about RAP as well.

RAP_Components-1-1024x508 ABAP RESTful Application Programming Model
ABAP Restful Application Programming Model

CDS Data Modeling

Administrative Data

At the beginning, we had to manually update admin fields of changes within BOPF determinations manually. Alternatively we created reusable base determination classes combined with custom Z annotations and reuse it within CDS views and generated BOPF objects freely. We can relax a bit more, RAP supports this out of the box, you just need to annotate your timestamp and username fields, system does the rest.

"Database table fields

  created_by            : syuname;
  created_at            : timestampl;
  last_changed_by       : syuname;
  last_changed_at       : timestampl;
  local_last_changed_at : timestampl;

--CDS: Data Modelling

      @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,
      @Semantics.systemDateTime.localInstanceLastChangedAt: true
      local_last_changed_at as LocalLastChangedAt,
Admin-fields ABAP RESTful Application Programming Model

Admin fields populated automatically

Inheriting authorization from lower level CDS views

Let’s say You wrap a standard CDS view into a custom one (Z/Y). Doing this the CDS Access control is lost, due it is not inherited when wrapping a CDS Entity. You like to provide authorization check for READ access, which is actually the same as in the standard CDS View. Instead of redoing, You have the possibility to inherit the CDS Access control attached to the view from which Your custom view is selecting. This is possible until You include all those properties used in the Access Control in Your CDS entity as well. In our case the custom CDS Entity ZI_PurchaseOrder_Simple select from standard CDS I_PurchaseOrderItemAPI01, which has all the required access control in place by SAP already.

@EndUserText.label: 'Inheriting access control'
@MappingRole: true
define role ZI_RAP_PurchaseOrder_Simple {
	grant select on ZI_PurchaseOrder_Simple
	  where
	    inheriting conditions from entity I_PurchaseOrderItemAPI01;
}

In case You wrap the ZI_PurchaseOrder_Simple into another CDS Entity, You need to repeat this step, but in that case You continue the inheritance chain, inheriting conditions from ZI_PurchaseOrder_Simple.

@EndUserText.label: 'Inheriting access control'
@MappingRole: true
define role ZC_RAP_PurchaseOrder_Simple {
grant select on ZC_RAP_PurchaseOrder_Simple
  where
    inheriting conditions from entity ZI_RAP_PurchaseOrder_Simple;
}		

Behaviour Defintion

Mapping

Mapping in behavior definition is not only used for mapping fields of DB table fields for persistency.

Let’s say You have a managed scenario with unmanaged save, where the RAP framework is responsible for the interaction phase, and You are responsible for the peristency. An example could be to trigger an API class or function module. In this case the mapping serves not as matching the DB table fields, but to map the fields of the CDS Entity rather to the importing structure of the API.

An optional and really nice feature is that the X structure of a BAPI is also supported:

  mapping for bapimepoheader control bapimepoheaderx corresponding
    {
      PurchaseOrder          = po_number;
      PurchasingOrganization = purch_org;
      PurchasingGroup        = pur_group;
      Supplier               = vendor;
    }

So far so good, but what is the benefit ? The benefit is that You can do the mapping in data object constructor expressions within the behavior implementation. I find this really cool, we neither need to do mapping at multiple places nor to implement the mapping logic by ourselves.

DATA: ls_po_header TYPE bapimepoheader, po_entity type ZI_PurchaseOrder_Simple.

ls_po_header = CORRESPONDING #( po_entity MAPPING FROM ENTITY ).

Behavior Implementation

Reporting Messages

Using the helper method NEW_MESSAGE

The easy way of reporting messages in validations is to use the generic helper method of the base class of Your behavior handler class ( CL_ABAP_BEHAVIOR_HANDLER / CL_ABAP_BEHV ). You can mark the field what caused the issue exactly providing together with the Message Class and ID.

       IF material-active NE abap_true.
          reported-purchaseorder = VALUE #( BASE reported-purchaseorder
                                            ( %tky = po-%tky
                                              %element-material = if_abap_behv=>mk-on
                                              %msg = me->new_message( severity = if_abap_behv_message=>severity-error
                                                                      id       = 'ZPO'
                                                                      number   = '001'
                                                                      v1 = |{ po-Material ALPHA = OUT }| ) ) ).
        ENDIF.

Using custom message class

If You like to find examples for custom message classes, Open the ABAP Type Hierarchy for the interface IF_ABAP_BEHV_MESSAGE.

        APPEND VALUE #(  %tky        = po-%tky
                         %state_area = 'VALIDATE_PO'
                         %msg        = NEW zcm_po_messages(
                                         po_id  = po-DocumentNr
                                         textid = zcm_po_messages=>po_unkown
                                         severity = if_abap_behv_message=>severity-error )
                         %element-DocumentNr = if_abap_behv=>mk-on
                      ) TO reported-purchaseorder.

Execution sequence of determinations

You are in charge of executing dependent operations in the right order. The order in Behavior Definition (BDEF) does not count!

This like with kernel driven BAdI implementations, RAP determination execution sequence is non-deterministic. History: BOPF is a pure ABAP solution w/o kernel, there You have the possibility to define a relative order (before/after aka. predecessor/successor).

What You can do, is that for an entity in the BO hierarchy You encapsulate dependent determinations into same handler method, where You are in control for the order of execution.

Point of no return

This is about save sequence and Your save handler class in the behavior pool inheriting from cl_abap_behavior_saver, in case You have such.

When the method adjust_numbers is called in the save sequence that is the point of no return, which means returning/reporting messages has no effect on the steps of the save execution, the RAP framework will not stop. In case any error occurs during the save phase, You have to raise a short dump, like:
RAISE SHORTDUMP NEW zcx_po( textid = VALUE #( msgid = <msgid> msgno = <msgno> ) ).

That is why You need to cover as many case You can in the interaction phase with the validations, so that You do not reach that state with the dump.

Share this content: