cancel
Showing results for 
Search instead for 
Did you mean: 

Read with an after hook implementation fails from Fiori Elements

rpanneel
Participant
0 Kudos

Hello CAP Enthusiasts,

During my local testing I ran into a strange issue using the Fiori Elements front-end.

I've added a virtual spotsAvalaible property to my entity Courses. This virtual property I'm going to fill in the after hook of the READ of the Courses:

srv.after('READ', 'Courses', (courses, req) => {
            // problem from fiori: couses has counted object --> $count
            return courses
                .map(async course => {
                    // Get the reservations for this course
                    const reservations = await cds.transaction(req).run(
                        SELECT.from(Reservations).where({course_ID: course.ID})
                    )
                    // Caluclate spots taken
                    const spotsTaken = reservations.reduce((total, reservation) => total + reservation.quantity, 0)
                    course.spotsAvailable = course.spots - spotsTaken
                })
        })

This works when I use postman and do a get on the courses. (http://localhost:4004/training/Courses)

{
"@odata.context": "$metadata#Courses",
"@odata.metadataEtag": "W/\"BmT9sdj4fnWqz4BxxDN0o+XSNGD5CW69Y8wbhe1uRYk=\"",
"value": [
{
"ID": "ee0b3d38-729d-4237-a7fe-b922f753e87e",
"modifiedAt": null,
"createdAt": "2020-01-18T18:45:57Z",
"createdBy": "anonymous",
"modifiedBy": null,
"title": "Some course",
"days": 1,
"spots": 15,
"courseType": "Technical",
"spotsAvailable": 15,
"trainer_ID": "880331ae-ae41-4a46-b49b-b7a3cbd049b0"
}
]
}

But when using the Fiori elements front-end the hook crashes with following error. For the following request in the $batch:
GET Courses?$count=true&$select=ID,days,spots,title,trainer_ID&$expand=trainer($select=ID,name)&$skip=0&$top=30 HTTP/1.1

POST /training/$batch

READ Courses {'$count': 'true','$select': 'ID,days,spots,title,trainer_ID','$expand': 'trainer($select=ID,name)','$skip': '0','$top': '30'}READ Courses {'$count': 'true','$select': 'ID,days,spots,title,trainer_ID','$expand': 'trainer($select=ID,name)','$skip': '0','$top': '30'}[2020-01-19T14:45:18.101Z | ERROR | 1221510]: Cannot read property 'run' of undefinedTypeError: Cannot read property 'run' of undefinedat C:\repo\CAP\training-reservations\node_modules@sap\cds-services\lib\connect\Transaction.js:84:27

When debugging i notice that the after-hook gets called a second time, but this time the courses-parameter is filled with a counted object:

[{counted: 1}]

I have logged the same issue on the cap-community: https://github.com/sapmentors/cap-community/issues/32

Is there a way to solve this?

Thanks!

Kind regards,

Robin

Accepted Solutions (0)

Answers (2)

Answers (2)

david_kunz2
Advisor
Advisor
0 Kudos

Hi Robin,

Yes, you need to manually check that in your after handler.

Something like this:

if (req.query.SELECT.columns.filter(({ func }) => func === 'count').length) return

Best regards,
David

rpanneel
Participant
0 Kudos

Hi David,

Sorry for the delay in my answer. But when I add the return statement in the after-handler I still receive the same error. It looks like the method _matchHandlers of the After.js file has an undefined promise for the count-call.

When the call for the actual entity is handled everything is ok.

But when the count call is handled fnReturn is undefined...

And then he seems to get confused and throw the error.

Any idea what might be causing this?

Thanks.

Regards, Robin

david_kunz2
Advisor
Advisor
0 Kudos

Hi Robin,

That looks weird. Can you tell us which version of @sap/cds you're using? Can you also share the whole custom handler? Thanks a lot and best regards, David

david_kunz2
Advisor
Advisor
0 Kudos

Hi Robin,

Whenever you request a count on an entity collection, we trigger two read requests.

The first one works in your example. The second one fails.

To work around this problem, you need to prevent your after handler from running if it is the count request (i.e. the req.query object only selects `count(*)` as a column).

Best regards,
David

rpanneel
Participant
0 Kudos

Hello David,

Thanks for your answer. But how do I prevent the after handler from running if it is a count request?

Do I need to add a check inside the after handler to check if it is a count-request or is there another way of solving this?

I don't see anything regarding this topic in the documentation.

Thanks,

Kind regards,

Robin