This is a test message to test the length of the message box.
ABAP RAP Deep Action
Created by Software-Heroes

RAP - Deep Action in OData v4


In this article we will look at actions with deep structures, how we can create them and pass data to an API endpoint.

In this scenario, we extend our complex entity with an additional action. In this case, we want to pass deep data (header and position) to the action in order to be able to execute our own code and work with the entire data package. Thanks to Saptarshi for the question.



In certain situations, we don't just want to get individual data into our RAP object, but rather deep structures that already contain all the data. This allows us to check the data for completeness, trigger various SAP APIs in the system, or even address HTTP interfaces. As a developer, this gives you full control over processing.



Accordingly, there are also requirements for such an action to work. We want to implement the action in a web API so that we can call it from outside. However, we can only work with deep structures in OData version 4, which is the minimum version for such an implementation. With OData v2, an error occurs when it is released and a note is made about support.



In the first step we extend the existing RAP object with an additional action.



Before we can implement the action, however, we need to create the structures for it and define the fields that we want to receive in our service. To do this, let's create the abstract entity that should receive the data.

@EndUserText.label: 'Create Invoice'
define root abstract entity ZBS_S_RAPCreateInvoice
  key DummyKey  : abap.char(1);
      Document  : abap.char(8);
      Partner   : abap.char(10);
      _Position : composition [0..*] of ZBS_S_RAPCreatePosition;


The DummyKey is needed to link the two entities (head and position). Using the composition "_Position", we define the positions that are passed along. The entity for the positions now looks like this:

@EndUserText.label: 'Create Position'
define abstract entity ZBS_S_RAPCreatePosition
  key DummyKey          : abap.char(1);
      Material          : abap.char(5);
      @Semantics.quantity.unitOfMeasure : 'Unit'
      Quantity          : abap.quan(10,0);
      Unit              : abap.unit(3);
      @Semantics.amount.currencyCode : 'Currency'
      Price             : abap.curr(15,2);
      Currency          : abap.cuky;
      _DummyAssociation : association to parent ZBS_S_RAPCreateInvoice on $projection.DummyKey = _DummyAssociation.DummyKey;


Accordingly, we also create a dummy key here and define the fields that we need at position level. We use the association to establish the relationship to the header or the invoices. Finally, we need a behavior definition to define the relationships between each other.

strict( 2 );
with hierarchy;

define behavior for ZBS_S_RAPCreateInvoice alias Invoice
  field ( suppress ) DummyKey;
  association _Position;

define behavior for ZBS_S_RAPCreatePosition alias Position
  field ( suppress ) DummyKey;


Special features here are the type "abstract", the view also needs the addition "with hierarchy" to work properly. If you forget the addition, an error will appear when you include the action and point it out. Finally, we suppress the dummy key, as we do not need it in the data and do not want to provide it. The rest of the behavior definition corresponds to the standard.


Behavior definition

In the next step, we can now extend the behavior definition and create the new action. So let's start in the interface and define a static action in the "Invoice" entity at the top level.

static action CreateInvoiceDocument deep parameter ZBS_S_RAPCreateInvoice;


We need a static action because we want to work without a reference to a data set. We use the DEEP addition to indicate that this is a deep data type. We can then use CTRL + 1 to create the method in the behavior implementation. Finally, we extend the consumption view so that the action is available in the API. 

use action CreateInvoiceDocument;



To access the API from outside, we create a service binding of type API and OData v4.


Now we need to create a communication scenario and carry out the corresponding configuration in the ABAP environment. You can read how this works step by step in this article.


Execution - ABAP

Let's start the first call with EML and try to pass the data to the action in the system. To do this, we create an internal table for the action, fill it with data and pass it to the RAP business object.

DATA lt_document TYPE TABLE FOR ACTION IMPORT ZBS_R_RAPCInvoice~CreateInvoiceDocument.

lt_document = VALUE #( ( %cid   = to_upper( cl_uuid_factory=>create_system_uuid( )->create_uuid_x16( ) )
                         %param = VALUE #( Document  = 'TEST'
                                           Partner   = '1000000004'
                                           _position = VALUE #(
                                               Unit     = 'ST'
                                               Currency = 'EUR'
                                               ( Material = 'F0001' Quantity = '2' Price = '13.12' )
                                               ( Material = 'H0001' Quantity = '1' Price = '28.54' ) ) ) ) ).

       ENTITY Invoice
       EXECUTE CreateInvoiceDocument FROM lt_document
       FAILED DATA(ls_failed_deep)
       REPORTED DATA(ls_reported_deep).


This completes the first test and we can test the path via the API.


Execution - POSTMAN

As a second test, we want to call the endpoint in the system via POSTMAN in order to test the API and the transfer. To do this, we have to perform two steps: create a CSRF token (GET) and trigger the action (POST), where we then transfer the data.



To do this, we create a new collection (folder) in POSTMAN and under the "Authorization" tab we switch to "Basic Authentication" so that we can log in to the endpoint using a user and password. This means that all requests under the collection receive this method and we no longer have to worry about logging in for each request.


Hint: The entry of user and password is for demonstration purposes only; normally such information is stored in environment variables, where it is also encrypted.


CSRF Token

Before we can execute the action, we need a token. To do this, we create a new GET request under our collection and use the endpoint of our service. You can find this, for example, in the Communication Arrangement in the ABAP Environment, where we set up the scenario.


In the header we also include the field "x-csrf-token" with the value "fetch". This tells the endpoint that we are requesting a new token from it. After executing, you will find the token in the response header in the field "x-csrf-token".



Now we want to execute the actual action. To do this, we first need the endpoint of our action from the service. This consists of various components:



We have already used the service URL for the token, followed by the entity where we defined the action. You can find the namespace in the service metadata. The last part of the URL would then look like this:



We now create a POST request and specify the token and the content type in the header.


In the body we enter the data we want to pass to the endpoint, using JSON as the format. Make sure that the numbers are actually passed in numeric format. The data is passed as an object, with the association "_Position" consisting of your array, which in turn contains objects.



So that we can check whether the call works, we want to debug the data in the implementation. To do this, we need to set a breakpoint for the technical user, as this is the user who executes the code. In the ABAP Development Tools, you would need to go to the "Properties" of the ABAP project. Under "ABAP Development -> Debug" you will find the settings for debugging another user. Here you can use the search function to find the Communication User (CC); the Named User is only needed for API access.


If we now execute the request, the debugger opens and we can access the information via the import variables.


Now you can freely take over the further implementation and design it according to your wishes.


Full example

All changes made to the objects and the new objects can be found in the commit of the GitHub repository if you want to recreate the individual steps or are still missing information.



Implementing a deep-structure action can feel a bit special when it comes to defining the abstract RAP entity. However, the actual implementation and invocation is then in the RAP standard.


SAP Help - Input Parameter (Deep)

Included topics:
Comments (2)

And further ...

Are you satisfied with the content of the article? We post new content in the ABAP area every Friday and irregularly in all other areas. Take a look at our tools and apps, we provide them free of charge.

BTP - Connect On-Premise (Consumption Model v2)

Category - ABAP

In this article we want to provide another update on the connection of on-premise systems and how this is done with a communication arrangement.


RAP - Show app count (Tile)

Category - ABAP

This example is about displaying a counter on the tile of a Fiori Elements application and how such a thing can be implemented.


RAP - Generator (Fiori)

Category - ABAP

In this article we will look at the more complex RAP generator as a Fiori Elements application and what advantages you have with it.


RAP - Generator (ADT)

Category - ABAP

Today, let's take a look at the RAP Generator, which is already integrated into ABAP Development Tools, and how you can use it to easily build new RAP apps.


RAP - Unmanaged (Remote)

Category - ABAP

Here you can learn more about the unmanaged RAP object with a remote data source and how you can implement such scenarios.