Providing Actions and Functions

Objective

After completing this lesson, you will be able to declare and implement domain-specific custom operations

Modeling Actions and Functions in CDS

Earlier, we discussed examples of before and after event handlers. Now let's look at a use case for on handlers.

An on handler actually fulfills requests, e.g. by reading or writing data from or to databases. In our example, we want to use an on handler to implement a domain-specific custom operation.

Watch the video to see how CAP supports the implementation of domain-specific custom operations.

Note

SAP recommends preferring unbound actions/functions, as these are much more straightforward to implement and invoke.

As an example, we want to implement an unbound action called submitOrder for the CatalogService. It is used to place orders (in a very simplified way). We declare this action within the service definition as shown in the following figure.

The submitOrder action has two inbound parameters. The first parameter called book is used to identify the book to be ordered. The parameter is typed using the data type of the ID element from the Books entity defined in the imported domain model.

The second parameter quantity is of type Integer and specifies the number of items to be ordered.

An object with a property called stock is defined as the response type. This property is typed using the data type of the stock element from the Books domain model entity. The value of the property should be the new stock for the book after the order has been executed.

Note

Further examples for defining unbound and bound actions and functions in CDS models can be found in the CAP documentation.

Next, let's take a look at how to implement the declared submitOrder action.

Implementing Actions/Functions

Actions and functions can be implemented as on event handlers. The handlers for actions and functions are very similar to the event handlers that we have already used for CRUD events.

We register the on handler for our unbound submitOrder action in the init() method of the implementation class for the CatalogService (see following figure). To do this, we call the inherited on() method via this.on().

In the example shown, we register the named function this.reduceStock as the implementation for the submitOrder action. To do this, the name of the action is passed to the on() method as the first parameter and the function as the second parameter. We will cover the implementation of the reduceStock function in more detail in the next section.

Note

Please refer to the CAP documentation for information on how to register handlers for bound actions/functions.

Preliminary Method Implementation

The idea for implementing the reduceStock method is as follows.

We update the stock of the ordered book according to the passed quantity on the database and return the updated stock via the return parameter of the action.

The following validations are to be carried out:

  • The passed quantity must be greater than or equal to 1.
  • The passed book ID must exist in the database.
  • The stock for the book must be greater than or equal to the ordered quantity.

If one of these validations fails, an error message should be issued.

To implement this logic, we need to access the database. However, we will not learn how to do this until the next lesson. We will therefore restrict ourselves here to an incomplete, preliminary implementation of the reduceStock method (see following figure).

First, we assign the CSN definition of the Books entity to the Books constant via a destructuring assignment. We will use this constant later to access the database.

On handlers receive, among other things, an instance of cds.Request as an argument. In the example shown, we have called this argument req. The data property of the req object is used to access the values of the parameters book and quantity of the action. We use a destructuring assignment here to assign the passed values to the constants book and quantity.

If the passed quantity is less than 1, we issue a corresponding error message using the req.error() method.

For now, we simply return 10 as the updated stock. We will adjust this accordingly later.

Note

As an alternative to registering an on handler, actions and functions can also be implemented as conventional JavaScript methods on a service implementation class. The method name must then match the name of the action/function. The implementation for our submitOrder action would then look like this:

Code snippet
class CatalogService extends cds.ApplicationService {

  submitOrder(book, quantity) {
    const { Books } = this.entities;

    if (quantity < 1) {
      return req.error('The quantity must be at least 1.');
    }

    let stock = 10;
    return { stock };
  }

}
Expand

Such a method-style implementation of an action/function is not registered in the init() method.

Calling Actions/Functions

Programmatic Usage via APIs

Actions and functions can be called within the coding. CAP provides two different options for this programmatic usage: a generic API and a typed API. For the latter, Node.js equips generated service instances with specific methods whose names and parameters correspond to the declaration of the actions/functions in the service model. These automatically generated methods provide a convenient way of invoking actions/functions.

Note

Examples for the programmatic usage of actions/functions can be found in the CAP documentation.

HTTP Requests

Actions and functions can also be accessed directly via HTTP requests. Functions are invoked via GET requests, while actions are addressed via POST requests.

For example, an HTTP request to call our unbound submitOrder action could look like this:

Code Snippet
1234567
POST <service_url>/submitOrder Content-Type: application/json { "book": "0ec991dc-95c5-4d8f-91e6-f81a92744781", "quantity": 2 }

Note

Further examples of HTTP requests to call unbound and bound actions/functions can be found in the CAP documentation.

Demonstration & Exercise: Define and Implement a Custom Action

Note

As exercise, carry out the step-by-step instructions in the following demonstration yourself in the SAP Business Application Studio.

As a starting point for the exercise, use the outcome of the previous exercise Provide a .after Event Handler if you have successfully completed it. Alternatively, you can also use the branch 12_.after_event_handler from the following GitHub repository as a starting point:

https://github.com/SAP-samples/cap-development-learning-journey

The complete implementation of the simulation can be found in the 13_custom_action branch of the GitHub repository.

Detailed information on the content of the repository and how to use it can be found here.

Watch the video to see how to define and implement a custom action.

Log in to track your progress & complete quizzes