cancel
Showing results for 
Search instead for 
Did you mean: 

Receive arbitrary payloads with CAP service?

Hi experts,

I am wondering how I can make a CAP service receive and process an arbitrary payload? Background: A CAP application should offer a webhook for another application X to submit notifications to. X sends something like a https://cloudevents.io/ payload.

First I tried to model an entity matching the payload I am expecting, but that doesn't work as the payload includes names incompatible with CDS syntax.

I then tried a simple service with one action and a custom implementation. This works as in the request is successfully posted to the action with 204. However, I can't find the payload in my custom implementation.

service EventsService {
    action notify();
}
module.exports = srv => {
    srv.on('notify', async (req) => {
        console.log(req.data);
    })
}

Example payload

{"event-type": "SalesQuote.Root.Updated","event-type-version": "v1","event-id": "00163e87-5338-1eda-a0dd-83d76d55a94a","event-time": "2020-04-20T09:48:41Z",
"data": {"root-entity-id":"00163E8753AA1EDA9CE545168DEE707B","entity-id":"00163E8753AA1EDA9CE545168DEE707B"}}

req.data is empty. This makes sense I suppose, since I modelled the action without any parameters. I looked through req._.req but can't find my payload there either.

I tried payload content types other than JSON but they can't be deserialized.

Is there a way to access arbitrary payload data in a CAP service? Am I off in my approach and is there another pattern to implement a webhook notification?

Best regards,

Manuel

0 Kudos

Has anyone here insight on my original question? How would I go about building a service with CAP and receive and process arbitrary payloads with this service. Like receive an XML document, or even a binary?

Accepted Solutions (1)

Accepted Solutions (1)

marcmaurí
Participant
0 Kudos

Hi Manuel,

It might help the answer I received here some days ago.

It might be useful depending on what you need to do with this data. In my case, it had to simply return a string and a return code. I solved my requirement just adding the following lines in my express server file:

await app.put('/callback/v1.0/tenants/*', function (req, res) {
     .... do some stuff
     res.status(200).send(tenantAppURL);
});
await app.delete('/callback/v1.0/tenants/*', function (req, res) {
    ... do some stuff
    res.status(204);   
}); 

Hope this helps.

Best regards,

Marc

I did end up using something similar to this: added my own express router and send whatever came in to the rest of the CAP app via enterprise messaging, after processing the incoming XML document. Thus I can write the business logic still in CAP handlers attached to the messaging topics.

Answers (1)

Answers (1)

david_kunz2
Advisor
Advisor
0 Kudos

Hi,

>> First I tried to model an entity matching the payload I am expecting, but that doesn't work as the payload includes names incompatible with CDS syntax.

Did you try to quote it, e.g.

 function myFunc (
        "Key": String
    ) returns String
0 Kudos

Hi David,

this (or rather the ![] syntax) did get me a little further, but I am struggling defining a more complex payload like the one in my example. If I declare data as in the cloud event syntax, the resulting metadata flattens the inline struct type and the payload can't be deserialized.

service EventsService {
    entity Events {
        key ![event-id]: UUID;
        ![event-type]: String;
        ![event-type-version]: String(2);
        ![event-time]: DateTime;
        data: {
            ![root-entity-id]: String;
            ![entity-id]: String;
        };
    }
}
<EntityType Name="Events">
<Key>
<PropertyRef Name="event-id"/>
</Key>
<Property Name="event-id" Type="Edm.Guid" Nullable="false"/>
<Property Name="event-type" Type="Edm.String"/>
<Property Name="event-type-version" Type="Edm.String" MaxLength="2"/>
<Property Name="event-time" Type="Edm.DateTimeOffset"/>
<Property Name="data_root-entity-id" Type="Edm.String"/>
<Property Name="data_entity-id" Type="Edm.String"/>
</EntityType

However my payload is

{
    "event-type": "SalesQuote.Root.Updated",
    "event-type-version": "v1",
    "event-id": "00163e87-5338-1eda-a0dd-83d76d55a94a",
    "event-time": "2020-04-20T09:48:41Z",
    "data": {
        "root-entity-id": "00163E8753AA1EDA9CE545168DEE707B",
        "entity-id": "00163E8753AA1EDA9CE545168DEE707B"
    }
}

Which then results in

"Error while deserializing payload. 'data' does not exist in type 'EventsService.Events'."

0 Kudos

After some tinkering I managed to make above example work by modelling it with an association. The "deep insert" then does the trick and my example payload is deserialized (and shows up in req.data of my implementation).

service EventsService {
    entity Events {
        key ![event-id]: UUID;
        ![event-type]: String;
        ![event-type-version]: String(2);
        ![event-time]: DateTime;
        data: Association to Data;
    }
    entity Data {
        ![root-entity-id]: String;
        key ![entity-id]: String;
    }
}

However, now I have the next problem, that I can't build my project anymore:

[ERROR] srv/event.cds:12:13-25: Quoted identifiers are not allowed in plain mode of toHana: "entity-id"
[ERROR] srv/event.cds:3:13-24: Quoted identifiers are not allowed in plain mode of toHana: "event-id"
[ERROR] srv/event.cds:6:9-22: Quoted identifiers are not allowed in plain mode of toHana: "event-time"
[ERROR] srv/event.cds:4:9-22: Quoted identifiers are not allowed in plain mode of toHana: "event-type"
[ERROR] srv/event.cds:5:9-30: Quoted identifiers are not allowed in plain mode of toHana: "event-type-version"
[ERROR] srv/event.cds:11:9-26: Quoted identifiers are not allowed in plain mode of toHana: "root-entity-id"

I don't have any DB dependencies on my project, it's just a service. There is nothing to deploy to Hana. How can I make my project build without using a DB backend? I would handle everything I need in custom implementations.

0 Kudos

I got around this by specifying the build tasks explicitly in .cdsrc.json - or rather, NOT specifying a build taks for DB.

{
    "build": {
        "target": "gen",
        "tasks": [
            {
                "for": "node-cf",
                "src": "srv",
                "options": {
                    "model": [
                        "db",
                        "srv",
                        "app"
                    ]
                }
            }
        ]
    }
}


I am still wondering about my original question though: How to receive arbitrary payloads in a service - without having to model the payload?

0 Kudos

Please - where is the syntax "![]" documented? Can't find it here:

https://cap.cloud.sap/docs/cds/cdl

0 Kudos

I can't find it documented anywhere either, but I found a warning in the build process that recommended to use ![] syntax over quoted property identifiers.