on 04-23-2020 11:18 AM
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
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
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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.
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
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
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'."
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.
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?
Please - where is the syntax "![]" documented? Can't find it here:
User | Count |
---|---|
83 | |
11 | |
10 | |
8 | |
6 | |
6 | |
6 | |
6 | |
5 | |
5 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.