on 2020 Sep 25 10:22 AM
Hallo Experts, I have an issue about READ handler.
I have an Entity with calculation fields. In order to set these fields, I have implemented READ handler.

Triggering a GET request everythings is ok: fields are calculated.

Triggering a PUT request in order to modify entity’s data, fields are empty.


CAP after update runs _readAfterWrite function; this function re-loads entity’s data from DB correctly. After this function, CAP should trigger my READ handler, but it doesn’t.
As a result my calculated fields are empty.

Any ideas on how to solve this problem?
Best regards.
Alice
Help others by sharing your knowledge.
AnswerRequest clarification before answering.
Thank you Sebastian Van Syckel,
the right answer is:
/*
* srv/raw-service.js
*/
const cds = require('@sap/cds')
module.exports = async function() {
const { ReadAfterWrite } = this.entities //> ReadAfterWriteService.ReadAfterWrite
const DatabaseService = await cds.connect.to('db')
// register for ReadAfterWriteService.ReadAfterWrite as this is the target of the SELECT query
DatabaseService.after('each', ReadAfterWrite, (row) => { //> invoked for each row of a READ result
row.text2 = 'virtual text'
})
}
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi alicegavanelli,
Your service-level handlers for read shall not be invoked during non-read events, as this could have unintended consequences. However, your use case is certainly valid and we will discuss it.
In the meantime, you could lower your read handler to the database-level, where it would get invoked, like this:
module.exports = cds.service.impl(async function () {
const DatabaseService = await cds.connect.to('db')
DatabaseService.after('READ', '<qualifier>.RemoteConnections', function (data, req) {
for (let row of data) {
row.description = '<computed value>'
}
})
})
Best,
Sebastian
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
Hi vansyckel,
I've treied to test your solution but this hanlder is never triggered.
I've put a console.log inside my function _setDescriptions and it is never called; both in GET and PUT calls.
Maybe I'm wrong something.
module.exports = cds.service.impl (async function(){
const DatabaseService = await cds.connect.to('db');
DatabaseService.on('READ', 'RemoteConnections', _setDescriptions);
this.before("*", _isAuthenticated );
this.before("CREATE", "AppUsers", _validateCreateAppUsers );
this.before("*", _checkRoleBinding );
this.before(["CREATE", "UPDATE"], "AppUsers", _setMailLowerCase );
this.before("CREATE", _fillCreatedBY );
this.before("UPDATE", _fillChangedBY );
this.before("CREATE", "Messages", _setDefaultMessage );
this.on("changeUserPassword", _changeUserPassword);
this.on("checkRemoteSystem", "RemoteConnections",_checkRemoteSystem);
});
Thank you in advance.
Alice
Hi alicegavanelli ,
That was my bad, sorry. You need to register using the fully qualified name of the entity (e.g., "my.bookshop.RemoteConnections"), as the database service may serve multiple service models and, hence, the service-local entity name ("RemoteConnections") may not be unique. I've updated my answer above.
Best,
Sebastian
Hi vansyckel,
thank you for you help, but I've tried also this new solution and unfortunatly It doesn't work.
Event is not triggered yet.
I've tried both this possibility but nothing:
module.exports = cds.service.impl (async function(){
const db = await cds.connect.to('db');
db.on("READ", "<qualifier>.RemoteConnections", _setDescriptions);
........
});module.exports = cds.service.impl (async function(){
const db = await cds.connect.to('db');
const {RemoteConnections} = db.entities("<qualifier>");
db.on("READ", RemoteConnections, _setDescriptions);
......
});
If I try test db.BEFORE, it is triggered but the calculation fields are empty.
Thank you for you help.
Best,
Alice
Hi alicegavanelli,
"<qualifier>" was meant as a placeholder to make the entity name fully qualified, as I don't know your model.
Example: Given this cds model, you can register a handler for the service entity "CatalogService.Books" or the database entity "sap.capire.bookshop.Books", i.e., the "<qualifier>" is either "CatalogService" or "sap.capire.bookshop".
Best,
Sebastian
Hi vansyckel,
sorry for the misunderstanding, I've put my namespace in the actual code.
Here in the blog I put <Qualifier> just to not show my namespace.
So the problem is real not my misinterpretation of the suggestion.
Now I show you the exactly code.
using {it.conse.wispin.users as wispinUsers } from '../db/users-model';
service WispinService @(path:'/odata/wispinService') {
@Capabilities: { Insertable:true,Updatable:true, Deletable:true }
entity RemoteConnections as projection on wispinUsers.RemoteConnections;
}
module.exports = cds.service.impl (async function(){
const db = await cds.connect.to('db');
db.on("READ", "it.conse.wispin.users.RemoteConnections", _setDescriptions);
........
});Or
module.exports = cds.service.impl (async function(){
const db = await cds.connect.to('db');
const {RemoteConnections} = db.entities("it.conse.wispin.users");
db.on("READ", RemoteConnections, _setDescriptions);
......
});
Thank you in advance.
Alice
Hi alicegavanelli,
My bad again, sorry! It needs to be db.after not db.on, as you want to augment the result and not override the result fetching itself. I'll update my answer above.
BTW, The reason why your code is not called if using db.on is that the on phase is an interceptor stack and you'd need to prepend your handler (db.on(...) appends).
Best,
Sebastian
Hi vansyckel,
thank you for you help but unfortunatly this solution doesn't work.
Problems are:
const db = await cds.connect.to('db');
const {RemoteConnections} = db.entities("it.conse.wispin.users");
db.after(["READ","CREATE","UDPATE"], RemoteConnections, async function(data, req) {
" Set my description
}
)function (data, req) {
for (let row of data) {
row.description = '<computed value>'
}
})So my problem is the result of PUT and POST event that are not changed.
thank you.
Alice
Hi alicegavanelli,
Please see the working example below.
Best,
Sebastian
/*
* db/data-model.cds
*
namespace my.bookshop;
entity ReadAfterWrite {
key ID : Integer;
text1 : String;
virtual text2 : String;
}
*/
/*
* srv/raw-service.cds
*
using my.bookshop as my from '../db/data-model';
service ReadAfterWriteService {
entity ReadAfterWrite as projection on my.ReadAfterWrite;
}
*/
/*
* srv/raw-service.js
*/
const cds = require('@sap/cds')
module.exports = async function() {
const { ReadAfterWrite } = this.entities //> ReadAfterWriteService.ReadAfterWrite
const DatabaseService = await cds.connect.to('db')
// register for ReadAfterWriteService.ReadAfterWrite as this is the target of the SELECT query
DatabaseService.after('each', ReadAfterWrite, (row) => { //> invoked for each row of a READ result
row.text2 = 'virtual text'
})
}
/*
* HTTP
*
PUT http://localhost:4004/read-after-write/ReadAfterWrite(1)
Content-Type: application/json
{ "text1": "text on db" }
response:
{
"@odata.context": "$metadata#ReadAfterWrite/$entity",
"ID": 1,
"text1": "text on db",
"text2": "virtual text"
}
*/
With a PUT call, CAP triggers these events in that orders:
Never call after-READ or on-READ.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
| User | Count |
|---|---|
| 12 | |
| 9 | |
| 6 | |
| 4 | |
| 4 | |
| 4 | |
| 3 | |
| 3 | |
| 2 | |
| 2 |
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.