Skip to Content
Technical Articles
Author's profile photo Jhodel Cailan

Keep the Core Clean with Rizing CDSX

In my last blog post Keep the Core Clean with CAP Model, we found out that CAP’s service API doesn’t support handling the draft-generated properties out-of-the-box, however, it’s technically feasible to handle this ourselves in the custom handler. And you might be wondering, has anyone done it? Well, the answer is yes, because I did!

Let me backtrack and recall how I got here in the first place. First, I wanted to do a side-by-side extensibility using standard OData APIs with the goal in mind to keep the core clean. Then I looked into the available frameworks to help me achieve that, which are the RAP and CAP frameworks. And then I quickly realized that there was very little support in making this happen because both frameworks only supported the OData V2 service (non-draft) for this scenario. The golden standard now (the year 2023) for building applications from scratch is using Fiori Elements with draft capabilities (OData V4 is implied here). It’s hard to settle for something less, especially when development is so much easy when using Fiori Elements with draft.

Therefore, if there’s a tiny sliver of chance of making this functionality work, then I will take it. And so I did take that opportunity since CAP doesn’t completely block me from implementing functionality through a custom handler! However, the road was bumpy and so painful because there are so many case scenarios that you need to write the custom logic for. And I believe that no developer should be subjected to such kind of torture ever again! That’s why I made the Rizing CDSX (@rizing/cds-extension) module so you don’t have to suffer like I did. If that sounds interesting to you, then please continue reading.

 

 

The Demo Project


Just like in the previous blog post that uses the CAP framework, the demo project can be found in the same GitHub repository: https://github.com/jcailan/cap-fe-samples.

 

Data Model

For the data model definition, we need to create an entity which I call Shadow Persistence Entity. This Shadow Persistence Entity (or SPE for short) simply means that the entity from a remote service is recreated as a CAP persistence entity. In some ways, it’s like tricking the CAP framework that this is the real persistence entity, but in actual fact, the data will be persisted in a remote server using an external service.

Data Model: db > remote.cds

context remote {

    @cdsx.api: 'product_external'
    entity Products {
        key ID               : UUID;

            @mandatory
            name             : String;

            @mandatory
            description      : String;
            imageUrl         : String;
            releaseDate      : DateTime;
            discontinuedDate : DateTime;

            @mandatory
            price            : Decimal(16, 2);
            height           : Decimal(16, 2);
            width            : Decimal(16, 2);
            depth            : Decimal(16, 2);

            @(
                mandatory,
                assert.range: [
                    0.00,
                    20.00
                ]
            )
            quantity         : Decimal(16, 2);

            @mandatory
            UnitOfMeasure_ID : String(3);

            @mandatory
            Currency_ID      : String(3);
            DimensionUnit_ID : String(2);

            @mandatory
            Category_ID      : String(1);
            Supplier_ID      : UUID;
            createdBy        : String;
            createdAt        : Timestamp;
            modifiedBy       : String;
            modifiedAt       : Timestamp;
    }
}

The above cds file is an example of SPE. It’s a replication of the entity definition of the Products entity for the remote service called product_external. The SPE contains the typical CAP CDS annotations except only for one annotation at the entity level — @cdsx.api. The @cdsx.api annotation is used to specify that this Products entity should be proxied by an external service called product_external. If you have already worked on CAP that consumes external services before then you are already aware that this external service is configured in the package.json file, otherwise, you might want to check my earlier blog post that discusses this topic.

 

Service Model

The service model definition is very straightforward, it just contains the projection to the SPE Products entity with the @odata.draft.enabled annotation.

Service Model: srv > ProductCDSX.cds

using {remote} from '../db/remote';

service ProductCDSX {

    @odata.draft.enabled
    entity Products as projection on remote.Products;

}

 

Remote Service Configuration

It goes without saying that I have used cds import command to import the remote service’s metadata.xml file to generate the so-called service definition file (csn file). And with this step, a remote service configuration is automatically added to my package.json file.

Configuration: package.json

	"cds": {
		"requires": {
			"product_external": {
				"kind": "odata-v2",
				"model": "srv/external/products"
			}
		}
	}

 

Rizing CDSX Module

The Rizing CDSX module (or just CDSX) is easy to set up. Of course, like any other node module, you need to install it first and then bootstrap it into CAP’s runtime by using one of CAP’s available hook events.

Bootstrap CDSX: srv > server.js

const cds = require("@sap/cds");
const cdsx = require("@rizing/cds-extension");

cds.on("bootstrap", (app) => {
	cdsx.load();
});

Just like that, it’s very easy!

 

Testing


Now it’s time to test the CAP service powered by CDSX! Since we are using the OData draft feature, it’s best that we test this using a Fiori Element app. Luckily, the sample project is already fitted with a generated Fiori Element app and is ready to test.

  • just run the command cds watch as normal
  • then launch the only web application available — launchpage.html
  • finally, click on the tile Manage Products (External Service) CDSX

And you will be able to test a Fiori Element app backed by a CAP-based OData V4 service with draft capabilities.

 

Closing


In my journey of finding the ideal solution for keeping the core clean and trying to make use of the latest available SAP frameworks, I found myself hitting some limitations. Through CAP’s guiding principle of being an open but opinionated framework, I found a tiny sliver of hope that I could bridge the gap and implement the solution myself. And that solution was developed as a reusable node module which then gave birth to what I call now the Rizing CDSX — it is easy to use and I find it very helpful as a complementary tool/framework that helps in keeping the core clean. I hope you may find it useful as well.

 

 

 

~~~~~~~~~~~~~~~~

Appreciate it if you have any comments, suggestions, or questions. Cheers!~

Assigned Tags

      4 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Ahmed Ali Khan
      Ahmed Ali Khan

      Great explanation Jhodel, It's very informative.

       

      I was wondering how easy it is to create a value help using fiori elements in your cds entity you have created.

      Suppose, I click on create button and in that i want to choose the product in product field from a value help and fill rest of the fields for that product, the value help will fetch the list of products available in the api

       

      I had a previous experience that it is not achieveable easily by using fiori elements and for this kind of scenario i have to choose the UI5 at last.

       

      Try this kind of scenario sometime as well and if you achieve it very simply do share your experiences please.

      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Hi Ahmed,

      Thanks for the feedback! I'm not sure I understand your requirement, because the example you gave is a bit off to me. The fictional API I used in the demo is products, so effectively the app I created is like a master data management app for products. Therefore, I can't use the same external service as value help, especially when creating a new product.

      But if what you really mean is to create a value help against another property, let's say category, then it's relatively easy. All you have to do is create another entity for this value help like you normally would using CAP (persisted in HANA Cloud DB), or you could use another external API and then follow the same principle of creating an SPE with the @cdsx.api annotation.

      Author's profile photo David Kunz
      David Kunz

      Hi Jhodel Cailan ,

       

      Cool stuff! Watch out for my talk at https://recap-conf.dev/ "A Leaner Approach to Draft", that might be useful for you!

       

      Best regards,

      David

      Author's profile photo Jhodel Cailan
      Jhodel Cailan
      Blog Post Author

      Hi David Kunz ,

      Thanks for the feedback! Will definitely watch out for that, my talk will start in 10 minutes after yours. See you there! 🙂

      Thanks and regards,

      Jhodel