Skip to Content
Technical Articles
Author's profile photo Ajit Kumar Panda

SAP CAP function and action with Unstructured/Dynamic Type Input/Output

While developing enterprise ready cloud applications using Cloud Application Application programming model (CAP), you can define / model custom operations specific to business entity or process via functions or actions in addition to CRUD operations.

  • Actions are used for operations, which add or modify data via POST request.
  • Functions are used to only retrieve data via GET request.

Both actions and functions can be bound which means this custom operation is connected to a business entity or unbound which means this operation can be on one or more entities. More information can be found here.

In most cases, You will have a fixed structure/type input or output for custom operations. However in very few cases you might have an operation where input or output changes time to time. In this blog post, i will show how you can define such an action or function which takes input and provides output with dynamic type.

CAP currently support 3 protocols i.e. OData V4, Rest, GraphQL. Out of these 3 protocols, only in Rest protocol based services you can define functions and actions with dynamic input and/or output.

Let’s look at an example:

Service Definiation:

@protocol: 'rest'
service RootService {

    @open
    type object {};

    action   MyAction(input : object)       returns object;
    function MyFunction(category : Integer) returns object;

}

By annotating the service with @protocol: ‘rest’, the service becomes rest compliant. In above example, cds type “object” is annotated with @open to indicate that the structure or type is unknown. Then it is used in both MyAction and MyFunction operations.

Service Implementation

const cds = require("@sap/cds");

module.exports = cds.service.impl(async function (srv) {

    srv.on('MyAction', async(req)=>{
        req.data["AdditionalField"] = "AdditionalFieldValue";
        return req.data;
    }),

    srv.on('MyFunction', async(req)=>{
        let result = {};
        if(req.data.category === 1){
            result.category = 'Category 1';
            result.field1 = "Random Field Value";
            result.field2 = [{"f1":"f1 Value1"}]
        }else{
            result.category = {"Info":"Category2"};
            result.field1 = "Random Field Value";
            result.field2 = [{"f1":"f1 Value1"}, {"f1":"f1 Value2", "f2":"f2 Value2"}];
        }
        return result;
    })
    
});

In the implementation of MyAction operation, an additional field is added with the incoming data. Simillarly the result of MyFunction operation is different based on the input category. Both of these implementations are coded just to indicate that the resultant structres are dynamic.

Testing the above code and its corresponding responses.

Operation Request/Response
MyAction Request:

POST http://localhost:4004/root/MyAction
Content-Type: application/json

{
    "input":{
        "field1":123,
        "field2":"Value"
    }
}

Response:

{
  "input": {
    "field1": 123,
    "field2": "Value"
  },
  "AdditionalField": "AdditionalFieldValue"
}
MyAction Request:

POST http://localhost:4004/root/MyAction
Content-Type: application/json

{
    "input":{
        "field1":123,
        "field2":[10,20]
    }
}

Response:

{
  "input": {
    "field1": 123,
    "field2": [ 10, 20 ]
  },
  "AdditionalField": "AdditionalFieldValue"
}
MyFunction Request:

GET http://localhost:4004/root/MyFunction(category=1)
Accept: application/json

Response:

{
  "category": "Category 1",
  "field1": "Random Field Value",
  "field2": [
    {
      "f1": "f1 Value1"
    }
  ]
}
MyFunction Request:

GET http://localhost:4004/root/MyFunction(category=2)
Accept: application/json

Response:

{
  "category": { "Info": "Category2" },
  "field1": "Random Field Value",
  "field2": [
    {
      "f1": "f1 Value1"
    },
    {
      "f1": "f1 Value2",
      "f2": "f2 Value2"
    }
  ]
}

Note that this is currently possible in CAP services with Rest Protocol only. Hope this feature will be added for other protocols as well.

More information aboue cloud application programming model can be found here. You can follow my profile to get notification of the next blog post on CAP. Please feel free to provide any feedback you have in the comments section below and ask your questions about the topic in sap community using this link.

 

 

Assigned Tags

      2 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Martin Stenzig
      Martin Stenzig

      Ajit, great example and I use it as well in one of my developments.

      At the moment (and I believe still in version 6.3.2) there seems to be a limitation in the REST adapter to limit the message content to 5MB.

       

      Author's profile photo Ajit Kumar Panda
      Ajit Kumar Panda
      Blog Post Author

      Hi Martin,

      Thanks for reading the blog post. I am not sure about the limitation. However,  the limit seems to be reasonable. Message content beyond 5MB is possible only in case of Media files. Personally I have not come across any other scenario where it could require more than 5MB message content.

      I would recommend you to reach out to support for more information.

      Best Regards, Ajit